Contents

ANGR Python Scripting Cheatsheet

ANGR Python CheatSheet

Symbolic execution is a technique that involves the systematic exploration of all possible paths in a program’s code using abstract syntax trees (ASTs).

Assembly as an AST

Placeholder

Without Branching

In the following example, the assembly code moves the value of 5 into the 32-bit register eax, moves the value 7 into the 32-bit register ebx, then adds both eax and ebx together with the result being stored in eax.

1
2
3
0:  b8 05 00 00 00          mov    eax,0x5
5:  bb 07 00 00 00          mov    ebx,0x7
a:  01 d8                   add    eax,ebx

Each register is 32-bit and can be considered a bit vector symbol (BVS). Once created, the add operation can be represented as ast = eax + ebx. Next, we create a solver object where we add constraints to the bit vector symbols we created earlier such that eax must equal 5 and the result must equal 12. Once the constraints are added, we can evaluate the AST using the method eval.

1
2
3
4
5
6
7
8
9
from claripy import Solver, BVS
eax = BVS('eax', 32)
ebx = BVS('ebx', 32)
ast = eax + ebx
s = Solver()
s.add(ebx == 7)
s.add(eax + ebx == 12)
s.eval(eax, 1)
# (5,)

With Branching

Placeholder.

Starting a Project

1
2
3
4
5
import angr, claripy
# Create the Project
p = angr.Project("stealer.exe")
# Terminate Project Execution
p.terminate_execution()

Creating Project Hooks

1
2
3
4
5
6
7
# Hook an Address
skip_bytes = 4
@p.hook(0xdeadbeef, length=skip_bytes)
def hook_state(s):
    # Change State Here
# Check If Address Hooked (Bool)
p.is_hooked(0xdeadbeef)

Creating a State

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
start_address = 0xdeadbeef
end_address   = 0xbeefdead
avoid_address = 0xcafef00d
# Create Blank State
s = p.factory.blank_state(addr=start_address)
# Create State for Executing a Function
s = p.factory.call_state(addr=start_address)
# Setting Registers and Memory
s.regs.ebp = s.regs.esp
s.regs.ebx = 0xdeadbeef
s.mem[0x1000].uint32_t = s.regs.eax
# Setting State Options
s.options.add(angr.options.LAZY_SOLVES)
# Use unicorn engine to execute symbolically when data is concrete
s.options.add(angr.options.UNICORN)
# Make the value of memory read from an uninitialized address zero instead of an unconstrained symbol
s.options.add(angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY)
# Single Step State
s.step()

Bit Vectors (ASTs)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
x = s.solver.BVS('x', 32)
# <BV32 x_54_32>
y = s.solver.BVS('y', 32)
# <BV32 y_55_32>
(x + y).op
# '__add__'
(x + y).args
#(<BV32 x_54_32>, <BV32 y_55_32>)
(x + y).args[0]
# <BV32 x_54_32>

Creating Constraints

1
2
3
4
5
6
7
# Add Constraints to the State
s.add_constraints(s.regs.eax != 0xdeadbeef)
# s.add_constraints(s.regs.eip != 0xcafef00d)
# Access or Print Constraints
s.constraints
# Check if State is Satisfiable
state.satisfiable()

Starting your Simulation

1
2
3
4
# Create the Simulation Manager with the Initial Execution State
sm = p.factory.simulation_manager(s)
# Symboliclly Execute to Find Addresses and Avoiding Others
sm.explore(find=(end_address,), avoid=(avoid_address))

Reading Memory

1
sm.found[0].solver.eval(sm.found[0].memory.load(sm.found[0].regs.eax, 32), cast_to=bytes)