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
Running ropinator with no arguments launches the interactive TUI, where you
load binaries and search for gadgets — no flags required:
ropinator
Optionally preload a binary on startup:
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 gadgets to a file non-interactively (find, write, exit — the only batch mode):
ropinator -f binary.elf -o gadgets.txt
Preload previously exported gadgets into the TUI:
ropinator -f binary.elf -g gadgets.txt
Load gadgets without the original binary (requires --arch):
ropinator --arch x86_64 -g gadgets.txt
Override architecture detection:
ropinator -f binary.raw --arch arm -b 0x10000
Expand all gadget addresses:
ropinator -f binary.elf -a
Interactive TUI
Launch the full-screen TUI and do everything from there — load a binary, search by behavior, inspect, and export. No commands to memorize: you pick a search from a sidebar, fill in a small form, and read the results in a table.
ropinator
With no binary loaded you land on a load screen: enter a file path, optionally
pick an architecture (defaults to auto-detect), depth, and base, then load. As you
type a path, a dropdown lists the matching files and folders (native Windows
paths included) — arrow keys or click to choose, and selecting a folder descends
into it. A second tab loads a previously exported gadgets file. (ropinator -f binary.elf preloads and drops you straight into the workspace.)
The workspace has four parts:
- a sidebar of searches, grouped Structural / Semantic / Plan (semantic and plan searches are greyed out on architectures without a solver),
- a form that changes to match the selected search — register fields autocomplete from the loaded architecture,
- a results table (address · effect · instructions · notes), and
- a detail panel showing the full symbolic register state of the highlighted result.
| Key | Action |
|---|---|
| ↑/↓ or click | Pick a search in the sidebar |
| Enter / Run search | Run the current search |
| ↑/↓ in the table | Highlight a result → its register state fills the detail panel |
| ↑/↓, Enter/Tab, click (path fields) | Choose from the file dropdown; picking a folder descends into it |
| a | Toggle showing unchanged registers in the detail panel |
| F2 | Command palette — jump straight to any search or action |
| F3 | Load another binary |
| F4 | Re-scan the current binary at a new depth (deeper = more, longer gadgets) |
| F6 | Export the current results to a file |
| F7 | Show the architecture's registers |
| Ctrl-C | Quit |
The hotkeys are function keys (plus Ctrl-C) chosen so nothing collides with VS Code's default shortcuts when you run ropinator in its integrated terminal — and because function keys reach the app even while a text field is focused.
While a search or a re-scan is running, a status box keeps you informed. Re-scan (F4) re-runs gadget finding on the loaded binary with a new depth, so you can start shallow (fast) and go deeper when you need longer gadgets.
Searches
| Search | Fields | Finds |
|---|---|---|
| Text search | pattern, regex | Raw instruction-text matches |
| Control transfer | kind (syscall/call/jmp/any) | Gadgets that fire the chain |
| Set constant | dst, value | dst = value |
| Pop / controllable set | dst | Controllable setters (pop reg ; ret) |
| Move register | dst, src | dst = src |
| Load from memory | dst, addr reg/literal, offset | dst = [src + offset] |
| Store to memory | addr reg, src, offset | Write-what-where (mov [addr+off], src) |
| Arithmetic | dst, op (add/sub), src1, src2 | dst = src1 op src2 |
| Stack pivot | src, max offset | rsp = src + constant |
| Plan register goals | goals (rdi=0x404000 rsi=0) |
Every gadget needed to set a group of registers |
| Plan a call | abi, target, args | Argument-register setters + a control transfer |
Text search and Control transfer are structural (no symbolic execution) and work on every supported architecture. The semantic and plan searches require the constraint solver (x86-64, x86, ARM64).
The two Plan searches don't return a result table — they render a requirements bundle: for each register, the direct constant setters, the controllable (pop) setters, and one-level move fallbacks, plus the control transfer for a call. Nothing is ordered or emitted — it's the raw material for building a chain.
Stack-pivot offsets
For pivots, 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]. Memory pivots (mov rsp, [rax+0x10]) show up under Load
from memory with dst = rsp.
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
All options are optional. With none, ropinator launches the TUI; the flags
below just preload a binary/gadgets (or, with -o, run a one-shot export).
-f, --file FILE Binary to preload into the TUI
-b, --base ADDR Override base address
-d, --depth N Max gadget depth (default: 3)
-a, --all Expand all gadget addresses (in the -o export)
-o, --output FILE Non-interactive: find gadgets, export to file, and exit
-g, --gadgets-file FILE Preload gadgets from an exported file
--arch ARCH Override architecture detection (see below)
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
- Textual - interactive TUI 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.3.0.tar.gz.
File metadata
- Download URL: ropinator-0.3.0.tar.gz
- Upload date:
- Size: 65.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 |
d9e7a737617c713112b2afe68bd1a8751505025e1a9eeb5df4ff537a71d0be70
|
|
| MD5 |
27921428e60bd6fa9315feadcfff28fb
|
|
| BLAKE2b-256 |
6ae8e9ae99f319d65ef575804c24d324f66693ad06f2c8309e653fe0e670978f
|
File details
Details for the file ropinator-0.3.0-py3-none-any.whl.
File metadata
- Download URL: ropinator-0.3.0-py3-none-any.whl
- Upload date:
- Size: 80.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 |
2f0ba138831400461bf24111836320d092fd1e8ca2827db5c3c2b5a8ebc6b84b
|
|
| MD5 |
873595108b7587e731a93dd1b95a5981
|
|
| BLAKE2b-256 |
c7842780ff0ab26a8df0504d8a83dd872a08269f9c52b708ff2d6da3d1c92995
|