阴影空间示例

Sim*_*ead 5 windows 64-bit assembly nasm

编辑:

我在下面接受了一个答案,并在代码的最终修订版中添加了自己的答案。希望它能向人们展示阴影空间分配的实际示例,而不是更多的单词。

编辑2:我还设法在(所有内容的)YouTube视频的注释中找到了调用约定PDF的链接,该链接在Shadow Space和Linux的Red Zone上有一些有趣的花絮。可以在这里找到:http : //www.agner.org/optimize/calling_conventions.pdf

原版的:

我在这里和整个Internet上都看过其他几个问题,但是当在64位Windows程序集中调用子例程/ Windows API时,似乎找不到合适的分配“影子空间”的示例。

我的理解是:

  • 来电者应sub rsp,<bytes here>call callee
  • 被调用方应使用它来存储寄存器(如果需要)(或局部变量,如果不需要保存寄存器)
  • 呼叫者将其清除,例如: 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
Run Code Online (Sandbox Code Playgroud)

我的两个字符串是“ Hello”和“ World!\ n”。这样可以在崩溃前打印“ Hello”。我怀疑自己做得正确……除了我应该以某种方式清理(而且我不确定如何)。

我究竟做错了什么?我尝试了多种尺寸的组合,还尝试了在WinAPI调用之前“分配阴影空间”(我应该这样做吗?)。

应当指出,当我根本不关心影子空间时,这可以很好地工作。但是,由于我的write函数调用WinAPI(因此不是叶函数),因此我试图与ABI兼容。

rkh*_*khb 8

必须在调用之前直接提供阴影空间。把阴影空间想象成旧的 stdcall/cdecl 约定的遗物:因为WriteFile你需要五次推动。影子空间代表最后四次推送(前四个参数)。现在您需要四个寄存器,影子空间(只是空间,内容无关紧要)和影子空间之后堆栈上的一个值(实际上是第一次推送)。当前,调用者 ( start)的返回地址位于WriteFile将用作影子空间 -> 崩溃的空间中。

您可以为函数内的WinAPI 函数(GetStdHandleWriteConsoleA)创建一个新的阴影空间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
Run Code Online (Sandbox Code Playgroud)