593 字
2 分钟
c03
函数调用约定(calling convention)
调用者与被调用者关于栈上参数传递和清理得一个约定
调用得约定
- 参数传递
- 栈平衡(参数占用的栈空间清理)
调用约定类型
- cdecl (C 语言默认)
- stdcall (Windows API, 内核驱动)
- fastcall (x64)
- thiscall (C++)
- nakedcall (底层HOOK)
调用约定:
cdecl 调用约定 是C语言缺省得调用约定 他的定义域语法是
int func(int, int);int __cdecl func(int, int);该调用遵循下面的调用规则:
- 还原入栈顺序: 从右到左
- 还原栈者: 调用者修改栈,所以可以支持变参函数,因为可以恢复平衡
stdcall 调用约定:
int __stdcall func(int, int);该调用遵循以下规则:
- 参数入栈顺序: 从右向左压入栈
- 还原栈者:被调用函数自身修改栈
fastcall 调用约定:
int __fastcall func(int, int, int)该调用遵循下面规则:
- 还原入栈顺序: 函数得第一个和第二个参数通过ecx 和 edx 传递,剩余参数从右到左入栈
- 还原栈者:被调用者修改栈
调用约定例子:

x64 和 x86 的fastcall 的区别
- 比ecx(rcx),dex(rdx) 寄存器多了两个r8,r9
- 参数入栈,会对齐到8个字节
- 函数的前4个参数存放到了rcx,rdx,r8,r9 四个寄存器
- 但栈上也会预留4个空间
- 调用者来负责栈的平衡
- 栈整体要被16整除
- 局部变量空间的分配和初始化由调用者完成
缓冲区溢出-栈溢出
#include <stdio.h>
int main(int argc, char* argv[]){ char x, y, z; int i; int a[16]; for(i = 0; i <= 16; i++) { a[i] = 0; printf("\n"); } return 0;}假设该程序得内存如图所示

note: ret 返回地址, ebp 栈帧指针, esp 栈顶指针
a 数组发生溢出将恰好占据变量 i 所在地址 此时 a[i] = 0 就是令 i = 0 导致死循环发生,(虽然现代编译器可能会对于这种情况做出预警和优化)
观察如下代码;
void msg_display(char* buf){ char msg[200]; strcpy(msg, buf); printf("%s\n", msg);}假设该程序内存分布如下:

此时如果多拷贝8个Byte得数据那么,将会把ret 的地址覆盖, 此时如果将最后4个字节设计成一段其他代码的地址那么程序将不会停止而是直接跳转到这段地址继续执行,这就是栈溢出漏洞的基本原理
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐










