; ----------------------------------------------------------------------------
; 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
include 'win32ax.inc'
.code
start:
invoke MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
invoke ExitProcess,0
.end start
9条答案
按热度按时间ldfqzlk81#
此示例说明如何直接转到Windows API,而不是在C标准库中链接。
要进行编译,您将需要NASM和LINK.EXE(来自Visual Studio标准版)
5anewei62#
NASM examples。
正在调用libc标准输入输出
printf
,正在实现int main(){ return printf(message); }
然后运行
也有The Clueless Newbies Guide to Hello World in Nasm没有使用C库,那么代码看起来就像这样。
MS-DOS系统调用的16位代码:可在DOS仿真器或具有NTVDM支持的32位Windows中运行。不能在任何64位Windows下“直接”(透明)运行,因为x86-64内核不能使用vm 86模式。
将其构建到
.com
可执行文件中,以便在cs:100h
处加载,所有段寄存器彼此相等(微型内存模型)。祝你好运
wd2eg0qa3#
这些是使用Windows API调用的Win32和Win 64示例。它们是针对MASM而不是NASM的,但请查看它们。您可以在this文章中找到更多详细信息。
这将使用MessageBox而不是打印到标准输出。
Win32多媒体子系统
Win 64 MASM软件包
要使用MASM组装和链接这些文件,请对32位可执行文件使用以下代码:
对于64位可执行文件,则为:
**为什么x64 Windows需要在
call
之前保留28 h字节的堆栈空间?**这是32字节(0x 20)的阴影空间(也称为主空间),这是调用约定所要求的。另外8个字节用于按16重新对齐堆栈,因为调用约定要求RSP在call
* 之前 * 对齐16字节。(我们的main
的调用程序(在CRT启动代码中)就是这样做的。8字节的返回地址意味着在进入函数时,RSP距离16字节的边界有8个字节。)函数可以使用Shadow space将其寄存器参数转储到堆栈参数(如果有)所在的位置。
system call
需要30 h(48字节)来为r10和r11预留空间,此外还有前面提到的4个寄存器。但是DLL调用只是函数调用,即使它们是syscall
指令的 Package 器。有趣的事实:非Windows,即x86-64 System V调用约定(例如在Linux上)根本不使用影子空间,并使用多达6个整数/指针寄存器参数,* 和 * 多达8个FP参数在XMM寄存器中。
通过使用MASM的
invoke
指令(它知道调用约定),可以使用一个ifdef来创建一个版本,它可以构建为32位或64位。两者的宏变量是相同的,但你不会通过这种方式学习汇编。你将学习C风格的asm。
invoke
是stdcall
或fastcall
,而cinvoke
是cdecl
或变量参数fastcall
。汇编程序知道该使用哪一个。您可以反汇编输出以查看
invoke
如何展开。9rbhqvlz4#
要获取一个.exe文件,并将NASM作为汇编程序和Visual Studio的链接程序,以下代码可以正常工作:
如果这个代码保存为
test64.asm
,那么要汇编:生成
test64.obj
然后从命令提示符链接:其中,path_to_link 可以是 C:\Program Files(x86)\Microsoft Visual Studio 10.0\VC\bin 或计算机中link.exe程序所在的位置,path_to_libs 可以是 C:\Program Files(x86)\Windows Kits\8.1\Lib\winv6.3\um\x64 或您的库所在的位置(在本例中,kernel32.lib和user32.lib位于同一位置,否则为所需的每个路径使用一个选项),/largedressaware:no 选项是必要的,以避免链接器抱怨地址太长(在本例中为user32.lib)。此外,正如此处所做的那样,如果Visual的链接器是从命令提示符调用的,则必须预先设置环境(
(使用
default rel
使得lea
指令可以在任何地方工作,包括在2GiB的虚拟地址空间之外。但是call MessageBoxA
仍然是一个直接的call rel32
,它只能到达距离它自己+-2GiB的指令。)yptwkmov5#
Flat Assembler不需要额外的连接器。这使得汇编程序的编程非常容易。它也适用于Linux。
这是Fasm示例中的
hello.asm
:Fasm创建一个可执行文件:
这是IDA中的程序:
您可以看到三个调用:
GetCommandLine
、MessageBox
和ExitProcess
中的一个或多个。t30tvxxf6#
除非调用 some 函数,否则这一点都不简单(而且,说真的,调用printf和调用win32 api函数在复杂性上没有真实的的区别)。
即使DOS int 21 h实际上只是一个函数调用,即使它是一个不同的API。
如果你想在没有帮助的情况下完成这件事,你需要直接和你的视频硬件对话,比如把“Hello world”的位图写入帧缓冲区。即使这样,显卡也会把这些内存值转换成DisplayPort/HDMI/DVI/VGA信号。
请注意,实际上,在ASM中,从硬件到软件的所有内容都不如在C中有趣。“hello world”程序可以归结为一个函数调用。ASM的一个优点是,您可以相当容易地使用任何您想要的ABI;你只需要知道ABI是多少。
z6psavjg7#
如果您想在anderstornvig的Hello World示例中使用NASM和Visual Studio的链接器(link.exe),则必须手动链接到包含printf()函数的C运行时库。
希望这能帮助到一些人。
li9yvcax8#
最好的例子是fasm,因为fasm不使用链接器,这通过另一层不透明的复杂性隐藏了windows编程的复杂性。如果你满足于一个写入gui窗口的程序,那么在fasm的example目录中有一个例子。
如果你想要一个控制台程序,它允许标准输入和标准输出的重定向,这也是可能的。有一个(helas高度重要的)例子程序,它不使用GUI,并且严格地与控制台一起工作,那就是fasm本身。这可以被精简到本质。(我已经写了一个forth编译器,这是另一个非GUI的例子,但它也是重要的)。
这样的程序使用以下命令为32位可执行文件生成正确的头文件,通常由链接器完成。
名为.“idata”的节包含一个表,该表可帮助窗口在启动期间将函数名与运行时地址相关联。它还包含对KERNEL.DLL(Windows操作系统)的引用。
表格格式是由Windows强制的,并且包含了程序启动时在系统文件中查找的名称。FASM隐藏了rva关键字背后的一些复杂性。因此_ExitProcess@4是一个fasm标签,而_exitProcess是一个由Windows查找的字符串。
您的程式位于区段'. text'中。如果您将该区段宣告为可读取、可写入且可执行,则它是您唯一需要加入的区段。
您可以调用在.idata节中声明的所有功能。对于控制台程序,您需要_GetStdHandle来找到标准输入和标准输出的文件描述符(使用像STD_INPUT_HANDLE这样的符号名称,它可以在包含文件www.example.com中找到win32a.inc)。一旦您有了文件描述符,您就可以执行WriteFile和ReadFile。所有函数都在kernel32文档中进行了描述。您可能已经意识到了这一点,否则您不会尝试汇编程序编程。
总之:有一个表与asci名称耦合到windows操作系统。在启动过程中,这被转换为一个可调用地址表,您可以在程序中使用。
iklwldmw9#
对于ARMWindows: