assembly x64快速调用调用程序堆栈管理

kzipqqlq  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(196)

MSDN说
在RCX、RDX、R8和R9中,最左边四个位置的整数值参数分别按从左到右的顺序传递。调用堆栈上的空间被分配为被调用方保存这些寄存器的影子存储区。其余参数按从右到左的顺序推入堆栈。
所以,我尝试调用CreateFileW函数,这是我的代码:

sub rsp, 20h             ; Allocate 32 bytes because 4 registers 8 byte each
mov rcx, offset filename ; lpFileName
mov rdx, GENERIC_READ or GENERIC_WRITE ; dwDesiredAccess
mov r8, FILE_SHARE_DELETE              ; dwShareMode
xor r9, r9                             ; LpSecurityAttributes
          ;__And right-to-left order remaining arguments__
push 0 ; hTemplateFile
push FILE_ATTRIBUTE_NORMAL             ;dwFlagsAndAttributes
push CREATE_ALWAYS                     ; dwCreationDisposition
call CreateFileW

它可以进行汇编,但无法工作,win64dbg导致下一个错误:
00000057(错误_无效_参数)
参数是100%正确的,因为它与Invoke宏一起工作,只有生成的代码不同。

mov rcx,src.403000    ;name          
mov edx,C0000000      ;GENERIC_READ or GENERIC_WRITE                  
mov r8d,4             ;FILE_SHARE_DELETE                  
xor r9d,r9d           ;0                  
mov qword ptr ss:[rbp-20],2; ;CREATE_ALWAYS           
mov qword ptr ss:[rbp-18],80 ;FILE_ATTRIBUTE_NORMAL          
and qword ptr ss:[rbp-10],0  ;0           
call qword ptr ds:[<&CreateFileW>]

因此,我的问题是,为什么它使用RBP寄存器而不是push,并且不为“影子存储”分配32个字节?

备注

由于微软的64位MASM不再有invoke指令,我使用的是一个俄罗斯的MASM64 SDK项目,它有一个invoke宏。

rt4zxlrg

rt4zxlrg1#

如果你想要push参数,你必须在 * sub rsp,20h之前做它。(这样做效果不好,因为通常整个函数只需要一个sub rsp,20h,而不是每次调用都有一个)。并且您必须正确计数,才能在最后一个push之后使RSP%16 == 0。通常,您不希望更改RSP,除非在函数序言/中在Windows x64中的尾声,除了alloca类型的东西。

堆栈参数位于影子空间之上 *,所以如果函数要将其寄存器参数转储到影子空间中的“home space”,那么它将拥有一个连续的参数数组。(像printf这样的变量函数实际上会这样做;正常功能,除非是调试版本。)

使用调试器查看call之前的堆栈内存(RSP之上)的内容,以了解您的方式与正常方式(将mov存储到阴影空间之上的堆栈参数空间)的区别。
请注意,sub rsp,20h不够大,不足以为shadow space * 和 * stack args保留空间,因此您显示的“invoke macro”代码必须在此函数的开头保留更多空间。
为什么它使用RBP寄存器而不是push,并且不为“影子存储”分配32字节?
它使用RBP,因为如果你已经花费了指令将RBP设置为帧指针,那么这是访问堆栈空间的一种正常方式。
如果能更清楚、更容易地了解寻址模式相对于RSP的情况,比如[rsp+20h]访问影子空间上方的第一个槽,在那里你想存储第一个堆栈参数。
如果你已经分配了一个可变的空间,所以从RBP到阴影空间上方的距离是未知的,那么这是必要的,但是你可以这样做,只是为了清晰和容易地获得正确的偏移量。但是如果编译器或聪明的宏可以计算正确的偏移量,并且你已经花费了指令来设置RBP作为一个帧指针,那么使用它会稍微更有效一些,因为它在机器代码中节省了一个字节([rsp+constant]需要一个SIB字节来编码)。
常规的MASM在64位模式下没有invoke。我不知道你在使用什么。也许你需要手动保留堆栈空间,或者它在函数的顶部为你做了这件事,但你忽略了这一点。Michael Petch说MASM 64附带了一个invoke宏,它会在你的代码中添加一个sub rsp

相关问题