Ropper: a gadget finder for ROP chains with an assembly-backed search
Display information about files in different file formats and find gadgets to build rop chains for different architectures (x86/x86_64, ARM/ARM64, MIPS, PowerPC, SPARC64). For disassembly ropper uses the awesome Capstone Framework.
At a glance
- What is it?
- A BSD-licensed Python tool that reads ELF, PE, Mach-O and raw binaries, disassembles them with Capstone, and finds gadgets for seven architectures. Its install instructions still point at a setup.py the repository no longer contains, and the packaging config has a typo that quietly disables a filter.
- Who is it for?
- Ropper is a good fit for someone reverse engineering a stripped binary who wants a searchable gadget list rather than a full exploitation framework, and a poor fit if you need Windows PE exploitation tooling beyond a single generator or if you want a supported package rather than a hobby one.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 48 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The install instructions point at a setup.py that is not there
The README walks through installing dependencies with pip, then says to install and execute Ropper by running a setup script from the source tree.
$ python setup.py install
$ ropperThere is no setup.py in the repository. The tree contains pyproject.toml, requirements.txt, tox.ini, Ropper.py, the ropper package directory, test.py, test.sh, a sample script, test binaries and test cases. Nothing named setup.py appears. The same paragraph offers the modern alternative immediately afterwards, so the answer is not hard to find:
$ pip install ropperThat is the path to use, and the pyproject file confirms it declares a console script named ropper bound to ropper.__main__:main, so the executable name in the README is correct. You can also skip installation entirely and run the entry script from a clone.
The dependency instructions have the same drift in a milder form. The README asks for an unpinned capstone and an unpinned filebytes, while requirements.txt pins capstone at 4.0.1 and filebytes at 0.10.0, and pyproject asks for filebytes 0.10.0 or newer with capstone 3 or newer. Three different statements about the same two packages. The pinned file is the one that matches a known working combination, so it is the one to follow if you want reproducibility rather than the newest release.
The filebytes submodule route is the tidiest part of the documentation. If you would rather not install filebytes from PyPI, cloning with submodules gives you the same code, and the tree does carry both .gitmodules and a filebytes directory, so that path is real.
A packaging typo that silently disables the package filter
The pyproject file has a small mistake with a real consequence. It declares the setuptools package discovery in two places, and the second heading is misspelled.
[tool.setuptools.packages]
find = {}The heading carries three l's where it should carry two, so the include filter written underneath it is read as an unrelated table and ignored. The line beneath the bad heading is an instruction to include only packages matching ropper.
[tooll.setuptools.packages.find]
include = ['ropper*']In practice this does not break the install, because the earlier `find = {}` table still triggers automatic discovery and the project has one real package directory. But the filter the author intended is not being applied, and that is worth knowing if you fork the project, add a second top-level package, or vendor it inside something larger, since the include list will not narrow what ships. It is a one-character fix in a file you can read before you install anything, which is a better position to be in than most projects with this class of problem.
The declared version in the same file is 1.13.13, and the project requires Python 3 or newer. The licence field in the file says BSD and the classifier agrees, while the repository licence identifier is BSD-3-Clause and a COPYING file sits at the tree root.
Seven architectures, four file formats, and what the flags actually do
Ropper has two jobs and they are separable. The first is inspection: it reads the container and prints structure without searching for anything. The second is gadget search, which needs Capstone to disassemble.
Inspection is the part you should learn first, because every search you do later depends on knowing what you loaded. The file header, entry point, image base, sections, segments, imports, symbols, and the PE DLL characteristics flag are all separate switches. The `--section` flag dumps one section's data, `--hex` prints it in hex, and `--string` looks for a string across all data sections. That combination is how you confirm a file loaded the way you think it did before you trust any result that depends on its addresses.
Search has more knobs than anyone uses on a first run. `--search` takes a regex over gadget text, `--opcode` takes a byte pattern with wildcards like ffe4 or ff??, `--instructions` takes an instruction sequence such as a jump through esp or a pop followed by a return, and `--quality` ranks found gadgets from best. `--inst-count` caps how many instructions count as one gadget, six by default. `--badbytes` takes a byte set to exclude, which is the flag that turns a gadget list into something you can actually chain.
Two shortcuts are architecture-specific and worth knowing: `--ppr` finds pop-reg pop-reg return sequences and `--jmp <reg>` finds jumps through a register, both limited to x86 and x86_64. For a stack pivot there is a dedicated flag.
Raw binary files are loaded with `--raw` plus an explicit architecture, since there is no header to read. That combination is the one to get right, because a wrong architecture produces a gadget list full of nonsense that looks plausible.
Chain generators cover four cases and one operating system
The generators turn a gadget list into a starting chain, which is where Ropper differs from a bare gadget dumper. The usage text lists four.
execve takes an optional command and defaults to /bin/sh, and it is marked for Linux on x86, x86_64 and ARM. spawn_shell takes a command path and the address of system, writes the command into .bss if there is nowhere better, and covers the same three architectures. mprotect takes an address and size and is listed for Linux x86 and x86_64. virtualprotect takes the address of the import table entry and a size, and is the only Windows generator, for x86.
That table is the honest limit of the project. It is a Linux tool with one Windows affordance, it does not advertise a 64-bit Windows generator, and the MIPS, PowerPC and SPARC architectures it can disassemble have no generator at all. So the architecture list in the README describes what Ropper can read and search, while the generator list describes what it can build for you. Those are different capabilities and the documentation does not blur them, which is worth crediting.
Options like `--set aslr nx` and `--unset aslr nx` let you tell the generators what protections are in play, and `--imagebase` or `-I` lets you tell it where the file will be loaded. Both matter, because a chain built against the wrong base produces addresses that are wrong in exactly the way that is hard to notice.
Semantic search is the feature the README admits is unfinished
There is a second search mode that goes beyond pattern matching, and the README introduces it with a line in capitals saying the feature is still under development.
$ ropper --file <afile> --semantic "<any constraint>"Instead of a regex, you describe a property in a small constraint language and Ropper searches for gadgets that satisfy it. The documented constraints cover assigning a register to another register, assigning a number or a dereferenced memory location to a register, and the four arithmetic operations on a register with a number, a register, or a dereferenced register.
The example given is register equality with a negation, which sets one register to a value and then asks for gadgets that do not clobber a second one. That is a real capability no regex gives you, because it reasons about register state across the gadget rather than matching bytes.
The cost is a heavier dependency set. Semantic search needs pyvex and z3py, and the README does not offer them on PyPI: it tells you to clone pyvex, clone the Z3 repository, run a script to generate a makefile, then build and install with make. That is a source build of the Z3 solver, which is the part of the setup most likely to occupy your afternoon. The pyproject file lists only keystone-engine as an optional extra, so nothing in the packaging reflects this requirement.
Keystone is the other optional piece and it is the lighter one. It is declared as a ropchain extra in pyproject and is only needed to assemble found gadgets, not to search for them, which the README also points out.
A Dockerfile that builds from master, and a badge that points at retired CI
The container recipe is short and worth reading closely, because it clones and installs rather than pinning.
FROM python
WORKDIR /app
RUN apt-get update \
&& apt-get install git -y \
&& git clone https://github.com/sashs/Ropper.git \
&& cd Ropper \
&& pip3 install .
ENTRYPOINT ["python", "/app/Ropper/Ropper.py"]
CMD ["--console"]The comment at the top tells you the intended invocation mounts the host filesystem into the container so you can point Ropper at a file without copying it in. The default command starts the interactive console, and the entry point is the same Ropper.py you can run directly.
Two things to note. The clone has no branch or tag, so the image you build contains whatever master happens to be on the day you build, which makes the image non-reproducible and means the version you get is not the version named in pyproject unless you check. And the maintainer label in the image says oddrabbit while the package author and maintainer fields in pyproject both say Sascha Schirra, which is a small sign of outside contributions being accepted into the build.
The build badge is another sign of age. The README's status badge points at travis-ci.org and the tree still carries a .travis.yml, which is the Travis configuration format from a service that open source projects largely stopped using. The pyproject and the presence of tox.ini and test.sh suggest the project did move to a modern Python packaging layout while its visible CI indicator stayed behind. Practical consequence for a user: the badge tells you nothing about whether the current master passes, so check the test setup yourself. The repository has test.py, test.sh and a test-binaries directory, which is where the answers are.
Activity is current enough to keep using: the last push was on 2026-08-22, and the project publishes no GitHub releases, so the pyproject version of 1.13.13 is the only version number the repository states outright.
Editorial conclusion
Ropper is a good fit for someone reverse engineering a stripped binary who wants a searchable gadget list rather than a full exploitation framework, and a poor fit if you need Windows PE exploitation tooling beyond a single generator or if you want a supported package rather than a hobby one. Install it with pip and skip the README's setup.py line, which refers to a file the repository does not have, and pin capstone yourself because the README asks for an unpinned install while requirements.txt asks for 4.0.1. Start with the file header and sections output before searching for gadgets, pass bad bytes explicitly rather than discovering them during chain assembly, and treat the semantic search as the unfinished feature the README says it is.
Frequently asked questions
How do I install ropper?
Use pip install ropper, or clone the repository with its filebytes submodule and run Ropper.py directly. The README also suggests running python setup.py install, but the repository contains pyproject.toml and no setup.py, so that line does not work.
Which architectures can ropper find gadgets for?
x86, x86_64, MIPS and MIPS64, ARM and Thumb, ARM64, PowerPC and PPC64, and SPARC64. Chain generators are narrower: execve and spawn_shell cover Linux x86, x86_64 and ARM, mprotect covers Linux x86 and x86_64, and virtualprotect covers Windows x86.
What extra packages does ropper need for semantic search?
Semantic search needs pyvex and z3py. The README does not take z3py from PyPI: you clone the Z3 repository, run a script to generate the makefile, then build and install it, which makes it the heaviest part of the setup.
Can ropper open PE and Mach-O files as well as ELF?
Yes. The supported file types are ELF, PE, Mach-O and raw, and the inspection flags cover each container: header, entry point, image base, sections, segments, imports, symbols, plus PE DLL characteristics. For a raw file you must pass the architecture explicitly.
How do I exclude unusable bytes when building a chain in ropper?
Use the badbytes option to pass the byte set that must not appear in a gadget, so the search skips candidates containing them. The interactive console is the default command for a session, and the set and unset options let you declare aslr and nx to the generators.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sashs-ropper)