Skip to content

调用约定

在逆向工程中,调用约定(Calling Convention) 是一套关于函数如何“沟通”的协议。它规定了参数如何传递、由谁清理栈空间以及返回值如何处理。

在 32 位(x86)环境下,理解这些约定是你进行栈帧回溯和参数分析的基础。


1. 调用约定的三大核心任务

  1. 参数传递顺序:是从左往右压栈,还是从右往左?是通过栈传递,还是通过寄存器(如 ECX, EDX)?

  2. 栈平衡负责方:函数返回后,是由调用者(Caller)清理栈上的参数,还是由被调用者(Callee)自己清理?

  3. 名字修饰(Name Mangling):编译器在目标文件中如何重命名函数(例如加上 @ 或 _)。


2. 常见调用约定详解

以下是你在 32 位逆向中最常遇到的四种约定:

① __cdecl (C Declaration)

这是 C/C++ 程序的默认约定,也是最常见的一种。

  • 参数传递:从右向左压入栈中。

  • 栈平衡:调用者(Caller)清理。在 CALL 指令之后,你会看到类似 add esp, 8 的指令。

  • 特点:支持可变参数(如 printf),因为只有调用者知道到底压入了多少个参数。

② __stdcall (Standard Call)

这是 Windows API(Win32 API)的标准约定。

  • 参数传递:从右向左压入栈中。

  • 栈平衡:被调用者(Callee)清理。函数结尾通常是 ret 8(表示返回并弹出 8 字节的参数空间)。

  • 特点:生成的代码体积比 __cdecl 小,因为清理代码只在函数末尾出现一次。

③ __fastcall

顾名思义,为了提高速度。

  • 参数传递:前两个参数分别放入 ECX 和 EDX,剩下的参数再从右往左压栈。

  • 栈平衡:被调用者清理。

  • 特点:减少了内存访问(栈操作),速度最快。在逆向时,如果你看到函数开头就直接用 ECX 或 EDX 而没看它们被赋值,那通常就是 __fastcall。

④ __thiscall

专门用于 C++ 类成员函数。

  • 参数传递:参数从右往左压栈,但 this 指针 必须存放在 ECX 中。

  • 栈平衡:被调用者清理。

  • 特点:在逆向 C++ 程序(如游戏逆向)时,看到 mov ecx, [某个地址] 后跟一个 CALL,这通常是在调用一个类方法。


3. 综合对比表

约定 参数顺序 栈平衡负责方 常用于 逆向识别标志
__cdecl 右 -> 左 调用者 几乎所有 C 程序 CALL 后有 add esp, XX
__stdcall 右 -> 左 被调用者 Windows 系统 API 函数结尾是 ret XX
__fastcall 寄存器 + 栈 被调用者 性能敏感函数 开头使用 ECX/EDX 传参
__thiscall 右 -> 左 被调用者 C++ 类成员函数 ECX 存放 this 指针

4. 逆向中的实战意义

当你正在进行栈帧回溯时:

  • 如果是 __cdecl,你可以在 CALL 指令后通过 add esp 的数值确定参数个数。

  • 如果是 __stdcall,你必须进入函数内部查看最后的 ret 指令才能确定参数个数。

  • 如果是 __fastcall,除了看栈,你还得回溯 ECX 和 EDX 在调用前被赋了什么值。