记录下第二次思考过程,终于看明白了,不需要死记。
64位系统下调用函数先抬栈,然后默认需要4个寄存器rcx,rdx,r8,r9,每个寄存器8字节,之后call进去把寄存器的值放到栈上。
所以说问题来了,先抬栈应该抬多少?4个寄存器每个8字节,一共32字节,call有个返回地址占用8字节,但是栈必须按照16字节分配,前面4个寄存器32字节已经对齐了16字节,但是返回地址只有8字节没有对齐,所以还需要多分配8字节。最终得到答案,先抬栈32+8=40=0x28,之后加上返回地址8字节=48字节就对齐了。
那么当参数超过4个的时候应该抬栈多少呢?很简单,4个以上的参数只能通过栈传递,栈每次必须分配16字节,所以假设有5个参数需要传递,32字节(前4个参数)+16字节(第5个参数)+8字节(为了与返回地址对齐)=56字节=0x38。另外函数调用完之后最好xor eax, eax清空eax保持好习惯。
分割线下方是第一次的学习过程
64位寄存器占用8字节(RAX,RCX…)
参数传递顺序如下:
第一个参数:RCX
第二个参数:RDX
第三个参数:R8
第四个参数:R9
第五个参数:rsp+20
第六个参数:rsp+28
第七个参数:rsp+30
...以此类推,目的是保持栈对齐16字节(记结论好了)
栈调整量=32字节影子空间+8字节返回地址+额外参数量*16字节 转换为16进制
示例调用MessageBoxExA
int MessageBoxExA(
[in, optional] HWND hWnd,
[in, optional] LPCSTR lpText,
[in, optional] LPCSTR lpCaption,
[in] UINT uType,
[in] WORD wLanguageId
);
32+8+1*16=56 = 38(hex)
sub rsp,栈调整量
mov rcx,参数
mov rdx,参数
mov r8,参数
mov r9,参数
mov [rsp+20],
call MessageBoxExA
add rsp,栈调整量