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

fei*_*oox 84 x86 assembly winapi nasm

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

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

caf*_*end 115

此示例显示如何直接转到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:
Run Code Online (Sandbox Code Playgroud)

要编译,你需要NASM和LINK.EXE(来自Visual Studio标准版)

   nasm -fwin32 hello.asm
   link /subsystem:console /nodefaultlib /entry:main hello.obj 

  • 你可能需要包含kernel32.lib来链接它(我做了).link/subsystem:console/nodefaultlib/entry:main hello.obj kernel32.lib (19认同)
  • 如何将obj与MinGW中的ld.exe链接? (4认同)
  • @DarrenVortex`gcc hello.obj` (3认同)
  • 这也可以使用来自http://sourceforge.net/projects/alink/的Alink等免费链接器或来自http://www.godevtool.com/#linker的GoLink吗?我不想只为此安装visual studio? (3认同)

and*_*vig 30

NASM的例子.

调用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
Run Code Online (Sandbox Code Playgroud)

然后跑

nasm -fwin32 helloworld.asm
gcc helloworld.obj
a
Run Code Online (Sandbox Code Playgroud)

还有无能的新手指南的Hello World在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,'$'
Run Code Online (Sandbox Code Playgroud)

将其构建为.com可执行文件,以便将cs:100h所有段寄存器加载到彼此相同的位置(微小的内存模型).

祝好运.

  • 错误.C库本身显然可以,所以它是可能的.事实上,这只是稍微困难一些.您只需要使用正确的5个参数调用WriteConsole(). (23认同)
  • 问题明确提到"不使用C库" (20认同)
  • 虽然第二个示例不调用任何C库函数,但它也不是Windows程序.将触发虚拟DOS机器来运行它. (9认同)
  • @Alex Hart,他的第二个例子是DOS,而不是Windows.在DOS中,微小模式的程序(.COM文件,64Kb总代码+数据+堆栈)从0x100h开始,因为段中的前256个字节由PSP(命令行参数等)获取.请参阅此链接:http://en.wikipedia.org/wiki/Program_Segment_Prefix (6认同)
  • 这不是要求的.第一个示例使用C库,第二个示例是MS-DOS,而不是Windows. (6认同)
  • 是的,第二个例子是经典之一.技术上中断0x21是DOS"API"的一部分 - 中断指向DOS启动时安装到内存中的一大块代码,不像BIOS映射中断甚至在加载操作系统之前工作.然而,对于ah = 0x09子函数来说,关键是字符串以$结尾(否则它只是从内存开始写入垃圾).或者您也可以使用ah = 0x40函数,为其指定字符数(此函数也用于写入文件,因为"屏幕"只是另一个管道) (5认同)
  • @OP“如何在 Windows 上不借助 C 函数来编写和编译 hello world?” (2认同)

Phi*_*hiS 18

这些是使用Windows API调用的Win32和Win64示例.它们是用于MASM而不是NASM,但看看它们.你可以找到更多的细节文章.

;---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
Run Code Online (Sandbox Code Playgroud)

要使用MASM汇编和链接这些,请将其用于32位可执行文件:

;---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
Run Code Online (Sandbox Code Playgroud)

或者对于64位可执行文件:

ml.exe [filename] /link /subsystem:windows 
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
Run Code Online (Sandbox Code Playgroud)

  • 为什么 rsp 需要 0x28 字节而不是 0x20?调用约定上的所有引用都说它应该是 32,但实际上似乎需要 40。 (2认同)

cev*_*ing 14

Flat Assembler不需要额外的链接器.这使汇编程序编程变得非常简单.它也适用于Linux.

hello.asm来自Fasm的例子:

include 'win32ax.inc'

.code

  start:
    invoke  MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
    invoke  ExitProcess,0

.end start
Run Code Online (Sandbox Code Playgroud)

Fasm创建一个可执行文件:

>fasm hello.asm
flat assembler  version 1.70.03  (1048575 kilobytes memory)
4 passes, 1536 bytes.

这是IDA的计划:

在此输入图像描述

你可以看到三个电话:GetCommandLine,MessageBoxExitProcess.


rar*_*ias 10

要使用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
Run Code Online (Sandbox Code Playgroud)

如果此代码保存在例如"test64.asm"上,则编译:

nasm -f win64 test64.asm
Run Code Online (Sandbox Code Playgroud)

生成"test64.obj"然后从命令提示符链接:

path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain  /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
Run Code Online (Sandbox Code Playgroud)

其中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:没有选项是必须避免链接器抱怨地址为long(对于这种情况下的user32.lib).此外,正如在此处所做的那样,如果从命令提示符调用Visual的链接器,则必须先设置环境(运行一次vcvarsall.bat和/或查看MS C++ 2010和mspdb100.dll)).

  • 我强烈建议在文件顶部使用“default rel”,以便这些寻址模式(“[msg]”和“[title]”)使用 RIP 相对寻址而不是 32 位绝对寻址。 (3认同)

Cap*_*ult 6

除非您调用某些函数,否则这并非易事。(而且,严重的是,调用printf和调用win32 api函数之间在复杂性方面没有真正的区别。)

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

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

请注意,实际上,在ASM中,所有这些东西一直到硬件都比在C中更有趣。“ hello world”程序归结为函数调用。关于ASM的一件好事是,您可以相当轻松地使用任何您想要的ABI。您只需要知道ABI是什么。

  • 我不明白 kernel32.dll 为何不是 C(或至少是 C++)库。对于这个提问者(或其他提出类似问题的人)真正想问的内容有合理的解释。“...例如kernel32.dll”是一个相当不错的。(“eg int 21h”是我隐式采用的,现在显然已经过时了,但 2009 年 64 位 Windows 是例外。)这里的其他答案有效地涵盖了这些内容;这个答案的目的是指出这不是一个正确的问题。 (3认同)
  • 这完全是重点。张贴者询问“在Windows下”运行的汇编程序。这意味着可以使用Windows工具(例如kernel32.dll),但不能使用Cygwin下的其他工具(如libc)。为了大声喊叫,张贴者明确说没有c-libraries。 (2认同)

小智 6

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

nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助。


Alb*_*rst 6

最好的示例是那些具有fasm的示例,因为fasm不使用链接器,后者通过另一个不透明的复杂性层隐藏了Windows编程的复杂性。如果您对写入gui窗口的程序感到满意,那么fasm的example目录中有一个示例。

如果需要控制台程序,则还可以重定向标准输入和标准输出。有一个(不十分实用的)示例程序不使用gui,而是严格与控制台一起使用,这本身就是fasm。这可以简化为要点。(我已经编写了第四个编译器,这是另一个非GUI示例,但它也不是简单的)。

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

FORMAT PE CONSOLE 
Run Code Online (Sandbox Code Playgroud)

名为“ .idata”的节包含一个表,该表可在启动过程中帮助Windows将功能名称与运行时地址耦合。它还包含对Windows操作系统KERNEL.DLL的引用。

 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
Run Code Online (Sandbox Code Playgroud)

该表格式由Windows强制执行,并且包含启动程序时在系统文件中查找的名称。FASM隐藏了rva关键字的某些复杂性。因此,_ExitProcess @ 4是fasm标签,_exitProcess是Windows查找的字符串。

您的程序位于“ .text”部分。如果您声明该部分可读可写且可执行,则这是您唯一需要添加的部分。

    section '.text' code executable readable writable
Run Code Online (Sandbox Code Playgroud)

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

总结:有一个带有asci名称的表与Windows OS耦合。在启动过程中,该表将转换为可调用地址表,供您在程序中使用。