ropinator
ROP gadget finder with constraint-based semantic search.
Finds gadgets in ELF, PE, Mach-O, and raw binaries across x86, ARM, MIPS, PowerPC, and RISC-V. Includes a Z3-backed symbolic execution engine for searching gadgets by behavior rather than text patterns.
Install
pip install ropinator
Requires Python 3.13+.
Usage
Find gadgets:
ropinator -f binary.elf
Set search depth (max instructions per gadget, default 3):
ropinator -f binary.elf -d 5
Custom base address:
ropinator -f binary.elf -b 0x400000
Export to file:
ropinator -f binary.elf -o gadgets.txt
Load previously exported gadgets:
ropinator -f binary.elf -g gadgets.txt --start-solver
Load gadgets without the original binary (requires --arch):
ropinator --arch x86_64 -g gadgets.txt --start-solver
Override architecture detection:
ropinator -f binary.raw --arch arm -b 0x10000
Expand all gadget addresses:
ropinator -f binary.elf -a
Constraint Solver
Launch the interactive solver to search gadgets by semantic behavior:
ropinator -f binary.elf --start-solver
Commands
| Command | Description |
|---|---|
pivot [src] [--max-offset N] [--max N] |
Find stack-pivot gadgets |
move [dst] [src] |
Find register-to-register moves |
const <dst> <value> |
Find gadgets that load a constant |
load <dst> <src> [offset] |
Find memory read gadgets |
store <addr_reg> [src] [offset] |
Find write-what-where gadgets (mov [addr+off], src) |
setreg [dst] |
Find controllable setters (pop reg ; ret) |
arith <dst> add|sub <src1> [src2] |
Find arithmetic gadgets |
search <pattern> [--regex] |
Raw instruction-text search (all architectures) |
branch [syscall|call|jmp] |
Find control-transfer gadgets (fire the chain) |
setregs reg=val ... |
Bundle: all gadgets needed to set several registers |
plan_call [--abi A] [--target T] arg... |
Bundle: gadgets to call a function/syscall with args |
show <index> [--all] |
Inspect symbolic register state |
export <file> |
Export results to file |
registers |
List available registers |
search and branch are structural (no symbolic execution) and work on every
supported architecture. The semantic commands (move, const, load, store,
setreg, arith, pivot) require the constraint solver (x86-64, x86, ARM64).
Examples
Stack pivots
rop> pivot rcx
Found 3 pivot gadget(s) (rsp <- rcx):
[0] 0x0000000000401000 rsp <- rcx + 0x8 mov rsp, rcx ; ret [clobbers: none]
[1] 0x0000000000401010 rsp <- rcx + 0x8 xchg rsp, rcx ; ret [clobbers: rcx]
[2] 0x0000000000401020 rsp <- rcx + 0x18 lea rsp, [rcx + 0x10] ; ret [clobbers: none]
The offset is the signed difference rsp_final = src + offset. For a clean x64 pivot
(mov rsp, rcx ; ret) the offset is +0x8 because ret consumes one return address
from the newly-pivoted stack — place your fake ROP stack starting at [rcx].
Search all registers for any pivot:
rop> pivot
Limit to pivots that land within 0x10 bytes of the source:
rop> pivot rcx --max-offset 0x10
Memory pivots (mov rsp, [rax+0x10]) are found with the load command:
rop> load rsp rax 0x10
Register moves
rop> move rax rbx
Found 3 move gadget(s):
[0] 0x0000000000401000: rax <- rbx | mov rax, rbx ; ret
[1] 0x0000000000401020: rax <- rbx | push rbx ; pop rax ; ret
[2] 0x0000000000401040: rax <- rbx | xchg rax, rbx ; xchg rax, rbx ; mov rax, rbx ; ret
Constant loads
rop> const rax 0xdeadbeef
Found 1 constant-loading gadget(s):
[0] 0x0000000000401234: rax = 0xdeadbeef | pop rax ; ret
Memory reads
rop> load rax rbx 0x10
Found 2 load gadget(s):
[0] 0x0000000000402000: rax <- [rbx + 0x10] | mov rax, qword ptr [rbx + 0x10] ; ret
[1] 0x0000000000402030: rax <- [rbx + 0x10] | lea rcx, [rbx + 0x10] ; mov rax, qword ptr [rcx] ; ret
Inspecting results
show prints only the registers changed by the gadget. Add --all to see the full
register file:
rop> show 0
Result [0]
Address: 0x0000000000401000
Instructions: mov rsp, rcx ; ret
Effect: rsp <- rcx + 0x8
Changed registers:
rsp = rcx + 0x8
(15 registers unchanged — use 'show 0 --all' to see them)
MCP Server
Ropinator ships an MCP server so AI agents (Claude, Cursor, etc.) can search gadgets programmatically without a human at the shell.
Setup
Add to your MCP client config (e.g. Claude Code's .claude/settings.json):
{
"mcpServers": {
"ropinator": {
"command": "ropinator-mcp"
}
}
}
Or if running from the repo with uv:
{
"mcpServers": {
"ropinator": {
"command": "uv",
"args": ["run", "--project", "/path/to/ropinator", "ropinator-mcp"]
}
}
}
Tools
Search tools return JSON so agents can parse results directly. Each result
has address, instructions, type, effect, and (where relevant) clobbers
or control kind/target fields.
| Tool | Description |
|---|---|
load_binary |
Load a binary and scan for gadgets |
load_gadgets_file |
Load gadgets from a previously exported file |
search_gadgets |
Raw instruction-text search — the generic finder (all arches) |
find_pivots |
Find stack-pivot gadgets (src_reg, max_offset) |
find_moves |
Find register-to-register move gadgets |
find_loads |
Find memory-read gadgets (also covers memory pivots) |
find_const |
Find constant-loading gadgets |
find_stores |
Find write-what-where gadgets (mov [addr+off], src) |
find_setreg |
Find controllable setters (pop reg ; ret) |
find_arith |
Find arithmetic gadgets (add/sub) |
find_control |
Find control-transfer gadgets (syscall / call reg / jmp reg) |
plan_registers |
Bundle: all gadgets needed to set several registers to values |
plan_call |
Bundle: all gadgets needed to call a function/syscall with args |
show_result |
Full register state for a result index (JSON) |
get_registers |
List GP registers for the loaded architecture |
session_status |
Summary of the loaded binary and cached results |
Workflow
The server is stateful: call load_binary once, then run as many searches as
needed. Results from the most recent search are cached so show_result(index)
always works after any find_*/search_gadgets call.
load_binary(file_path="target.exe")
find_pivots(src_reg="rcx", max_offset="0x40")
show_result(0)
find_moves(dst_reg="rdi", src_reg="rax")
Getting all gadgets for a chain
plan_registers and plan_call return a requirements bundle: every gadget an
agent needs to reach a goal, annotated with clobbers. The bundle gathers
candidates but does not order them or emit a payload — the agent composes the
chain from the returned options.
# Set up a Linux execve/mprotect-style syscall (SysV ABI: rdi, rsi, rdx, ...)
plan_call(args="0x404000 0 7", abi="sysv64", target="syscall")
# Or drive individual register goals directly:
plan_registers(goals="rdi=0x404000 rsi=0 rdx=7")
Each register entry lists controllable (pop-style — supply the value on the
fake stack), direct (constant setters), and a one-level via_move fallback,
plus a control section with syscall/call/jmp gadgets to fire the chain.
Supported Formats
| Format | Description |
|---|---|
| ELF | Linux, BSD, embedded |
| PE | Windows executables and DLLs |
| Mach-O | macOS, iOS |
| Raw | Flat binaries (use with -b to set base address) |
Supported Architectures
Gadget finding: x86 (16/32/64-bit), ARM (32/64/Thumb), MIPS (32/64), PowerPC (32/64), RISC-V (32/64).
Constraint solver: x86-64. Other architectures planned.
Options
-f, --file FILE Binary to analyze (required unless --arch and -g are used)
-b, --base ADDR Override base address
-d, --depth N Max gadget depth (default: 3)
-a, --all Expand all gadget addresses
-o, --output FILE Export gadgets to file
-g, --gadgets-file FILE Load gadgets from exported file instead of searching
--arch ARCH Override architecture detection (see below)
--start-solver Launch constraint solver shell
Architecture names for --arch:
| Name | Aliases |
|---|---|
| x86_64 | x86-64, x64 |
| x86 | i386 |
| ARM64 | aarch64 |
| ARM32 | arm |
| ThumbBE | thumb-be |
| MIPS32 | mips |
| MIPS64 | |
| PowerPC32 | ppc, ppc32 |
| PowerPC64 | ppc64 |
| RISCV64 | riscv |
Dependencies
Installed automatically via pip:
- Capstone - disassembly engine
- Keystone - assembler engine (for gadget file loading)
- z3-solver - symbolic execution backend
- cmd2 - interactive shell framework
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ropinator-0.2.0.tar.gz.
File metadata
- Download URL: ropinator-0.2.0.tar.gz
- Upload date:
- Size: 55.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb3ad86eb108d28eeed6bf68534abd1988f82db72c1f861aa87b477994274c59
|
|
| MD5 |
329bdbc45bd88c7fdaba896db78a0ba7
|
|
| BLAKE2b-256 |
92f8a8ab6f655b47fcdc7a1fd8955b596c7b1a769a94ad2b0fe6bb9dea3b8dd1
|
File details
Details for the file ropinator-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ropinator-0.2.0-py3-none-any.whl
- Upload date:
- Size: 68.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
294f6a2ccdf1202b1ab85cf1f7c76a4821357c9e5e323789afbe9a0b6497d768
|
|
| MD5 |
ebe8b6455a6a521f6acbd11ee8041b64
|
|
| BLAKE2b-256 |
de4047440e3cb29e6f76909763d35a23c7e17bf2e31b881e128f8a75b43f5da0
|