Appendix J: Security and Analysis Tools Reference
This appendix covers the command-line and GUI tools used throughout Parts VI-VIII of this book for binary analysis, dynamic analysis, exploit development, and security assessment.
checksec
Purpose: Reports security mitigations present in a compiled binary.
Install: pip3 install checksec.py or apt install checksec
Usage
checksec --file=./binary
checksec --file=./binary --output=json # machine-readable output
Output Fields
| Field | Value | Meaning |
|---|---|---|
Arch |
amd64-64-little |
Architecture and byte order |
RELRO |
Full RELRO |
GOT is read-only after load |
RELRO |
Partial RELRO |
.init_array etc. are read-only; GOT writable |
RELRO |
No RELRO |
Nothing is made read-only |
Stack |
Canary found |
Stack canary in prologue/epilogue |
Stack |
No canary found |
No stack canary |
NX |
NX enabled |
Non-executable stack/heap (bit 63 of PTE) |
NX |
NX disabled |
Stack/heap may be executable |
PIE |
PIE enabled |
Position-independent executable (ASLR for .text) |
PIE |
No PIE (0x400000) |
Loaded at fixed address |
Symbols |
No Symbols |
Stripped binary |
Symbols |
Symbols |
Symbol table present |
Interpreting for Exploitation
Best case for attacker (no mitigations):
RELRO: No RELRO → GOT overwrite possible
Stack: No canary found → Stack overflow to return address is direct
NX: NX disabled → Shellcode injection possible
PIE: No PIE → All addresses fixed; no ASLR on text
Maximum hardening:
RELRO: Full RELRO → GOT is read-only
Stack: Canary found → Need to leak/bypass canary
NX: NX enabled → Must use ROP/ret2libc
PIE: PIE enabled → Need info leak for text addresses
objdump
Purpose: Static binary analysis — disassembly, symbol tables, section headers. Part of: GNU Binutils (pre-installed on Linux)
Most Used Flags
| Command | Description |
|---|---|
objdump -d binary |
Disassemble executable sections |
objdump -M intel -d binary |
Disassemble in Intel syntax |
objdump -D binary |
Disassemble ALL sections (including data) |
objdump -t binary |
Static symbol table |
objdump -T binary |
Dynamic symbol table (imported/exported functions) |
objdump -r binary |
Relocation entries |
objdump -R binary |
Dynamic relocation entries (PLT/GOT) |
objdump -x binary |
All headers |
objdump -p binary |
Private headers (program headers for ELF) |
objdump -s binary |
Full contents of all sections (hex+ASCII) |
objdump -s -j .rodata binary |
Contents of .rodata section only |
objdump -d --start-address=0x401000 --stop-address=0x401100 binary |
Disassemble range |
Finding Functions of Interest
# Find address of a specific function:
objdump -t binary | grep main
objdump -t binary | grep win
# Find all imported functions:
objdump -T binary | grep -v "^0"
# Disassemble a specific function (after finding its address):
objdump -M intel -d binary | grep -A 50 "<vulnerable_function>"
readelf
Purpose: Examine ELF file structure (more detailed than objdump for ELF internals). Part of: GNU Binutils
Common Usage
readelf -h binary # ELF header (magic, arch, entry point, etc.)
readelf -S binary # Section headers
readelf -l binary # Program headers (segments)
readelf -s binary # Symbol table
readelf -d binary # Dynamic section (.dynamic)
readelf -r binary # Relocations
readelf -a binary # All of the above
readelf -x .rodata binary # Hex dump of section
readelf -p .rodata binary # String dump of section
strings
Purpose: Extract printable strings from a binary. Pre-installed on Linux/macOS.
strings binary # strings of 4+ characters (default)
strings -n 8 binary # strings of 8+ characters
strings -t x binary # show file offset (hex) of each string
strings -a binary # scan all sections (not just initialized data)
CTF Recon with strings
strings binary | grep -i flag # look for flag-related strings
strings binary | grep -i pass # look for password-related strings
strings binary | grep / # look for file paths
strings binary | grep -E "^[A-Za-z_][A-Za-z0-9_]*$" # function-like names
Ghidra
Purpose: Full-featured reverse engineering suite with decompilation. Download: https://ghidra-sre.org/ (NSA, free, open source) Java 17+ required.
Workflow
- Open Ghidra → New Project → Import File → select binary
- Auto-analyze (accept defaults or enable additional analyzers):
Analysis > Auto Analyze - Symbol Tree (left panel): expand
Functionsto see all function names - Code Browser: double-click a function to see disassembly + decompiler output
- String search:
Search > For Strings→ find interesting strings → cross-reference to code (right-click → References → Show References to Address) - Rename symbols: double-click any label to rename it
- Patch bytes:
Edit > Patch Instructionto modify instructions
Key Windows
| Window | Purpose |
|---|---|
| Program Trees | Sections of the binary |
| Symbol Tree | Functions, labels, namespaces |
| Data Type Manager | Struct/enum definitions |
| Decompiler | C pseudocode reconstruction |
| Listing | Disassembly view |
| Defined Strings | All string literals with xrefs |
Ghidra Scripting
Ghidra includes a scripting console (Java and Python):
# Ghidra Python script example (run in Script Manager):
from ghidra.app.decompiler import DecompInterface
decomp = DecompInterface()
decomp.openProgram(currentProgram)
func = getGlobalFunctions("main")[0]
results = decomp.decompileFunction(func, 60, None)
print(results.getDecompiledFunction().getC())
GDB + pwndbg
See Appendix G (GDB Command Reference) for the full GDB reference. Key pwndbg-specific commands for security work:
# Install pwndbg:
git clone https://github.com/pwndbg/pwndbg
cd pwndbg && ./setup.sh
# Launch:
gdb -q ./binary
pwndbg Security Commands
checksec Display security mitigations for loaded binary
vmmap Show memory map with permissions (rwx)
got Show GOT entries and current values
canary Show the stack canary value at fs:0x28
cyclic 100 Generate 100-byte De Bruijn cyclic pattern
cyclic -l 0x6161616b Find offset from cyclic pattern value
heap Show heap state (chunks, bins)
bins Show tcache/fastbin/smallbin contents
telescope $rsp 20 Show annotated stack (dereferences pointers)
search -s "/bin/sh" Search memory for string
search -x 0x90909090 Search memory for bytes
rop Find ROP gadgets in loaded binary
ROPgadget
Purpose: Find ROP gadgets in a binary.
Install: pip3 install ROPgadget
Common Usage
# Find all gadgets:
ROPgadget --binary ./binary
# Find gadgets with specific instructions:
ROPgadget --binary ./binary | grep "pop rdi"
ROPgadget --binary ./binary | grep ": ret$" # bare ret gadgets
ROPgadget --binary ./binary | grep "pop rdi ; ret"
# Search for a specific byte sequence:
ROPgadget --binary ./binary --opcode "5f c3" # pop rdi; ret bytes
# Find /bin/sh string address:
ROPgadget --binary ./binary --string "/bin/sh"
# Generate a rop chain automatically (limited use in practice):
ROPgadget --binary ./binary --rop --badbytes "0x0a"
# Include all mapped libraries:
ROPgadget --binary ./binary --ropchain
Typical Gadgets for Common Tasks
| Task | Gadget Pattern | NASM bytes |
|---|---|---|
| Set RDI | pop rdi; ret |
5F C3 |
| Set RSI | pop rsi; ret |
5E C3 |
| Set RDX | pop rdx; ret |
5A C3 |
| Set RAX | pop rax; ret |
58 C3 |
| Zero RSI | xor esi, esi; ret |
31 F6 C3 |
| Syscall | syscall |
0F 05 |
| Alignment | ret |
C3 |
| Write to memory | mov [rdi], rsi; ret |
48 89 37 C3 |
ropper
Purpose: Alternative to ROPgadget for finding gadgets.
Install: pip3 install ropper
ropper --file ./binary
ropper --file ./binary --search "pop rdi"
ropper --file ./binary --type rop # only ROP gadgets (ret-terminated)
ropper --file ./binary --type jop # jump-oriented gadgets
ropper --file ./binary --string "/bin/sh"
ropper --file ./binary --badbytes "\x00\x0a" # exclude gadgets with bad bytes
pwntools
Purpose: Python library for exploit development and CTF challenges.
Install: pip3 install pwntools
Documentation: https://docs.pwntools.com/
Core Usage
from pwn import *
# Process interaction:
p = process('./binary') # local process
p = remote('host', 1337) # network connection
p = gdb.debug('./binary') # launch with GDB attached
# I/O:
p.sendline(b'input') # send line + newline
p.send(b'\x41' * 72) # send bytes
p.recvline() # receive one line
p.recvuntil(b'Enter name: ') # receive until pattern
p.interactive() # interactive mode (after shell)
# Packing:
p64(0x401160) # pack 64-bit little-endian: b'\x60\x11\x40\x00...'
p32(0x12345678) # pack 32-bit little-endian
u64(data[:8]) # unpack 64-bit little-endian
u32(data[:4]) # unpack 32-bit little-endian
# ELF analysis:
elf = ELF('./binary')
elf.address # base address
elf.symbols['win'] # address of symbol 'win'
elf.got['printf'] # GOT entry for printf
elf.plt['printf'] # PLT entry for printf
elf.search(b'/bin/sh') # find bytes in binary
# Libc interaction:
libc = ELF('./libc.so.6')
libc.address = leaked_base # set ASLR base after leaking
libc.symbols['system'] # address of system()
# ROP gadget finding:
rop = ROP(elf)
rop.raw(rop.find_gadget(['pop rdi', 'ret'])[0])
rop.raw(elf.symbols['flag_string'])
rop.raw(elf.symbols['win'])
# Assembly and disassembly:
asm('mov rax, 1', arch='amd64') # assemble instruction(s)
disasm(b'\x48\x89\xe5', arch='amd64') # disassemble bytes
# Logging:
log.info('found offset: %d', offset)
log.success('got shell!')
Standard ret2win Template
from pwn import *
elf = ELF('./challenge')
p = process('./challenge')
offset = 72
ret_gadget = 0x000000000040101a # find with: ROPgadget ... | grep ": ret$"
win_addr = elf.symbols['win']
payload = flat(
b'A' * offset,
ret_gadget, # alignment
win_addr, # win function
)
p.sendlineafter(b'Enter your name: ', payload)
p.interactive()
QEMU
Purpose: Full system and user-mode emulation for testing and debugging.
Install: apt install qemu-system-x86 qemu-user-static (or architecture-specific variant)
User-Mode Emulation (single binaries)
# Run an ARM64 binary on x86-64:
qemu-aarch64 ./arm64_binary
# Run a RISC-V binary:
qemu-riscv64 ./riscv_binary
# Run with GDB server:
qemu-aarch64 -g 1234 ./arm64_binary
# Then: gdb-multiarch ./arm64_binary -ex "target remote :1234"
System Emulation (full OS)
# MinOS (x86-64):
qemu-system-x86_64 -drive format=raw,file=minOS.img -m 64M -nographic
# MinOS with GDB stub:
qemu-system-x86_64 -drive format=raw,file=minOS.img -m 64M \
-nographic -S -s
# Then: gdb -ex "target remote :1234" -ex "c"
# ARM64 system:
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 128M \
-kernel Image -append "console=ttyAMA0" -nographic
# RISC-V system:
qemu-system-riscv64 -machine virt -kernel fw_jump.elf \
-device loader,file=Image,addr=0x80200000 \
-append "console=ttyS0" -nographic
Key QEMU Flags
| Flag | Description |
|---|---|
-nographic |
Disable GUI; redirect serial to terminal |
-serial stdio |
Connect serial port to terminal |
-m 64M |
Set RAM to 64 MB |
-s |
GDB stub on port 1234 |
-S |
Start suspended (wait for GDB) |
-d int |
Log interrupts (debugging) |
-d cpu |
Log CPU state on each instruction (very verbose) |
-monitor stdio |
QEMU monitor on stdin/stdout |
Ctrl-A X |
Exit QEMU (when -nographic) |
Ctrl-A C |
Enter QEMU monitor |
strace and ltrace
Purpose: Trace system calls (strace) and library calls (ltrace) at runtime.
# Trace all system calls:
strace ./binary
# Trace specific syscalls:
strace -e trace=open,read,write,execve ./binary
# Trace with timing:
strace -T ./binary
# Trace a running process:
strace -p PID
# Trace library calls:
ltrace ./binary
ltrace -C ./binary # demangle C++ names
strace is invaluable for understanding what a binary actually does at the OS interface level, without reverse engineering the logic.
addr2line
Purpose: Convert code addresses to source file and line numbers (requires debug symbols).
addr2line -e vmlinux 0xffffffff81234567 # kernel crash address
addr2line -e ./binary -f 0x401234 # function name + file:line
addr2line -e ./binary -f -C 0x401234 # demangle C++ names
Useful for interpreting GDB backtraces from stripped kernel crash reports when the unstripped vmlinux is available.
Tool Selection Guide
| Task | Primary Tool | Alternative |
|---|---|---|
| Quick security overview | checksec |
readelf -S (manual) |
| Static disassembly | objdump -M intel -d |
Ghidra (full RE) |
| Full reverse engineering | Ghidra | IDA Free, Binary Ninja |
| Interactive debugging | GDB + pwndbg | lldb |
| System call tracing | strace |
GDB catch syscall |
| Library call tracing | ltrace |
GDB + PLT breakpoints |
| ROP gadget finding | ROPgadget |
ropper, pwndbg rop |
| Exploit scripting | pwntools |
Python struct.pack |
| System emulation | qemu-system-* |
Bochs, VirtualBox |
| User-mode testing | qemu-user-* |
Docker + cross-gcc |
| Symbol resolution | addr2line |
gdb -ex "info symbol addr" |
| String extraction | strings |
readelf -p .rodata |