本日学习记录
参考链接:https://xuanxuanblingbling.github.io/iot/2020/07/08/stm32/
这是一个STM32F103C8T6 MCU密码锁 它具有4个按键,分别为1, 2, 3, 4. 分别对应GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4 flag1格式为SCTF{正确的按键密码} 输入正确的密码, 它将通过串口(PA9–TX)发送flag2
题目附件给出了一个Intel hex文件,并且给出了芯片信息我们可以确定程序的内存布局和外设寄存器与内存的对应。而逆向的关键就是读懂程序代码的含义,接下来我们将逐步分析这个hex文件。
Intel hex文件格式由纯文本构成,其中包含了程序的加载地址和程序入口地址等信息,读懂这些信息可以帮助我们快速定位程序的起始入口而不用在ida中进行配置。
我们可以使用文本编辑器打开题目附件,其中关键信息如下所示:
:020000040800F2
...
...
:04000005080000ED02
:00000001FF
0x08000000
0x080000ED
:00000001FF
结尾查找芯片手册的网站:https://www.alldatasheet.com/ 在里面我们可以找到STM32F103C8T6
的手册,第一页发现我们需要的一些信息
31页的Memory Map可以让我们更加直观的了解到内存的详细布局
综上所述我们得到了程序的完整内存布局信息:
经过刚才的分析我们了解了程序的内存布局,其中Flash段除了包含代码,还有中断向量表。Periphers段中的寄存器是我们在逆向过程中需要对齐有大体了解。而对于hex文件的分析我们了解到除了加载地址和入口地址,其他的所有内容都不在hex文件中,所以我们需要手动配置这些内存布局信息来告诉IDA怎么识别。 打开ida工具,根据刚才的手册中我们可以查到芯片是arm32 Armv7-M架构,如下图所示进行配置选择然后单击ok
可以看到已经能识别出一部分函数,其中start函数的地址与我们分析hex文件结构时找到的程序入口地址相同。
如果hex文件中没有给出入口地址信息我们也可以通过寻找RESET中断处理函数来确定程序入口函数。其中RESET中断函数的地址可以在STM32中文参考手册V10.pdf中找到相关信息
参考 STM32 中断向量表的位置 、重定向 中我们可以了解到在中断向量表中RESET的地址0x8000004
的地址是固定的,而可变的是程序的加载地址。我们跳转到0x8000004
这个地址上,按键盘D键将上面的数据分成4字节形式找到reset的地址为0x8000101
跳转到RESET中断处理函数,存在两次跳转。第一次跳转到nullsub_1上并将下一条指令地址放入LR寄存器,nullsub_1函数的作用是跳回LR寄存器中的地址,所以第一跳没有意义。第二次跳转就是我们的start地址,所以完全可以利用此方式定位到程序的入口地址。
一直跟着入口地址走就能找到这个程序的main函数所在,但是进来之后可以发现左边这一大片红色的标记,观察这些红色区域其实就是IDA没有识别的地址,也就是我们之前分析内存布局需要添加的内存段。
我们在IDA中新建Segment,如下图所示:
这时只要我们再次点击F5即可让这些红色的标识变成正常识别的内存了
使用IDApython恢复程序入口地址之前的信息
for i in range(0x8000000,0x80000eb,1):
del_items(i)
for i in range(0x8000000,0x80000eb,4):
create_dword(i)
修复完成后我们观察这里面的地址,会发现有很多重复的地址0x800016D
,跟进去会发现这些地址中没有定义函数功能。继续查看中断向量表会发现下面几个不同的内存地址
参考 STM32中文参考手册V10.pdf中的内容我们可以查找到这些就是EXTI的中断处理函数地址
跟进这些函数地址会发现IDA并没有将其识别为函数,所以我们先在函数起始地址处按P键,然后进行反汇编即可看到这些中断处理函数,这里我以EXTI_4的中断处理函数为例来简单介绍一下这些中断处理函数的功能。
int EXTI_4()
{
int result; // r0 EXTI_LINE = 16;
switch ( sum )
{
case 1:
unk_20000006 = 116;
return sum++ + 1;
case 2:
unk_20000010 = 95;
return sum++ + 1;
case 4:
unk_2000000E = unk_20000001;
return sum++ + 1;
default:
result = 0;
sum = 0;
break;
}
return result;
}
程序一开始先设置了中断/事件线,EXTI_4的中断/事件线为0x10,然后使用一块内存(这里记作sum)来作为累加数的保存位置。我们可以看到当sum中的值为1、2、4时sum的值会+1,如果不是的话则会重新开始。所以我们可以判断出1、2、4就是EXTI_4出现在密码中的位数,同理其他的三个按钮也是一样的,通过这些顺序我们可以得到最终的flag为flag{1442413}
,并且我们可以发现在main函数中程序先模拟输入了一次密码,通过将上面的值与EXTI_LINE进行对应也能得到flag值。
经过了上一题的入门接下来我们再来一道题目练习一下,打开IDA类型选择小段arm32,架构选择ARMv7-M架构。
接下来设置程序的加载地址和读取地址设置为0x8000000
,加载地址是指IDA加载的分析地址是多少,而Input File是指固件要从什么位置开始加载,设置好以后我们点击OK完成设置。
进入以后会发现IDA没有识别出任何函数,不用担心我们可以通过定位reset的中断处理来找到main函数的位置。将0x8000004
地址处的字节变成4字节,得到reset中断处理函数地址0x8000101
。
可以发现地址的结尾是奇数位,在arm中这代表了thumb模式。我们可以在0x8000101
地址按下C键即可生成代码
一直跟着程序流走我们就能找到main函数所在位置sub_80003C0
,并且在其中发现了需要逆向的函数sub_8000314
_BYTE *sub_8000314()
{
_BYTE *v0; // r4
char *v1; // r5
int v2; // r6
char v3; // t1 v0 = (_BYTE *)sub_80003F0(48);
v1 = &byte_8000344;
v2 = 0;
while ( v2++ != 0 )
{
v3 = *v1++;
*v0++ = (v3 ^ 0x1E) + 3;
sub_8000124(v1);
}
return v0;
}
写出解题脚本即可获得flag值
li = [0x7D, 0x77, 0x40, 0x7A, 0x66, 0x30, 0x2A, 0x2F, 0x28, 0x40, 0x7E, 0x30, 0x33, 0x34, 0x2C, 0x2E, 0x2B, 0x28, 0x34, 0x30, 0x30, 0x7C, 0x41, 0x34, 0x28, 0x33, 0x7E, 0x30, 0x34, 0x33, 0x33, 0x30, 0x7E, 0x2F, 0x31, 0x2A, 0x41, 0x7F, 0x2F, 0x28, 0x2E, 0x64]
print(''.join(chr((i ^ 0x1E) + 3) for i in li))
本文作者:合天网安实验室
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/182315.html