俺是新来的,发贴想混个邀请码,不知是否能如愿,无论如何希望对有些人有用(我在“安全编程论坛”中看到有人讨论相关问题)。
在此只是讨论VC6环境下C/C++ 32位函数调用约定,其它环境感兴趣的自己去发掘与验证。
函数调用约定(call convention)涉及函数参数如何传递, 谁平栈(还有命名,返回值等问题不在此讨论), 在VC6环境下有4种调用约定, 分别说明如下:
__cdecl 参数全部参过栈传递, 压栈顺序从右到左, 即最后一个参数先入栈; 调用者负责平栈。举例如下:
print("",1);的汇码如下:
004010B6 6A 01 push 1
004010B8 68 7C 20 42 00 push offset string "" (0042207c)
004010BD E8 7E 06 00 00 call printf (00401740)
004010C2 83 C4 08 add esp,8
最后一句 add esp,8就是平栈的,因为调用函数print时压入了两个DWORD大小的参数。
__stdcall 参数全部通过栈传递,压栈顺序从右到左; 被调用者负责平栈。
CreateFileA("\\\\.\\PHYSICALDRIVE0",GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,0,OPEN_EXISTING,0,0);的实现代码如下:
004010F4 6A 00 push 0
004010F6 6A 00 push 0
004010F8 6A 03 push 3
004010FA 6A 00 push 0
004010FC 6A 07 push 7
004010FE 68 00 00 00 80 push 80000000h
00401103 68 34 20 42 00 push offset string "\\\\.\\PHYSICALDRIVE0" (00422034)
00401108 FF 15 60 A1 42 00 call dword ptr [__imp__CreateFileA@28 (0042a160)]
调用CreateFileA的代码并没有去平栈,再看CreateFileA的部分代码如下
7C801A28 8B FF mov edi,edi
7C801A2A 55 push ebp
7C801A2B 8B EC mov ebp,esp
7C801A2D FF 75 08 push dword ptr [ebp+8]
7C801A30 E8 DF C6 00 00 call 7C80E114
7C801A35 85 C0 test eax,eax
7C801A37 74 1E je 7C801A57
7C801A39 FF 75 20 push dword ptr [ebp+20h]
7C801A3C FF 75 1C push dword ptr [ebp+1Ch]
7C801A3F FF 75 18 push dword ptr [ebp+18h]
7C801A42 FF 75 14 push dword ptr [ebp+14h]
7C801A45 FF 75 10 push dword ptr [ebp+10h]
7C801A48 FF 75 0C push dword ptr [ebp+0Ch]
7C801A4B FF 70 04 push dword ptr [eax+4]
7C801A4E E8 AD ED 00 00 call 7C810800
7C801A53 5D pop ebp
7C801A54 C2 1C 00 ret 1Ch
可以看到, 函数返回 ret 1ch, 函数返回时弹出了1Ch字节,因为它有7个DWORD大小的参数,7*4=1Ch
__fastcall, 前两个(如果有的话)DWORD或更小大小的参数通过REG传递,第一个在ecx中,第二个在edx中,如果还有更多的参数, 则通过栈传递, 同样是从右到左,由被调用者负责平栈
__thiscall, 类(包括class/struct/union)非静态成员函数默认的调用约定类型,C++中不能显式声明它,这种调用约定有点象是__fastcall与__stdcall的混合体, 隐含的this指针通过ecx传递,其它参数从右到左压栈, 被调用者负责平栈。
__cdecl类型的函数可以实现特殊的功能,即参数数量可变,如printf,其它调用类型不可以实现。
WINDOWS API大部声明为 WINAPI,实际上它是__stdcall,不过并非所有的WINDOWS API都是WINAPI调用约定的, 有许多__cdecl调用的。
__fastcall也是比较常见的,WINDOWS 内核的许多API是__fastcall类型,写驱动的要注意。
有一点是很少有资料提到,就是类的非静态成员调用约定不是一定是__thiscall, 你可以为一个非静态成员函数指定调用约定类型为__cdecl, __stdcall, 或__fastcall, 这种情况下,隐含的this指针总是函数的第一个参数, 对于__cdecl/__stdcall来说this指针被最后一个压入栈中,对于__fastcall来说, this指针仍然由ecx传递。所以对于类成员函数也可以实现可变参数,也可以强制要求将this指针通过栈传递。
- 标 题:VC6环境下C/C++ 32位函数调用约定
- 作 者:半道出家
- 时 间:2009-06-14 22:55:30
- 链 接:http://bbs.pediy.com/showthread.php?t=91541