5 Levels of Reverse Engineering Explained
A comprehensive breakdown of reverse engineering techniques from beginner to advanced: strings analysis to identify program behavior, static analysis using disassemblers and decompilers, dynamic analysis with debuggers to observe runtime state, symbolic execution to solve programs mathematically, and real-world firmware extraction and vulnerability discovery.
Level 1: Strings Analysis
Extracting Human-Readable Text from Binaries
Binaries contain human-readable strings left behind by programmers—error messages, URLs, function names, and metadata. Using the Linux strings utility with a length filter (e.g., strings -10) reveals these artifacts, which often expose the program's purpose without needing to understand assembly code.
Huawei Malware Case Study
A malware sample's strings revealed it exploited a command injection vulnerability in Huawei devices, called busybox to download binaries, and targeted IP 185.172.110.214. The string 'Huawei home gateway' immediately identified the attack surface and malware intent.
Level 2: Static Analysis
From Assembly to Pseudo-Code
Static analysis reads compiled assembly instructions to infer the original programmer's intent. Tools like objdump show raw opcodes; IDA Pro visualizes control flow graphs; Ghidra (NSA-created) uses 'lifters' to convert assembly into readable pseudo-C code, dramatically reducing manual analysis effort.
Crackme Password Challenge
A 'crackme' program prompts for a password and outputs 'Bad kitty' or 'Good kitty'. Static analysis reveals the program reads user input, compares it byte-by-byte against a hardcoded flag buffer computed via math operations, and sets a boolean flag to determine the output message.
Identifying Key Variables Through Code Flow
By tracing the pseudo-C code, the reverse engineer identified that a local variable (renamed 'is_correct') controls the output, a buffer (renamed 'flag') holds the expected password, and a loop counter iterates through 8 characters. This mapping of variable purpose is the core of static analysis.
Level 3: Dynamic Analysis
Observing Runtime State with Debuggers
Dynamic analysis runs a program under a debugger (GDB) and sets breakpoints to pause execution at specific instructions. This allows inspection of memory, registers, and stack at runtime, revealing actual values rather than guessing from static code.
Finding the Password on the Stack
By setting a breakpoint before the password-checking loop and inspecting the stack with GDB's context command, the reverse engineer directly observed the 8-character flag string '12345678' stored in memory—avoiding the need to manually solve the static math operations.
GDB and GEF Tools
GDB (GNU Debugger) is the standard Linux debugger; GEF (GDB Enhanced Features) adds prettier output and more context information. Together they enable setting breakpoints, stepping through code, viewing memory, and examining program state during execution.
Level 4: Symbolic Execution
Converting Programs to Math Problems
Symbolic execution treats a program as a mathematical constraint system. Instead of running with concrete values, it uses symbolic variables and a satisfiability (SMT) solver to determine what inputs satisfy specific conditions—e.g., 'what value of x makes this branch execute?'
Angr: Open-Source Binary Analysis
Angr is a Python library that performs symbolic execution on binaries. It creates a project from the binary, initializes symbolic input, adds constraints to reduce state space, and uses an SMT solver to find inputs that reach desired code paths or outputs.
Train Fact Crackme Example
A program asks for a 'train tidbit' and outputs success only if the input satisfies hidden logic. Rather than manually reverse-engineer the complex check function, symbolic execution with Angr automatically found the correct input by constraining the search to ASCII-printable characters and retaining only states producing the success message.
Path Explosion and Constraint Optimization
Symbolic execution can generate exponentially many program states, exhausting memory or CPU. By adding constraints (e.g., input must be ASCII printable, length limits), the state space shrinks dramatically, making the solver tractable and reducing runtime from hours to minutes.
Level 5: Real-World Firmware Hacking
Extracting Firmware from Devices
Physical devices contain firmware files that can be extracted via hardware interfaces or software methods. These files often contain compressed or nested file systems (bootloader, kernel, root filesystem) that can be unpacked using tools like binwalk.
Binwalk: Recursive Firmware Extraction
Binwalk recursively identifies and extracts embedded files, compressed archives, and file systems within firmware blobs. The -m (matryoshka) and -e (extract) flags automatically decompress nested layers, exposing the underlying Linux root filesystem and binaries.
Identifying Services via Startup Scripts
The /etc/rc.d directory contains startup scripts that reveal which services run and in what order. For the example Wi-Fi repeater, scripts showed GPIO initialization, SSH (dropbear), cron, and the main HTTP service (comm-u-os), identifying the attack surface.
Finding Vulnerabilities in Real Binaries
Extract service binaries (e.g., comm-u-os) and analyze them in Ghidra using static and dynamic analysis. Look for unsafe functions (strcpy, sprintf), user-controlled parameters, and command injection points. A table of opcodes mapped to functions can reveal which user inputs trigger which code paths, exposing potential zero-days.
Low-Cost Hardware Hacking
The example Wi-Fi repeater was purchased for approximately $5 from Teemu, demonstrating that real vulnerabilities can be found in inexpensive consumer devices. Firmware extraction and reverse engineering of these devices can reveal security flaws affecting thousands of users.
Key Concepts and Tools
Five Levels of Reverse Engineering Progression
The five levels form a progression from shallow to deep: Level 1 (strings) requires no tools; Level 2 (static analysis) uses disassemblers and decompilers; Level 3 (dynamic analysis) uses debuggers; Level 4 (symbolic execution) uses constraint solvers; Level 5 (firmware hacking) combines all previous levels on real devices.
Essential Tools Overview
Key tools include: strings (extract text), objdump (disassemble), IDA Pro and Ghidra (decompile), GDB and GEF (debug), Angr (symbolic execution), and binwalk (firmware extraction). Each tool addresses a specific level of analysis.
When to Use Each Level
Start with strings for quick reconnaissance. Use static analysis for understanding control flow and logic. Apply dynamic analysis when static analysis is tedious or uncertain. Employ symbolic execution for complex constraint problems. Use firmware extraction for real-world device vulnerabilities.
Notable quotes
Reverse engineering is the act of taking these instructions and trying to infer the human intent. — Presenter
We're literally going to watch it perform its actions. — Presenter
Symbolic execution, while powerful, doesn't apply to every scenario. — Presenter
Action items
- Use strings -10 on a binary to extract long human-readable text and identify program purpose
- Open a binary in Ghidra to view pseudo-C code and rename variables to match inferred functionality
- Set a breakpoint in GDB before a critical code section and inspect stack/memory to observe runtime values
- Write an Angr script to symbolically execute a crackme: define symbolic input, add ASCII constraints, and extract the solution from successful states
- Extract firmware from a device using binwalk -me and explore /etc/rc.d to identify running services and attack surfaces
- Analyze extracted service binaries in Ghidra for unsafe functions (strcpy, sprintf) and user-controlled parameters that could be exploited