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

问题来源:

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

 

问题探索与解答:

  函数1:

1 function add:Integer;stdcall;
2 begin
3 Result :=10;
4 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:

1 function add(a: Integer):Integer;stdcall;
2 begin
3 Result := a + 10;
4 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

posted @ 2012-02-20 10:46  书中翱翔  阅读(1647)  评论(0编辑  收藏  举报