函数调用约定
Calling Conventions For Reverse Engineering
什么是函数调用约定
函数调用约定(Calling Convention)是不同的编程语言或系统平台为函数调用所约定的规范。
主要决定了函数调用过程中参数如何传递、返回值如何传递、寄存器如何使用和保护,以及栈帧的管理方式。
- prologue
prologue
指的是函数开始部分的代码,用于准备函数的执行环境,设置栈空间和保存调用环境
保存ebp:保存调用者的ebp,以便在
epilog
中可以恢复。设置栈指针:将栈指针调整为当前函数的栈框架,为局部变量预留空间。
分配栈空间:为函数的局部变量和栈上参数分配所需空间。
push ebp |
在这个例子中:
push ebp
:将调用者的基址寄存器ebp
压入栈,以便在函数返回时恢复。mov ebp, esp
:将栈指针esp
复制到基址寄存器ebp
,创建一个新的栈帧基址。sub esp, <stack_space>
:从栈指针中减去一段空间,留给函数的局部变量。
- epilogue
epilogue
通常指的是函数的结束部分代码,负责清理栈空间和恢复调用之前的状态。
释放栈空间:释放函数使用的栈空间。
恢复寄存器:将
prolog
中保存的寄存器恢复到调用之前的状态。返回:通过指令如
ret
返回到调用该函数的地址。
mov esp, ebp |
mov esp, ebp
将栈指针恢复到函数调用前的状态pop ebp
从栈中恢复基址寄存器的原始值,ret
返回到调用者位置。
- 调用方(Caller)和被调用方(Callee)
- 调用方(Caller):是指发起函数调用的那一方。调用方负责将参数传递给函数、执行调用指令,并在函数返回后继续执行剩余代码。
- 被调用方(Callee):是指被调用的函数本身。被调用方负责根据传入的参数执行操作,并返回计算结果给调用方。
例如:
int add(int a, int b) { |
main
函数是调用方,因为它调用了add
函数。
add
函数是被调用方,因为它是被main
调用的函数。
初学
cdel
cdecl
(C Declaration)是C语言默认的函数调用约定,函数参数从右到左入栈,调用方负责清理栈,参数全部储存在栈上,返回值通过寄存器传递。
简单c语言加法,
int add(int a, int b) { |
函数调用
; 调用 add(5, 10) |
函数内部
add: |
stdcall
stdcall
的传参方式类似于cdel,都是从右到左,且参数都·储存在栈中。但与cdecl
不同的是,在stdcall
中,被调用函数负责栈的清理。
int __stdcall add(int a, int b) { |
函数调用
; 调用 add(5, 10) |
函数内部
add: |
fastcall
函数参数从左到右入栈,头两个参数储存在ecx和edx上,其余的按照从右到左的方式储存在栈上,调用方清理栈。
int __fastcall add(int a, int b,int c,int d) { |
函数调用
; 调用方代码 |
函数内部
; add 函数内部代码 |
清理栈
; 调用方代码继续 |
msfastcall
类似于fastcall
,但可以多储存俩个参数到寄存器内,共计4个参数,一次放在rcx,rdx,r8,r9