E9Patch: rewriting x86_64 ELF binaries without recovering the control flow graph
A powerful static binary rewriting tool
At a glance
- What is it?
- E9Patch takes an ELF executable or shared object and produces a patched binary that is a drop-in replacement, and it does so by working on a linear disassembly rather than by rebuilding the control flow graph, which is what lets it handle binaries of over 100MB in seconds. It is a research tool with a GPL-3.0 licence, and the projects built on top of it are the more interesting part of the story.
- Who is it for?
- Adopt E9Patch if you need to instrument or harden a third party binary that you cannot recompile, and you are prepared to work at a low level with the Programmer's Guide rather than treat it as a command line tool. Do not adopt it as a general purpose binary editor, because it targets x86_64 Linux ELF only and it needs matching plugin code for the instrumentation you want.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 117 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What static binary rewriting actually does
The README defines the operation precisely: static binary rewriting takes an input binary, which is an ELF executable or shared object, and generates an output binary with some patch or modification applied to it, and the patched binary can be used as a drop-in replacement of the original. That last clause is the whole point, and it is harder than it sounds. The output is not a wrapper that launches the original under a debugger, and it is not a shared library that intercepts calls. It is the original program, with new code spliced in and the control flow adjusted so that the new code runs. The consequences follow. The rewritten binary has no additional dependencies, which the README lists as the compatibility property, and it can be distributed to a user who has never heard of the tool, which is what makes it usable for hardening and for instrumentation of software you do not control. The set of properties the README claims is short and specific: it is scalable, so it can reliably rewrite large and complex binaries including web browsers over 100MB in size; it is compatible, in the drop-in sense; it is fast, rewriting most binaries in a few seconds; it has low overheads in both performance and memory; and it is programmable, meaning it is designed to be integrated into other projects.
The trick is a linear disassembly and a separate backend
E9Patch comes with two tools, and the split between them explains the design. `e9patch` is the binary rewriter backend and `e9tool` is a linear disassembly frontend for E9Patch. The academic framing is in the title of the PLDI 2020 paper the README points to, Binary Rewriting without Control Flow Recovery. Most binary rewriting tools start by recovering the control flow graph of the input, which is a costly and error prone step, since the recovery has to guess which bytes are code and which are data and then model every jump. E9Patch instead works from a linear disassembly, which is a stream of instructions in address order, and applies tactics to it. The repository layout shows how much machinery that involves, with a backend whose object files include `e9CFR` for control flow related work, `e9alloc`, `e9api`, `e9elf`, `e9emit`, `e9json`, `e9mapping`, `e9optimize`, `e9patch` itself, `e9pe`, `e9tactics`, `e9trampoline` and `e9x86_64`, and a frontend whose objects include `e9action`, `e9cfg`, `e9codegen`, `e9csv`, `e9dwarf`, `e9frontend`, `e9metadata`, `e9parser`, `e9tool`, `e9types` and `e9x86_64`. The `e9tactics` and `e9optimize` modules are where the difference from the prototype lives, and the README says the current version implements several new optimizations and can generate significantly faster binaries, sometimes by a factor of 2x, and that you enable them by passing the `-O2` option to E9Tool.
Using it: one command, and a rewritten binary in a.out
The example in the README instruments `xterm` so that every `xor` instruction prints itself at run time, and the command is short enough to remember.
$ ./e9tool -M 'asm=/xor.*/' -P print xtermThis generates a modified version of xterm written to the `a.out` file. The modified xterm can then be run as normal, and it prints the assembly string of each executed `xor` instruction to `stderr`, which in the README's example output looks like a stream of `xorl` instructions with their operands, one per line. Two details of the interface are worth noting. The `-M` option takes a match expression, and the `asm=` part of it matches against the assembly text, so the selection is by instruction pattern rather than by address, which is what makes the technique scale to a large binary. The `-P print` part names a plugin that performs the instrumentation, and the plugin is the piece that supplies the code to run at the matched site. The full option set is behind `./e9tool --help`, and the README points at the E9Tool User's Guide for the details, since the single example is a demonstration rather than a reference.
The projects on top of it show what programmable means
The README lists the projects that use E9Patch, and the list is a better argument for the tool than any of its own feature claims. RedFat is a binary hardening system based on low-fat pointers. E9AFL automatically inserts AFL instrumentation into binaries, which is the classic use of this kind of tool, since a binary you cannot recompile cannot be instrumented with a compiler flag. E9Syscall does system call interception using static binary rewriting of `libc.so`, which is a nice demonstration of the shared object case rather than the executable case. Hopper does automatic fuzzing test case generation for libraries, and EnvFuzz does program environment fuzzing. RFF is greybox fuzzing for concurrency testing, and AutoTrace is simple source line based tracing. Each of those is a different research question answered with the same mechanism, which is exactly what the programmable property means in practice. It also explains why the project ships a Programmer's Guide as well as a User's Guide: the intended user is often an author of a plugin, not a person running a command.
Building it, and what the build expects from your system
Building is described as very easy, namely run the `build.sh` script, which automatically builds the two tools. The Makefile behind it shows the requirements. The compiler flags include `-std=c++11`, `-Wall`, `-fPIC` and `-pie`, and the version is taken from a `VERSION` file at build time through a `-DVERSION` define. The install prefix defaults to `/usr` and can be overridden, and the frontend is linked with an rpath pointing into `$(PREFIX)/share/e9tool/lib/`, which is where the plugin runtime lives, so that a rewritten binary can find its instrumentation code. The frontend also links `-ldl` and `-lz`, and in the conventional build it uses system libdw and links `-ldw` and `-lZydis`, the disassembler library. There is a `Makefile.zydis` in the repository, which suggests a build that uses a different arrangement of that dependency. Alongside the build there are `install.sh`, `e9compile.sh` and `e9loader.ld`, the last being the linker script involved in loading a patched binary, plus a `test/` directory and an `examples/` directory holding the C sources of the plugins, among them `print.c`, `printf.c`, `nop.c`, `trap.c`, `bounds.c`, `cfi.c`, `count.c`, `cov.c`, `delay.c`, `limit.c`, `skip.c`, `state.c`, `stdlib.c` and a `win64_demo.c`.
Version history, licence and the research behind it
The release history shows a long incubation and a recent arrival. There is a 1.0.0-rc10 from April 2025, then v1.0.0 in March 2026, then v1.0.1 in June 2026, and the last push was on 2026-06-28. The README explains that the current version is significantly improved compared to the original prototype evaluated in the PLDI 2020 paper: several new optimizations, a faster generated binary, an improved implementation of the Physical Page Grouping space optimization, slightly improved patching coverage, and many new features. The licence is the GNU Public License version 3, with the specific note that some files are released under the MIT licence and that you should check the file preamble, which is the arrangement you would expect for a GPL tool that vendors or adapts smaller components. The work was partially supported by the National Satellite of Excellence in Trustworthy Software Systems, funded by the National Research Foundation of Singapore under the National Cybersecurity Research and Development programme, and the README asks users to cite the paper, with a BibTeX entry included.
Editorial conclusion
Adopt E9Patch if you need to instrument or harden a third party binary that you cannot recompile, and you are prepared to work at a low level with the Programmer's Guide rather than treat it as a command line tool. Do not adopt it as a general purpose binary editor, because it targets x86_64 Linux ELF only and it needs matching plugin code for the instrumentation you want. Before you start, build it with the bundled `build.sh` script, check the E9Tool User's Guide for the option set beyond the single example in the README, and read the GPL-3.0 licence carefully, since the tool is GPL and some individual files are MIT. Releases reach v1.0.1 from 2026-06-22, the last push was on 2026-06-28, and the underlying research is published as Binary Rewriting without Control Flow Recovery at PLDI 2020.
Frequently asked questions
What kind of binary does E9Patch rewrite?
E9Patch targets x86_64 Linux ELF binaries, that is ELF executables and shared objects. It takes an input binary and produces an output binary with a patch applied, which is a drop-in replacement of the original with no additional dependencies.
What is the difference between e9tool and e9patch?
e9tool is a linear disassembly frontend for E9Patch and e9patch is the binary rewriter backend. Building with the bundled `build.sh` script produces both tools automatically.
How do I instrument a binary with E9Patch?
Use the frontend with a match expression and a plugin, for example `./e9tool -M 'asm=/xor.*/' -P print xterm`, which writes a modified xterm to the a.out file that prints the assembly of each executed xor instruction to stderr. The full option list is in `./e9tool --help` and in the E9Tool User's Guide.
Which projects are built on E9Patch?
The README lists RedFat for binary hardening based on low-fat pointers, E9AFL for automatic AFL instrumentation, E9Syscall for system call interception by rewriting libc.so, Hopper for automatic fuzzing test case generation, EnvFuzz for program environment fuzzing, RFF for greybox concurrency testing and AutoTrace for source line based tracing.
How does E9Patch avoid control flow recovery?
It works from a linear disassembly instead of a recovered control flow graph, which is the subject of the PLDI 2020 paper Binary Rewriting without Control Flow Recovery. The current version adds optimizations that can generate significantly faster binaries, sometimes by a factor of 2x, enabled by passing the `-O2` option to E9Tool.
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/gjduck-e9patch)Was this useful?