x64位汇编学习(二)、调用约定与栈帧


一、32位下的调用约定

1、__cdecl

 
0
 
0
外平栈,从右至左入栈

2、__stdcall

__stdcall是windows API的默认调用约定
0
内平栈,从右至左入栈

3、__fastcall

0
 
0
内平栈,从右至左入栈,但前两个参数被分别放在了ecx,edx寄存器中
 

二、64位下的调用约定

64位下只有一种调用约定,即__fastcall
但64位下的__fastcall与32位下的__fastcall有不小差异
64位下的__fastcall是外平栈,从右至左入栈

1、64位下前四个参数分别保存在rcx,rdx,r8,r9寄存器中

2、就算是寄存器传参,64位下在调用函数前一样分配栈空间(存疑)

3、对于不定长参数,调用前至少分配四个参数的栈空间

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

4、push,pop指令仅仅保存非易变寄存器

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

5、64位下,通常使用rsp寻址(一次性分配局部变量和参数空间)

32位下,我们经常看到[esp + xx]或[ebp + xx]的字样
但在64位下,使用的基本都是rsp
所以,这也导致64位下一次性分配局部变量和参数空间
64位下的fastcall是外平栈,但我们常常见不到外平栈的身影,这就是因为64位一次性分配局部变量和参数空间
这点,和32位大不一样
 

6、64位下的call的栈地址一般都是0x10对齐

 

7、栈帧

程序进入函数的时候会push rbp,我们把两个push rbp之间的栈称为栈帧
或者在栈上两个保存函数返回地址的区段叫做栈帧
0
0
这两个r之间就是栈帧
其栈帧分析如下
0
rsp + 28的位置只是为了对齐
本来我们在IDA上看到的反汇编是这样的: rsp + 0x38h + var_xx 之类的
0
0
所以说IDA喜欢以0x38h的那个ret上做基准引用

8、叶函数

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM