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).
/usr/include/elf.h and the
System V ABI + x86-64 psABI PDFs.
lab/.cheatsheet.md as you go (offsets, struct layouts, gotchas).Goal: know what a compiler, assembler, and linker each produce, and read raw bytes.
compilation pipeline (.c → .s → .o → a.out), the difference between the
linking view (sections) and the execution view (segments/program headers).
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).readelf, objdump, nm, size, file, xxd/hexdump,
strings. (Debian: binutils, elfutils.)
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 $?.
Goal: decode the ELF header, section headers, and program headers from raw bytes, without tools.
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).
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.
readelf -h -S -l -x, xxd.
/usr/include/elf.h — canonical struct definitions.include/uapi/linux/elf.h — the kernel's copy (compare the two).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.
Goal: understand object files, symbol tables, and how the linker patches addresses.
.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).
nm(1), ar(1), ranlib(1), ld(1), strip(1).
nm, readelf -r -s, objdump -dr, ar.
binutils bfd/ (skim) and ld/ to see how a real linker is organized.
.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.
Goal: understand shared objects, lazy binding, and runtime symbol resolution.
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.
ld.so(8), dlopen(3), dlsym(3), dl_iterate_phdr(3), ldd(1).
readelf -d, objdump -R, ltrace, LD_DEBUG=all ./prog, patchelf.
ldso/dynlink.c — the whole dynamic linker in one clean file.elf/rtld.c, elf/dl-load.c, elf/dl-runtime.c, sysdeps/x86_64/dl-trampoline.S.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.
Goal: know exactly what happens between execve() and your _start running.
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.
execve(2), mmap(2), getauxval(3), vdso(7), proc(5) (maps/auxv).
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.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.
Goal: analyze hostile/unknown binaries; manipulate ELF structure.
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.
strace(1), ltrace(1), ptrace(2), objcopy(1).
gdb + pwndbg/GEF, Ghidra (free), rizin/Cutter or
radare2, checksec, LIEF (Python ELF manipulation), pyelftools, pahole,
llvm-dwarfdump, capstone/keystone.
LIEF and pyelftools source — great references for parsing edge cases.
main, and have it still run.Goal: understand ELF as the kernel consumes and produces it.
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.
core(5), vdso(7), gcore(1), insmod(8)/modprobe(8), kallsyms.
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)..ko with readelf -Srs and map
each section/reloc to what load_module() does with it./proc/self/maps, dump it, and disassemble
__vdso_gettimeofday.ulimit -c unlimited), then write a tool that parses the
PT_NOTE and prints the crashing thread's registers (compare with gdb core).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.
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.
System V ABI · x86-64 psABI · TIS ELF v1.2 · DWARF 5.
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).
binutils (readelf/objdump/nm/objcopy) · elfutils · gdb+pwndbg/GEF ·
Ghidra · rizin/radare2 · LIEF · pyelftools · patchelf · pahole · llvm-dwarfdump ·
checksec · ltrace/strace.
/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/.