阴影 space 示例

Shadow space example

编辑:

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

编辑 2:我还设法在 YouTube 视频(所有内容)的注释中找到了调用约定 PDF 的 link,其中有一些关于 Shadow Space 和Linux 上的红色区域。可以在这里找到:http://www.agner.org/optimize/calling_conventions.pdf

原文:

我在这里和整个互联网上查看了其他几个问题,但我似乎找不到在调用 subroutine/Windows [=56= 时分配 "Shadow Space" 的合适示例] 在 64 位 Windows 汇编中。

我的理解是这样的:

考虑到这一点,这就是我尝试过的方法:

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 调用之前尝试了 "allocating Shadow Space"(我应该这样做吗?)。

应该注意的是,当我根本不关心 Shadow Space 时,这工作得很好 。但是,由于我的 write 函数调用了 WinAPIs(因此不是叶函数),所以我试图与 ABI 兼容。

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

您可以在函数 write:

中为 WinAPI 函数(GetStdHandleWriteConsoleA)创建一个新的影子 space
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

为了完整起见,我将其张贴在这里,因为这就是我的最终结果。这非常有效,据我所知,除了 Windows 上 x64 ASM 的 UNWIND_INFO/异常处理要求外,这几乎是正确的。希望评论也是准确的。

编辑:

这是在 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

这导致...: