Skip to content

Instantly share code, notes, and snippets.

@yellowbyte
Last active March 8, 2024 21:44
Show Gist options
  • Star 83 You must be signed in to star a gist
  • Fork 18 You must be signed in to fork a gist
  • Save yellowbyte/d91da3c3b0bc3ee6d1d1ac5327b1b4b2 to your computer and use it in GitHub Desktop.
Save yellowbyte/d91da3c3b0bc3ee6d1d1ac5327b1b4b2 to your computer and use it in GitHub Desktop.
how to assemble assembly with NASM assembler to 32-bit or 64-bit ELF binary with or without libc

32-bit ELF binary

how to assemble and link:

nasm -f elf32 -o <filename>.o <filename>.asm
ld -m elf_i386 -o <filename> <filename>.o

template code (hello world):

section .text
global _start

%define system_call int 0x80

_start:
    mov ebx, 0x1
    mov ecx, hello
    mov edx, helloLen
    mov eax, 0x4
    system_call

    xor ebx, ebx
    mov eax, 0x1
    system_call

section .data
    hello db "Hello World", 0xa
    helloLen equ $-hello

64-bit ELF binary

how to assemble and link:

nasm -f elf64 -o <filename>.o <filename>.asm
ld -o <filename> <filename>.o

template code (hello world):

section .text
global _start

_start:
    mov rdi, 0x1
    mov rsi, hello
    mov rdx, helloLen
    mov rax, 0x1
    syscall

    xor rdi, rdi
    mov rax, 0x3c
    syscall

section .data
    hello db "Hello World", 0xa
    helloLen equ $-hello

32-bit ELF binary with libc

how to assemble and link:

nasm -f elf32 -o <filename>.o <filename>.asm
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o <filename> -lc <filename>.o

template code (hello world):

extern puts
extern exit

section .text
global _start

%macro call_func 1
    call %1
    add esp, 0x4
%endmacro

_start:
    push hello
    call_func puts

    mov eax, 0xa
    call exit

section .data
    hello db "Hello World"

64-bit ELF binary with libc

how to assemble and link:

nasm -f elf64 -o <filename>.o <filename>.asm
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o <filename> -lc <filename>.o

template code (hello world):

extern puts
extern exit

section .text
global _start

%macro call_func 1
    call %1
%endmacro

_start:
    mov rdi, hello
    call_func puts

    mov rax, 0xa 
    call exit

section .data
    hello db "Hello World"

NOTE :

  • Assembler Directives: instructions for the assembler (e.g. NASM) and not a part of the ISA
    • extern: declare a symbol which is defined in another module that will later be linked with current one
    • section: identifies the section the code you write will go into. Examples of common ones are .text, .data, and .bss
    • global: defines symbols such that other modules that EXTERN those symbols can correctly reference them after linking
    • macro: a name that represents a snippet of code
    • define: a single-line macro
@coffeehat
Copy link

Thank you, your template code helps me a lot

@yellowbyte
Copy link
Author

No problem. Thank you for the feedback :)

@s-ff
Copy link

s-ff commented Aug 30, 2021

I made a simple Makefile based on this to automate the build.

@yellowbyte
Copy link
Author

Great idea! Thanks for sharing

@rickhenderson
Copy link

This was very useful. I've seen another example where the code loops through the message byte by byte, without using syscall but it might have been for 16bit code.

@yellowbyte
Copy link
Author

yellowbyte commented Oct 9, 2021

Glad you found it useful :) And regarding the other example, if you find it in the future do share! I think that, even if it processes the message byte by byte, as long as it prints to console it will need to use system call (unless the code is supposed to run in kernel mode, or on a non-multitasking operating system). You are probably right about the code being for 16bit code though, since syscall is an instruction only available for x86-64. Both 16bit and 32bit x86 code use different instructions to make a system call (e.g., int 0x80).

@rickhenderson
Copy link

I believe it was meant to run in kernel mode. It called an interrupt. Is that something you'd still want?

@yellowbyte
Copy link
Author

yellowbyte commented Oct 12, 2021

Ohh no need to take too much time trying to find it. If you just happen to come across it again in the future, feel free to share :)

@torbjo
Copy link

torbjo commented May 30, 2022

There is (at least) one bug in the "64-bit ELF binary with libc" example:

The argument to 'puts' is passed in an register (not the stack), so the stack pointer should not be adjusted after the call!

Try returning from '_start' instead of calling libc's 'exit()'. Then it will fail miserably because the stack is messed up.

@yellowbyte
Copy link
Author

Oops, thank you for catching that @torbjo !! Code updated.

@metablaster
Copy link

Thank you a lot for this! very useful for a beginner.

@yellowbyte
Copy link
Author

Glad to hear :)

@b1uerry
Copy link

b1uerry commented May 23, 2023

Thanks a lot !

@yellowbyte
Copy link
Author

For sure!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment