Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows 阴影空间示例_Windows_Assembly_64 Bit_Nasm - Fatal编程技术网

Windows 阴影空间示例

Windows 阴影空间示例,windows,assembly,64-bit,nasm,Windows,Assembly,64 Bit,Nasm,编辑: 我已经接受了下面的答案,并在最后修订的代码中添加了我自己的答案。希望它能向人们展示阴影空间分配的实际例子,而不是更多的文字 编辑2:我还设法在YouTube视频(所有东西)的注释中找到了一个调用约定PDF的链接,其中有一些关于阴影空间和Linux上的红色区域的有趣花絮。可以在这里找到: 原件: 我已经在这里和整个互联网上研究了几个其他问题,但我似乎找不到在64位Windows程序集中调用子程序/Windows API时分配“阴影空间”的合适示例 我的理解是: 呼叫方应在呼叫被呼叫方之前

编辑:

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

编辑2:我还设法在YouTube视频(所有东西)的注释中找到了一个调用约定PDF的链接,其中有一些关于阴影空间和Linux上的红色区域的有趣花絮。可以在这里找到:

原件:

我已经在这里和整个互联网上研究了几个其他问题,但我似乎找不到在64位Windows程序集中调用子程序/Windows API时分配“阴影空间”的合适示例

我的理解是:

  • 呼叫方应在呼叫被呼叫方之前
    sub-rsp,
  • 如果需要,被调用方应该使用它来存储寄存器(如果不需要保存寄存器,则使用局部变量)
  • 调用者将其清除,例如:
    addrsp,
  • 分配的数量应与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
section.text
开始:

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

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

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
写:
推动rbp
mov rbp,rsp
次级rsp(16+32);WriteConsoleA(8)+阴影空间(32)的第5个参数
; 再加上8,使其成为16的倍数(在一次按下后保持堆栈对齐,在函数输入后保持堆栈对齐)

mov[rbp+16],rcx 为了完整起见,我在这里发布这篇文章,因为这就是我最后的结局。这非常有效,就我所见,除了Windows上x64 ASM的
展开信息
/异常处理要求之外,这是非常合适的。希望这些评论也是准确的

编辑:

这是现在更新后,雷蒙德评论如下。我删除了保留的
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
结果是…:


也许会有帮助?特别要注意的是,调用函数需要重新调整堆栈,看起来您没有这样做。谢谢@HarryJohnston。这是我明天早上要读的东西清单(现在有点晚了!)。我会回来检查,让您知道我是怎么做的:)除了上面提到的其他问题外,您还忘记了生成展开数据,以便在发生异常时系统可以遍历堆栈。@RaymondChen您能详细说明一下吗?“放松数据”对我来说是个新名词(顺便说一句:你的博客多年来对我很有帮助:))看起来它可能有用。但更一般地说,谷歌搜索结果的第一页都是“x64解绑数据”。-)这让我更加困惑,因为您已经声明应该“在调用之前”提供阴影空间(这也是我的理解)。但是,您的示例按照通常在函数中设置本地堆栈(直接在调用之后)。那是哪一个呢?(我不是想说得粗鲁——我还不确定你的意思)哦,对不起——我现在明白了。示例中的阴影空间是为调用WinAPI设置的。你是说我上面的例子是正确的,我只需要为WinAPI的调用添加阴影空间。是吗?@SimonWhitehead:是的。顺便说一句:我无意中发现了添加rsp,0x28,它与子rsp,0x20不匹配。这在这里无关紧要,但你将来可能会遇到麻烦。对。。我想我现在明白了。阴影空间通过方法调用向下级联。我应该在main中设置一些阴影空间,然后在write方法中,我应该为WinAPI调用保留另一个32字节的阴影空间,并且可以通过偏移量
rbp
使用main中的阴影空间。这对我来说是有道理的。我明天就试试!非常感谢!回复:32对40-是的,很抱歉我犯了个错误。谢谢!:)您的
写入
序言没有建立16字节堆栈对齐。@RaymondChen再次。。你能详细说明一下吗?我正在努力学习,所以如果它以某种方式关闭,我想知道为什么。我仔细考虑了一下,据我所知,这是正确的——所以如果我错了,我会有一个很大的误解。我想,
WriteFile
的第五个参数的32字节阴影空间+8字节等于40字节。堆栈上的返回地址为48-那么添加40字节是否不对齐?一整天都在想这件事。。。看来我已经完全忘记了序言中的
push rbp
。。。这是另一个8字节,发生在
子rsp 0x28
之前。实际上,这将堆栈增加到56字节,而不是16的倍数。因此,我可能应该使其
子rsp,0x30
将其推送到64字节。。我可以完全避免存储
rbp
(我想这是一个编译器会做的优化)。基本上,这一切似乎都是基于这样的想法:使用
push
会使事情复杂化,因为SP和堆栈帧之间的关系在代码的不同部分会有所不同,所以应该避免。不过,这可能过于简单化了。:-)