www.xbdev.net
xbdev - software development
Friday May 22, 2026
Home | Contact | Support | PE File Format... Inside with NASM Assembler...
     
 

PE File Format...

Inside with NASM Assembler...

 

Little PE Executable


To get the ball rolling - let's write a bare minimum executable in assembly using NASM - I mean really bare - just the essentials to create a valid executable that runs and closes - we won't include any libraries or complex code - it will be just the PE structures (headers/offsets) - code to return and exit and that's all. This helps you focus solely on the basic PE structure without getting into the import table complexities.

; Create the absolute minimal PE executable that does (almost) 
; nothing — just starts, and then returns cleanly.
; nasm -f bin hellope.asm -o hellope.exe

BITS 32
org 0x400000

start:
    ; === DOS Header ===
    db 'M','Z'                         ; DOS Signature
    times 58 db 0
    dd 0x80                            ; Offset to PE header

    times 64-62 db 0

    ; === PE Header ===
    db 'P','E',0,0
    dw 0x014c                          ; Machine: Intel 386
    dw 1                               ; Number of sections
    dd 0                               ; Timestamp
    dd 0                               ; Symbol table ptr
    dd 0                               ; Symbol count
    dw 0x00e0                          ; Optional header size
    dw 0x0002                          ; Characteristics: Executable

    ; === Optional Header ===
    dw 0x010b                          ; Magic: PE32
    db 8, 0                            ; Linker version
    dd 0x200                           ; SizeOfCode
    dd 0x0                             ; SizeOfInitData
    dd 0x0                             ; SizeOfUninitData
    dd entry_point - start            ; EntryPoint RVA
    dd text_section - start           ; BaseOfCode
    dd 0x0                             ; BaseOfData
    dd 0x400000                        ; ImageBase
    dd 0x1000, 0x200                   ; Alignment (Virtual, File)
    dd 0x0                             ; OS version
    dd 0x0                             ; Image version
    dd 0x0                             ; Subsystem version
    dd 0x0                             ; Win32 version
    dd 0x1000                          ; SizeOfImage
    dd 0x200                           ; SizeOfHeaders
    dd 0x0                             ; Checksum
    dd 0x2                             ; Subsystem: GUI
    dw 0x0, 0x0                        ; DLL Characteristics
    dd 0x100000, 0x1000               ; Stack reserve & commit
    dd 0x100000, 0x1000               ; Heap reserve & commit
    dd 0x0, 0x10                       ; LoaderFlags, RVA count

    times 128 db 0                     ; Null Data Directory entries

    ; === Section Header (.text) ===
    db '.text', 0, 0, 0                ; Name
    dd 0x200                           ; Virtual size
    dd text_section - start           ; Virtual address
    dd 0x200                           ; SizeOfRawData
    dd 0x200                           ; PointerToRawData
    times 12 db 0
    dd 0x60000020                      ; Characteristics

    ; === Padding to align to 0x200 ===
    times (0x200 - ($ - start)) db 0

; === .text section ===
text_section:
entry_point:
    ; Do nothing and return to system
    ret

    ; Pad to 0x200 bytes
    times (0x200 - ($ - text_section)) db 0



You can run it on a Window's machine after assembling - you just double-click the
hellope.exe
- nothing will show up or happen - it'll just run and close.

This code sample might seem a bit useless - but it's a springboard - perfect for layering on more functionality as you explore the PE format.

Easy to add in more pieces - such as imports sections, .data section and so on.


Alert Box


To take things a bit further - let's write a complete executable (.exe) in the assembly. We can do it in a single file with no external libraries or resources. We can define all the headers, offsets and code - in raw hex/assembly. The exe will be for a Win32/64 - just something that runs and shows a message on screen.

You can type (or copy paste) the assembly into your favourite text editor - and type the nasm command to compile the exe.

The implementation is for a bare-bones Windows PE executable in raw x86 assembly that displays a MessageBox using MessageBoxA from user32.dll, with no includes or libraries. It manually crafts the import directory and uses a basic PE layout.

