您的位置:首页 > 其它

汇编之函数调用(CALL)分析

2012-02-20 10:46 218 查看
问题来源:

  stdcall类型函数调用,参数倒序入栈,第一个参数最后入栈,函数内部访问第一个参数时经常看到用[ebp + $08]表示,ebp通常来自esp,即函数首部经常看到的push ebp, mov ebp, esp,为什么栈顶偏移$08才是第一个参数呢?按道理说第一个参数最后入栈,栈顶不应该就是第一个参数么?事实并非如此...

问题探索与解答:

  函数1:

function add:Integer;stdcall;
begin
Result :=10;
end;


调用该函数,观察汇编代码:(注意EIP寄存器变化)

0044D944 E8F3FFFFFF       call add      ;此时EIP=0044D944
0044D949 C3               ret

跟进函数CALL

Unit1.pas.27: Result :=10;
0044D93C B80A000000       mov eax,$0000000a
0044D941 C3               ret

此函数无参,跟进发现栈顶为 0044D949,这正是执行完CALL之后的下一句要执行的代码


  函数2:

function add(a: Integer):Integer;stdcall;
begin
Result := a + 10;
end;


调用该函数,观察汇编代码:(注意EIP寄存器变化及栈内栈顶到第一参数间的数值)

Unit1.pas.45: s := add(12);
0044D94C 6A0C             push $0c
0044D94E E8E9FFFFFF       call add ;此时EIP=0044D94E,跟进
0044D953 C3               ret

跟进CALL

Unit1.pas.31: begin
0044D93C 55               push ebp
0044D93D 8BEC             mov ebp,esp
Unit1.pas.32: Result := a + 10;
0044D93F 8B4508           mov eax,[ebp+$08];执行到此行,观察
0044D942 83C00A           add eax,$0a
Unit1.pas.33: end;
0044D945 5D               pop ebp
0044D946 C20400           ret $0004

栈顶是push ebp保存进EBP当时的值,结束前恢复现场用
[ebp+$08]是访问的第一个参数
那[ebp+$04]是何作用呢,又是何时进到栈里的呢?
观察发现[ebp+$04]= 0044D953,是CALL返回后要执行的下一句


问题解答:
  因此,在调用函数的时候,程序会偷偷的将执行CALL之后的下一条语句的地址压入栈中,同时呢,一般带有参数的程序,进入程序内部后会首先保存当时的EBP,这就造成了栈内多出两个数值,而这两个又都是在参数入栈之后发生的,因此第一个参数的地址就变成了[EBP+$08]。

  明眼人也发现了,函数1返回时用的是ret直接返回,而函数2返回时是ret $0004, 这又是何意呢?原来ret时就自动把CALL之后的下一条语句的地址出栈了,后面的$0004意味着出栈后ESP这个栈顶指针要进行的偏移,因为你传进的这些参数在函数返回后它们就没必要存在栈里面了,所以移动下栈顶指针就可以。

ret $0004= ESP + $0004
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: