Overview
angrop is built on top of angr’s symbolic execution engine. Instead of executing instructions with concrete values, symbolic execution treats program inputs as symbolic variables and tracks all possible states through constraints. This allows angrop to:- Analyze gadget effects - Understand what a gadget does to registers and memory
- Find gadget capabilities - Determine which registers can be controlled
- Generate chains - Solve constraints to build working ROP payloads
- Verify correctness - Ensure generated chains achieve their goals
Symbolic Execution in angr
From the README:“angrop uses symbolic execution to understand the effects of gadgets and uses constraint solving and graph search for generating chains.”
Key Concepts
Symbolic State: Represents program state where values can be symbolic (unknown) rather than concrete. Constraints: Logical conditions that must be satisfied (e.g.,rax + rbx == 0x1234).
Constraint Solver: Uses SMT solvers (Z3) to find values satisfying all constraints.
Unconstrained State: A state where the instruction pointer is symbolic, indicating a controlled jump.
Analyzing Gadget Effects
Creating Symbolic States
Frombuilder.py:47-55:
- All GP registers are symbolic:
rax = sreg_rax_0,rbx = sreg_rbx_0, etc. - Stack slots are symbolic:
[rsp+0] = symbolic_stack_0,[rsp+8] = symbolic_stack_1, etc. - We can track how the gadget transforms these symbols
Stepping Through Gadgets
Frombuilder.py:419-437:
- Execute each instruction symbolically
- Track all possible execution paths
- Apply constraints from branches
- Reach the final state where
pcis symbolic (return)
Extracting Effects
After stepping through a gadget, angrop analyzes the final state: Register effects:Constraint Solving for Chain Generation
The Core Algorithm
Frombuilder.py:362-420, the chain building process:
Example: Setting RAX to 0x1234
Let’s trace through finding a chain to setrax = 0x1234:
Gadget: pop rax; ret at 0x401000
Advanced Constraint Solving
Rebalancing ASTs
When gadgets transform values, we need to invert the operations: Frombuilder.py:245-359:
pop rax; add rax, 0x10; ret
Handling Symbolic Memory Accesses
Frombuilder.py:472-492:
mov [rax+0x10], rbx; ret
Verification
After generating a chain, angrop verifies it works: Fromreg_setter.py:161-200:
- No unintended register modifications
- No out-of-bounds memory accesses
- All target registers reach desired values
- Control flow remains in our hands
Symbolic State Management
State Copying and Constraints
angr’s symbolic states are copied when exploring multiple paths:- Exploring conditional gadgets: Try both branches
- Preserving states: Keep original state while constraining copies
- Parallel chain attempts: Try multiple gadget combinations
Constraint Satisfiability
Before building chains, check if constraints are satisfiable:Performance Considerations
Constraint Complexity
Frombuilder.py:361:
- Many gadgets in a chain (exponential state explosion)
- Complex arithmetic operations (hard for SMT solver)
- Many symbolic memory accesses
- Filtering gadgets early
- Limiting chain length
- Using timeouts
- Caching results
Caching Symbolic Results
Frommem_writer.py:494-515:
Integration with Chain Building
Symbolic execution enables the entire chain building pipeline:- Gadget Discovery: Symbolically execute candidate gadgets to analyze effects
- Capability Analysis: Determine which registers/memory each gadget can control
- Graph Construction: Build state-transition graph based on symbolic effects
- Path Finding: Search graph for paths that achieve goals
- Constraint Solving: Solve for concrete stack values that satisfy all constraints
- Verification: Symbolically re-execute to confirm correctness
“angrop uses symbolic execution to understand the effects of gadgets and uses constraint solving and graph search for generating chains.”This combination of techniques allows angrop to automatically build complex ROP chains that would take humans hours to construct manually.