Project 5: Bomb Lab Workflow — Binary Reverse Engineering Playbook
Project 5: Bomb Lab Workflow — Binary Reverse Engineering Playbook
Build a repeatable methodology for defusing binary puzzles and document your solutions with assembly-level precision.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Advanced |
| Time Estimate | 1-2 weeks |
| Language | C (analysis target), no coding required |
| Prerequisites | Project 4 (Calling Convention Crash Cart) or equivalent |
| Key Topics | Reverse engineering, x86-64 control flow, GDB debugging, disassembly |
| CS:APP Chapters | 3 |
1. Learning Objectives
By completing this project, you will:
- Read x86-64 assembly fluently: Translate disassembly into high-level logic without relying on source code
- Extract constraints from machine code: Identify what inputs satisfy comparison and branch conditions
- Master GDB for binary analysis: Set breakpoints, examine registers, inspect memory, and trace execution
- Recognize common compiler patterns: Identify loops, conditionals, switch statements, and function calls
- Apply systematic reverse engineering methodology: Document your approach in a reproducible “defusal dossier”
- Handle indirect control flow: Understand jump tables, computed jumps, and lookup operations
- Develop defensive reasoning: Verify hypotheses before committing to potentially destructive inputs
2. Deep Theoretical Foundation
2.1 Reverse Engineering Methodology
Reverse engineering is the systematic process of understanding a program’s behavior without access to source code. For bomb labs, this means:
┌─────────────────────────────────────────────────────────────────────────────┐
│ REVERSE ENGINEERING WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. RECONNAISSANCE 2. STATIC ANALYSIS 3. DYNAMIC ANALYSIS │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ • File type │ ──► │ • Disassemble │ ──► │ • Run in GDB │ │
│ │ • Strings │ │ • Map functions │ │ • Set breakpts │ │
│ │ • Symbols │ │ • Trace logic │ │ • Test inputs │ │
│ │ • Entry point │ │ • Find patterns │ │ • Verify hypo. │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │ │ │
│ └─────────────────────────┴─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 4. DOCUMENTATION │ │
│ │ • Input found │ │
│ │ • Reasoning chain │ │
│ │ • Assembly proof │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
The Golden Rules of Bomb Defusal
- Never run without a breakpoint: The bomb will explode if you give wrong input
- Prove before you try: Extract constraints mathematically, don’t guess
- Read assembly, not intuition: Your assumptions about “what it should do” will mislead you
- Document everything: You’ll need to explain your reasoning later
- Use GDB’s power: Examine memory, trace calls, modify registers to test hypotheses
2.2 x86-64 Control Flow Patterns
Understanding how C constructs map to assembly is essential. Here are the critical patterns:
2.2.1 Comparisons and Conditional Jumps
The comparison-jump pattern is the foundation of all control flow:
# C: if (x == 5)
cmpq $5, %rax # Compare rax with 5
je .L_equal # Jump if equal (ZF=1)
jne .L_not_equal # Jump if not equal (ZF=0)
Key Comparison Instructions:
| Instruction | Operation | Flags Set |
|---|---|---|
cmp a, b |
Compute b - a (discard result) | CF, ZF, SF, OF |
test a, b |
Compute b & a (discard result) | ZF, SF, PF |
Common Conditional Jumps:
| Signed | Unsigned | Condition | Meaning |
|---|---|---|---|
je/jz |
je/jz |
ZF=1 | Equal / Zero |
jne/jnz |
jne/jnz |
ZF=0 | Not equal / Not zero |
jg |
ja |
Greater / Above | |
jge |
jae |
Greater or equal / Above or equal | |
jl |
jb |
Less / Below | |
jle |
jbe |
Less or equal / Below or equal | |
js |
- | SF=1 | Negative (sign flag set) |
jns |
- | SF=0 | Non-negative |
Critical Insight: The difference between signed (jg, jl) and unsigned (ja, jb) comparisons is crucial. A value like 0xFFFFFFFF is -1 when signed but 4294967295 when unsigned.
2.2.2 If-Then-Else Structures
// C code
if (x > 10) {
result = a;
} else {
result = b;
}
# Assembly pattern
cmpq $10, %rdi # Compare x with 10
jle .L_else # If x <= 10, go to else
movq %rsi, %rax # result = a
jmp .L_end
.L_else:
movq %rdx, %rax # result = b
.L_end:
2.2.3 Loops
While Loop Pattern:
while (i < n) {
// body
i++;
}
.L_loop_start:
cmpq %rsi, %rdi # Compare i with n
jge .L_loop_end # Exit if i >= n
# ... loop body ...
incq %rdi # i++
jmp .L_loop_start # Back to start
.L_loop_end:
For Loop Pattern (often optimized):
movl $0, %ecx # i = 0
.L_loop:
# ... body using %ecx as counter ...
incl %ecx # i++
cmpl %esi, %ecx # Compare i with limit
jl .L_loop # Continue if i < limit
Key Insight: Compilers often restructure loops to have the test at the end (do-while style) for efficiency.
2.3 Switch Statements and Jump Tables
Switch statements with dense case values typically compile to jump tables:
switch (x) {
case 0: return 'a';
case 1: return 'b';
case 2: return 'c';
default: return '?';
}
# Range check first
cmpl $2, %edi # Is x > 2?
ja .L_default # If so, use default
# Jump table access
leaq .L_jump_table(%rip), %rax # Load table address
movslq (%rax,%rdi,4), %rdx # Get offset for case x
addq %rax, %rdx # Compute absolute address
jmpq *%rdx # Indirect jump
.L_jump_table:
.long .L_case0 - .L_jump_table # Offset to case 0
.long .L_case1 - .L_jump_table # Offset to case 1
.long .L_case2 - .L_jump_table # Offset to case 2
.L_case0:
movl $97, %eax # 'a'
ret
.L_case1:
movl $98, %eax # 'b'
ret
# ... etc
Recognizing Jump Tables:
- Look for a range check (
cmpfollowed byjaor similar) - Look for
lealoading an address followed by indexed memory access - The pattern
jmpq *%reg(indirect jump through register) - A data section with a sequence of
.longor.quadentries
2.4 String Operations and Comparisons
Bombs often compare input strings to expected values:
2.4.1 Character-by-Character Comparison
# Loop comparing strings
.L_loop:
movzbl (%rdi), %eax # Load byte from string1
movzbl (%rsi), %edx # Load byte from string2
cmpb %dl, %al # Compare bytes
jne .L_not_equal # Mismatch found
testb %al, %al # Check for null terminator
je .L_equal # Both strings ended
incq %rdi # Move to next char
incq %rsi
jmp .L_loop
2.4.2 Using Library Functions
# Using strcmp
movq input_string, %rdi # First argument
leaq expected(%rip), %rsi # Second argument
call strcmp
testl %eax, %eax # strcmp returns 0 if equal
jne explode_bomb
Key Insight: When you see call strcmp or call strings_not_equal, you can use GDB to examine both string arguments before the call executes.
2.4.3 Finding String Constants
# Find all printable strings in the binary
$ strings bomb | head -20
# Or use objdump to find strings in .rodata
$ objdump -s -j .rodata bomb
2.5 Function Calls and Parameter Passing
Understanding the x86-64 calling convention is essential:
┌───────────────────────────────────────────────────────────────────────────┐
│ x86-64 CALLING CONVENTION (System V ABI) │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ INTEGER/POINTER ARGUMENTS (in order): │
│ 1st: %rdi 2nd: %rsi 3rd: %rdx 4th: %rcx 5th: %r8 6th: %r9│
│ │
│ RETURN VALUE: │
│ %rax (and %rdx for 128-bit values) │
│ │
│ CALLER-SAVED (may be clobbered by callee): │
│ %rax, %rcx, %rdx, %rsi, %rdi, %r8, %r9, %r10, %r11 │
│ │
│ CALLEE-SAVED (must be preserved by callee): │
│ %rbx, %rbp, %r12, %r13, %r14, %r15 │
│ │
│ STACK POINTER: │
│ %rsp (always 16-byte aligned before CALL instruction) │
│ │
└───────────────────────────────────────────────────────────────────────────┘
Example: Analyzing a Function Call
movl $6, %edx # 3rd arg: 6
movl $2, %esi # 2nd arg: 2
leaq input(%rip), %rdi # 1st arg: pointer to input
call read_n_numbers
# Returns count in %eax
This translates to: read_n_numbers(input_ptr, 2, 6)
2.6 Reading Disassembly Systematically
2.6.1 The STRIDE Method
When analyzing a function, use STRIDE:
- Signature: What are the inputs (register arguments, stack args)?
- Termination: Where does the function return? What conditions lead there?
- Register usage: Track which values go into which registers
- Important operations: Mark comparisons, branches, and calls
- Data access: Note memory reads/writes
- Explode paths: Find all paths that lead to
explode_bomb
2.6.2 Annotation Example
# PHASE 2 ANALYSIS
# Input: %rdi = pointer to input string
#
phase_2:
pushq %rbp # Save callee-saved register
pushq %rbx
subq $40, %rsp # Allocate 40 bytes on stack
movq %rsp, %rsi # 2nd arg: stack buffer (array of 6 ints)
call read_six_numbers # Parse 6 integers from input
cmpl $1, (%rsp) # CONSTRAINT: first number must be 1
je .L1 # If first == 1, continue
call explode_bomb # Otherwise BOOM
.L1:
leaq 4(%rsp), %rbx # rbx = &array[1]
leaq 24(%rsp), %rbp # rbp = &array[6] (end marker)
.L2:
movl -4(%rbx), %eax # eax = previous element
addl %eax, %eax # eax = 2 * previous (PATTERN: doubling!)
cmpl %eax, (%rbx) # CONSTRAINT: current == 2 * previous
je .L3
call explode_bomb
.L3:
addq $4, %rbx # Move to next element
cmpq %rbp, %rbx # Check if at end
jne .L2 # Continue loop if not
# SOLUTION: 1 2 4 8 16 32 (each number is double the previous)
2.7 Using GDB for Binary Analysis
2.7.1 Essential GDB Commands for Bomb Lab
# Starting GDB with the bomb
$ gdb bomb
# Set breakpoint at a phase function
(gdb) break phase_1
(gdb) break explode_bomb
# Run with input file
(gdb) run answers.txt
# Examine disassembly
(gdb) disassemble phase_1
(gdb) disas phase_1 # Short form
(gdb) x/20i phase_1 # 20 instructions from phase_1
# Examine registers
(gdb) info registers
(gdb) info reg rax rdi rsi # Specific registers
(gdb) print $rax # Print value of rax
(gdb) print/x $rax # Print in hex
# Examine memory
(gdb) x/s 0x402400 # Print string at address
(gdb) x/6d $rsp # 6 decimal integers at stack pointer
(gdb) x/10x $rsp # 10 hex words at stack pointer
(gdb) x/8c 0x402400 # 8 characters at address
# Step through execution
(gdb) stepi # Execute one instruction
(gdb) nexti # Execute one instruction (skip calls)
(gdb) continue # Continue to next breakpoint
# Examine stack
(gdb) x/20x $rsp # View 20 words on stack
(gdb) info frame # Current stack frame info
# Set conditional breakpoints
(gdb) break *0x401000 if $rax == 5
# Print with nice format
(gdb) print (char *)$rdi # Print string argument
(gdb) print/d *(int *)$rsp # Print integer at stack top
2.7.2 GDB Memory Examination Formats
| Format | Meaning | Example Use |
|---|---|---|
x |
Hexadecimal | x/x $rsp |
d |
Signed decimal | x/d $rsp |
u |
Unsigned decimal | x/u $rsp |
s |
String | x/s 0x402400 |
c |
Character | x/8c $rdi |
i |
Instructions | x/10i $rip |
2.7.3 Defusing Strategy in GDB
# 1. Always set breakpoint before the bomb
(gdb) break explode_bomb
# 2. Set breakpoint at the start of the phase
(gdb) break phase_3
# 3. Run until you hit the phase
(gdb) run answers.txt
# 4. When at phase_3, examine what comparison is made
(gdb) disas
# Look for cmp instructions, note addresses
# 5. Set breakpoints at key comparison points
(gdb) break *0x401234
# 6. Step through, examining registers before comparisons
(gdb) stepi
(gdb) print $rax
(gdb) print $rdi
# 7. If you hit explode_bomb breakpoint:
# - You can inspect why the comparison failed
# - Modify your input and restart
(gdb) quit
2.8 Static vs Dynamic Analysis
Both approaches are valuable and complementary:
Static Analysis (reading disassembly without running):
- Safe: Can’t trigger the bomb
- Complete: See all code paths
- Challenging: Must trace logic mentally
- Tools:
objdump,readelf, Ghidra, IDA
Dynamic Analysis (running under debugger):
- Interactive: See actual values
- Focused: Follow execution path
- Risky: Could trigger bomb
- Tools: GDB,
ltrace,strace
Best Practice: Do static analysis first to understand the structure, then use dynamic analysis to verify your understanding and extract exact values.
┌─────────────────────────────────────────────────────────────────────────────┐
│ STATIC + DYNAMIC WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ STATIC ANALYSIS DYNAMIC ANALYSIS │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Disassemble │ │ Set breakpoints │ │
│ │ phase function │ │ at phase start │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Identify │ │ Examine string │ │
│ │ comparisons │──── Verify ───▶ │ / number args │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Trace logic │ │ Step through │ │
│ │ to constraints │◀─── Confirm ────│ with test input │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ └────────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ SOLUTION CONFIRMED │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
3. Project Specification
3.1 What You Will Build
A comprehensive “Defusal Dossier” documenting:
- Your systematic methodology for approaching each phase
- Annotated disassembly with constraint identification
- The reasoning chain from assembly to solution
- Verified solutions with execution traces
- Generalized patterns applicable to other bomb variants
3.2 Deliverable Structure
Your dossier should contain:
bomb-defusal-dossier/
├── methodology.md # Your systematic approach
├── tools-setup.md # GDB configuration, scripts
├── phase1/
│ ├── disassembly.txt # Annotated disassembly
│ ├── analysis.md # Constraint extraction
│ ├── solution.txt # The answer
│ └── verification.txt # GDB trace proving it works
├── phase2/
│ └── ...
├── phase3/
│ └── ...
├── phase4/
│ └── ...
├── phase5/
│ └── ...
├── phase6/
│ └── ...
├── secret_phase/ # If you find it!
│ └── ...
└── patterns.md # Common patterns you identified
3.3 Example Phase Walkthrough
Here’s a complete example of analyzing a typical Phase 1:
Step 1: Initial Reconnaissance
$ objdump -d bomb | grep -A20 "<phase_1>:"
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 call 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 ret
Step 2: Static Analysis
PHASE 1 ANALYSIS
================
Function signature:
- Input: %rdi = pointer to input string (standard calling convention)
- Calls: strings_not_equal(input, expected)
Key observation:
- %esi is set to 0x402400 before calling strings_not_equal
- This is the EXPECTED STRING address!
- If strings_not_equal returns non-zero, bomb explodes
Constraint:
- Input string MUST equal the string at address 0x402400
Step 3: Dynamic Analysis (GDB)
$ gdb bomb
(gdb) break phase_1
(gdb) run
# Enter any input when prompted
# Now at phase_1 entry, examine the expected string
(gdb) x/s 0x402400
0x402400: "Border relations with Canada have never been better."
# That's the answer! Let's verify:
(gdb) quit
$ echo "Border relations with Canada have never been better." > answers.txt
$ ./bomb answers.txt
# Phase 1 defused!
Step 4: Documentation
## Phase 1 Solution
### Answer
Border relations with Canada have never been better.
### Reasoning
1. Phase 1 calls `strings_not_equal(input, 0x402400)`
2. If the return value is non-zero (strings differ), bomb explodes
3. The expected string at 0x402400 is: "Border relations with Canada..."
4. Therefore, input must exactly match this string
### Assembly Evidence
```asm
400ee4: mov $0x402400,%esi ; Load expected string address
400ee9: call strings_not_equal ; Compare input (%rdi) with expected (%rsi)
400eee: test %eax,%eax ; Check if return == 0
400ef0: je 400ef7 ; If equal, skip explosion
400ef2: call explode_bomb ; Otherwise, BOOM
Verification
Tested in GDB with breakpoint at phase_1. String comparison succeeds, execution continues past the explode_bomb call.
---
## 4. Solution Architecture
### 4.1 The Defusal Dossier Format
For each phase, document using this template:
```markdown
# Phase N: [Descriptive Title]
## 1. Quick Summary
- **Answer**: [The solution]
- **Type**: [String compare / Numeric sequence / etc.]
- **Difficulty**: [Easy/Medium/Hard]
## 2. Disassembly (Annotated)
```asm
[Full disassembly with your comments]
3. Constraint Analysis
- [List each constraint derived from the code]
- [Show the assembly instruction that creates each constraint]
4. Solution Derivation
[Step-by-step reasoning from constraints to answer]
5. GDB Verification
[Commands and output showing successful defusal]
6. Patterns Identified
- [Any reusable patterns for other bombs] ```
4.2 Methodology Documentation
Create a reusable methodology document:
# Bomb Lab Methodology
## Phase Approach Checklist
### Before Starting
- [ ] Set up GDB with .gdbinit for convenience
- [ ] Create answers.txt file for inputs
- [ ] Set breakpoint at explode_bomb
- [ ] Disassemble all phase_N functions
### For Each Phase
1. [ ] Disassemble the phase function
2. [ ] Identify the input format (string? numbers? how many?)
3. [ ] Find all comparison instructions
4. [ ] Trace paths leading to explode_bomb
5. [ ] Extract mathematical constraints
6. [ ] Solve constraints for valid input
7. [ ] Test in GDB with breakpoints
8. [ ] Document solution and reasoning
### Input Reading Functions
- `read_line()`: Reads one line of text input
- `sscanf()`: Often parses numbers from input
- `read_six_numbers()`: Common helper, reads 6 integers
### Common Patterns
- String comparison: Look for `strings_not_equal` or `strcmp` calls
- Number sequences: Look for loops with arithmetic constraints
- Lookup tables: Look for indexed memory access patterns
4.3 Tools Setup Document
# Tools Setup for Bomb Lab
## GDB Configuration (.gdbinit)
Create a file `~/.gdbinit` or a local `.gdbinit`:
Don’t confirm quit
set confirm off
Better assembly display
set disassembly-flavor intel
OR for AT&T syntax (matches CS:APP):
set disassembly-flavor att
History
set history save on set history size 10000
Pretty printing
set print pretty on
## Useful GDB Scripts
### bomb_init.gdb
Run this at the start of each session
file bomb break explode_bomb break phase_1 break phase_2 break phase_3 break phase_4 break phase_5 break phase_6
### Usage
```bash
$ gdb -x bomb_init.gdb
(gdb) run answers.txt
Alternative Tools
objdump
objdump -d bomb > bomb.asm # Full disassembly
objdump -t bomb # Symbol table
objdump -s -j .rodata bomb # Read-only data section
strings
strings bomb | less # All strings
strings -t x bomb # With offsets
---
## 5. Implementation Guide
### 5.1 Development Environment Setup
```bash
# Essential tools (already installed on most systems)
gdb --version # GNU Debugger
objdump --version # From binutils
strings --version # String extractor
# Create working directory
mkdir -p bomb-lab-work
cd bomb-lab-work
# Copy bomb binary here
cp /path/to/bomb .
# Create answer file
touch answers.txt
# Create dossier structure
mkdir -p dossier/{phase1,phase2,phase3,phase4,phase5,phase6,secret_phase}
5.2 Phase-by-Phase Strategy
Phase 1: String Comparison (Typical)
Goal: Find the expected string
Approach:
- Disassemble phase_1
- Look for
movinstruction loading an address before a compare function - Use GDB to examine the string at that address
(gdb) disas phase_1
# Look for: mov $0x4024XX, %esi or lea 0xXXXX(%rip), %rsi
# Then:
(gdb) x/s 0x4024XX
Phase 2: Numeric Sequence (Typical)
Goal: Find a sequence of numbers that satisfies constraints
Approach:
- Look for
read_six_numbersor similar - Identify loop structure
- Extract the mathematical relationship
Common patterns:
- Arithmetic sequence: each number = previous + constant
- Geometric sequence: each number = previous * constant
- Fibonacci-like: each number depends on two previous
# Example: doubling sequence (1, 2, 4, 8, 16, 32)
cmpl $1, (%rsp) # First must be 1
...
addl %eax, %eax # Double the previous
cmpl %eax, (%rbx) # Current must equal doubled
Phase 3: Switch Statement (Typical)
Goal: Find an index and corresponding value
Approach:
- Look for jump table pattern
- Identify the range check (determines valid cases)
- Trace each case to find expected second value
# Examine jump table
(gdb) x/8x 0x402470
# For each case, find what value is expected
# Often: sscanf reads two numbers, first selects case, second must match
Phase 4: Recursive Function (Typical)
Goal: Find input that produces expected output through recursion
Approach:
- Understand the recursive function
- Work backwards from expected output
- Use GDB to test specific inputs
Common: Binary tree traversals, recursive calculations
Phase 5: String Manipulation (Typical)
Goal: Find input that transforms into expected string
Approach:
- Identify the lookup table
- Understand the transformation (often: input char -> index -> lookup)
- Reverse engineer: for each output char, find input that produces it
# Example: input[i] & 0xF used as index into lookup table
movzbl (%rdi,%rax), %ecx # Get input char
andl $15, %ecx # Use low 4 bits as index
movzbl lookup(,%rcx,1), %ecx # Get transformed char
Phase 6: Linked List Reordering (Typical)
Goal: Find a sequence that correctly orders a linked list
Approach:
- Identify the linked list structure in memory
- Understand how input maps to node selection
- Determine the required ordering
This is typically the hardest phase!
Secret Phase (If Present)
Finding it:
- Search for references to “secret” in strings
- Look for conditional calls to phase_defused
- Often triggered by specific input format in an earlier phase
5.3 Checkpoint Milestones
| Milestone | Criteria |
|---|---|
| Setup Complete | GDB configured, bomb disassembled, structure created |
| Phase 1 | Solved within 30 minutes using systematic approach |
| Phase 2 | Numeric pattern identified and documented |
| Phase 3 | Jump table analyzed, all cases mapped |
| Phase 4 | Recursive logic understood, solution derived |
| Phase 5 | Transformation table reversed |
| Phase 6 | Linked list ordering solved |
| Dossier Complete | All phases documented with reasoning |
6. Testing Strategy
6.1 Verification Method
For each phase, create a verification trace:
# Start GDB with breakpoints
$ gdb bomb
(gdb) break phase_1
(gdb) break phase_2
# etc.
(gdb) break explode_bomb
# Run with your answers
(gdb) run answers.txt
# At each phase, verify:
# 1. Input was read correctly
(gdb) x/s $rdi
# 2. Comparison values are as expected
(gdb) print $eax
# 3. Correct path taken (no hit on explode_bomb)
(gdb) continue
6.2 Testing Checklist
## Phase N Verification Checklist
- [ ] Disassembly matches my understanding
- [ ] All constraints identified and documented
- [ ] Solution satisfies each constraint
- [ ] GDB trace shows correct execution path
- [ ] No false assumptions about signed/unsigned
- [ ] No off-by-one errors in loops
- [ ] Solution works on fresh run (not just in GDB)
6.3 Edge Cases to Consider
| Phase Type | Common Edge Cases |
|---|---|
| Strings | Whitespace, case sensitivity, null terminator |
| Numbers | Signed vs unsigned, overflow, negative values |
| Sequences | First/last element special cases |
| Switch | Default case, sparse case values |
| Recursion | Base case conditions |
| Lists | Node ordering direction |
7. Common Pitfalls
7.1 Critical Mistakes to Avoid
| Pitfall | Why It’s Dangerous | Prevention |
|---|---|---|
| Running without breakpoint | Bomb explodes on wrong input | Always break explode_bomb first |
| Assuming string content | Strings may have hidden chars | Use x/s to verify exactly |
| Confusing signed/unsigned | jg vs ja behave differently | Check jump instruction carefully |
| Ignoring calling convention | Wrong arg in wrong register | Follow %rdi, %rsi, %rdx order |
| Off-by-one in loops | Wrong array index | Trace loop boundaries exactly |
| Modifying binary | May invalidate lab submission | Work with pristine copy |
7.2 Common Misreads
# WRONG: Thinking cmp works like subtraction
cmpq %rax, %rbx # This computes %rbx - %rax, not %rax - %rbx!
jg somewhere # Jumps if %rbx > %rax (signed)
# WRONG: Confusing lea with mov
leaq 8(%rsp), %rax # %rax = %rsp + 8 (address calculation)
movq 8(%rsp), %rax # %rax = memory contents at %rsp + 8
# WRONG: Forgetting sign extension
movl (%rsp), %eax # Loads 32-bit value
# Upper 32 bits of %rax are zeroed (zero extension for 32-bit ops)
movslq (%rsp), %rax # Sign-extends 32-bit to 64-bit
7.3 Debugging Strategies
# When stuck: examine all interesting memory
(gdb) x/20x $rsp # Stack contents
(gdb) x/s $rdi # Input string
(gdb) info registers # All register values
# When comparison fails: check both sides
(gdb) break *0x401234 # Address of cmp instruction
(gdb) continue
(gdb) print $rax
(gdb) print $rdx
# Now you know exactly what's being compared
# When loop seems wrong: trace iteration by iteration
(gdb) break *0x401240 # Top of loop
(gdb) continue
(gdb) print $rcx # Loop counter
# Repeat to see pattern
8. Extensions
8.1 Beginner Extensions
- Create GDB scripts: Automate common sequences of commands
- Visualize control flow: Draw flowcharts for each phase
- Time yourself: Track how long each phase takes to improve efficiency
8.2 Intermediate Extensions
- Solve multiple bomb variants: Apply your methodology to different bomb instances
- Write a constraint solver: Automate extraction of numeric constraints
- Create annotated walkthrough video: Explain your process for others
8.3 Advanced Extensions
- Use Ghidra/IDA: Repeat analysis with professional tools, compare results
- Static analysis only: Solve without running the bomb at all
- Create your own bomb: Design phases for others to solve
- Binary patching: (Educational only) Patch the binary to skip explode_bomb
- Automation: Write a solver that automatically defuses given any bomb
9. Real-World Connections
9.1 Capture The Flag (CTF) Competitions
Bomb lab skills directly apply to CTF reverse engineering challenges:
- Crackmes: Binaries that require finding a valid input/key
- Binary exploitation: Understanding assembly for writing exploits
- Protocol analysis: Reverse engineering network protocols
- Malware analysis: Understanding malicious software behavior
Popular CTFs:
- picoCTF (beginner-friendly)
- DEF CON CTF
- Google CTF
- Pwn2Own
9.2 Malware Analysis
Security researchers use these exact techniques:
- Unpacking: Analyzing obfuscated malware
- C2 analysis: Finding command-and-control servers
- IoC extraction: Identifying indicators of compromise
- Behavioral analysis: Understanding what malware does
9.3 Software Protection
Understanding reverse engineering helps:
- Design better copy protection
- Implement anti-debugging techniques
- Protect intellectual property in software
9.4 Career Applications
| Role | How Bomb Lab Skills Apply |
|---|---|
| Security Researcher | Vulnerability discovery, exploit development |
| Malware Analyst | Reverse engineering malicious code |
| Game Hacker/Modder | Understanding and modifying game binaries |
| Embedded Developer | Debugging without source code |
| Compiler Engineer | Understanding code generation |
9.5 Related Open Source Projects
- radare2: Open source reverse engineering framework
- Ghidra: NSA’s reverse engineering tool
- Binary Ninja: Commercial binary analysis platform
- pwntools: CTF framework for exploitation
- angr: Binary analysis framework with symbolic execution
10. Resources
10.1 Essential Reading
- CS:APP Chapter 3: “Machine-Level Representation of Programs”
- x86-64 Reference: felixcloutier.com/x86
- System V ABI: x86-64 psABI
10.2 GDB Resources
- GDB Manual:
info gdbor online documentation - GDB Cheat Sheet: Quick reference for common commands
- CGDB: Curses-based GDB frontend (easier visualization)
- pwndbg/GEF: Enhanced GDB for reverse engineering
10.3 Video Resources
- CMU 15-213 Bomb Lab recitation videos
- LiveOverflow YouTube series on reverse engineering
- “Reverse Engineering for Beginners” lectures
10.4 Practice Materials
- Crackmes.one: Collection of practice binaries
- Pwnable.kr: Wargames for binary exploitation
- Microcorruption: Embedded CTF challenges
- Nightmare: CTF reverse engineering course
10.5 Related Projects in This Series
- Previous: P4 (x86-64 Calling Convention Crash Cart) - Foundation for understanding assembly
- Next: P6 (Attack Lab Workflow) - Applying RE skills to exploitation
11. Self-Assessment Checklist
Before considering this project complete, verify:
Understanding
- I can read x86-64 assembly without constantly referencing documentation
- I understand the difference between signed and unsigned comparisons
- I can identify loops, conditionals, and switch statements in assembly
- I can trace function calls and understand parameter passing
- I can recognize jump table patterns
- I understand how strings are stored and compared
Practical Skills
- I can use GDB to set breakpoints, examine registers, and inspect memory
- I can disassemble a function and annotate it meaningfully
- I can extract constraints from comparison instructions
- I can verify solutions before committing input
- I can work systematically rather than guessing
Documentation
- Each phase has annotated disassembly
- Each solution includes the reasoning chain
- I can explain WHY each answer works, not just THAT it works
- My methodology is documented and reproducible
- I’ve identified patterns applicable to other bombs
Growth
- I solved phases faster as I progressed
- I can approach new binary puzzles with confidence
- I understand how compiler output relates to source code
- I’ve developed intuition for common patterns
12. Submission / Completion Criteria
Minimum Viable Completion:
- All six phases defused
- Basic documentation of solutions
- Can explain each answer
Full Completion:
- Complete defusal dossier with all sections
- Annotated disassembly for each phase
- GDB verification traces
- Methodology document
- Pattern identification
Excellence (Going Above & Beyond):
- Secret phase discovered and solved
- Multiple bomb variants solved
- Automated analysis scripts
- Video walkthrough created
- Contribution to bomb lab resources for others
13. Real World Outcome
When you complete this project, here is exactly what you will see when running your defusal dossier:
$ cd bomb-defusal-dossier
$ ls -la
total 48
drwxr-xr-x 10 user staff 320 Dec 15 14:23 .
drwxr-xr-x 5 user staff 160 Dec 15 10:00 ..
-rw-r--r-- 1 user staff 2048 Dec 15 14:23 methodology.md
-rw-r--r-- 1 user staff 1536 Dec 15 12:00 tools-setup.md
drwxr-xr-x 5 user staff 160 Dec 15 14:00 phase1
drwxr-xr-x 5 user staff 160 Dec 15 14:10 phase2
drwxr-xr-x 5 user staff 160 Dec 15 14:15 phase3
drwxr-xr-x 5 user staff 160 Dec 15 14:18 phase4
drwxr-xr-x 5 user staff 160 Dec 15 14:20 phase5
drwxr-xr-x 5 user staff 160 Dec 15 14:23 phase6
-rw-r--r-- 1 user staff 3072 Dec 15 14:23 patterns.md
$ cat phase1/solution.txt
Border relations with Canada have never been better.
$ cat phase2/solution.txt
1 2 4 8 16 32
$ ./bomb answers.txt
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Congratulations! You've defused the bomb!
$ gdb bomb
GNU gdb (GDB) 13.2
(gdb) break phase_1
Breakpoint 1 at 0x400ee0
(gdb) run answers.txt
Starting program: /home/user/bomb-lab/bomb answers.txt
Welcome to my fiendish little bomb...
Breakpoint 1, 0x0000000000400ee0 in phase_1 ()
(gdb) x/s 0x402400
0x402400: "Border relations with Canada have never been better."
(gdb) disas
Dump of assembler code for function phase_1:
0x0000000000400ee0 <+0>: sub $0x8,%rsp
=> 0x0000000000400ee4 <+4>: mov $0x402400,%esi
0x0000000000400ee9 <+9>: call 0x401338 <strings_not_equal>
0x0000000000400eee <+14>: test %eax,%eax
0x0000000000400ef0 <+16>: je 0x400ef7 <phase_1+23>
0x0000000000400ef2 <+18>: call 0x40143a <explode_bomb>
0x0000000000400ef7 <+23>: add $0x8,%rsp
0x0000000000400efb <+27>: ret
End of assembler dump.
(gdb) continue
Continuing.
Phase 1 defused. How about the next one?
...
14. The Core Question You’re Answering
“How do I systematically extract program behavior from machine code when I have no source code, and how do I prove my understanding is correct before committing to a potentially destructive action?”
This question sits at the heart of reverse engineering. You’re learning to:
- Read a language (assembly) that was never meant for humans to read
- Reconstruct the programmer’s intent from compiled output
- Build mental models and validate them through controlled experimentation
- Document your reasoning chain so others can verify and replicate your work
15. Concepts You Must Understand First
Before starting this project, ensure you understand these concepts:
| Concept | Where to Learn | Why It’s Needed |
|---|---|---|
| x86-64 Register Set | CS:APP 3.4 | You must know %rax-%r15, their purposes, and calling conventions |
| Instruction Encoding | CS:APP 3.1-3.3 | Understand how instructions are represented in bytes |
| Control Flow (jumps, calls) | CS:APP 3.6 | Recognize conditionals and loops in assembly |
| Stack Operations | CS:APP 3.7 | Understand push/pop, call/ret, stack frames |
| Condition Codes | CS:APP 3.6.1 | Know how ZF, SF, OF affect conditional jumps |
| Memory Addressing Modes | CS:APP 3.4.1 | Decode operands like 8(%rbp) or (%rax,%rcx,4) |
| Calling Convention | CS:APP 3.7.2 | Know argument registers and return value location |
16. Questions to Guide Your Design
As you work through each phase, ask yourself:
- What is the input format? Is the bomb expecting a string? Numbers? How many?
- Where does the input go? Which register holds the pointer to your input?
- What functions are called? Do you see
sscanf,strings_not_equal,read_six_numbers? - What are the constraints? For each comparison instruction, what condition must be satisfied?
- What paths lead to explosion? Trace every path to
explode_bomb- what conditions trigger them? - What is the mathematical relationship? For numeric phases, what formula relates the numbers?
- Can I verify without exploding? How can I use GDB to test my hypothesis safely?
17. Thinking Exercise
Before you start coding/analyzing, work through this hand-trace exercise:
Given this assembly snippet from a hypothetical phase:
phase_X:
sub $0x18, %rsp
mov %rsp, %rsi
call read_six_numbers
cmpl $0x0, (%rsp)
jns .L1
call explode_bomb
.L1:
mov $0x1, %eax
jmp .L3
.L2:
mov -0x4(%rsp,%rax,4), %edx
add %edx, %edx
cmp %edx, (%rsp,%rax,4)
je .L4
call explode_bomb
.L4:
add $0x1, %rax
.L3:
cmp $0x6, %rax
jne .L2
add $0x18, %rsp
ret
Questions to answer on paper:
- How many numbers does this phase read? Where are they stored?
- What constraint does the first
cmpl/jnsenforce about the first number? - What is the loop doing? What does
-0x4(%rsp,%rax,4)access? - Write out the mathematical relationship between consecutive numbers.
- If the first number is 3, what must the remaining numbers be?
Expected answers (don’t peek until you’ve tried!):
- Six numbers, stored on the stack at %rsp (24 bytes = 6 x 4-byte integers)
- First number must be >= 0 (non-negative, since
jns= jump if not sign/negative) - Loop checks that each number equals twice the previous:
arr[i] == 2 * arr[i-1] - Geometric sequence with ratio 2: each number is double the previous
- 3, 6, 12, 24, 48, 96
18. The Interview Questions They’ll Ask
After completing this project, you should be able to answer these interview questions:
- “Explain the difference between static and dynamic analysis. When would you use each?”
- Static: Reading code without executing (safe, complete view, but harder to trace)
- Dynamic: Running under debugger (see actual values, but can trigger side effects)
- Best practice: Static first to understand structure, dynamic to verify specific values
- “How does the x86-64 calling convention work? Where are arguments passed?”
- First 6 integer args: %rdi, %rsi, %rdx, %rcx, %r8, %r9
- Return value in %rax
- Callee-saved: %rbx, %rbp, %r12-r15
- “What is the difference between
cmpandtestinstructions?”cmp a, bcomputes b-a and sets flags (for comparing values)test a, bcomputes a&b and sets flags (typically used astest %rax, %raxto check zero)
- “How would you identify a switch statement in assembly?”
- Look for bounds check (
cmpthenjato default) - Look for jump table access (
leaof table address, indexed read,jmp *%reg) - Data section with sequence of addresses or offsets
- Look for bounds check (
- “What is a jump table and why do compilers use them?”
- Array of addresses for switch case targets
- O(1) dispatch instead of O(n) if-else chain
- Efficient for dense, integer-based switch statements
- “How do you safely debug a program that has destructive behavior?”
- Set breakpoint at destructive function FIRST
- Use watchpoints to catch memory corruption
- Never run without breakpoints; verify hypotheses before testing
19. Hints in Layers
If you get stuck, reveal hints one at a time. Try each level before moving to the next.
Hint 1 - General Approach
For any phase: (1) Disassemble completely, (2) Find all paths to explode_bomb, (3) Work backwards from each comparison to determine what makes it succeed.
Hint 2 - Finding Strings
If a phase compares strings, look for a mov or lea instruction that loads an address into %rsi or %esi before calling a compare function. Use GDB’s x/s address to see the string.
Hint 3 - Numeric Phases
For phases that read numbers:
read_six_numbersreads 6 integers onto the stacksscanfwith format strings like"%d %d"indicates number count- Look for loop patterns with increment and comparison to find relationships
Hint 4 - When Truly Stuck
If you’ve spent more than 2 hours on a single phase:
- Write out EVERY instruction with a comment explaining what it does
- Draw the stack layout showing where values live
- Create a table: for each comparison, list “what’s compared” and “what must be true”
- Consider: Is there a lookup table? A mathematical formula? A string encoding?
20. Books That Will Help
| Topic | Book | Chapter/Section |
|---|---|---|
| x86-64 Assembly | CS:APP 3e | Chapter 3 (entire chapter) |
| Control Flow in Assembly | CS:APP 3e | Section 3.6 |
| Procedure Calls | CS:APP 3e | Section 3.7 |
| Data Representations | CS:APP 3e | Sections 2.1-2.4 |
| Reverse Engineering Fundamentals | Practical Binary Analysis | Chapters 1-4 |
| GDB Mastery | Art of Debugging with GDB | Chapters 1-3 |
| x86 Instruction Reference | Intel SDM Vol. 2 | Instruction Set Reference |
| Exploit Development Context | Hacking: Art of Exploitation | Chapter 2 |
This guide was expanded from CSAPP_3E_DEEP_LEARNING_PROJECTS.md. For the complete learning path, see the project index.