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
Low Level
25 min video
3 min read
5 Levels of Reverse Engineering Explained
You just saved 22 min.
The big takeaway
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.
1
HTTP POST request headers
Firefox/Opera spoofing
2
Command injection target
Huawei SSDP/SOAP protocol
3
Payload delivery
Binary download to /mips
4
C2 IP address
185.172.110.214
Malware capabilities revealed by strings analysis
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.
1
Binary compiled from source code (C/C++)
2
Disassembler extracts raw assembly instructions
3
Decompiler lifts assembly to pseudo-C representation
4
Reverse engineer renames variables and infers logic
Static analysis workflow from binary to readable code
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.
1
Locate output strings ('Good kitty', 'Bad kitty')
2
Trace backward to find control variable (is_correct)
3
Identify comparison loop and flag buffer
4
Rename variables to reflect inferred purpose
5
Determine flag length (8 characters) from loop bounds
Static analysis steps to identify password logic
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.
12345678
Password discovered on stack via dynamic analysis
Breakpoint inspection revealed the correct input without static math
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?'
1
Define symbolic input variables (not concrete values)
2
Trace program paths as mathematical constraints
3
Add constraints (e.g., ASCII printable characters)
4
SMT solver computes satisfying input values
5
Extract solution as the flag or password
Symbolic execution transforms reverse engineering into constraint solving
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.
Unconstrained symbolic execution
Path explosion, hours/OOM
With ASCII + length constraints
Tractable, minutes to solve
Constraint optimization prevents state space explosion
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.
1
GPIO switch
Hardware initialization
2
Dropbear
SSH server
3
Cron
Scheduled tasks
4
comm-u-os
HTTP web interface
Wi-Fi repeater startup sequence reveals services
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.
$5
Cost of Wi-Fi repeater with discoverable vulnerabilities
Low-cost devices often contain exploitable firmware
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.
1
Level 1: Strings
Extract readable text
2
Level 2: Static Analysis
Read assembly and pseudo-code
3
Level 3: Dynamic Analysis
Observe runtime state
4
Level 4: Symbolic Execution
Solve as math problem
5
Level 5: Firmware Hacking
Extract and analyze real devices
Five levels of reverse engineering from beginner to advanced
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.
1
strings
Extract readable text from binaries
2
objdump / IDA / Ghidra
Disassemble and decompile
3
GDB / GEF
Debug and inspect runtime state
4
Angr
Symbolic execution solver
5
binwalk
Extract firmware layers
Essential reverse engineering tools by function
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.
Worth quoting
"Reverse engineering is the act of taking these instructions and trying to infer the human intent."
— Presenter, at [6:05]
"We're literally going to watch it perform its actions."
— Presenter, at [10:35]
"Symbolic execution, while powerful, doesn't apply to every scenario."
— Presenter, at [20:48]
Try this
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
Made with Glimpse by Wozart
glimpse.wozart.com/v/oytpur9x
Share this infographic

More like this