编辑:
我已经接受了下面一个答案并添加了我的最终版本的代码。希望它展示给人们实际的影子空间分配示例,而不是更多的文字。
编辑2: 我还找到了一个链接到通话约定PDF的链接,在YouTube视频(所有东西中)的注释中有一些关于Linux上的影子空间和红色区域的有趣信息。它可以在这里找到:http://www.agner.org/optimize/calling_conventions.pdf
原始:
我查看了这里和互联网上的几个其他问题,但似乎找不到在64位Windows汇编中调用子例程/Windows API时分配“Shadow Space”的适当示例。
我的理解是:
- 调用者应该在
call callee
之前进行sub rsp,<bytes here>
- 如果需要保存寄存器(或者不需要保存寄存器,则使用局部变量),则被调用者应该使用它
- 调用者清理它,例如:
add rsp,<bytes here>
- 分配的数量应该对齐到32字节
考虑到这一点,这是我尝试的内容:
section .text
start:
sub rsp,0x20 ; <---- Allocate 32 bytes of "Shadow space"
mov rcx,msg1
mov rdx,msg1.len
call write
add rsp,0x20
mov rcx,NULL
call ExitProcess
ret
write:
mov [rsp+0x08],rcx ; <-- use the Shadow space
mov [rsp+0x10],rdx ; <-- and again
mov rcx,STD_OUTPUT_HANDLE ; Get handle to StdOut
call GetStdHandle
mov rcx,rax ; hConsoleOutput
mov rdx,[rsp+0x08] ; lpBuffer
mov r8,[rsp+0x10] ; nNumberOfCharsToWrite
mov r9,empty ; lpNumberOfCharsWritten
push NULL ; lpReserved
call WriteConsoleA
ret
我的两个字符串是 "Hello " 和 "World!\n"。这样做可以在崩溃之前打印出 "Hello "。我怀疑我做得很正确...除了我应该以某种方式清理(但我不确定如何)。
我做错了什么?我已经尝试过一些组合大小,也尝试过在调用 WinAPI 之前 "分配 Shadow Space"(我应该这样做吗?)。
应该注意的是,当我根本不关心 Shadow Space 时,这个方法完全可以正常工作。然而,由于我的 write
函数调用 WinAPI(因此不是叶子函数),所以我正在尝试遵守 ABI。