我想使用x86汇编语言为Windows写一些简单的程序(控制台输入/输出),主要是因为我很好奇。如果有人能指点我正确的方向那就太好了。目前我已经对一些比较简单的x86指令、寄存器功能等有了相当不错的理解,但对程序如何与操作系统接口并使用标准输入和输出还是个迷。我知道这些东西与advapi32.dll和kernel32.dll等库有关,并且有相关的静态库.lib文件可以使编译器动态链接这些库,但除此之外我对此一无所知。我甚至模糊地知道像C语言中的头文件如何使用.lib文件。
我想使用x86汇编语言为Windows写一些简单的程序(控制台输入/输出),主要是因为我很好奇。如果有人能指点我正确的方向那就太好了。目前我已经对一些比较简单的x86指令、寄存器功能等有了相当不错的理解,但对程序如何与操作系统接口并使用标准输入和输出还是个迷。我知道这些东西与advapi32.dll和kernel32.dll等库有关,并且有相关的静态库.lib文件可以使编译器动态链接这些库,但除此之外我对此一无所知。我甚至模糊地知道像C语言中的头文件如何使用.lib文件。
.386
.MODEL flat, stdcall
; This is what would come from a header -- a declaration of a the Windows function:
MessageBoxA PROTO near32 stdcall, window:dword, text:near32,
windowtitle:near32, style:dword
.stack 8192
.data
message db "Hello World!", 0
windowtitle db "Win32 Hello World.", 0
.code
main proc
invoke MessageBoxA, 0, near32 ptr message, near32 ptr windowtitle, 0
ret
main endp
end main
masm
:ml hello32.asm -link -subsystem:windows user32.lib
这里告诉编译器要组装的文件,以及在链接时将其标记为Windows子系统(主要替代方案是-subsystem:console
),并链接user32.lib
。后者为我们提供了MessageBoxA
的定义。
一个类似的向控制台输出的程序则稍微复杂一些:
.386
.MODEL flat, stdcall
getstdout = -11
WriteFile PROTO NEAR32 stdcall, \
handle:dword, \
buffer:ptr byte, \
bytes:dword, \
written: ptr dword, \
overlapped: ptr byte
GetStdHandle PROTO NEAR32, device:dword
ExitProcess PROTO NEAR32, exitcode:dword
.data
message db "Hello World!", 13, 10
msg_size dd $ - offset message
.data?
written dd ?
.code
main proc
invoke GetStdHandle, getstdout
invoke WriteFile, \
eax, \
offset message, \
msg_size, \
offset written, \
0
invoke ExitProcess, 0
main endp
end main
建立过程基本相同,只是这里使用控制台,因此我们需要指定控制台子系统,而我们使用的函数在内核中定义:
ml hello_console.asm -link -subsystem:console kernel32.lib
MessageBoxA
、GetStdHandle
、WriteFile
等指定的声明的等效内容。每个头文件通常都会有更多这样的声明,例如 kernel32 中的所有函数可能在一个头文件中。
至于库,涉及的机制有些复杂,但大多数情况下并不相关。要完成任务,您可以查看 MSDN(例如),看看哪个库要求链接,然后将其添加到命令行上。
更复杂的解释是,至少当您链接静态库时,它会简单地查找您调用的任何函数,并将每个函数的副本放入可执行文件或 DLL 中。如果(如上所述)您正在使用 DLL 中的代码,则基本上只需将一条记录放入可执行文件中,告诉它依赖于哪个 DLL 中的哪个函数。然后,当您加载/运行程序时,加载器会查找您的程序依赖的所有 DLL,并加载它们(当然还递归地加载它们依赖的任何东西)。然后,加载器会修复这些引用,使对 DLL 中函数的引用填充为已分配给该函数在该 DLL 中的任何地址。