如何在Windows下的汇编程序中编写hello world?

时间:2009-06-21 10:14:14

标签: winapi assembly x86 nasm

我想在Windows下编写基本的程序集,我正在使用NASM,但我无法正常工作。

如何在Windows上没有C函数的帮助下编写和编译hello world?

8 个答案:

答案 0 :(得分:113)

此示例显示如何直接转到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 

答案 1 :(得分:27)

NASM examples

调用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

还有The Clueless Newbies Guide to Hello World in 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加载,所有段寄存器彼此相等(微小内存模型)。

祝你好运。

答案 2 :(得分:17)

这些是使用Windows API调用的Win32和Win64示例。它们是用于MASM而不是NASM,但看看它们。您可以在this文章中找到更多详细信息。

;---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

答案 3 :(得分:13)

Flat 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中的程序:

enter image description here

您可以看到三个电话:GetCommandLineMessageBoxExitProcess

答案 4 :(得分:8)

要使用NASM'编译器和Visual Studio的链接器获取.exe,此代码可以正常工作:

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  
    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
    call ExitProcess

    hlt     ; never here

如果此代码保存在例如“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 可以是 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在同一个地方,否则为你需要的每个路径使用一个选项)和 / largeaddressaware:no 选项是必要的,以避免链接器抱怨地址长(对于user32)在这种情况下.lib)。 此外,正如在此处所做的那样,如果从命令提示符调用Visual的链接器,则必须先设置环境(运行一次vcvarsall.bat和/或查看MS C++ 2010 and mspdb100.dll)。

答案 5 :(得分:5)

除非你打电话给一些功能,否则这一点都不重要。 (而且,严肃地说,调用printf和调用win32 api函数之间的复杂性没有真正的区别。)

即使DOS int 21h实际上只是一个函数调用,即使它是一个不同的API。

如果您想在没有帮助的情况下进行操作,您需要直接与视频硬件对话,可能会将“Hello world”字母的位图写入帧缓冲区。即便如此,视频卡正在将这些存储器值转换为VGA / DVI信号。

请注意,实际上,这些东西一直到硬件都不是在ASM中比在C中更有趣。“hello world”程序归结为函数调用。 ASM的一个好处是你可以使用任何你想要的ABI;你只需要知道ABI是什么。

答案 6 :(得分:4)

如果要在anderstornvig的Hello World示例中使用NASM和Visual Studio的链接器(link.exe),则必须手动链接包含printf()函数的C运行时库。

nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib

希望这有助于某人。

答案 7 :(得分:4)

最好的例子是那些带有fasm的例子,因为fasm没有使用链接器,它通过另一个不透明的复杂层来隐藏Windows编程的复杂性。 如果您对写入gui窗口的程序感兴趣,那么在fasm的示例目录中就有一个例子。

如果你想要一个控制台程序,它允许重定向标准输入和标准输出也是可能的。 有一个(helas高度非平凡)示例程序可用,不使用gui,并严格使用控制台,这是自己的魅力。这可以简化为必需品。 (我已经编写了第四个编译器,这是另一个非gui的例子,但它也是非平凡的。)

这样的程序具有以下命令来生成正确的可执行标头,通常由链接器完成。

FORMAT PE CONSOLE 

一个名为' .idata'包含一个表,在启动过程中帮助窗口将函数名称与运行时地址相结合。它还包含对KERNEL.DLL的引用,这是Windows操作系统。

 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

您可以调用.idata部分中声明的所有设施。对于控制台程序,您需要_GetStdHandle来查找标准输入和标准输出的文件描述符(使用符号名称,如fasm在包含文件win32a.inc中找到的STD_INPUT_HANDLE)。 获得文件描述符后,您可以执行WriteFile和ReadFile。 kernel32文档中描述了所有函数。您可能已经意识到这一点,或者您不会尝试汇编程序编程。

总结:有一个带有asci名称的表连接到Windows操作系统。 在启动过程中,这将转换为可调用地址表,您可以在程序中使用该表。