我用FASM编写了一个简短的PE 32+程序,它向stdout写入“Hello World!”并退出。
format PE64 console
include 'win64wx.inc'
.code
start:
invoke WriteFile,<invoke GetStdHandle,STD_OUTPUT_HANDLE>,hello,hello.length,dummy,0
invoke ExitProcess,0
.end start
.data
dummy rd 1
hello db "Hello world!",13,10,0
hello.length = $ - hello
我已经看过生成的机器码,但是我不明白为什么要这样操作RSP。
sub rsp,byte +0x08 ;Allocate 8 bytes on the stack.
sub rsp,byte +0x30 ;Allocate shadow space for WriteFile (48 bytes)
sub rsp,byte +0x20 ;Allocate shadow space for GetStdHandle
mov rcx,0xfffffffffffffff5 ;Set the constant for stdout
call [rel 0x1060] ;Call GetStdHandle. The handle for stdout is now in RAX
add rsp,byte +0x20 ;Deallocate shadow space for GetStdHandle
mov rcx,rax ;Set stdout handle: hFile
mov rdx,0x403004 ;Set the pointer to string "Hello World!\r\n": lpBuffer
mov r8,0xf ;Set the length of the string: nNumberOfBytesToWrite
mov r9,0x403000 ;Set the pointer for lpNumberOfBytesWritten
mov qword [rsp+0x20],0x0 ;Push a 64 bit NULL pointer onto the stack: lpOverlapped
call [rel 0x1068] ;Call WriteFile
add rsp,byte +0x30 ;Deallocate shadow space for WriteFile
sub rsp,byte +0x20 ;Allocate shadow space for ExitProcess
mov rcx,0x0 ;Set the return value
call [rel 0x1058] ;Call ExitProcess
add rsp,byte +0x20 ;Deallocate shadow space for ExitProcess
我知道提前为WriteFile分配空间并不重要,但为什么是sub rsp,byte +0x30
而不是sub rsp,byte +0x28
?为什么第一个sub rsp,byte +0x08
在那里?是FASM的特性还是我从根本上误解了Microsoft x64堆栈管理规则?
1条答案
按热度按时间db2dz4w81#
注解已经解决了16字节的堆栈对齐问题,这会导致在调用指令中压入返回地址后堆栈变得不对齐,这意味着您必须在函数prolog中将其与
sub rsp, 8
重新对齐,请参见https://learn.microsoft.com/en-us/cpp/build/stack-usage。(0x20
)字节,可用于溢出前4个参数,这些参数在寄存器中传递,参见https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention。虽然调用方负责分配影子堆栈,当然,这可以组合起来用于多个函数调用。FASM实际上可以使用frame
/endf
宏来完成这一操作,请参见https://flatassembler.net/docs.php?article=win32#1.4。现在,如果您显式调用GetStdHandle
,将删除嵌套(不确定为什么需要这样做),您可以合并分配:其组装成:
sub rsp, 0x20
现在没有了。不幸的是,sub rsp, 8
仍然在这里(它是由.code
宏插入的),但它要干净得多。如果你使用proc
宏,那么push rbp
将已经对齐堆栈,所以你不需要额外的sub rsp, 8
。当然,这是FASM,因此,如果不使用
proc
,您可以添加/重新定义一些宏来合并所有分配,甚至可以使用影子堆栈来处理您使用的非易失性寄存器的溢出,并在不调用函数时保持堆栈不对齐。这是我在https://github.com/stevenwdv/asmsequent/blob/main/proc_mod.inc中所做的(使用frame
/save
/rest
(ore)/...宏),它可能有点难看,我不知道它的大部分工作原理,但它完成了任务。注意组合分配(以及可能的情况下影子堆栈的使用)。