Advanced binary analysis with runtime execution and symbolic path exploration (RE Levels 3-4). Use when need runtime behavior, memory dumps, secret extraction, or input synthesis to reach specific program states. Completes in 3-7 hours with GDB+Angr.
Before writing ANY code, you MUST check:
.claude/library/catalog.json.claude/docs/inventories/LIBRARY-PATTERNS-GUIDE.mdD:\Projects\*| Match | Action | |-------|--------| | Library >90% | REUSE directly | | Library 70-90% | ADAPT minimally | | Pattern exists | FOLLOW pattern | | In project | EXTRACT | | No match | BUILD (add to library after) |
Use this skill when analyzing malware samples, reverse engineering binaries for security research, conducting vulnerability assessments, extracting IOCs from suspicious files, validating software for supply chain security, or performing CTF challenges and binary exploitation research.
Do NOT use for unauthorized reverse engineering of commercial software, analyzing binaries on production systems, reversing software without legal authorization, violating terms of service or EULAs, or analyzing malware outside isolated environments. Avoid for simple string extraction (use basic tools instead).
All reverse engineering findings MUST be validated through:
Performs deep reverse engineering through runtime execution and symbolic exploration:
Decision Gate: After Level 3, evaluates if symbolic execution needed to reach unexplored paths.
Timebox: 3-7 hours total
sandbox-validator - Safe binary executionmemory-mcp - Store runtime findingssequential-thinking - Path exploration decisionsgraph-analyst - Visualize execution pathsNEVER execute unknown binaries on your host system!
All dynamic analysis, debugging, and symbolic execution MUST be performed in:
--security-opt, --cap-drop=ALL)Consequences of unsafe execution:
Safe Practices:
# 1. Full deep analysis (Levels 3+4)
/re:deep crackme.exe
# 2. Dynamic analysis only (Level 3)
/re:dynamic server.bin --args "--port 8080"
# 3. Symbolic execution only (Level 4)
/re:symbolic challenge.exe --target-addr 0x401337
/re:dynamic binary.exe --args "test input" --sandbox true
Sandboxing:
Before executing, the skill automatically retrieves Level 2 findings:
// Check memory-mcp for static analysis results
const staticFindings = await mcp__memory-mcp__vector_search({
query: binary_hash,
filter: {category: "reverse-engineering", re_level: 2}
})
// Extract critical functions and suggested breakpoints
const breakpoints = staticFindings.critical_functions.map(f => f.address)
// Example: ["0x401234", "0x401567", "0x4018ab"]
Automatically loads breakpoints from Level 2 static analysis:
# Auto-generated from static analysis
break *0x401234 # check_password function
break *0x401567 # validate_license function
break *0x4018ab # decrypt_config function
# Run with test input
run --flag "test_input_from_user"
GDB Session Commands (executed automatically):
# At each breakpoint:
# 1. Dump all registers
info registers
# 2. Dump stack (100 bytes)
x/100x $rsp
# 3. Dump heap allocations (if applicable)
info proc mappings
x/100x [heap_address]
# 4. Search for secrets in memory
find 0x600000, 0x700000, "password"
find 0x600000, 0x700000, "admin"
# 5. Dump interesting strings from registers
x/s $rdi # First argument (often string pointer)
x/s $rsi # Second argument
At each breakpoint, the skill captures:
Register State:
RAX: 0x0000000000401337
RBX: 0x0000000000000000
RCX: 0x00007fffffffe010 → "user_input_here"
RDX: 0x0000000000000010
RSI: 0x00007fffffffe020 → "expected_password"
RDI: 0x00007fffffffe030 → buffer
RBP: 0x00007fffffffe100
RSP: 0x00007fffffffe0e0
RIP: 0x0000000000401234 → check_password
Stack Dump (saved to re-project/dbg/0x401234-stack.bin):
0x7fffffffe0e0: 0x0000000000401337 0x0000000000000000
0x7fffffffe0f0: 0x00007fffffffe200 0x0000000000000001
Memory Secrets (extracted automatically):
Found at 0x601000: "admin:SecretP@ss123"
Found at 0x601020: "license_key=ABC-DEF-GHI-JKL"
Found at 0x601040: "api_token=eyJhbGciOiJIUzI1NiIs..."
Syscall Trace (via strace):
# Automatically executed in parallel
strace -o re-project/dbg/syscalls.log ./binary.exe --flag test
Output:
open("/etc/config.ini", O_RDONLY) = 3
read(3, "password=admin123\n", 1024) = 18
socket(AF_INET, SOCK_STREAM, 0) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("192.168.1.100")}, 16) = 0
send(4, "POST /api/login HTTP/1.1\r\n...", 256, 0) = 256
re-project/dbg/
├── gdb-session.log # Full GDB transcript
├── breakpoints.txt # List of breakpoints set
├── memory-dumps/
│ ├── 0x401234-registers.txt
│ ├── 0x401234-stack.bin
│ ├── 0x401567-registers.txt
│ ├── 0x401567-stack.bin
│ └── 0x4018ab-heap.bin
├── syscalls.log # strace output
├── libcalls.log # ltrace output
└── runtime-secrets.txt # Extracted passwords, keys, tokens
// Automatically evaluated via sequential-thinking MCP
const decision = await mcp__sequential-thinking__evaluate({
question: "Should we proceed to symbolic execution (Level 4)?",
factors: [
`Branches explored: ${explored_branches}/${total_branches}`,
`Unreachable code found: ${unreachable_functions.length > 0}`,
`User's question answered: ${findings_sufficient}`,
`Input-dependent paths: ${symbolic_paths_needed}`
]
})
// Example evaluation:
// - Explored 12/20 branches (60% coverage)
// - Found 3 unreachable functions (possible anti-debug)
// - User wants to reach "win" function at 0x401337 (NOT YET REACHED)
// - Input-dependent path detected (password check with strcmp)
// DECISION: ESCALATE TO LEVEL 4
# From Level 3: Couldn't reach "win" function at 0x401337 with manual inputs
target_addr = 0x401337 # Goal: Find input that reaches this
# From Level 3: These functions lead to failure/exit
avoid_addrs = [
0x401400, # fail_message function
0x401500, # bad_password function
0x401600 # exit_program function
]
/re:symbolic binary.exe \
--target-addr 0x401337 \
--avoid-addrs 0x401400,0x401500,0x401600 \
--max-states 1000 \
--timeout 7200
What Happens Under the Hood:
import angr
import claripy
# Step 2.1: Load binary into Angr project
project = angr.Project('./binary.exe', auto_load_libs=False)
# Step 2.2: Create symbolic input
# Assume input is 32-byte flag
flag_length = 32
flag = claripy.BVS('flag', flag_length * 8)
# Step 2.3: Create entry state with symbolic stdin
state = project.factory.entry_state(
stdin=flag,
add_options={angr.options.LAZY_SOLVES}
)
# Step 2.4: Add constraints - printable ASCII only
for byte in flag.chop(8):
state.add_constraints(byte >= 0x20) # Printable ASCII start
state.add_constraints(byte <= 0x7e) # Printable ASCII end
# Step 2.5: Create simulation manager
simgr = project.factory.simulation_manager(state)
# Step 2.6: Explore paths (DFS strategy)
simgr.explore(
find=0x401337, # Target address
avoid=[0x401400, 0x401500, 0x401600], # Avoid addresses
num_find=1, # Stop after finding first solution
max_states=1000 # Prevent state explosion
)
# Step 2.7: Check if solution found
if simgr.found:
# Extract concrete input
solution_state = simgr.found[0]
solution = solution_state.solver.eval(flag, cast_to=bytes)
print(f"Solution: {solution.decode()}")
# Save solution
with open('re-project/sym/solutions/solution-1.txt', 'wb') as f:
f.write(solution)
else:
print("No solution found within constraints")
# Replace complex library functions with symbolic summaries
import angr
# Hook strcmp to return symbolic value
class StrCmpHook(angr.SimProcedure):
def run(self, s1, s2):
# Return symbolic comparison result
s1_str = self.state.memory.load(s1, 32)
s2_str = self.state.memory.load(s2, 32)
return s1_str == s2_str
project.hook_symbol('strcmp', StrCmpHook())
# Merge states at loop entry to prevent explosion
simgr.use_technique(angr.exploration_techniques.Veritesting())
# Alternative: Manual state merging
while simgr.active:
simgr.step()
if len(simgr.active) > 50:
# Merge similar states
simgr.merge()
# Add intermediate constraints to guide exploration
state.add_constraints(
flag[0:4] == b'FLAG' # Known prefix from hints
)
# This reduces search space dramatically
# Without: 256^32 possibilities
# With: 256^28 possibilities (4 bytes fixed)
# Test synthesized input
echo "FLAG_synthesized_solution_here" | ./binary.exe
# Expected output:
# "Success! You reached the target state."
# "Congratulations! Flag: CTF{...}"
Validation Steps:
re-project/sym/
├── angr-script.py # Reproducible Angr script
├── solutions/
│ ├── solution-1.txt # First valid solution
│ ├── solution-2.txt # Alternative solution (if --find-all)
│ └── solution-3.txt
├── constraints/
│ ├── path-1.smt2 # Z3 constraints for path 1
│ ├── path-2.smt2
│ └── simplified.smt2 # Simplified constraint set
├── validation.log # Validation test results
└── exploration-metrics.json # States explored, time taken, coverage
exploration-metrics.json:
{
"total_states": 847,
"found_states": 3,
"avoided_states": 124,
"deadended_states": 720,
"execution_time_sec": 3245,
"coverage_percent": 78.5,
"memory_usage_mb": 2847,
"solutions_found": 3
}
# Set breakpoints at crypto functions
/re:dynamic binary.exe --breakpoints AES_encrypt,RSA_sign,MD5_update
# Set breakpoints at specific addresses
/re:dynamic binary.exe --breakpoints 0x401000,0x402000,0x403000
# Conditional breakpoints (GDB syntax)
/re:dynamic binary.exe --breakpoints "0x401234 if $rdi == 0x601000"
Advanced GDB Scripting:
# Custom GDB Python script (auto-loaded if found)
# re-project/gdb-script.py
import gdb
class PasswordBreakpoint(gdb.Breakpoint):
def __init__(self, location):
super().__init__(location)
def stop(self):
# Extract password from RDI register
rdi = gdb.parse_and_eval('$rdi')
password = gdb.execute(f'x/s {rdi}', to_string=True)
# Log to file
with open('passwords.log', 'a') as f:
f.write(f"{password}\n")
# Continue execution
return False
# Set custom breakpoint
PasswordBreakpoint("check_password")
# Exhaustive search (may take hours)
/re:symbolic binary.exe \
--target-addr 0x401337 \
--find-all true \
--max-solutions 10 \
--timeout 14400
# In Angr script
simgr.explore(
find=0x401337,
avoid=avoid_addrs,
num_find=10 # Find up to 10 solutions
)
# Process all found solutions
for idx, state in enumerate(simgr.found):
solution = state.solver.eval(flag, cast_to=bytes)
with open(f'solution-{idx+1}.txt', 'wb') as f:
f.write(solution)
# Aggressive pruning
/re:symbolic binary.exe \
--max-states 100 \
--avoid-addrs 0x401400,0x401500,0x401600 \
--strategy dfs # Depth-first search (memory efficient)
# Use Veritesting to merge paths
simgr.use_technique(angr.exploration_techniques.Veritesting())
# Drop states if too many active
simgr.use_technique(angr.exploration_techniques.LengthLimiter(max_length=100))
# Prioritize states closer to target
simgr.use_technique(angr.exploration_techniques.Explorer(
find=0x401337,
avoid=avoid_addrs,
num_find=1
))
# Start from target address and work backwards
project = angr.Project('./binary.exe')
# Create state at target address (not entry point)
state = project.factory.blank_state(addr=0x401337)
# Make all memory symbolic
state.options.add(angr.options.SYMBION_SYNC_CLE)
# Explore backwards to find required input
simgr = project.factory.simulation_manager(state)
simgr.explore(find=project.entry)
# This finds inputs that MUST lead to target
# Dump specific memory regions
/re:dynamic binary.exe \
--dump-regions heap,stack,data \
--dump-at-breakpoints 0x401234,0x401567
Custom Memory Analysis:
# GDB Python script for heap analysis
import gdb
def analyze_heap():
# Get heap boundaries
mappings = gdb.execute('info proc mappings', to_string=True)
heap_start = extract_heap_start(mappings)
heap_end = extract_heap_end(mappings)
# Scan for interesting patterns
for addr in range(heap_start, heap_end, 8):
value = gdb.execute(f'x/g {addr}', to_string=True)
# Check if value looks like a pointer
if is_valid_pointer(value):
gdb.execute(f'x/s {value}') # Dereference as string
analyze_heap()
Scenario: Analyze malware sample to extract C2 server URL and encryption keys
Phase 1: Dynamic Analysis (Level 3)
# Step 1: Safe sandbox execution
/re:dynamic malware.exe --sandbox true --network-monitor true
# Step 2: GDB session auto-starts with breakpoints from static analysis
# Breakpoints at: decrypt_config, connect_to_c2, send_beacon
# Step 3: At decrypt_config breakpoint (0x401234)
(gdb) info registers
RAX: 0x0000000000601000 → encrypted_buffer
RDI: 0x0000000000601100 → decryption_key
(gdb) x/s 0x601100
0x601100: "hardcoded_AES_key_12345"
# Step 4: Continue to connect_to_c2 breakpoint (0x401567)
(gdb) continue
(gdb) x/s $rdi
0x601200: "http://malicious-c2.tk:8443/beacon"
# Step 5: Extract all findings
Runtime Secrets Found:
- AES Key: "hardcoded_AES_key_12345"
- C2 URL: "http://malicious-c2.tk:8443/beacon"
- User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
Phase 2: Decision Gate
// Automatically evaluated
QUESTION: "Proceed to symbolic execution?"
FACTORS:
- All critical functions reached ✅
- Secrets extracted (AES key, C2 URL) ✅
- User's question answered (extract IOCs) ✅
- No unreachable paths requiring symbolic execution ❌
DECISION: STOP AT LEVEL 3 (sufficient findings)
Output: Malware analysis complete in 45 minutes with full IOC extraction.
Scenario: Find valid license key to unlock "premium features" in binary
Phase 1: Dynamic Analysis (Level 3)
# Step 1: Test with invalid key
/re:dynamic challenge.exe --args "--license AAAA-BBBB-CCCC-DDDD"
# Output: "Invalid license key"
# Step 2: GDB reveals license check at 0x401234
(gdb) break *0x401234
(gdb) run --license AAAA-BBBB-CCCC-DDDD
# Step 3: Examine comparison
(gdb) x/s $rdi
0x7fffffffe010: "AAAA-BBBB-CCCC-DDDD" # User input
(gdb) x/s $rsi
0x601000: [encrypted data, not readable]
# Observation: License key is compared against encrypted/hashed value
# Cannot extract valid key directly from memory
Phase 2: Decision Gate
QUESTION: "Proceed to symbolic execution?"
FACTORS:
- License check function found ✅
- Valid key NOT extractable from memory ✅
- Comparison is complex (encryption/hashing) ✅
- User wants valid license key ✅
DECISION: ESCALATE TO LEVEL 4 (symbolic execution required)
Phase 3: Symbolic Execution (Level 4)
# Launch Angr symbolic execution
/re:symbolic challenge.exe \
--target-addr 0x401337 \ # "Premium features unlocked" message
--avoid-addrs 0x401400 \ # "Invalid license" path
--input-format "FLAG-XXXX-XXXX-XXXX" \
--max-states 500
Angr Script (auto-generated):
import angr
import claripy
project = angr.Project('./challenge.exe', auto_load_libs=False)
# License key format: FLAG-XXXX-XXXX-XXXX (19 chars)
license_key = claripy.BVS('license', 19 * 8)
# Create entry state with symbolic license as argv[2]
state = project.factory.entry_state(args=['./challenge.exe', '--license', license_key])
# Constrain to valid format: FLAG-XXXX-XXXX-XXXX
for i in range(19):
if i in [0, 1, 2, 3]: # "FLAG"
state.add_constraints(license_key.get_byte(i) == ord("FLAG"[i]))
elif i in [4, 9, 14]: # Dashes
state.add_constraints(license_key.get_byte(i) == ord('-'))
else: # X = uppercase letters or digits
byte = license_key.get_byte(i)
state.add_constraints(
claripy.Or(
claripy.And(byte >= ord('A'), byte <= ord('Z')),
claripy.And(byte >= ord('0'), byte <= ord('9'))
)
)
# Explore
simgr = project.factory.simulation_manager(state)
simgr.explore(find=0x401337, avoid=0x401400)
if simgr.found:
solution = simgr.found[0].solver.eval(license_key, cast_to=bytes)
print(f"Valid License: {solution.decode()}")
# Output: "FLAG-A7B2-C9D4-E1F6"
Validation:
$ ./challenge.exe --license FLAG-A7B2-C9D4-E1F6
Premium features unlocked!
Congratulations! Here is your flag: CTF{symbolic_execution_wins}
Output: Challenge solved in 3.5 hours total (45min dynamic + 3hr symbolic).
Scenario: Find exploitable buffer overflow in server binary
Phase 1: Dynamic Analysis (Level 3)
# Step 1: Launch server in sandbox
/re:dynamic server.bin --args "--port 8080" --sandbox true
# Step 2: Fuzz with large inputs
echo "A"*1000 | nc localhost 8080
# GDB catches segfault
Program received signal SIGSEGV, Segmentation fault.
0x4141414141414141 in ?? ()
# Step 3: Analyze crash
(gdb) info registers
RIP: 0x4141414141414141 # Overwritten return address
RSP: 0x7fffffffe100
RBP: 0x4141414141414141
(gdb) x/100x $rsp
# Shows stack completely overwritten with 'A' (0x41)
# Step 4: Find offset to return address
# Use pattern_create and pattern_offset from GEF/Pwndbg
(gdb) pattern create 1000
(gdb) run
# Crash at offset 512
# Confirmed: Buffer overflow at offset 512, control of RIP
Phase 2: Decision Gate
QUESTION: "Proceed to symbolic execution?"
FACTORS:
- Buffer overflow confirmed ✅
- Offset to RIP known (512 bytes) ✅
- Exploitation demonstrated ✅
- User's goal: find vulnerability ✅ (COMPLETE)
DECISION: STO
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer