ELF Mastery Roadmap

Reverse Engineer + Kernel Developer. A milestone-driven path. Each milestone lists what to learn, what to read, and a validation challenge you must complete before moving on. Two tracks converge: RE (understand & manipulate binaries you didn't write) and KERNEL (know how the OS actually parses, loads, and runs ELF).

SHARED CORE 0–4 → RE TRACK 5A / KERNEL TRACK 5B → CAPSTONE 6
How to use 0 Toolchain 1 ELF header 2 Static linking 3 Dynamic linking 4 Loading 5A RE track 5B Kernel track 6 Capstone Practice Quick reference
shared core · ~1–2 weeks per milestone M0 Toolchain M1 ELF header M2 Static linking M3 Dynamic linking M4 Loading & exec 5A · RE TRACK analysis · obfuscation · forensics 5B · KERNEL TRACK modules · vDSO · core dumps M6 Capstone practice grounds (ongoing) — crackmes.one · pwnable.kr/tw · ROP Emporium · exploit.education · Nightmare
Do milestones in order; 0→4 are the shared core, 5 splits into tracks, both converge at the capstone.
Primary reference always open in a tab: /usr/include/elf.h and the System V ABI + x86-64 psABI PDFs.

i How to use this

0 Toolchain & mental model Milestone 0

Goal: know what a compiler, assembler, and linker each produce, and read raw bytes.

Learn

compilation pipeline (.c → .s → .o → a.out), the difference between the linking view (sections) and the execution view (segments/program headers).

Read (books)
  • CS:APP (Computer Systems: A Programmer's Perspective) — Ch. 7 Linking. Best on-ramp.
  • Linkers and Loaders, John Levine — Ch. 1–3.
Read (man)
  • elf(5) — read it three times; it is the map for everything below.
  • gcc(1) (-c, -S, -static, -no-pie, -nostdlib), as(1), ld(1).
Tools to install & try

readelf, objdump, nm, size, file, xxd/hexdump, strings. (Debian: binutils, elfutils.)

Challenge 0: Compile int main(){return 42;} four ways: default, -static, -no-pie, and -nostdlib (with a hand-written _start). For each, run readelf -h, -l, -S and explain in cheatsheet.md why the type, entry point, and segment count differ. Confirm exit code with echo $?.

1 The ELF header & structure by hand Milestone 1

Goal: decode the ELF header, section headers, and program headers from raw bytes, without tools.

Learn

Elf64_Ehdr, Elf64_Shdr, Elf64_Phdr field-by-field; e_type (REL/EXEC/DYN/CORE), section types (SHT_*), section flags (SHF_*), segment types (PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_GNU_STACK, PT_NOTE).

Read

elf(5) again (now for the structs), and skim the ELF spec (TIS v1.2 or System V ABI Ch. 4–5). Study /usr/include/elf.h.

Tools

readelf -h -S -l -x, xxd.

Source to navigate
  • /usr/include/elf.h — canonical struct definitions.
  • Linux include/uapi/linux/elf.h — the kernel's copy (compare the two).
Challenge 1: Write a C (or Python) program myreadelf that mmaps an ELF file and prints the -h header and -S section table matching readelf output. Validate by diffing your output against readelf on /bin/ls, a .o, and a .so.

2 Static linking, symbols & relocations Milestone 2

Goal: understand object files, symbol tables, and how the linker patches addresses.

Learn

.symtab/.strtab, symbol binding (LOCAL/GLOBAL/WEAK) & types (FUNC/OBJECT/SECTION), SHN_UNDEF/SHN_ABS/SHN_COMMON; relocation entries (Elf64_Rela), relocation types (R_X86_64_PC32, R_X86_64_PLT32, R_X86_64_64), the meaning of A/S/P in reloc formulas; static libraries (.a).

Read
  • Linkers and Loaders — Ch. 4–7 (symbols, relocation, libraries).
  • CS:APP Ch. 7 (relocation section).
Read (man)

nm(1), ar(1), ranlib(1), ld(1), strip(1).

Tools

nm, readelf -r -s, objdump -dr, ar.

Source

binutils bfd/ (skim) and ld/ to see how a real linker is organized.

Challenge 2: Compile two .o files where one calls a function in the other, without linking. Using readelf -r and objdump -dr, find the relocation for the call site, compute the final address the linker will write by hand, then link and confirm the disassembled call matches your computation.

3 Dynamic linking, PLT/GOT & the loader Milestone 3

Goal: understand shared objects, lazy binding, and runtime symbol resolution.

Learn

PT_INTERP / ld.so, .dynamic (DT_NEEDED, DT_RELA, DT_JMPREL, DT_PLTGOT, DT_SYMTAB, DT_HASH/DT_GNU_HASH), .plt/.got/.got.plt, lazy vs. eager binding (LD_BIND_NOW), symbol interposition, RUNPATH/RPATH, PIE and relocation types R_X86_64_GLOB_DAT/R_X86_64_JUMP_SLOT/R_X86_64_RELATIVE, IFUNC.

Read
  • Linkers and Loaders — Ch. 8–10 (dynamic linking, loading).
  • Ulrich Drepper, How To Write Shared Libraries (free PDF) — essential.
  • Learning Linux Binary Analysis (Ryan "elfmaster" O'Neill) — Ch. 1–2.
Read (man)

ld.so(8), dlopen(3), dlsym(3), dl_iterate_phdr(3), ldd(1).

Tools

readelf -d, objdump -R, ltrace, LD_DEBUG=all ./prog, patchelf.

Source to navigate (pick musl first — it's readable)
  • musl ldso/dynlink.c — the whole dynamic linker in one clean file.
  • glibc elf/rtld.c, elf/dl-load.c, elf/dl-runtime.c, sysdeps/x86_64/dl-trampoline.S.
Challenge 3: In gdb, set a breakpoint in main, print .got.plt entries before and after the first call to a libc function, and watch a JUMP_SLOT get resolved (use LD_DEBUG=bindings to cross-check). Then write an LD_PRELOAD library that intercepts malloc (or puts) and logs calls — prove interposition works.

4 Loading & execution (kernel + userspace loader) Milestone 4

Goal: know exactly what happens between execve() and your _start running.

Learn

execve path, how the kernel maps PT_LOAD segments, sets up the stack (argv/envp/auxv), hands control to ld.so, then to _start; AT_* auxiliary vector entries; brk/mmap layout, ASLR.

Read
  • Linux System Programming (Robert Love) — process & exec.
  • APUE — process control chapters.
  • LWN articles: "How programs get run: ELF binaries" (Lameter/Corbet).
Read (man)

execve(2), mmap(2), getauxval(3), vdso(7), proc(5) (maps/auxv).

Source to navigate (the crown jewel)
  • Linux fs/binfmt_elf.c — load_elf_binary(), load_elf_interp(), create_elf_tables() (stack/auxv setup), elf_map(). Read it line by line.
  • fs/binfmt_elf_fdpic.c (no-MMU variant) — optional contrast.
  • fs/exec.c — do_execve / search_binary_handler.
Challenge 4: Write a userspace ELF loader in C: mmap the PT_LOAD segments of a static, no-PIE executable with correct perms, set up the stack + auxv, and jump to e_entry. Run /bin/busybox or your own static binary with it. (Stretch: handle PIE by processing R_X86_64_RELATIVE.) Then read binfmt_elf.c and annotate where your loader diverges from the kernel's.

5A RE TRACK: analysis, obfuscation, forensics Milestone 5A

Goal: analyze hostile/unknown binaries; manipulate ELF structure.

Learn

stripped binaries, section vs. segment reconstruction, packing (UPX), anti-debugging/anti-disassembly, ELF infection techniques (PT_NOTE→PT_LOAD, text padding, reverse-text), symbol recovery, DWARF basics.

Read
  • Learning Linux Binary Analysis — Ch. 3–8 (the ELF virus/forensics core).
  • Practical Binary Analysis (Dennis Andriesse) — Ch. 1–7, 12–13.
  • Programming Linux Anti-Reversing Techniques (Jonathan Thomas).
Read (man)

strace(1), ltrace(1), ptrace(2), objcopy(1).

Tools to master

gdb + pwndbg/GEF, Ghidra (free), rizin/Cutter or radare2, checksec, LIEF (Python ELF manipulation), pyelftools, pahole, llvm-dwarfdump, capstone/keystone.

Source

LIEF and pyelftools source — great references for parsing edge cases.

Challenge 5A:
  1. Pack a binary with UPX, then unpack it manually in gdb (dump memory after the stub decompresses, rebuild a runnable ELF).
  2. Take a stripped binary and recover function boundaries + rename them in Ghidra.
  3. Use LIEF to add a section and inject a code cave that prints a message before main, and have it still run.
  4. Solve 5 Linux crackmes from crackmes.one (start "easy", work up).

5B KERNEL TRACK: modules, vDSO, core dumps Milestone 5B

Goal: understand ELF as the kernel consumes and produces it.

Learn

kernel module format (relocatable ELF .ko, MODINFO, module relocations, __ksymtab), how the kernel resolves module symbols; vDSO construction & mapping; core dump generation (PT_NOTE with NT_PRSTATUS, NT_PRPSINFO, register/aux state); kallsyms; the difference between vmlinux (ELF) and bzImage.

Read
  • Linux Kernel Development (Robert Love) — modules chapter.
  • Understanding the Linux Kernel (Bovet & Cesati) — program execution chapter.
  • LWN: vDSO and module-loading articles.
Read (man)

core(5), vdso(7), gcore(1), insmod(8)/modprobe(8), kallsyms.

Source to navigate
  • kernel/module/main.c (older trees: kernel/module.c) — load_module(), simplify_symbols(), apply_relocations(), layout_sections().
  • arch/x86/kernel/module.c — apply_relocate_add() (kernel-side relocs!).
  • arch/x86/entry/vdso/ — vDSO build & vdso2c.
  • fs/binfmt_elf.c — elf_core_dump() (core generation path).
Challenge 5B:
  1. Write a "hello" kernel module; then dissect its .ko with readelf -Srs and map each section/reloc to what load_module() does with it.
  2. Extract the running vDSO from /proc/self/maps, dump it, and disassemble __vdso_gettimeofday.
  3. Trigger a core dump (ulimit -c unlimited), then write a tool that parses the PT_NOTE and prints the crashing thread's registers (compare with gdb core).

6 Capstone (choose per track or do both) Milestone 6

RE capstone: Write your own ELF analysis tool (in C or Python) that, for an arbitrary binary, prints header/segments/sections, resolves dynamic symbols & PLT/GOT targets, flags suspicious traits (RWX segments, packed entropy, unusual PT_LOAD), and dumps DWARF function info if present. Test on real malware samples in a VM or on CTF binaries.

Kernel capstone: Extend your Milestone 4 loader into a working binfmt-style interpreter, OR patch/instrument fs/binfmt_elf.c in a kernel build to log every segment it maps for a given binary, boot it in QEMU, and verify against your userspace loader's behavior.

★ Practice grounds (ongoing)

⚑ Quick reference — the essential set

Books

CS:APP (Ch.7) · Levine Linkers and Loaders · elfmaster Learning Linux Binary Analysis · Andriesse Practical Binary Analysis · Drepper How To Write Shared Libraries · Love Linux Kernel Development + Linux System Programming.

Specs

System V ABI · x86-64 psABI · TIS ELF v1.2 · DWARF 5.

Man pages

elf(5) · ld.so(8) · ld(1) · as(1) · nm(1) · ar(1) · objdump(1) · readelf(1) · strip(1)/objcopy(1) · execve(2) · mmap(2) · dlopen(3) · dl_iterate_phdr(3) · getauxval(3) · vdso(7) · core(5) · ptrace(2) · proc(5) · strace(1)/ltrace(1).

Tools

binutils (readelf/objdump/nm/objcopy) · elfutils · gdb+pwndbg/GEF · Ghidra · rizin/radare2 · LIEF · pyelftools · patchelf · pahole · llvm-dwarfdump · checksec · ltrace/strace.

Source

/usr/include/elf.h · musl ldso/dynlink.c · glibc elf/rtld.c · Linux fs/binfmt_elf.c · Linux kernel/module/main.c · arch/x86/entry/vdso/ · binutils bfd/.