微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尝试使用纯Win64 API从x64汇编器读取控制台input无C运行时

我只是学习x64汇编,而我刚遇到一个我不能解释的问题。 从Kernel32.dll的ReadFile如何从C代码工作,我期待它停止在控制台,并等待我input一个完整的行之前返回给调用者,这令人惊讶的是根本不工作。 ReadFile过程似乎返回一个零长度的string,无论键盘上按下什么,或者是从命令shell的pipe道传递给它的东西。

;%USERPROFILE%nasmlearningstdio.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 "incnasmx.inc" %include "incwin32windows.inc" %include "incwin32kernel32.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?",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,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,bName,bNamelim,bNamelen,zero invoke WriteFile,greet,greetlen,crlf,crlflen,qword [hStdError],errmsg,errmsglen,zero invoke ExitProcess,zero endproc

我已经使用C运行时完成了相同的function,并且可以工作,但是现在我正在试图在不使用那个拐杖的情况下运行一个版本。 我正在使用NASM(NASMX包含文件提供macros)和GoLink,链接到kernel32.dll。 我究竟做错了什么? 我错过了哪些API的行为? 从Win32控制台API上的MSDN文章,ReadFile的行为令我感到惊讶。

另外,如果我从程序集中删除了WaitForSingleObject调用,那么在C代码中不存在的东西,整个程序就不会停下来等待控制台input,尽pipeReadFile应该这样做。

编辑那么,雷蒙德问macros观扩张,如果他们根据调用惯例是正确的,那么:

在Linux上混合syscall写与printf

连续几个int3

assembly – 为什么打印是相反的顺序?

80×86程序集 – 非常基本的I / O程序从Windows转换到Linux

使用Kdbgdebugging汇编代码

invoke GetStdHandle,rax

这变成了

sub rsp,byte +0x20 mov rcx,0xfffffffffffffff6 call qword 0x2000 add rsp,byte +0x20 mov [0x402038],rax

这似乎遵循Win64调用约定0-4整数参数调用就好了。 五个参数表格呢?

invoke WriteFile,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

从这个angular度来看,至less在我看来, invokemacros是正确的。 proc – locals – endprocmacros很难,因为它是分散的,我相信invokemacros以某种方式依赖于它。 无论如何,序幕最终扩展到:

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%nasmlearningstdio.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 "incnasmx.inc" %include "incwin32windows.inc" %include "incwin32kernel32.inc" %ifidn __BITS__,0x30 %endif entry toplevel section .data errmsg db "No errors to report!",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,zero invoke ReadFile,[bNamelen],zero endproc

但是,这个代码仍然没有回答陈macros伟对macros的问题以及他们是否违反了Win64 ABI,所以我不得不再看一下。

编辑一个没有macros的版本,我相信完全遵循x64 ABI,包括展开数据。

;%USERPROFILE%nasmlearningstdio.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?",0xa querylen: equ $-query greet: db "Welcome," greetlen: equ $-greet errmsg: db "No errors to report!",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,&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,&bNameLen,qword [hStdInput] mov rdx,qword bName mov r8d,dword bNamelim mov r9,qword bNamelen mov qword [rsp+0x20],zero call ReadFile ; WriteFile (*hStdOutput,&crlf,qword crlf mov r8d,dword crlflen mov r9,zero call WriteFile ; WriteFile (*hStdOutput,&greet,qword greet mov r8d,dword greetlen mov r9,*bNamelen,dword [bNamelen] mov r9,zero call WriteFile ; WriteFile (*hStdError,&errmsg,qword [hStdError] mov rdx,qword errmsg mov r8d,dword errmsglen mov r9,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 @R_654_4045@ion 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

C ++内联汇编(Intel编译器):LEA和MOV在Windows和Linux中performance不同

x86指令格式:“ba 0e 00 00 00”…“mov $ 0xe,%edx”

“assembly炼狱”会发生什么?

Linux系统调用表或汇编语言的cheetsheet

如何在汇编中正确调用64位Windows API

我怀疑这是你的问题:

bNamelim db 0xff

[…]

invoke ReadFile,zero

你传递的是地址而不是bNamelim的值。

我不确定ReadFile应该如何应对大于32位的值,但这肯定不是你想要做的。

问题实际上是当你改变控制台模式时:

... and dl,ENABLE_PROCESSED_INPUT and dl,ENABLE_LINE_INPUT and dl,ENABLE_ECHO_INPUT ...

由于ENABLE_*宏是单个位,所以将它们放在一起会导致零,这意味着您将零传递给SetConsoleMode 。 如果你想设置位,使用or代替and 。 如果要清除这些位,则需要通过预先加上~ (二进制NOT)来反转这些位。

此外,根据GetConsoleInput的MSDN (特别是dwMode参数),控制台dwMode以设置的那些位开始,因此您不需要再次设置它们。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