我剛學習x64彙編程序,剛剛遇到一個我無法解釋的問題。從Kernel32.dll的ReadFile如何從C代碼運行,我期待它停止在控制檯,並等待我輸入一個完整的行,然後再返回給調用者,這令人驚訝的是根本無法爲我工作。 ReadFile過程似乎返回一個零長度的字符串,不管鍵盤上按下什麼,或者從那個命令外殼上的管道傳遞給它。嘗試從使用純Win64 API(無C運行時)的x64彙編程序讀取控制檯輸入
;%USERPROFILE%\nasm\learning\stdio.asm
;
;Basic usage of the standard input/output/error channels.
;
;nasm -f win64 stdio.asm
;golink /console /ni /entry main stdio.obj kernel32.dll
%include "\inc\nasmx.inc"
%include "\inc\win32\windows.inc"
%include "\inc\win32\kernel32.inc"
%ifidn __BITS__, 0x40
;// assert: set call stack for procedure prolog to max
;// invoke param bytes for 64-bit assembly mode
DEFAULT REL
NASMX_PRAGMA CALLSTACK, 0x30
%endif
entry toplevel
;
section .data
errmsg db "No errors to report!",0xd,0xa
errmsglen equ $-errmsg
query db "What is your name?",0xd,0xa
querylen equ $-query
greet db "Welcome, "
greetlen equ $-greet
crlf db 0xd,0xa
crlflen equ $-crlf
bNamelim db 0xff
minusone equ 0xffffffffffffffff
zero equ 0x0
section .bss
hStdInput resq 0x1
hStdOutput resq 0x1
hStdError resq 0x1
hNum resq 0x1
hMode resq 0x1
bName resb 0x100
bNamelen resq 0x1
section .text
proc toplevel, ptrdiff_t argcount, ptrdiff_t cmdline
locals none
invoke GetStdHandle, STD_INPUT_HANDLE
mov qword [hStdInput], rax
; invoke GetConsoleMode, qword [hStdInput], hMode
; mov rdx, [hMode]
; and dl, ENABLE_PROCESSED_INPUT
; and dl, ENABLE_LINE_INPUT
; and dl, ENABLE_ECHO_INPUT
; invoke SetConsoleMode, qword [hStdInput], rdx
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov qword [hStdOutput], rax
invoke GetStdHandle, STD_ERROR_HANDLE
mov qword [hStdError], rax
invoke WriteFile, qword [hStdOutput], query, querylen, hNum, zero
invoke WaitForSingleObject, qword[hStdInput], minusone
invoke ReadFile, qword [hStdInput], bName, bNamelim, bNamelen, zero
invoke WriteFile, qword [hStdOutput], greet, greetlen, hNum, zero
invoke WriteFile, qword [hStdOutput], bName, bNamelen, hNum, zero
invoke WriteFile, qword [hStdOutput], crlf, crlflen, hNum, zero
invoke WriteFile, qword [hStdError], errmsg, errmsglen, hNum, zero
invoke ExitProcess, zero
endproc
我做了使用C運行時相同的功能和工作原理,但現在我試圖讓一個工作優化版本,而不使用柺杖。我正在使用NASM(NASMX包含提供宏的文件)和GoLink,並與kernel32.dll進行鏈接。我究竟做錯了什麼?我錯過了哪些API的行爲? 從Win32控制檯API上的MSDN文章中,ReadFile的行爲讓我感到驚訝。另外,如果我從程序集中刪除了WaitForSingleObject調用,那麼在C等價物中不存在的東西,整個程序將不會停止等待控制檯輸入而運行結束,儘管ReadFile應該這樣做。
編輯 嗯,雷蒙德陳問起宏展開,如果他們根據調用約定是正確的,那麼:
invoke GetStdHandle, STD_INPUT_HANDLE
mov qword [hStdInput], rax
這個被變成
sub rsp,byte +0x20
mov rcx,0xfffffffffffffff6
call qword 0x2000
add rsp,byte +0x20
mov [0x402038],rax
這似乎按照Win64的調用約定進行0-4整數參數調用就可以了。五個參數表單怎麼樣?
invoke WriteFile, qword [hStdOutput], query, querylen, hNum, zero
這被變成
sub rsp,byte +0x30
mov rcx,[0x402040]
mov rdx,0x402016
mov r8d,0x14
mov r9,0x402050
mov qword [rsp+0x20],0x0
call qword 0x2006
add rsp,byte +0x30
,並從它似乎對我來說,至少invoke
宏是正確的。 proc
- locals
- endproc
宏很難,因爲它是分散的,我相信invoke
宏以某種方式依賴於它。總之,序言最終擴展到這一點:
push rbp
mov rbp,rsp
mov rax,rsp
and rax,byte +0xf
jz 0x15
sub rsp,byte +0x10
and spl,0xf0
mov [rbp+0x10],rcx
mov [rbp+0x18],rdx
和結語最終擴大到這一點:
mov rsp,rbp
pop rbp
ret
這兩者中,從我的Win64中的公認微薄的知識,似乎是好的。
編輯 好了,這要歸功於哈里·約翰斯頓的回答我的代碼工作:
;%USERPROFILE%\nasm\learning\stdio.asm
;
;Basic usage of the standard input/output/error channels.
;
;nasm -f win64 stdio.asm
;golink /console /ni /entry main stdio.obj kernel32.dll
%include "\inc\nasmx.inc"
%include "\inc\win32\windows.inc"
%include "\inc\win32\kernel32.inc"
%ifidn __BITS__, 0x40
;// assert: set call stack for procedure prolog to max
;// invoke param bytes for 64-bit assembly mode
DEFAULT REL
NASMX_PRAGMA CALLSTACK, 0x30
%endif
entry toplevel
section .data
errmsg db "No errors to report!",0xd,0xa
errmsglen equ $-errmsg
query db "What is your name?",0xd,0xa
querylen equ $-query
greet db "Welcome, "
greetlen equ $-greet
crlf db 0xd,0xa
crlflen equ $-crlf
bNamelim equ 0xff
minusone equ 0xffffffffffffffff
zero equ 0x0
section .bss
hStdInput resq 0x1
hStdOutput resq 0x1
hStdError resq 0x1
hNum resq 0x1
hMode resq 0x1
bName resb 0x100
bNamelen resq 0x1
section .text
proc toplevel, ptrdiff_t argcount, ptrdiff_t cmdline
locals none
invoke GetStdHandle, STD_INPUT_HANDLE
mov qword [hStdInput], rax
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov qword [hStdOutput], rax
invoke GetStdHandle, STD_ERROR_HANDLE
mov qword [hStdError], rax
invoke WriteFile, qword [hStdOutput], query, querylen, hNum, zero
invoke ReadFile, qword [hStdInput], bName, bNamelim, bNamelen, zero
invoke WriteFile, qword [hStdOutput], greet, greetlen, hNum, zero
invoke WriteFile, qword [hStdOutput], bName, [bNamelen], hNum, zero
invoke WriteFile, qword [hStdOutput], crlf, crlflen, hNum, zero
invoke WriteFile, qword [hStdError], errmsg, errmsglen, hNum, zero
invoke ExitProcess, zero
endproc
然而,該代碼仍然沒有回答Raymond Chen的問題與宏,以及他們是否違反了Win64平臺ABI或者不是,所以我不得不再看看更多。
編輯沒有宏的版本,我認爲完全遵循x64 ABI,包括展開數據。
;%USERPROFILE%\nasm\learning\stdio.asm
;
;Basic usage of the standard input/output/error channels.
;
;nasm -f win64 stdio.asm
;golink /console /ni /entry main stdio.obj kernel32.dll
;Image setup
bits 64
default rel
global main
;Linkage
extern GetStdHandle
extern WriteFile
extern ReadFile
extern ExitProcess
;Read only data
section .rdata use64
zero: equ 0x0
query: db "What is your name?",0xd,0xa
querylen: equ $-query
greet: db "Welcome, "
greetlen: equ $-greet
errmsg: db "No errors to report!",0xd,0xa
errmsglen: equ $-errmsg
crlf: db 0xd,0xa
crlflen: equ $-crlf
bNamelim: equ 0xff
STD_INPUT_HANDLE: equ -10
STD_OUTPUT_HANDLE: equ -11
STD_ERROR_HANDLE: equ -12
UNW_VERSION: equ 0x1
UNW_FLAG_NHANDLER: equ 0x0
UNW_FLAG_EHANDLER: equ 0x1
UNW_FLAG_UHANDLER: equ 0x2
UNW_FLAG_CHAININFO: equ 0x4
UWOP_PUSH_NONVOL: equ 0x0
UWOP_ALLOC_LARGE: equ 0x1
UWOP_ALLOC_SMALL: equ 0x2
UWOP_SET_FPREG: equ 0x3
UWOP_SAVE_NONVOL: equ 0x4
UWOP_SAVE_NONVOL_FAR: equ 0x5
UWOP_SAVE_XMM128: equ 0x8
UWOP_SAVE_XMM128_FAR: equ 0x9
UWOP_PUSH_MACHFRAME: equ 0xa
;Uninitialised data
section .bss use64
argc: resq 0x1
argv: resq 0x1
envp: resq 0x1
hStdInput: resq 0x1
hStdOutput: resq 0x1
hStdError: resq 0x1
hNum: resq 0x1
hMode: resq 0x1
bName: resb 0x100
bNamelen: resq 0x1
;Program code
section .text use64
main:
.prolog:
.argc: mov qword [argc], rcx
.argv: mov qword [argv], rdx
.envp: mov qword [envp], r8
.rsp: sub rsp, 0x8*0x4+0x8
.body:
; hStdInput = GetStdHandle (STD_INPUT_HANDLE)
mov rcx, qword STD_INPUT_HANDLE
call GetStdHandle
mov qword [hStdInput], rax
; hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE)
mov rcx, qword STD_OUTPUT_HANDLE
call GetStdHandle
mov qword [hStdOutput], rax
; hStdError = GetStdHandle (STD_ERROR_HANDLE)
mov rcx, qword STD_ERROR_HANDLE
call GetStdHandle
mov qword [hStdError], rax
; WriteFile (*hStdOutput, &query, querylen, &hNum, NULL)
mov rcx, qword [hStdOutput]
mov rdx, qword query
mov r8d, dword querylen
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; ReadFile (*hStdInput, &bName, bNamelim, &bNameLen, NULL)
mov rcx, qword [hStdInput]
mov rdx, qword bName
mov r8d, dword bNamelim
mov r9, qword bNamelen
mov qword [rsp+0x20], zero
call ReadFile
; WriteFile (*hStdOutput, &crlf, crlflen, &hNum, NULL)
mov rcx, qword [hStdOutput]
mov rdx, qword crlf
mov r8d, dword crlflen
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; WriteFile (*hStdOutput, &greet, greetlen, &hNum, NULL)
mov rcx, qword [hStdOutput]
mov rdx, qword greet
mov r8d, dword greetlen
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; WriteFile (*hStdOutput, &bName, *bNamelen, &hNum, NULL)
mov rcx, qword [hStdOutput]
mov rdx, qword bName
mov r8d, dword [bNamelen]
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; WriteFile (*hStdOutput, &crlf, crlflen, &hNum, NULL)
mov rcx, qword [hStdOutput]
mov rdx, qword crlf
mov r8d, dword crlflen
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; WriteFile (*hStdError, &errmsg, errmsglen, &hNum, NULL)
mov rcx, qword [hStdError]
mov rdx, qword errmsg
mov r8d, dword errmsglen
mov r9, qword hNum
mov qword [rsp+0x20], zero
call WriteFile
; ExitProcess(0)
.exit: xor ecx, ecx
call ExitProcess
.rval: xor eax, eax ; return 0
.epilog:
add rsp, 0x8*0x4+0x8
ret
.end:
; Win64 Windows API x64 Structured Exception Handling (SEH) - procedure data
section .pdata rdata align=4 use64
pmain:
.start: dd main wrt ..imagebase
.end: dd main.end wrt ..imagebase
.info: dd xmain wrt ..imagebase
; Win64 Windows API x64 Structured Exception Handling (SEH) - unwind information
section .xdata rdata align=8 use64
xmain:
.versionandflags:
db UNW_VERSION + (UNW_FLAG_NHANDLER << 0x3) ; Version = 1
; Version is low 3 bits. Handler flags are high 5 bits.
.size: db main.body-main.prolog ; size of prolog that is
.count: db 0x1 ; Only one unwind code
.frame: db 0x0 + (0x0 << 0x4) ; Zero if no frame pointer taken
; Frame register is low 4 bits, Frame register offset is high 4 bits,
; rsp + 16 * offset at time of establishing
.codes: db main.body-main.prolog ; offset of next instruction
db UWOP_ALLOC_SMALL + (0x4 << 0x4) ; UWOP_INFO: 4*8+8 bytes
; Low 4 bytes UWOP, high 4 bytes op info.
; Some ops use one or two 16 bit slots more for addressing here
db 0x0,0x0 ; Unused record to bring the number to be even
.handl: ; 32 bit image relative address to entry of exception handler
.einfo: ; implementation defined structure exception info
您是否記得將您的程序設置爲控制檯程序? –
當它發送到GoLink時,我使用'/ console'參數。如果我需要將它設置在其他地方,比如在NASM命令行或程序集本身,那麼我不知道如何。 – liorean
您正在使用隱藏實際代碼的各種宏。按照正確的調用約定調用'invoke'? 'proc'是否設置了正確的64位堆棧幀?當您在調試器中逐步完成程序時,您是否觀察到任何不尋常的錯誤代碼? –