我想在Windows下使用汇编语言写一些基本的程序。我正在使用NASM,但是一直无法让它正常工作。
如何在Windows上编写和编译一个简单的hello world程序,而不需要借助C函数的帮助?
我想在Windows下使用汇编语言写一些基本的程序。我正在使用NASM,但是一直无法让它正常工作。
如何在Windows上编写和编译一个简单的hello world程序,而不需要借助C函数的帮助?
这个示例展示了如何直接使用Windows API而不链接C标准库。
global _main
extern _GetStdHandle@4
extern _WriteFile@20
extern _ExitProcess@4
section .text
_main:
; DWORD bytes;
mov ebp, esp
sub esp, 4
; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
push -11
call _GetStdHandle@4
mov ebx, eax
; WriteFile( hstdOut, message, length(message), &bytes, 0);
push 0
lea eax, [ebp-4]
push eax
push (message_end - message)
push message
push ebx
call _WriteFile@20
; ExitProcess(0)
push 0
call _ExitProcess@4
; never here
hlt
message:
db 'Hello, World', 10
message_end:
要进行编译,你需要使用NASM和LINK.EXE (从Visual Studio标准版获取)。
nasm -fwin32 hello.asm link /subsystem:console /nodefaultlib /entry:main hello.obj
link
: Microsoft (R) 增量链接器版本 14.29.30133.0
,我得到了 unresolved external symbol _GetStdHandle@4
以及所有外部项? - Ben调用 libc stdio 的 printf
,实现 int main(){ return printf(message); }
; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World" on one line and
; then exits. It needs to be linked with a C library.
; ----------------------------------------------------------------------------
global _main
extern _printf
section .text
_main:
push message
call _printf
add esp, 4
ret
message:
db 'Hello, World', 10, 0
然后运行
nasm -fwin32 helloworld.asm
gcc helloworld.obj
a
还有Nasm中的新手指南,不需要使用C库。那么代码看起来会像这样。
使用MS-DOS系统调用的16位代码:在DOS模拟器或支持NTVDM的32位Windows中工作。不能在任何64位Windows下“直接”(透明地)运行,因为x86-64内核无法使用vm86模式。
org 100h
mov dx,msg
mov ah,9
int 21h
mov ah,4Ch
int 21h
msg db 'Hello, World!',0Dh,0Ah,'$'
.com
可执行文件,以便在所有段寄存器相等的情况下(小内存模型)加载到 cs:100h
。;---ASM Hello World Win32 MessageBox
.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
title db 'Win32', 0
msg db 'Hello World', 0
.code
Main:
push 0 ; uType = MB_OK
push offset title ; LPCSTR lpCaption
push offset msg ; LPCSTR lpText
push 0 ; hWnd = HWND_DESKTOP
call MessageBoxA
push eax ; uExitCode = MessageBox(...)
call ExitProcess
End Main
;---ASM Hello World Win64 MessageBox
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
.data
title db 'Win64', 0
msg db 'Hello World!', 0
.code
main proc
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, msg ; LPCSTR lpText
lea r8, title ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
add rsp, 28h
mov ecx, eax ; uExitCode = MessageBox(...)
call ExitProcess
main endp
End
要使用MASM汇编和链接这些文件,使用以下内容生成32位可执行文件:
ml.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
或者对于64位可执行文件:
ml64.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
使用MASM的invoke
指令(它了解调用约定),您可以使用一个ifdef来制作一个版本,该版本可以构建为32位或64位。
ifdef rax
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
else
.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
endif
.data
caption db 'WinAPI', 0
text db 'Hello World', 0
.code
main proc
invoke MessageBoxA, 0, offset text, offset caption, 0
invoke ExitProcess, eax
main endp
end
要使用NASM作为汇编器和Visual Studio的链接器生成.exe文件,以下代码可以正常工作:
default rel ; Use RIP-relative addressing like [rel msg] by default
global WinMain
extern ExitProcess ; external functions in system libraries
extern MessageBoxA
section .data
title: db 'Win64', 0
msg: db 'Hello world!', 0
section .text
WinMain:
sub rsp, 28h ; reserve shadow space and make RSP%16 == 0
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx,[msg] ; LPCSTR lpText
lea r8,[title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
mov ecx,eax ; exit status = return value of MessageBoxA
call ExitProcess
add rsp, 28h ; if you were going to ret, restore RSP
hlt ; privileged instruction that crashes if ever reached.
如果将此代码保存为test64.asm
,则进行汇编:
nasm -f win64 test64.asm
生成test64.obj
文件,然后通过命令提示符进行链接:
path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
path_to_link指向你的机器上的link.exe程序所在的位置,例如C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin。 path_to_libs指向库文件所在的位置,例如C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64,在这种情况下,kernel32.lib和user32.lib位于同一位置,否则请为每个路径使用一个选项。必须使用/largeaddressaware:no选项,以避免链接程序对地址过长的抱怨(在此情况下为user32.lib)。 另外,如此做的话,如果从命令提示符中调用Visual的链接器,则需要先设置环境(运行一次vcvarsall.bat和/或查看MS C++ 2010 and mspdb100.dll)。
(使用default rel
使lea
指令从任何地方工作,包括虚拟地址空间低2GiB之外。但是,call MessageBoxA
仍然是直接的call rel32
,只能到达距离自己+-2GiB的指令。)
default rel
,这样就可以使用RIP相对寻址而不是32位绝对寻址来处理那些寻址方式([msg]
和 [title]
)。 - Peter CordesFlat Assembler无需额外的链接器,使汇编程序设计变得非常容易。它也可用于Linux。
这是来自Fasm示例的hello.asm
:
include 'win32ax.inc'
.code
start:
invoke MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
invoke ExitProcess,0
.end start
Fasm可以创建一个可执行文件:
>fasm hello.asm flat assembler version 1.70.03 (1048575 kilobytes memory) 4 passes, 1536 bytes.
这是在 IDA 中显示的程序:
您可以看到三个调用:GetCommandLine
,MessageBox
和ExitProcess
。
除非你调用某些函数,否则这并不是一件微不足道的事情。(而且,说实话,在调用printf和调用win32 api函数之间没有真正的复杂度差异。)
即使是DOS int 21h,它实际上也只是一个函数调用,尽管它是不同的API。
如果你想在没有帮助的情况下完成它,你需要直接与视频硬件通信,可能要将“Hello world”字母的位图写入帧缓冲区。即使如此,视频卡也会将这些内存值转换为DisplayPort/HDMI/DVI/VGA信号。
请注意,实际上,从硬件一直到底层都不比C语言更有趣。一个“hello world”程序归结为一个函数调用。ASM的一个好处是你可以相对容易地使用任何ABI;你只需要知道那个ABI是什么。
如果你想使用NASM和Visual Studio的链接器(link.exe)来运行anderstornvig的Hello World例子,你需要手动链接包含printf()函数的C运行时库。
nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
FORMAT PE CONSOLE
section '.idata' import data readable writeable
dd 0,0,0,rva kernel_name,rva kernel_table
dd 0,0,0,0,0
kernel_table:
_ExitProcess@4 DD rva _ExitProcess
CreateFile DD rva _CreateFileA
...
...
_GetStdHandle@4 DD rva _GetStdHandle
DD 0
表格格式是由Windows强制执行的,其中包含在程序启动时在系统文件中查找的名称。FASM通过rva关键字隐藏了一些复杂性。所以_ExitProcess@4是一个fasm标签,而_exitProcess是一个被Windows查找的字符串。
你的程序位于'.text'段。如果你声明该段可读写和可执行,它就是你需要添加的唯一段。
section '.text' code executable readable writable
针对ARM版Windows:
AREA data, DATA
Text DCB "Hello world(text)", 0x0
Caption DCB "Hello world(caption)", 0x0
EXPORT WinMainCRTStartup
IMPORT __imp_MessageBoxA
IMPORT __imp_ExitProcess
AREA text, CODE
WinMainCRTStartup PROC
movs r3,#0
ldr r2,Caption_ptr
ldr r1,Text_ptr
movs r0,#0
ldr r4,MessageBoxA_ptr @ nearby, reachable with PC-relative
ldr r4,[r4]
blx r4
movs r0,#0
ldr r4,ExitProcess_ptr
ldr r4,[r4]
blx r4
MessageBoxA_ptr DCD __imp_MessageBoxA @ literal pool (constants near code)
ExitProcess_ptr DCD __imp_ExitProcess
Text_ptr DCD Text
Caption_ptr DCD Caption
ENDP
END