windows - 阴影空间示例

标签 windows assembly x86-64 nasm stack-memory

编辑:

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

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

原创:

我在这里和整个互联网上查看了其他几个问题,但我似乎找不到在 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

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

我做错了什么?我已经尝试了大小的组合,也尝试了在 WinAPI 调用之前“分配影子空间”(我应该这样做吗?)。

应该注意的是,当我根本不关心阴影空间时,它工作得很好。但是,自从我的 write 以来,我一直在努力遵守 ABI。函数调用 WinAPI(因此不是叶函数)。

最佳答案

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

您可以在 write 函数内为 WinAPI 函数(GetStdHandleWriteConsoleA)创建一个新的阴影空间:

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 - 阴影空间示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33273797/

相关文章:

PHP内存缓存问题

node.js - 在 Windows 7 中打开太多文件(EMFILE 错误)

function - Delphi - 编写带有函数的 .pas 库

assembly - x86指令编码如何选择操作码

gcc - 无法从内核模式更改 CS 寄存器值。无效的操作码 : 0000

assembly - x86堆方法

c# - 当*任何*进程启动时得到通知,然后卡住/暂停/挂起它

algorithm - 内存黑客中的指针扫描

assembly - 汇编中的相对vs绝对jmp

windows - 如何从 'call' ed 例程中终止 Windows 批处理文件?