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

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

假设断在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)

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

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

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

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

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

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

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

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

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

 

 

源链接

Hacking more

...