在Windows汇编中使用NASM编写的Hello World程序

14

我使用nasm编译以下汇编代码。然而在Windows下,代码在控制台中崩溃了。

C:\>nasm -f win32 test.asm -o test.o

C:\>ld test.o -o test.exe

section .data
  msg   db    'Hello world!', 0AH
  len   equ   $-msg

section .text
  global _WinMain@16

_WinMain@16:
  mov   edx, len
  mov   ecx, msg
  mov   ebx, 1
  mov   eax, 4
  int   80h

  mov   ebx, 0
  mov   eax, 1
  int   80h
根据这篇文章,在Windows下不支持main函数,需要用WinMain来替代。

因此,如果你的入口点是_startmain,应将其更改为_WinMain@16,并将过程末尾的ret更改为ret 16:

以下是我的工作示例:

section .text       
 global _WinMain@16       

_WinMain@16:       
 mov eax, 0       
 ret 16 

3
请编辑标题,以便对未来访问者有用。否则它可能会因为过于局部而被关闭。 - Raymond Chen
@RaymondChen 转发到哪里? - fulvio
2个回答

37

最大的问题是你试图在Windows上使用Linux中断! int 80在Windows上不起作用。

我们正在使用汇编语言,因此您的入口点可以是任何标签。 ld要查找的标准入口点是_start,如果您想使用另一个标签,则需要使用-e选项告诉ld。因此,如果您希望您的起始标签为main,则需要

global main
ld -e main test.o -o test.exe

如果你打算在Windows上使用NASM,我建议使用GoLink作为你的链接器。 这是一个简单的Windows控制台应用:

STD_OUTPUT_HANDLE   equ -11
NULL                equ 0

global GobleyGook
extern ExitProcess, GetStdHandle, WriteConsoleA

section .data
msg                 db "Hello World!", 13, 10, 0
msg.len             equ $ - msg

section .bss
dummy               resd 1

section .text
GobleyGook:
    push    STD_OUTPUT_HANDLE
    call    GetStdHandle

    push    NULL
    push    dummy
    push    msg.len
    push    msg
    push    eax
    call    WriteConsoleA 

    push    NULL
    call    ExitProcess

Makefile:

hello: hello.obj
    GoLink.exe  /console /entry GobleyGook hello.obj kernel32.dll  

hello.obj: hello.asm
    nasm -f win32 hello.asm -o hello.obj

1
@Gunner 谢谢。 不过我有一个问题 - 你为什么推荐GoLink而不是其他链接器呢? - Boris Milner
@Boris 个人偏好吧。在 Windows 上,我发现它比其他链接器更容易使用。 - Gunner
消息字符串末尾的零字节不应该存在。WriteConsoleA不需要它,它使用提供的字符串长度,并且实际上会被写入控制台,当我尝试时会显示为空格。移除零字节可以解决这个问题。 - Phil Dennis
Windows有没有相当于简单地执行syscall的等效方式,而不是使用call ExitProcesscall WriteConsoleA - Pacerier
是否有相应的东西适用于64位Windows?如果我尝试使用“-f win64”,则在“push eax”行处会出现“错误:指令不支持64位模式”。 - kepe

9
尽管如此,这个程序很可能会在Linux上的WINE上轻松运行。:)
WINE并不会阻止在Windows PE二进制文件内部使用Linux系统调用;机器指令运行本地化,而WINE仅提供DLL函数。

4
这是一个答案,因为它指出了操作系统,在没有提到崩溃的情况下程序将正常工作。 - johnfound
哦,你的意思是在Linux机器上,系统调用是本地运行的,而WINE与它们无关。一开始我以为你的意思是WINE可以双向运行,并且它可以在Windows机器上模拟Linux的'int 0x80' ABI! - Peter Cordes
@PeterCordes WINE只能在Unix机器上运行,并模拟Windows API。但只要程序不是在虚拟机中运行,而是在Linux操作系统的内存中本地运行,它们就可以使用int 0x80系统调用。当然,这样的程序在真正的Windows上会崩溃。但程序可以检测操作系统,并仅在Linux上运行时使用这些调用。 - johnfound
是的,我知道这些。我只是想说你的答案误导了我一会儿。请注意,WINE可以在一些非Linux操作系统(*BSD,包括OS X)上运行,而本地的Linux系统调用则无法工作。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接