IDA中利用”Stack pointer”识别栈中数据

以32-bits ARM汇编为例,函数前四个形参通过r0-r3传递,第五形参通过栈传递,返回值在r0,返回地址在lr。

Options->General->Disassembly->Stack pointer

勾中”Stack pointer”之后,IDA的反汇编窗口地址列右侧多出一列,比如下面的000、008、02C、058:

--------------------------------------------------------------------------
008148DC var_58= -0x58
008148DC var_4C= -0x4C
008148DC ptr = -0x48
008148DC a4 = -0x44
008148DC s = -0x40
008148DC var_34= -0x34
008148DC varg_r2= -8
008148DC varg_r3= -4
008148DC
008148DC 000 0C 00 2D E9 STMFD SP!, {R2,R3}
008148E0 008 F0 4F 2D E9 STMFD SP!, {R4-R11,LR}
008148E4 02C 2C D0 4D E2 SUB SP, SP, #0x2C
008148E8 058 14 10 8D E5 STR R1, [SP,#0x58+a4]
008148EC 058 00 B0 A0 E1 MOV R11, R0
008148F0 058 00 10 A0 E3 MOV R1, #0 ; c
008148F4 058 18 00 8D E2 ADD R0, SP, #0x58+s ; s
008148F8 058 0A 20 A0 E3 MOV R2, #0xA ; n
008148FC 058 00 50 A0 E3 MOV R5, #0
00814900 058 50 80 9D E5 LDR R8, [SP,#0x58+varg_r2]
00814904 058 8F 16 E0 EB BL memset
00814908 058 54 30 8D E2 ADD R3, SP, #0x58+varg_r3
0081490C 058 24 30 8D E5 STR R3, [SP,#0x58+var_34]
00814910 058 01 96 A0 E3 MOV R9, #0x100000
00814914 058 05 A0 A0 E1 MOV R10, R5
00814918 058 10 50 8D E5 STR R5, [SP,#0x58+ptr]
0081491C 058 05 70 A0 E1 MOV R7, R5
00814920 058 05 40 A0 E1 MOV R4, R5
00814924 058 05 60 A0 E1 MOV R6, R5
00814928 058 D9 00 00 EA B loc_814C94
--------------------------------------------------------------------------

假设断在0x814928时想访问此函数的5个形参及RetAddr。

第一形参 r11
第二形参 *(int*)($sp+0x58-0x44)
第三形参 *(int*)($sp+0x58-0x8) 或 r8
第四形参 *(int*)($sp+0x58-0x4)
第五形参 *(int*)($sp+0x58)
RetAddr *(int*)($sp+0x58-0xc)

--------------------------------------------------------------------------
008148EC 058 00 B0 A0 E1 MOV R11, R0
--------------------------------------------------------------------------

0x8148ec处把r0赋给r11,0x814904处的memset()并没有破坏r11。一般如果未将r0压栈保存,那它赋给谁,谁就不会被破坏。

--------------------------------------------------------------------------
008148E8 058 14 10 8D E5 STR R1, [SP,#0x58+a4]
--------------------------------------------------------------------------

0x814928、0x8148e8处”Stack pointer”都是0x58,可以直接断定”[SP,#0x58+a4]”用于保存r1,即”*(int*)($sp+0x58-0x44)”。

--------------------------------------------------------------------------
008148DC varg_r2= -8
...
008148DC 000 0C 00 2D E9 STMFD SP!, {R2,R3}
...
00814900 058 50 80 9D E5 LDR R8, [SP,#0x58+varg_r2]
--------------------------------------------------------------------------

0x8148dc处的STMFD是伪指令显式方式,实际是压栈指令:

$ rasm2 -a arm -b 32 -o 0x8148dc -D "0c 00 2d e9"
0x008148dc 4 0c002de9 push {r2, r3}

先压R3、后压R2,或者说{R2,R3}表示在内存中从低址到高址依次是”R2 R3″。
0x8148dc处”Stack pointer”是0,0x814928处”Stack pointer”是0x58,0x814928处”$sp+0x58″对应0x8148dc处”$sp”,理解这点就很容易定位0x8148dc处压栈后的r2:

R2 R3 第五形参
^ ^ ^
| | |
| | +-0x8148dc处"$sp"/0x814928处"$sp+0x58"
| |
| +----0x814928处"$sp+0x58-0x4"
|
+-------0x8148e0处"$sp"/0x814928处"$sp+0x58-0x8"

IDA识别出栈中保存的第三形参,起名varg_r2。0x814900处的LDR将栈中保存的第三形参读入r8。

同理,IDA识别出栈中保存的第四形参,起名varg_r3,对应0x814928处”$sp+0x58-0x4″。

第五形参通过栈传递,函数入口(0x8148dc)处的$sp指向第五形参(假设存在的话),对应0x814928处”$sp+0x58″。

--------------------------------------------------------------------------
008148E0 008 F0 4F 2D E9 STMFD SP!, {R4-R11,LR}
--------------------------------------------------------------------------

函数返回地址对应函数入口(0x8148dc)处的lr寄存器,由于每个bl指令都会改写lr,一般情况下函数入口附近会立即将lr保存到栈中,0x8148e0处的代码正是如此。

R4 R5 R6 R7 R8 R9 R10 R11 LR R2 R3 第五形参
^ ^ ^
| | |
| | +-0x8148dc处"$sp"/0x814928处"$sp+0x58"
| |
| +-------0x8148e0处"$sp"/0x814928处"$sp+0x58-0x8"
|
+----------0x814928处"$sp+0x58-0xc"

示例中0x814928很靠前,可能不觉得显示”Stack pointer”有多大帮助,如果在一个很靠后的位置,就会发现显示”Stack pointer”有助于快速识别栈中数据。

 

 

Spread the word. Share this post!

Meet The Author

C/ASM程序员

Leave Comment