Documentation Index
Fetch the complete documentation index at: https://mintlify.com/angr/angrop/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Register control is fundamental to ROP chain construction. angrop provides powerful primitives for setting and moving register values, which can be combined to create complex chains.
Setting Registers
Basic set_regs()
The set_regs() method sets registers to specific values:
import angr
import angrop
# Load binary and initialize
p = angr.Project("/bin/ls")
rop = p.analyses.ROP()
rop.find_gadgets()
# Set single register
chain = rop.set_regs(rax=0x1337)
# Set multiple registers
chain = rop.set_regs(rax=0x1337, rbx=0x56565656)
# Print the chain
chain.print_payload_code()
Example Output
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(0x56565656)
chain += p64(0x0)
chain += p64(0x0)
chain += p64(code_base + 0x812f) # pop rsi; pop rbp; ret
chain += p64(0x1337)
chain += p64(0x0)
chain += p64(code_base + 0x169dd) # mov rax, rsi; ret
chain += p64(code_base + 0x10a55)
Moving Registers
Using move_regs()
The move_regs() method copies values between registers:
# Move value from rdx to rax
chain = rop.move_regs(rax='rdx')
chain.pp()
Example Output
0x0000000000034573: pop rcx; ret
<preserved rax value>
0x000000000004a1dd: pop rdi; mov edx, 0x89480002; ret
<rdx value>
0x00000000000d5a94: mov qword ptr [rcx + 8], rdi; ret
<BV64 next_pc_1081_64>
This chain moves the value from rdx into rax using intermediate gadgets.
Combining Chains
Set up initial values
import angr
import angrop
p = angr.Project("/bin/ls")
rop = p.analyses.ROP()
rop.find_gadgets()
# Set initial register values
chain = rop.set_regs(rbx=0x41414141, rcx=0x42424242)
Add more operations
# Chain additional register operations
chain += rop.set_regs(rdx=0xdeadbeef)
# Move registers around
chain += rop.move_regs(rax='rbx')
View the complete chain
# Pretty-print for debugging
chain.pp()
# Or generate exploit code
chain.print_payload_code()
Complete Example
import angr
import angrop
# Initialize
p = angr.Project("/bin/ls")
rop = p.analyses.ROP()
rop.find_gadgets()
# Build complex chain
chain = rop.set_regs(rax=0x1337, rbx=0x56565656, rcx=0xdeadbeef)
chain += rop.move_regs(rdx='rax')
chain += rop.set_regs(rsi=0xcafebabe)
# View the chain
chain.print_payload_code()
Preserving Registers
When calling functions, you can preserve specific registers:
# Preserve rdi register during function call
chain = rop.func_call(
"prepare_kernel_cred",
(0x41414141, 0x42424242),
preserve_regs={'rdi'}
)
chain.pp()
Output
0xffffffff81489752: pop rsi; ret
0x42424242
0xffffffff8114d660: <prepare_kernel_cred>
<BV64 next_pc_4280_64>
Notice that the first argument (0x41414141) is ignored because rdi is preserved, so the function will use whatever value is already in rdi.
Avoiding Bad Bytes
You can specify bad bytes to avoid in your chain:
# Avoid null bytes and newlines
rop.set_badbytes([0x0, 0x0a])
# This chain will not contain 0x00 or 0x0a bytes
chain = rop.set_regs(eax=0)
chain.print_payload_code()
angrop will automatically find alternative gadgets that don’t contain the specified bad bytes.
Real-World Example: Setting Up Syscall Arguments
import angr
import angrop
# Load binary
p = angr.Project("/bin/ls")
rop = p.analyses.ROP()
rop.find_gadgets()
# Set up for read(0, buffer, 0x100) syscall
buffer_addr = 0x804f000
chain = rop.set_regs(
rax=0, # syscall number for read
rdi=0, # fd = stdin
rsi=buffer_addr, # buffer address
rdx=0x100 # count = 256 bytes
)
# Or use the built-in do_syscall method
chain = rop.do_syscall(0, [0, buffer_addr, 0x100], needs_return=False)
chain.print_payload_code()
Advanced: Register Arithmetic
You can also perform operations on registers:
# Move register and then set another
chain = rop.move_regs(rax='rdx')
chain += rop.set_regs(rbx=0x1000)
# The order matters - operations are applied sequentially
chain.pp()
Debugging Tips
Pretty Print
Use pp() to see exactly what each gadget does:
chain = rop.set_regs(rax=0x1337, rbx=0x5656)
chain.pp()
Payload Code
Use print_payload_code() to get ready-to-paste Python code:
chain.print_payload_code()
This outputs code you can directly copy into your exploit script.
Chain Composition Patterns
Pattern 1: Setup, Execute, Cleanup
# Setup registers
setup = rop.set_regs(rax=0x1337, rbx=0x2000)
# Execute function
execute = rop.func_call("dangerous_function", [0x41414141, 0x42424242])
# Combine
chain = setup + execute
Pattern 2: Multi-Stage Chains
# Stage 1: Write "/bin/sh" to memory
stage1 = rop.write_to_mem(0x61b100, b"/bin/sh\x00")
# Stage 2: Set up registers for execve
stage2 = rop.set_regs(rdi=0x61b100, rsi=0, rdx=0)
# Stage 3: Call execve
stage3 = rop.func_call("execve", [0x61b100, 0, 0], needs_return=False)
# Complete chain
chain = stage1 + stage2 + stage3
chain.print_payload_code()
This modular approach makes complex chains easier to build and debug.