4

我想使用 x86 程序集为 Windows 编写简单的程序(控制台输入/输出),主要是因为我只是好奇。如果有人能指出我正确的方向,那就太好了。我已经对一些更简单的 x86 指令、寄存器的功能等有了相当好的理解,但对我来说程序如何与操作系统交互以及如何使用标准输入和输出仍然是个谜。我知道这些事情与诸如 advapi32.dll 和 kernel32.dll 之类的库有关,并且有关联的静态库 .lib 文件使编译器能够使用这些动态链接的库,但除此之外,我几乎不知道这是怎么回事发生。我什至对 C 等语言中的头文件如何使用 .lib 文件感到困惑。

4

1 回答 1

4

也许最简单的方法是为构建一些简单的程序提供指导,并让您从那里进行推断。首先你需要一些源代码:

.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

头文件将包含我上面为MessageBoxAGetStdHandleWriteFile等给出的声明的等价物。但每个头文件通常会有更多的声明——例如,kernel32 中的所有函数可能都在一个头文件中。

就图书馆而言,所涉及的机制有些涉及,但大多无关紧要。要完成这项工作,您可以查看(例如)MSDN,查看要链接的库,然后将其添加到命令行中。

更复杂的解释是,至少当您链接到静态库时,它只会找到您调用的任何函数,并将每个函数的副本放入您的可执行文件/DLL 中。如果(如上所述)您使用的是 DLL 中的代码,它基本上只是将一条记录放入可执行文件中,以告知它所依赖的 DLL 中的哪个函数。然后,当您加载/运行程序时,加载程序会查找您的程序所依赖的所有 DLL,并加载它们(当然,也递归加载它们所依赖的任何内容)。然后加载程序修复这些引用,因此对 DLL 中函数的引用将填充为该 DLL 中已分配给该函数的任何地址。

于 2012-06-03T06:15:19.183 回答