一、32位下的调用约定
1、__cdecl


外平栈,从右至左入栈
2、__stdcall
__stdcall是windows API的默认调用约定

内平栈,从右至左入栈
3、__fastcall


内平栈,从右至左入栈,但前两个参数被分别放在了ecx,edx寄存器中
二、64位下的调用约定
64位下只有一种调用约定,即__fastcall
但64位下的__fastcall与32位下的__fastcall有不小差异
64位下的__fastcall是外平栈,从右至左入栈
1、64位下前四个参数分别保存在rcx,rdx,r8,r9寄存器中
2、就算是寄存器传参,64位下在调用函数前一样分配栈空间(存疑)
3、对于不定长参数,调用前至少分配四个参数的栈空间
我们构造不定长函数test:


我只传递了一个参数,但它需要保存rcx,rdx,r8,r9四个寄存器,同理,如果我们在其内部调用printf函数,需要在调用前至少分配4个参数的栈空间


4、push,pop指令仅仅保存非易变寄存器
易变寄存器包括:rax,rcx,rdx,r8,r9,r10,r11
用IDA64随便打开一个64位文件
alt + t搜索push ,可以发现,几乎所有的push指令都属于非易变寄存器,且根本没有push一个立即数的情况

5、64位下,通常使用rsp寻址(一次性分配局部变量和参数空间)
32位下,我们经常看到[esp + xx]或[ebp + xx]的字样
但在64位下,使用的基本都是rsp
所以,这也导致64位下一次性分配局部变量和参数空间
64位下的fastcall是外平栈,但我们常常见不到外平栈的身影,这就是因为64位一次性分配局部变量和参数空间
这点,和32位大不一样
6、64位下的call的栈地址一般都是0x10对齐
7、栈帧
程序进入函数的时候会push rbp,我们把两个push rbp之间的栈称为栈帧
或者在栈上两个保存函数返回地址的区段叫做栈帧


这两个r之间就是栈帧
其栈帧分析如下

rsp + 28的位置只是为了对齐
本来我们在IDA上看到的反汇编是这样的: rsp + 0x38h + var_xx 之类的


所以说IDA喜欢以0x38h的那个ret上做基准引用
8、叶函数
叶函数的概念

像下面的main函数就算叶函数

非叶函数至少分配4个参数的栈空间
上面的main函数也只是分配了0x10个字节的空间(0x10是为了满足对齐)
