Tutorial :How to write hello world in assembler under Windows?


I wanted to write something basic in assembly under Windows, I'm using NASM, but I can't get anything working.

How to write and compile hello world without the help of C functions on Windows?


NASM examples.

Calling libc stdio printf, implementing 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  

Then run

nasm -fwin32 helloworld.asm  gcc helloworld.obj  a  

There's also The Clueless Newbies Guide to Hello World in Nasm without the use of a C library. Then the code would look like this.

16-bit code with MS-DOS system calls: works in DOS emulators or in 32-bit Windows with NTVDM support. Can't be run "directly" (transparently) under any 64-bit Windows, because an x86-64 kernel can't use vm86 mode.

org 100h  mov dx,msg  mov ah,9  int 21h  mov ah,4Ch  int 21h  msg db 'Hello, World!',0Dh,0Ah,'$'  

Build this into a .com executable so it will be loaded at cs:100h with all segment registers equal to each other (tiny memory model).

Good luck.


This example shows how to go directly to the Windows API and not link in the C Standard Library.

    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:  

To compile, you'll need NASM and LINK.EXE (from Visual studio Standard Edition)

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


These are Win32 and Win64 examples using Windows API calls. They are for MASM rather than NASM, but have a look at them. You can find more details in this article.

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

To assemble and link these using MASM, use this for 32-bit executable:

ml.exe [filename] /link /subsystem:windows   /defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main  

or this for 64-bit executable:

ml64.exe [filename] /link /subsystem:windows   /defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main  


Flat Assembler does not need an extra linker. This makes assembler programming quite easy. It is also available for Linux.

This is hello.asm from the Fasm examples:

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 creates an executable:

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

And this is the program in IDA:

enter image description here

You can see the three calls: GetCommandLine, MessageBox and ExitProcess.


To get an .exe with NASM'compiler and Visual Studio's linker this code works fine:

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  

If this code is saved on e.g. "test64.asm", then to compile:

nasm -f win64 test64.asm  

Produces "test64.obj" Then to link from command prompt:

path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain  /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no  

where path_to_link could be C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin or wherever is your link.exe program in your machine, path_to_libs could be C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64 or wherever are your libraries (in this case both kernel32.lib and user32.lib are on the same place, otherwise use one option for each path you need) and the /largeaddressaware:no option is necessary to avoid linker's complain about addresses to long (for user32.lib in this case). Also, as it is done here, if Visual's linker is invoked from command prompt, it is necessary to setup the environment previously (run once vcvarsall.bat and/or see MS C++ 2010 and mspdb100.dll).


Unless you call some function this is not at all trivial. (And, seriously, there's no real difference in complexity between calling printf and calling a win32 api function.)

Even DOS int 21h is really just a function call, even if its a different API.

If you want to do it without help you need to talk to your video hardware directly, likely writing bitmaps of the letters of "Hello world" into a framebuffer. Even then the video card is doing the work of translating those memory values into VGA/DVI signals.

Note that, really, none of this stuff all the way down to the hardware is any more interesting in ASM than in C. A "hello world" program boils down to a function call. One nice thing about ASM is that you can use any ABI you want fairly easy; you just need to know what that ABI is.


If you want to use NASM and Visual Studio's linker (link.exe) with anderstornvig's Hello World example you will have to manually link with the C Runtime Libary that contains the printf() function.

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

Hope this helps someone.


The best examples are those with fasm, because fasm doesn't use a linker, which hides the complexity of windows programming by another opaque layer of complexity. If you're content with a program that writes into a gui window, then there is an example for that in fasm's example directory.

If you want a console program, that allows redirection of standard in and standard out that is also possible. There is a (helas highly non-trivial) example program available that doesn't use a gui, and works strictly with the console, that is fasm itself. This can be thinned out to the essentials. (I've written a forth compiler which is another non-gui example, but it is also non-trivial).

Such a program has the following command to generate a proper executable header, normally done by a linker.


A section called '.idata' contains a table that helps windows during startup to couple names of functions to the runtimes addresses. It also contains a reference to KERNEL.DLL which is the Windows Operating System.

 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  

The table format is imposed by windows and contains names that are looked up in system files, when the program is started. FASM hides some of the complexity behind the rva keyword. So _ExitProcess@4 is a fasm label and _exitProcess is a string that is looked up by Windows.

Your program is in section '.text'. If you declare that section readable writeable and executable, it is the only section you need to add.

    section '.text' code executable readable writable  

You can call all the facilities you declared in the .idata section. For a console program you need _GetStdHandle to find he filedescriptors for standard in and standardout (using symbolic names like STD_INPUT_HANDLE which fasm finds in the include file win32a.inc). Once you have the file descriptors you can do WriteFile and ReadFile. All functions are described in the kernel32 documentation. You are probably aware of that or you wouldn't try assembler programming.

In summary: There is a table with asci names that couple to the windows OS. During startup this is transformed into a table of callable addresses, which you use in your program.

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »