为了加深对call、retn的理解,我今天用delphi写了一个小程序,在OD中跟踪程序执行call及retn时堆栈的变化。
程序很简单:
对应的汇编代码:
004A4D28 . B8 3C4D4A00 mov eax, 004A4D3C ; ASCII "bbbbbbbb"
004A4D2D . E8 9EFFFFFF call 004A4CD0
004A4D32 . C3 retn
004A4CD0 /$ 55 push ebp
...........
004A4D21 . 59 pop ecx
004A4D22 . 59 pop ecx
004A4D23 . 5D pop ebp
004A4D24 . C3 retn
执行到004A4D2D,esp=0013F630,F7进去后到达004A4CD0 也就是summ的第一句代码,esp=0013F62C,也就是说执行call xx系统进行了一个压栈操作!执行到 004A4D24时,esp=0013F62C(和执行到第一句代码时相等),执行了retn后,esp=0013F630,也就是说retn进行了一个出栈的动作!
另外,函数执行完毕的前后,不管是stdcall还是fastcall,都不保证寄存器不被修改!
总结:
1、执行到函数内的第一个语句时的esp值和到最后一个语句(也就是retn)的esp值是一样的:
004A4CD0 /$ 55 push ebp ;esp=0013F628
004A4CD1 |. 8BEC mov ebp, esp
004A4CD3 |. 5D pop ebp
004A4CD4 /. C2 0400 retn 4 ;esp=0013F628
2、retn 4表示pop 2次。
3、执行到call xx时的esp值与执行完xx时的esp值不一定相等,对stdcall的尤其是这样。
粗浅分析,不当之处还请指出。