一、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是為了滿足對齊)
