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

  1. Open Ghidra → New Project → Import File → select binary
  2. Auto-analyze (accept defaults or enable additional analyzers): Analysis > Auto Analyze
  3. Symbol Tree (left panel): expand Functions to see all function names
  4. Code Browser: double-click a function to see disassembly + decompiler output
  5. String search: Search > For Strings → find interesting strings → cross-reference to code (right-click → References → Show References to Address)
  6. Rename symbols: double-click any label to rename it
  7. Patch bytes: Edit > Patch Instruction to 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