Skip to content

Instantly share code, notes, and snippets.

@typeswitch-dev
Created September 2, 2022 17:07
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save typeswitch-dev/700d4d3fc5226bd9949c2da8b0306905 to your computer and use it in GitHub Desktop.
Save typeswitch-dev/700d4d3fc5226bd9949c2da8b0306905 to your computer and use it in GitHub Desktop.
Minimal win64 executable in NASM assembly.
org 0 ; We use "org 0" so Relative Virtual Addresses (RVAs) are easy.
; This means that when we want an absolute Virtual Address we have
; to add IMAGE_BASE to the RVA (or whatever the base of that section is)
IMAGE_BASE equ 0x400000
SECT_ALIGN equ 0x200
FILE_ALIGN equ 0x200
msdos_header:
.magic db 'MZ'
.cblp dw 0x0090 ; bytes on last page of file
.cp dw 0x0003 ; pages in file
.crlc dw 0x0000 ; relocations
.cparhdr dw 0x0004 ; size of header in paragraphs
.minalloc dw 0x0000 ; minimum extra paragraphs needed
.maxalloc dw 0xFFFF ; maximum extra paragraphs needed
.ss dw 0x0000 ; initial (relative) SS value
.sp dw 0x00B8 ; initial SP value
.csum dw 0x0000 ; checksum
.ip dw 0x0000 ; initial IP value
.cs dw 0x0000 ; initial (relative) CS value
.lfarlc dw 0x0040 ; file address of relocation table
.ovno dw 0x0000 ; overlay number
.res dw 0,0,0,0 ; reserved words
.oemid dw 0x0000 ; OEM identifier
.oeminfo dw 0x0000 ; OEM information
.res2 times 10 dw 0 ; reserved words
.lfanew dd pe_header ; relative address of PE header
bits 16
msdos_program:
.entry push cs
pop ds
mov dx, .message - .entry
mov ah, 0x09
int 0x21
mov ax, 0x4C01
int 0x21
.message db "This program cannot be run in DOS mode.", 0xD, 0xD, 0xA, '$'
times 0x80 + msdos_header - $ db 0x00
pe_header:
.magic db 'PE', 0, 0
.machine dw 0x8664 ; x86-64
.nsections dw 1 ; number of sections
.timestamp dd 0 ; 32-bit timestamp for program
.symtable dd 0 ; symbol table location (0 for image file)
.nsymbols dd 0 ; number of symbols (0 for image file)
.opthdrsize dw opt_header.end - opt_header ; size of "optional" header
.chrctrstcs dw 0x0022 ; 0x0002 = IMAGE_FILE_EXECUTABLE_IMAGE
; 0x0020 = IMAGE_FILE_LARGE_ADDRESS_AWARE
opt_header:
.magic dw 0x020B ; PE32+ (i.e. a 64-bit PE file)
.linkver db 0, 0
.codesize dd code_section_end - code_section ; size of code section
.datasize dd 0 ; size initialized data
.bsssize dd 0 ; size uninitialized data
.entry dd main ; relative address of entry point
.codebase dd code_section ; relative address of code section
.imagebase dq IMAGE_BASE
.sectalign dd SECT_ALIGN ; alignment of sections in VM
.filealign dd FILE_ALIGN ; alignment of sections in file
.osver dw 6, 0
.imgver dw 0, 0
.subsysver dw 6, 0
.win32ver dd 0
.sizeimage dd image_end ; size of image (multiple of SECT_ALIGN)
.sizehdrs dd headers_end ; size of headers (multiple of FILE_ALIGN)
.checksum dd 0
.subsystem dw 3 ; windows console subsystem (2 for gui)
.dllchars dw 0x8140 ; 0x0040 = DLL can be relocated at load time
; 0x0100 = image is NX compatible
; 0x8000 = Terminal server aware
.stkresv dq 0x100000
.stkcommit dq 0x1000
.heapresv dq 0x100000
.heapcommit dq 0x1000
.loaderflgs dd 0 ; reserved, must be 0
.nrvasize dd (.end-.edata)/8 ; number of entries in "data directory"
.edata dd 0, 0 ; (which is still part of optional header)
.idata dd import_table
dd import_table.end - import_table
.res dd 0, 0
.exn dd 0, 0
.sec dd 0, 0
.reloc dd 0, 0
.debug dd 0, 0
.arch dd 0, 0
.gp dd 0, 0
.tls dd 0, 0
.lconfig dd 0, 0
.bimport dd 0, 0
.iat dd iat
dd iat.end - iat
.dimport dd 0, 0
.com dd 0, 0
.resvd dd 0, 0
.end:
code_section_header:
.name db ".text",0,0,0
.vsize dd code_section_end - code_section
.vaddr dd code_section
.psize dd code_section_end - code_section
.paddr dd code_section
.preloc dd 0
.plines dd 0
.nreloc dw 0
.nlines dw 0
.chars dd 0xE0000020 ; 0x00000020 = IMAGE_SCN_CNT_CODE
; 0x20000000 = IMAGE_SCN_MEM_EXECUTE
; 0x40000000 = IMAGE_SCN_MEM_READ
; 0x80000000 = IMAGE_SCN_MEM_WRITE
.end align FILE_ALIGN
headers_end: align SECT_ALIGN ; headers must be file aligned
code_section: ; section must be section aligned
import_table:
.ilt dd ilt
.ts dd 0
.fc dd 0
.name dd names.kernel32_dll
.iat dd iat
.null dd 0,0,0,0,0
.end align 8
ilt:
.ExitProcess dq names.ExitProcess
.CreateFileA dq names.CreateFileA
.WriteFile dq names.WriteFile
.null dq 0
.end align 8
iat:
.ExitProcess dq names.ExitProcess
.CreateFileA dq names.CreateFileA
.WriteFile dq names.WriteFile
.null dq 0
.end align 8
names: ; each entry should be aligned on an even boundary by zero-padding
; also a 2 byte "hint" is needed for imported function names
; -- using 0,0 is fine, the loader falls back to binary search
.kernel32_dll db "kernel32.dll",0,0
.ExitProcess db 0,0,"ExitProcess",0
.CreateFileA db 0,0,"CreateFileA",0
.WriteFile db 0,0,"WriteFile",0
.end align 8
bits 64
; PROGRAM ENTRY
main: sub rsp, 32 ; "the shadow space"
mov rcx, 42
.bye call [rel iat.ExitProcess]
jmp .bye
align FILE_ALIGN
code_section_end:
align SECT_ALIGN
image_end:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment