阴影空间示例 [英] Shadow space example
问题描述
我在下面接受了一个答案,并在代码的最终修订版中添加了自己的答案.希望它能向人们展示阴影空间分配的实际示例,而不是更多的单词.
I have accepted an answer below and also added my own with my final revision of the code. Hopefully it shows people actual examples of Shadow Space allocation rather than more words.
我还设法在(全部)YouTube视频的注释中找到了调用约定PDF的链接,该链接在Shadow Space和Linux的Red Zone上有一些有趣的花絮.可以在这里找到: http://www.agner.org/optimize/calling_conventions.pdf
EDIT 2: I also managed to find a link to a calling conventions PDF in the Annotations of a YouTube video (of all things) which has some interesting tidbits on Shadow Space and the Red Zone on Linux. It can be found here: http://www.agner.org/optimize/calling_conventions.pdf
原始:
我在这里和整个互联网上都看过其他几个问题,但是当在64位Windows程序集中调用子例程/Windows API时,我似乎找不到合适的分配影子空间"的例子.
I have looked at a couple of other questions here and all over the internet but I can't seem to find a proper example of allocating "Shadow Space" when calling a subroutine/Windows API in 64 bit Windows assembly.
我的理解是这样:
- 呼叫者应该在
call callee
之前 - 如果需要,被调用方应使用它来存储寄存器(如果不需要保存寄存器,则应使用局部变量)
- 呼叫者对其进行清理,例如:
add rsp,<bytes here>
- 分配的数量应与32个字节对齐
sub rsp,<bytes here>
- Caller should
sub rsp,<bytes here>
prior tocall callee
- Callee should use it to store registers if need be (or local variables, if register saving isn't required)
- Caller cleans it up, e.g:
add rsp,<bytes here>
- The amount allocated should be aligned to 32 bytes
考虑到这一点,这就是我尝试过的:
With that in mind, this is what I have tried:
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".我怀疑自己做得正确...除了我应该以某种方式清理(而且我不确定如何).
My two strings are "Hello " and "World!\n". This manages to print "Hello " before crashing. I have a suspicion that I am doing it correctly ... except I should be cleaning up somehow (and I'm not sure how).
我做错了什么?我曾经尝试过各种尺寸的组合,还尝试过在WinAPI调用之前分配阴影空间"(我应该这样做吗?).
What am I doing wrong? I have tried a combination of sizes and also tried "allocating Shadow Space" prior to the WinAPI calls too (am I supposed to be doing that?).
应该注意的是,当我根本不关心影子空间时,这非常好用.但是,由于我的write
函数调用WinAPI(因此不是叶函数),因此我试图与ABI兼容.
It should be noted that this works perfectly fine when I don't care about Shadow Space at all. However, I am trying to be compliant with the ABI since my write
function calls WinAPIs (and is therefore, not a leaf function).
推荐答案
阴影空间必须直接在调用之前提供.想象一下影子空间是旧的stdcall/cdecl约定中的遗物:对于WriteFile
,您需要五次按下.阴影空间代表最后四个推动(前四个参数).现在,您需要四个寄存器,影子空间(只是空间,内容无关紧要)和影子空间 之后的堆栈上的一个值(实际上是第一次压入).当前,到调用方的返回地址(start
)在WriteFile
将用作影子空间的空间中->崩溃.
The shadow space must be provided directly previous to the call. Imagine the shadow space as a relic from the old stdcall/cdecl convention: For WriteFile
you needed five pushes. The shadow space stands for the last four pushes (the first four arguments). Now you need four registers, the shadow space (just the space, contents don't matter) and one value on the stack after the shadow space (which is in fact the first push). Currently the return address to the caller (start
) is in the space that WriteFile
will use as shadow space -> crash.
您可以在函数write
中为WinAPI函数(GetStdHandle
和WriteConsoleA
)创建一个新的影子空间:
You can create a new shadow space for the WinAPI functions (GetStdHandle
and WriteConsoleA
) inside the function write
:
write:
push rbp
mov rbp, rsp
sub rsp, (16 + 32) ; 5th argument of WriteConsoleA (8) + Shadow space (32)
; plus another 8 to make it a multiple of 16 (to keep stack aligned after one push aligned it after function entry)
mov [rbp+16],rcx ; <-- use our Shadow space, provided by `start`
mov [rbp+24],rdx ; <-- and again, to save our incoming args
mov rcx, -11 ; Get handle to StdOut
call GetStdHandle
mov rcx,rax ; hConsoleOutput
mov rdx, [rbp+16] ; lpBuffer ; reloaded saved copy of register arg
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
这篇关于阴影空间示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!