编辑:
我已经接受了下面的答案,并且还添加了我自己的代码的最终修订版本。 希望它能向人们展示影子空间分配的实例,而不是更多的单词。
编辑2:我也设法find一个YouTubevideo(所有东西)注释中的调用约定PDF的链接,这些video在Shadow Space和Linux上的红色区域有一些有趣的花絮。 它可以在这里find: http : //www.agner.org/optimize/calling_conventions.pdf
原版的:
我已经在这里和所有的互联网上看了一些其他问题,但我似乎无法find调用64位Windows程序集中的子例程/ Windows API时分配“影子空间”的正确示例。
我的理解是这样的:
call callee
之前将sub rsp,<bytes here>
add rsp,<bytes here>
考虑到这一点,我尝试了这一点:
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
我的两个string是“Hello”和“World!\ n”。 这在pipe理打印“你好”之前崩溃。 我有一个怀疑,我正在做正确的…除了我应该清理了(我不知道如何)。
我究竟做错了什么? 我已经尝试了大小的组合,也试图在WinAPI调用之前“分配阴影空间”(我是否应该这样做?)。
应该指出的是, 当我根本不关心暗影空间的时候 ,这个效果非常好。 但是,我试图符合ABI,因为我的write
函数调用WinAPIs(因此,不是叶函数)。
必须在呼叫之前直接提供阴影空间。 想象一下阴影空间是从旧的stdcall / cdecl约定的遗物:对于WriteFile
你需要五个推动。 阴影空间代表最后四次推进(前四个参数)。 现在你需要四个寄存器,影子空间(只是空间,内容无关紧要)和影子空间之后的一个值(实际上是第一次推动)。 目前,调用者的返回地址( start
)位于WriteFile
将用作影子空间 – >崩溃的空间中。
您可以在函数write
为WinAPI函数( GetStdHandle
和WriteConsoleA
)创建一个新的阴影空间:
write: push rbp mov rbp, rsp sub rsp, (8 + 32) ; 5th argument of WriteConsoleA + Shadow space mov [rbp+16],rcx ; <-- use the Shadow space of `start` mov [rbp+24],rdx ; <-- and again mov rcx, -11 ; Get handle to StdOut call GetStdHandle mov rcx,rax ; hConsoleOutput mov rdx, [rbp+16] ; lpBuffer mov r8, [rbp+24] ; nNumberOfCharsToWrite mov r9,empty ; lpNumberOfCharsWritten mov qword [rsp+32],0 ; lpReserved - 5th argument directly behind the shadow space call WriteConsoleA leave ret
为了完整起见,我在这里张贴这个,因为这是我已经结束了。 这是完美的,据我所知,除了Windows的x64 ASM的UNWIND_INFO
/ Exception Handling要求之外,这是非常重要的。 评论也有希望准确。
编辑:
现在在Raymonds评论如下之后更新。 我删除了rbp
的保存,因为它不是必需的,把我的堆栈对齐比我想要的更远。
; Windows APIs ; GetStdHandle ; ------------ ; HANDLE WINAPI GetStdHandle( ; _In_ DWORD nStdHandle ; ); extern GetStdHandle ; WriteFile ; ------------ ; BOOL WINAPI WriteFile( ; _In_ HANDLE hFile, ; _In_ LPCVOID lpBuffer, ; _In_ DWORD nNumberOfBytesToWrite, ; _Out_opt_ LPDWORD lpNumberOfBytesWritten, ; _Inout_opt_ LPOVERLAPPED lpOverlapped ; ); extern WriteFile ; ExitProcess ; ----------- ; VOID WINAPI ExitProcess( ; _In_ UINT uExitCode ; ); extern ExitProcess global start section .data STD_OUTPUT_HANDLE equ -11 NULL equ 0 msg1 db "Hello ", 0 msg1.len equ $-msg1 msg2 db "World!", 10, 0 msg2.len equ $-msg2 section .bss empty resd 1 section .text start: sub rsp,0x28 ; Allocate 32 bytes of Shadow Space + align it to 16 bytes (8 byte return address already on stack, so 8 + 40 = 16*3) mov rcx,msg1 mov rdx,msg1.len call write mov rcx,msg2 mov rdx,msg2.len call write mov rcx,NULL call ExitProcess add rsp,0x28 ; Restore the stack pointer before exiting ret write: ; Allocate another 40 bytes of stack space (the return address makes 48 total). Its 32 ; bytes of Shadow Space for the WinAPI calls + 8 more bytes for the fifth argument ; to the WriteFile API call. sub rsp,0x28 mov [rsp+0x30],rcx ; Argument 1 is 48 bytes back in the stack (40 for Shadow Space above, 8 for return address) mov [rsp+0x38],rdx ; Argument 2 is just after Argument 1 mov rcx,STD_OUTPUT_HANDLE ; Get handle to StdOut call GetStdHandle mov rcx,rax ; hFile mov rdx,[rsp+0x30] ; lpBuffer mov r8,[rsp+0x38] ; nNumberOfBytesToWrite mov r9,empty ; lpNumberOfBytesWritten ; Move the 5th argument directly behind the Shadow Space mov qword [rsp+0x20],0 ; lpOverlapped, Argument 5 (just after the Shadow Space 32 bytes back) call WriteFile add rsp,0x28 ; Restore the stack pointer (remove the Shadow Space) ret
结果是…: