rop3 is a tool developed in Python and it relies on the Capstone disassembly framework to search for gadgets, operations, and ROP chains using a backtracking algorithm in a tree-like structure:
- Multi-format, multi-arch: analyzes ELF, PE and Mach-O binaries (x86 and x86-64). For fat/universal Mach-O binaries,
--archselects the slice to analyze. - Gadget search: ROP, JOP and RETF gadgets, with controls for search depth (
--depth), undeterministic gadgets (--allow-undeterministic-gadgets) and complex memory operands (--allow-complex-memory-ops). - Operations and ROP chains: search for high-level operations (
--op/--dst/--src) and build ROP chains from a ROPLang file (--ropchain,--exhaustive), including multi-step composite operations. - Relocation: rebase any binary (ELF/PE/Mach-O) with
--base, one address per binary. - Bad-char filtering: avoid bytes in the gadget address (
--badchar) and/or in the gadget opcode bytes (--badchar-bytes). By default, duplicate gadgets prefer canary-free addresses (0x00,0x0a,0x0d,0xff); disable with--keep-canary-address. - Symbol annotation: with
--symbols, each gadget is tagged with the nearest symbol (name+offset) when the binary is not stripped. - Output formats: human-readable text (default), or machine-readable
--output json/--output csvfor scripting. Colors are emitted only on a TTY. - Interactive mode:
--interactivescans the binary once and drops into a REPL to explore gadgets, operations and chains without re-scanning. - Performance: parallel scanning across processes (
--jobs N) and an optional on-disk gadget cache (--cache) for repeated runs over the same file. - Library API: use rop3 programmatically through the
Rop3class (see Use as a library).
We recommend to install rop3's dependencies with pip in a virtual environment to not to mess up with your current configuration:
$ sudo apt update
$ sudo apt install python3-pip python3-venvCreate and activate your virtual environment:
$ python3 -m venv .
$ source bin/activate
(venv) $ git clone https://github.com/reverseame/rop3.git
(venv) $ cd rop3Now, you can install dependencies in requirements.txt:
(venv) rop3 $ python3 -m pip install -r requirements.txtusage: rop3.py [-h] [-v] [--depth <bytes>] [--all] [--rop | --no-rop] [--retf | --no-retf] [--jop | --no-jop] [--allow-undeterministic-gadgets] [--allow-complex-memory-ops] [--verbose]
[--binary <file> [<file> ...]] [--badchar <hex> [<hex> ...]] [--badchar-bytes <hex> [<hex> ...]] [--keep-canary-address] [--base <hex> [<hex> ...]] [--arch <name>] [--symbols]
[--output {text,json,csv}] [--op <op>] [--dst <reg>] [--src <reg>] [--ropchain <file>] [--exhaustive | --no-exhaustive] [--interactive] [--jobs <n>] [--cache] [--cache-dir <dir>]
This tool allows you to search for gadgets, operations, and ROP chains using a backtracking algorithm in a tree-like structure
options:
-h, --help show this help message and exit
-v, --version display rop3.py's version and exit
--depth <bytes> depth for search engine (default to 5 bytes)
--all show the same gadget in different addresses
--rop, --no-rop search for ROP gadgets
--retf, --no-retf search for RETF gadgets
--jop, --no-jop search for JOP gadgets
--allow-undeterministic-gadgets
allow gadgets with conditional branches (e.g. jne) as intermediate instructions
--allow-complex-memory-ops
allow gadgets whose first instruction uses complex memory addressing (e.g. [r1*r2], [r1+r2*s+disp])
--verbose show progress information (gadget counts, combinations)
--binary <file> [<file> ...]
specify a list of binary path files to analyze
--badchar <hex> [<hex> ...]
specify a list of chars to avoid in gadget address
--badchar-bytes <hex> [<hex> ...]
specify a list of chars to avoid in gadget opcode bytes
--keep-canary-address
do not prefer canary-free addresses (0x00, 0x0a, 0x0d, 0xff) when discarding duplicate gadgets
--base <hex> [<hex> ...]
specify a base address to relocate binary files (it may take a while). When you specify more than one base address, you need to provide one address for each binary
--arch <name> select the architecture slice of a fat Mach-O binary (e.g. x86_64, i386)
--symbols annotate gadgets with the nearest symbol (when the binary is not stripped)
--output {text,json,csv}
output format (default: text)
--op <op> search for operation
--dst <reg> specify a destination register for the operation
--src <reg> specify a source register for the operation
--ropchain <file> plain text file with a ROP chain
--exhaustive, --no-exhaustive
exhaustive search for ROP chains
--interactive scan the binary once and drop into an interactive prompt
--jobs <n> number of worker processes for the gadget scan (default: 1)
--cache cache discovered gadgets on disk and reuse them on repeated runs over the same file and options
--cache-dir <dir> directory for the gadget cache (default: $XDG_CACHE_HOME/rop3)
--jobs N distributes the gadget scan over N worker processes. Each executable section is split into chunks scanned independently, then the results are merged and deduplicated, so the output is identical to a serial run. The speedup is sublinear (the merge, deduplication and sort run in the parent, and there is per-process start-up cost), so it is worth it mainly for large binaries and/or a high --depth; on small inputs the process overhead dominates and --jobs 1 (the default) is faster.
With --cache, the gadgets discovered for a binary are stored on disk and reused on later runs over the same file and options, skipping the scan. The cache key binds the file content hash and every option that affects the result, so a changed binary or option misses cleanly. This is especially handy for large binaries and for the interactive mode.
$ python rop3.py --binary libc.so.6 --cache # first run scans and caches
$ python rop3.py --binary libc.so.6 --cache --op mov --dst rdi --src rax # reuses the cacheWith --interactive, rop3 scans the binary once and drops into a prompt so you can explore its gadgets without re-scanning on every query:
$ python rop3.py --binary /bin/ls --interactive
Loaded 71 gadgets from /bin/ls
rop3> count
71
rop3> search pop rbp
[ls @ 0x100000777]: pop rbp ; ret (x29)
...
rop3> op mov rdi rax
rop3> chain chain.txt
rop3> quitCommands: gadgets/search [substring], count, op <name> [dst] [src], chain <file>, help, quit.
rop3 can also be used programmatically through the Rop3 class. Gadgets are scanned once and cached on the instance:
from rop3 import Rop3
r = Rop3("libc.so.6", base="0x7f0000000000", symbols=True)
for gadget in r.gadgets():
print(gadget)
r.find_op("mov", dst="rdi", src="rax") # list of matching gadgets
r.ropchain("chain.txt") # iterator over ROP chainsIn the work that we presented in 15th IEEE Workshop on Offensive Technologies (WOOT21), we used rop3 to evaluate the executional power of Return Oriented Programming in a subset of most common Windows DLLs. Check the paper for further details.
$ python rop3.py --binary ../tfg_inf/experiments/dlls/win10x86/SHELL32.dll --op mov --dst eax --src ecx
[SHELL32.dll @ 0x698a474c]: mov eax, ecx ; ret (x97)
[SHELL32.dll @ 0x698dc8c8]: mov eax, ecx ; pop ebx ; leave ; ret (x5) (modifies rbx, rbp)
[SHELL32.dll @ 0x6991a2b1]: mov eax, ecx ; pop ebx ; ret (x4) (modifies rbx)
[SHELL32.dll @ 0x6992d289]: mov eax, ecx ; pop esi ; ret (x11) (modifies rsi)
[SHELL32.dll @ 0x6995e30b]: mov eax, ecx ; pop edi ; ret (x2) (modifies rdi)
[SHELL32.dll @ 0x699670c1]: mov eax, ecx ; pop esi ; pop ebp ; ret (x1) (modifies rsi, rbp)
[SHELL32.dll @ 0x69b8a61b]: mov eax, ecx ; leave ; ret (x1) (modifies rbp)
[SHELL32.dll @ 0x69c3c483]: mov eax, ecx ; pop esi ; leave ; ret (x1) (modifies rsi, rbp)
# ...Licensed under the GNU GPLv3 license.