Path Tracing Reverse Engineering
Overview
This skill guides the systematic reverse engineering of compiled binaries to produce functionally identical source code. The primary challenge is achieving exact output reproduction, not approximate similarity. Common applications include recreating graphics programs (ray tracers, path tracers), understanding proprietary algorithms, and recovering lost source code.
Critical Success Criteria
Before beginning, establish clear success criteria:
- Exact output match: "identical" means byte-for-byte identical, not visually similar
- File size parity: Output files must match in size (header + data)
- Checksum verification: Use
md5sum or sha256sum to verify exact matches
- No tolerance for approximation: A 99% match is still a failure if 100% is required
Systematic Approach
Phase 1: Output Format Analysis
Start with the output format before analyzing the algorithm. Mismatched output formatting causes file size differences that are independent of algorithmic correctness.
Capture reference output:
./mystery > reference_output.ppm
ls -la reference_output.ppm # Note exact file size
xxd reference_output.ppm | head -20 # Examine header bytes
Analyze header format:
- For PPM: Check exact spacing, newlines, and number formatting
- Compare:
P6\n800 600\n255\n vs P6 800 600 255\n
- Whitespace differences affect file size
Verify pixel data layout:
xxd -s 15 reference_output.ppm | head # Skip header, view raw pixels
Phase 2: Binary Analysis Setup
Create a systematic disassembly workspace:
Extract symbol information:
nm ./mystery | grep -E "^[0-9a-f]+ T" > functions.txt
strings ./mystery > strings.txt
objdump -t ./mystery > symbols.txt
Generate complete disassembly:
objdump -d ./mystery > disassembly.txt
objdump -s -j .rodata ./mystery > rodata.txt # Read-only data
objdump -s -j .data ./mystery > data.txt # Initialized data
Identify main algorithm structure:
objdump -d ./mystery | grep -A 50 "<main>:" > main_function.txt
Phase 3: Constant Extraction
Extract ALL constants systematically before writing any code:
Float constants: Located in .rodata section
import struct
# Convert hex bytes to float
hex_bytes = bytes.fromhex('0000803f') # Example: 1.0f
value = struct.unpack('<f', hex_bytes)[0]
Integer constants: Often embedded in instructions
grep -E "mov.*\$0x" disassembly.txt # Find immediate values
Create constant catalog: Document every constant with its:
- Memory address
- Raw hex value
- Decoded value (int/float/double)
- Suspected purpose
Phase 4: Algorithm Reconstruction
Reconstruct the algorithm methodically:
Map function call graph:
- Identify all
call instructions in main
- Trace each called function
- Document parameters and return values
Trace data flow:
- Follow register usage through functions
- Identify loop structures (counters, bounds)
- Map memory accesses to array/struct operations
Handle floating-point operations:
- Check if code uses SSE/AVX or x87 FPU
- Note precision:
float (32-bit) vs double (64-bit)
- SSE:
movss, addss, mulss = single precision
- SSE:
movsd, addsd, mulsd = double precision
Phase 5: Incremental Verification
Never write the entire solution at once. Verify components individually:
Background/base case first:
- Render only the background (sky, ground)
- Compare specific pixel coordinates
- Achieve 100% match on background before adding objects
Pixel-by-pixel debugging:
# Create comparison script
def compare_pixels(ref_file, test_file):
with open(ref_file, 'rb') as f1, open(test_file, 'rb') as f2:
ref = f1.read()
test = f2.read()
# Find first difference
for i, (r, t) in enumerate(zip(ref, test)):
if r != t:
pixel = (i - header_size) // 3
x, y = pixel % width, pixel // width
print(f"First diff at byte {i}, pixel ({x},{y})")
print(f"Expected: {r}, Got: {t}")
return
Coordinate-specific testing:
# Extract specific pixel from PPM
# At offset = header_size + (y * width + x) * 3
Common Pitfalls
Output Format Errors
- Whitespace in headers: PPM allows various separators; match exactly
- Numeric formatting:
printf("%d", n) vs printf("%3d", n)
- Line endings: Unix LF vs Windows CRLF
- Trailing content: Extra newlines or padding
Floating-Point Mismatches
- Precision mismatch: Using
double when binary uses float
- Rounding modes: Compiler optimizations may change rounding
- Order of operations:
(a + b) + c vs a + (b + c) differs in FP
- Library differences:
sin(), sqrt() implementations vary
Algorithmic Assumptions
- Premature pattern matching: Don't assume "ray tracer" means standard formulas
- Missing components: Multiple light sources, reflections, ambient terms
- Coordinate systems: Left-handed vs right-handed, y-up vs y-down
- Iteration order: Row-major vs column-major pixel traversal
Verification Failures
- Visual comparison is insufficient: Images may look identical but differ by 1-2 RGB values
- Partial matches are failures: 25% match means 75% wrong
- File size differences indicate format issues: Address these first
Verification Strategy
Automated Testing Harness
Create this script early and use it consistently:
#!/bin/bash
# verify.sh - Compile, run, and compare
gcc -static -o reversed mystery.c -lm
./mystery > expected.ppm
./reversed > actual.ppm
echo "File sizes:"
ls -la expected.ppm actual.ppm
echo "Checksums:"
md5sum expected.ppm actual.ppm
if cmp -s expected.ppm actual.ppm; then
echo "SUCCESS: Files are identical"
else
echo "FAILURE: Files differ"
cmp -l expected.ppm actual.ppm | head -20
fi
Progressive Debugging
When outputs differ:
- Verify file sizes first - format issues vs algorithm issues
- Find first differing byte - localize the problem
- Convert byte offset to coordinates - identify which pixel/component
- Compare expected vs actual at that location - understand the discrepancy
- Trace the calculation - work backward to find the bug
Checkpoint Validation
At each phase, verify:
Tool Reference
Essential tools for binary analysis:
| Tool |
Purpose |
objdump -d |
Disassembly |
objdump -s -j .rodata |
Read-only data section |
nm |
Symbol table |
strings |
Embedded strings |
xxd |
Hex dump |
gdb |
Dynamic analysis |
ltrace |
Library call tracing |
strace |
System call tracing |
Resources
This skill includes reference materials to support reverse engineering tasks:
references/
reverse_engineering_checklist.md - Step-by-step verification checklist
float_extraction.md - Guide to extracting floating-point constants from binaries
1---2name: path-tracing-reverse3description: This skill provides guidance for reverse engineering compiled binaries to produce equivalent source code. It applies when tasks require analyzing executables, extracting algorithms and constants, and recreating identical program behavior in source form. Use when the goal is byte-for-byte or pixel-perfect reproduction of binary output.4---56# Path Tracing Reverse Engineering78## Overview910This skill guides the systematic reverse engineering of compiled binaries to produce functionally identical source code. The primary challenge is achieving exact output reproduction, not approximate similarity. Common applications include recreating graphics programs (ray tracers, path tracers), understanding proprietary algorithms, and recovering lost source code.1112## Critical Success Criteria1314Before beginning, establish clear success criteria:15161. **Exact output match**: "identical" means byte-for-byte identical, not visually similar172. **File size parity**: Output files must match in size (header + data)183. **Checksum verification**: Use `md5sum` or `sha256sum` to verify exact matches194. **No tolerance for approximation**: A 99% match is still a failure if 100% is required2021## Systematic Approach2223### Phase 1: Output Format Analysis2425Start with the output format before analyzing the algorithm. Mismatched output formatting causes file size differences that are independent of algorithmic correctness.26271. **Capture reference output**:28 ```bash29 ./mystery > reference_output.ppm30 ls -la reference_output.ppm # Note exact file size31 xxd reference_output.ppm | head -20 # Examine header bytes32 ```33342. **Analyze header format**:35 - For PPM: Check exact spacing, newlines, and number formatting36 - Compare: `P6\n800 600\n255\n` vs `P6 800 600 255\n`37 - Whitespace differences affect file size38393. **Verify pixel data layout**:40 ```bash41 xxd -s 15 reference_output.ppm | head # Skip header, view raw pixels42 ```4344### Phase 2: Binary Analysis Setup4546Create a systematic disassembly workspace:47481. **Extract symbol information**:49 ```bash50 nm ./mystery | grep -E "^[0-9a-f]+ T" > functions.txt51 strings ./mystery > strings.txt52 objdump -t ./mystery > symbols.txt53 ```54552. **Generate complete disassembly**:56 ```bash57 objdump -d ./mystery > disassembly.txt58 objdump -s -j .rodata ./mystery > rodata.txt # Read-only data59 objdump -s -j .data ./mystery > data.txt # Initialized data60 ```61623. **Identify main algorithm structure**:63 ```bash64 objdump -d ./mystery | grep -A 50 "<main>:" > main_function.txt65 ```6667### Phase 3: Constant Extraction6869Extract ALL constants systematically before writing any code:70711. **Float constants**: Located in `.rodata` section72 ```python73 import struct74 # Convert hex bytes to float75 hex_bytes = bytes.fromhex('0000803f') # Example: 1.0f76 value = struct.unpack('<f', hex_bytes)[0]77 ```78792. **Integer constants**: Often embedded in instructions80 ```bash81 grep -E "mov.*\$0x" disassembly.txt # Find immediate values82 ```83843. **Create constant catalog**: Document every constant with its:85 - Memory address86 - Raw hex value87 - Decoded value (int/float/double)88 - Suspected purpose8990### Phase 4: Algorithm Reconstruction9192Reconstruct the algorithm methodically:93941. **Map function call graph**:95 - Identify all `call` instructions in main96 - Trace each called function97 - Document parameters and return values98992. **Trace data flow**:100 - Follow register usage through functions101 - Identify loop structures (counters, bounds)102 - Map memory accesses to array/struct operations1031043. **Handle floating-point operations**:105 - Check if code uses SSE/AVX or x87 FPU106 - Note precision: `float` (32-bit) vs `double` (64-bit)107 - SSE: `movss`, `addss`, `mulss` = single precision108 - SSE: `movsd`, `addsd`, `mulsd` = double precision109110### Phase 5: Incremental Verification111112Never write the entire solution at once. Verify components individually:1131141. **Background/base case first**:115 - Render only the background (sky, ground)116 - Compare specific pixel coordinates117 - Achieve 100% match on background before adding objects1181192. **Pixel-by-pixel debugging**:120 ```python121 # Create comparison script122 def compare_pixels(ref_file, test_file):123 with open(ref_file, 'rb') as f1, open(test_file, 'rb') as f2:124 ref = f1.read()125 test = f2.read()126127 # Find first difference128 for i, (r, t) in enumerate(zip(ref, test)):129 if r != t:130 pixel = (i - header_size) // 3131 x, y = pixel % width, pixel // width132 print(f"First diff at byte {i}, pixel ({x},{y})")133 print(f"Expected: {r}, Got: {t}")134 return135 ```1361373. **Coordinate-specific testing**:138 ```bash139 # Extract specific pixel from PPM140 # At offset = header_size + (y * width + x) * 3141 ```142143## Common Pitfalls144145### Output Format Errors146147- **Whitespace in headers**: PPM allows various separators; match exactly148- **Numeric formatting**: `printf("%d", n)` vs `printf("%3d", n)`149- **Line endings**: Unix LF vs Windows CRLF150- **Trailing content**: Extra newlines or padding151152### Floating-Point Mismatches153154- **Precision mismatch**: Using `double` when binary uses `float`155- **Rounding modes**: Compiler optimizations may change rounding156- **Order of operations**: `(a + b) + c` vs `a + (b + c)` differs in FP157- **Library differences**: `sin()`, `sqrt()` implementations vary158159### Algorithmic Assumptions160161- **Premature pattern matching**: Don't assume "ray tracer" means standard formulas162- **Missing components**: Multiple light sources, reflections, ambient terms163- **Coordinate systems**: Left-handed vs right-handed, y-up vs y-down164- **Iteration order**: Row-major vs column-major pixel traversal165166### Verification Failures167168- **Visual comparison is insufficient**: Images may look identical but differ by 1-2 RGB values169- **Partial matches are failures**: 25% match means 75% wrong170- **File size differences indicate format issues**: Address these first171172## Verification Strategy173174### Automated Testing Harness175176Create this script early and use it consistently:177178```bash179#!/bin/bash180# verify.sh - Compile, run, and compare181182gcc -static -o reversed mystery.c -lm183./mystery > expected.ppm184./reversed > actual.ppm185186echo "File sizes:"187ls -la expected.ppm actual.ppm188189echo "Checksums:"190md5sum expected.ppm actual.ppm191192if cmp -s expected.ppm actual.ppm; then193 echo "SUCCESS: Files are identical"194else195 echo "FAILURE: Files differ"196 cmp -l expected.ppm actual.ppm | head -20197fi198```199200### Progressive Debugging201202When outputs differ:2032041. **Verify file sizes first** - format issues vs algorithm issues2052. **Find first differing byte** - localize the problem2063. **Convert byte offset to coordinates** - identify which pixel/component2074. **Compare expected vs actual at that location** - understand the discrepancy2085. **Trace the calculation** - work backward to find the bug209210### Checkpoint Validation211212At each phase, verify:213- [ ] Header format matches exactly214- [ ] Background pixels match (no objects)215- [ ] Object boundaries are correct216- [ ] Lighting/shading values match217- [ ] Final checksum matches218219## Tool Reference220221Essential tools for binary analysis:222223| Tool | Purpose |224|------|---------|225| `objdump -d` | Disassembly |226| `objdump -s -j .rodata` | Read-only data section |227| `nm` | Symbol table |228| `strings` | Embedded strings |229| `xxd` | Hex dump |230| `gdb` | Dynamic analysis |231| `ltrace` | Library call tracing |232| `strace` | System call tracing |233234## Resources235236This skill includes reference materials to support reverse engineering tasks:237238### references/239240- `reverse_engineering_checklist.md` - Step-by-step verification checklist241- `float_extraction.md` - Guide to extracting floating-point constants from binaries