I've written a short PE32+ program in FASM that writes "Hello World!" to stdout and quits.
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
I've looked at the generated machine code, but I cannot understand why RSP is manipulated the way it is. This is the disassembly:
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
I understand it doesn't really matter that the space for WriteFile is allocated well in advance, but why is it sub rsp,byte +0x30
and not sub rsp,byte +0x28
? And why is the first sub rsp,byte +0x08
there? Is it FASM's idiosyncrasy or am I fundamentally misunderstanding Microsoft x64 stack management rules?