CHNA 发表于 2012-12-15 18:15:17

ret问题

1. __cdecl
    这个是Visual C++中最最常用的调用约定,但是在代码里并不常见。为什么呢?原因就是它太常用了,VC把它作为了默认值,也就是说一个函数如果不声明任何的调用约定,那这个函数用的就是__cdecl。下面两句是等同的。
void f(int x);
void __cdecl f(int x);
现在让我们看看编译器到底怎么实现这种调用约定的。假设我们现在编译下面这段代码:

// 调用函数f1 f1(1, 2, 3, 4);// 函数f1的实现 int __cdecl f1(int a, int b, int c, int d){return a + b + c + d;}


编译后的反汇编是:
;调用函数f1,4个参数分别是1,2,3和4
00401093 push 4 ;参数从右到左开始压栈,先压最后一个
00401095 push 3 ;第3个参数压栈
00401097 push 2 ;第2个参数压栈
00401099 push 1 ;第1个参数压栈
0040109B call f1 (401005h) ;调用函数f1
004010A0 add esp,10h ;清除栈上的4个参数

;函数f1的实现
push ebp ;保存寄存器ebp
mov ebp,esp ;将当前栈指针赋值给ebp
mov eax,dword ptr ;eax为参数a
add eax,dword ptr ;eax = eax + 参数b
add eax,dword ptr ;eax = eax + 参数c
add eax,dword ptr ;eax = eax + 参数d
pop ebp ;恢复寄存器ebp的值
ret ;函数返回,返回值是eax

可以看到清除参数的工作是由caller(调用者,就是调用函数f1的地方)来负责。因为我们一共有4个int的参数,每个int是 4个byte,一共16个byte,换算成16进制是10h,所以上面粗体的反汇编(add esp,10h),通过直接把esp加10h来清除4个参数。(esp是指向栈顶的寄存器)
如果上面的反汇编有困难的话,可以记住这么一句话:__cdecl是由调用者来清除栈上的参数。


问题
;;;;
1.CALL是把下一条指令IP入栈
2.;调用函数f1,4个参数分别是1,2,3和4
00401093 push 4 ;参数从右到左开始压栈,先压最后一个
00401095 push 3 ;第3个参数压栈
00401097 push 2 ;第2个参数压栈
00401099 push 1 ;第1个参数压栈
0040109B call f1 (401005h) ;调用函数f1
3.ret 返回的不是   ;第1个参数压栈的地址吗?????????

s0512 发表于 2012-12-15 18:37:37

Call指令是先将EIP压栈,然后才JMP到 F1去的,所以,ret返回的是add esp,10的地址,欢迎纠错

阔怀 发表于 2015-8-29 10:28:35

{:1_1:}
页: [1]
查看完整版本: ret问题