This will create a dialog box saying "Hello from ASM!":


; A minimal Windows PE that shows a MessageBoxA using raw assembly.
; Assemble with: nasm -f bin messagebox.asm -o messagebox.exe

BITS 32
org 0x400000

start:
    ; === DOS Header ===
    db 'M','Z'                         ; DOS signature
    times 58 db 0
    dd 0x80                            ; Offset to PE Header

    times 64 - ($ - start) db 0       ; Padding

    ; === PE Signature & COFF Header ===
    db 'P','E',0,0
    dw 0x014c                          ; Machine
    dw 1                               ; NumberOfSections
    dd 0                               ; TimeDateStamp
    dd 0                               ; PointerToSymbolTable
    dd 0                               ; NumberOfSymbols
    dw 0x00E0                          ; SizeOfOptionalHeader
    dw 0x0102                          ; Characteristics

    ; === Optional Header ===
    dw 0x010b                          ; Magic
    db 8, 0                            ; Linker version
    dd 0x200, 0x200, 0                 ; Sizes: Code, Data, BSS
    dd entry - start                  ; Entry point RVA
    dd code_rva - start, data_rva - start
    dd 0x400000                        ; ImageBase
    dd 0x1000, 0x200                   ; Section and file alignment
    times 16 db 0                      ; Padding
    dd 0x1000, 0x200                   ; SizeOfImage, SizeOfHeaders
    dd 0                               ; Checksum
    dd 2                               ; Subsystem (GUI)
    dw 0, 0                            ; DLL Characteristics
    dd 0x100000, 0x1000                ; Stack reserve, commit
    dd 0x100000, 0x1000                ; Heap reserve, commit
    dd 0, 16                           ; Loader flags, RVA count

    ; === Data Directories ===
    dd import_table - start, 0x40      ; Import Table RVA/Size
    times 15 dd 0,0

    ; === Section Header (.text) ===
    db '.text', 0, 0, 0
    dd 0x200, code_rva - start         ; VirtualSize, RVA
    dd 0x200, 0x200                    ; SizeOfRawData, PointerToRawData
    times 12 db 0
    dd 0x60000020                      ; Characteristics

    ; === Align to 0x200 ===
    times (0x200 - ($ - start)) db 0

; === Code section ===
code_rva:
entry:
    call get_eip
get_eip:
    pop esi
    sub esi, get_eip - import_lookup_user32

    push 0
    lea eax, [esi + msg_title - import_lookup_user32]
    push eax
    lea eax, [esi + msg_text - import_lookup_user32]
    push eax
    push 0
    mov eax, [esi + user32_iat - import_lookup_user32]
    call eax

    ; Exit via kernel32 ExitProcess
    push 0
    mov eax, [esi + kernel32_iat - import_lookup_user32]
    call eax

hang:
    jmp hang

; === Import section ===
import_table:
    dd import_lookup_user32 - start
    dd 0, 0
    dd user32_name - start
    dd user32_iat - start

    dd import_lookup_kernel32 - start
    dd 0, 0
    dd kernel32_name - start
    dd kernel32_iat - start

    dd 0, 0, 0, 0

import_lookup_user32:
    dd hint_messagebox - start
    dd 0

user32_iat:
    dd hint_messagebox - start
    dd 0

import_lookup_kernel32:
    dd hint_exitprocess - start
    dd 0

kernel32_iat:
    dd hint_exitprocess - start
    dd 0

; === Hints ===
hint_messagebox:
    dw 0
    db 'MessageBoxA', 0
    db 0

hint_exitprocess:
    dw 0
    db 'ExitProcess', 0
    db 0

; === Strings ===
msg_text:
    db 'Hello from ASM!', 0

msg_title:
    db 'Hi', 0

user32_name:
    db 'USER32.dll', 0

kernel32_name:
    db 'KERNEL32.dll', 0

; === Data padding ===
data_rva:


It’ll pop up a nice little alert box when launched.









 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2026 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.