NASM中的Hello world,带有LINK.EXE和WinAPI
我正在尝试在NASM中运行一个简单的Hello world程序。 我希望打印到控制台而不使用C库,直接与WinAPI接口 我正在使用Visual Studio提供的LINK.EXE进行链接 以下是我目前的代码:NASM中的Hello world,带有LINK.EXE和WinAPI,winapi,assembly,x86,x86-64,nasm,Winapi,Assembly,X86,X86 64,Nasm,我正在尝试在NASM中运行一个简单的Hello world程序。 我希望打印到控制台而不使用C库,直接与WinAPI接口 我正在使用Visual Studio提供的LINK.EXE进行链接 以下是我目前的代码: section.data 消息:db“你好,世界!”,10 ; '你好,世界加上换行字符 messageLen:db$-消息;“你好,世界!”的长度一串 全球启动 外行 外部写操作 外部退出过程 第节.案文 _开始: ; 双字字节; mov rbp,rsp 副rsp,字节8 ; h
section.data
消息:db“你好,世界!”,10 ; '你好,世界加上换行字符
messageLen:db$-消息;“你好,世界!”的长度一串
全球启动
外行
外部写操作
外部退出过程
第节.案文
_开始:
; 双字字节;
mov rbp,rsp
副rsp,字节8
; hStdOut=GetStdHandle(标准输出句柄)
mov-ecx,-11
调用GetStdHandle
; WriteFile(hstdOut,消息,长度(消息),&字节,0);
mov-rcx,rax
mov-rdx,消息
mov r8,messageLen
lea r9[rsp-4]
推0
调用WriteConsoleW
; 退出进程(0)
mov-rcx,0
呼叫退出过程
ret
我将其组装并链接如下:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
messageLen equ $-message ; Length of the 'Hello world!' string
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
但是,当我运行生成的.exe文件时,我什么也得不到
到目前为止,我试过的一些东西是
- 使用修饰的名称(如_GetStdHandle@4),这导致链接器抱怨未解析的引用
- 不尝试打印任何内容并调用Sleep,这导致进程无限期休眠
- 使用不同的返回码退出,但再次无效
编辑:固定呼叫约定修改后的代码有三个问题。第一个是:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
您将messageLen
定义为包含消息长度的字节,并将该值存储在messageLen
的地址。然后执行以下操作:
mov r8, messageLen
这将把labelmessageLen
的地址移动到r8。您真正应该做的是将messageLen
定义为装配时间常数,如下所示:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
messageLen equ $-message ; Length of the 'Hello world!' string
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
第二个问题是将字符串定义为单字节字符序列:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
这没有什么问题,但要打印出来,您需要使用Ansi版本的函数WriteConsole
,即WriteConsoleA
。使用WriteConsoleW
将字符串打印为Unicode(Windows 2000及更高版本上的UTF-16,NT4及更早版本的Windows上的UTS-2)
第三个问题是,在进行函数调用之前,在堆栈上放置基于堆栈的参数之前,必须有32字节的阴影空间。在进行函数调用时,还需要确保堆栈(RSP)是一个16字节对齐的值。这些要求可在中找到 考虑到这一点的代码如下所示:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
messageLen equ $-message ; Length of the 'Hello world!' string
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
修改后的代码有三个问题。第一个是:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen: db $-message ; Length of the 'Hello world!' string
您将messageLen
定义为包含消息长度的字节,并将该值存储在messageLen
的地址。然后执行以下操作:
mov r8, messageLen
这将把labelmessageLen
的地址移动到r8。您真正应该做的是将messageLen
定义为装配时间常数,如下所示:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
messageLen equ $-message ; Length of the 'Hello world!' string
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
第二个问题是将字符串定义为单字节字符序列:
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
这没有什么问题,但要打印出来,您需要使用Ansi版本的函数WriteConsole
,即WriteConsoleA
。使用WriteConsoleW
将字符串打印为Unicode(Windows 2000及更高版本上的UTF-16,NT4及更早版本的Windows上的UTS-2)
第三个问题是,在进行函数调用之前,在堆栈上放置基于堆栈的参数之前,必须有32字节的阴影空间。在进行函数调用时,还需要确保堆栈(RSP)是一个16字节对齐的值。这些要求可在中找到 考虑到这一点的代码如下所示:
nasm -f win64 .\ASM.ASM
link /entry:_start /nodefaultlib /subsystem:console .\ASM.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\kernel32.lib" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
messageLen equ $-message ; Length of the 'Hello world!' string
section .data
message: db 'Hello world!',10 ; 'Hello world!' plus a linefeed character
messageLen equ $-message ; Length of the 'Hello world!' string
global _start
extern GetStdHandle
extern WriteConsoleA
extern ExitProcess
section .text
_start:
; At _start the stack is 8 bytes misaligned because there is a return
; address to the MSVCRT runtime library on the stack.
; 8 bytes of temporary storage for `bytes`.
; allocate 32 bytes of stack for shadow space.
; 8 bytes for the 5th parameter of WriteConsole.
; An additional 8 bytes for padding to make RSP 16 byte aligned.
sub rsp, 8+8+8+32
; At this point RSP is aligned on a 16 byte boundary and all necessary
; space has been allocated.
; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
mov ecx, -11
call GetStdHandle
; WriteFile(hstdOut, message, length(message), &bytes, 0);
mov rcx, rax
mov rdx, message
mov r8, messageLen
lea r9, [rsp-16] ; Address for `bytes`
; RSP-17 through RSP-48 are the 32 bytes of shadow space
mov qword [rsp-56], 0 ; First stack parameter of WriteConsoleA function
call WriteConsoleA
; ExitProcess(0)
; mov rcx, 0
; call ExitProcess
; alternatively you can exit by setting RAX to 0
; and doing a ret
add rsp, 8+8+32+8 ; Restore the stack pointer.
xor eax, eax ; RAX = return value = 0
ret
您的代码是x64,而不是x86。您首先需要了解x64调用约定我已经解决了调用约定问题,但最终结果仍然是一样的。请参阅问题的编辑您仍然没有完全修复调用约定:您没有保留32字节的阴影空间,因此您调用的函数可能会占用堆栈空间。您的代码是x64,而不是x86。您首先需要了解x64调用约定我已经解决了调用约定问题,但最终结果仍然是一样的。请参阅问题的编辑您仍然没有完全修复调用约定:您没有保留32字节的卷影空间,因此您调用的函数可能会占用堆栈空间。第三个问题:没有保留卷影空间。顺便说一句,当你说“unicode”时,在这种情况下是指UTF-16吗?因为消息是有效的UTF-8 unicode。@彼得科德斯:还有堆栈对齐的问题(不仅仅是阴影空间)。至于Unicode,在NT 4和更早版本的Windows操作系统上是UCS-2,在Windows 2000和更高版本上是UTF-16。NASM有一个
\uuuuuuuuuf16\uuuuu
命令来定义UTF-16字符串,因此编码不是问题。第三个问题:没有保留阴影空间。顺便说一句,当你说“unicode”时,在这种情况下是指UTF-16吗?因为消息是有效的UTF-8 unicode。@彼得科德斯:还有堆栈对齐的问题(不仅仅是阴影空间)。至于Unicode,在NT 4和更早版本的Windows操作系统上是UCS-2,在Windows 2000和更高版本上是UTF-16。NASM有一个定义UTF-16字符串的\uuuuuuuuu
命令,因此编码不是问题。