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
宏。
1条答案
按热度按时间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
。