Fast, parallel, cross-variant ROP/JOP gadget search for x86 (32-bit) and x64 (64-bit) binaries. Uses official Rust bindings for the zydis disassembler library.
Current state: decent test coverage, but still in beta. Issues/PRs welcome :)
To the best of my knowledge, xgadget
is the first gadget search tool to have these features:
--dispatcher
pop rsp; add [rax-0x77], cl; ret ------------------------------------- [ 0xc748d ]
pop rsp; add [rax-0x77], cl; ret; --- [ 'bin_v1.1': 0xc748d, 'bin_v1.2': 0xc9106 ]
Other features include:
Find gadgets:
```rust use xgadget;
let maxgadgetlen = 5; let search_config = xgadget::SearchConfig::DEFAULT;
// Search single binary let bin1 = xgadget::Binary::frompathstr("/path/to/binv1").unwrap(); let bins = vec![bin1]; let gadgets = xgadget::findgadgets(&bins, maxgadgetlen, searchconfig).unwrap(); let stackpivotgadgets = xgadget::filterstack_pivot(&gadgets);
// Search for cross-variant gadgets let bin1 = xgadget::Binary::frompathstr("/path/to/binv1").unwrap(); let bin2 = xgadget::Binary::frompathstr("/path/to/binv2").unwrap(); let bins = vec![bin1, bin2]; let crossgadgets = xgadget::findgadgets(&bins, maxgadgetlen, searchconfig).unwrap(); let crossregwritegadgets = xgadget::filterstacksetregs(&crossgadgets); ```
Run xgadget --help
:
``` xgadget v0.2.0
About: Fast, parallel, cross-variant ROP/JOP gadget search for x86/x64 binaries. Cores: 8 logical, 8 physical
USAGE:
xgadget [FLAGS] [OPTIONS] FLAGS:
-t, --att Display gadgets using AT&T syntax [default: Intel syntax]
-c, --check-sec Run checksec on the 1+ binaries instead of gadget search
-d, --dispatcher Filter to potential JOP 'dispatcher' gadgets [default: all gadgets]
-e, --extended-fmt Print in terminal-wide format [default: only used for partial match search]
-h, --help Prints help information
-i, --imm16 Include '{ret, ret far} imm16' (e.g. add to stack ptr) [default: don't include]
-j, --jop Search for JOP gadgets only [default: ROP, JOP, and SYSCALL]
-n, --no-color Don't color output, useful for UNIX piping [default: color output]
-m, --partial-match Include cross-variant partial matches [default: full matches only]
-w, --reg-write Filter to 'pop {reg} * 1+, {ret or ctrl-ed jmp/call}' gadgets [default: all gadgets]
-r, --rop Search for ROP gadgets only [default: ROP, JOP, and SYSCALL]
-p, --stack-pivot Filter to gadgets that write the stack ptr [default: all gadgets]
-s, --sys Search for SYSCALL gadgets only [default: ROP, JOP, and SYSCALL]
-V, --version Prints version information OPTIONS:
-a, --arch ARGS:
Build from source and install locally: Commits to this repo's On an i7-9700K (8C/8T, 3.6GHz base, 4.9 GHz max, e.g. an older-gen consumer CPU) machine with Note this is a statistical benchmark that samples from many iterations, and requires a lot of RAM (> 32GB). If you just want to run Searching all 10 kernels for both partial and full matches is still in beta, no benchmarks yet (implemented but not yet optimized). Because of the performance hit and the lower utility of partial gadget matches, this search option is disabled by default. It can be enabled with the This project started as an optimized solution to Chapter 8, exercise 3 of "Practical Binary Analysis" by Dennis Andreisse [6], and builds on the design outlined therein.CLI Build and Install
bash
sudo apt-get install cmake # Ubuntu-specific, adjust for your package manager
cargo install xgadget --features cli-bin # Build on host (pre-req: https://www.rust-lang.org/tools/install)
CLI Binary Releases for Linux
master
branch automatically run integration tests and build a dynamically-linked binary for 64-bit Linux.
You can download it here and use the CLI immediately, instead of building from source.
Static binaries for Linux and Windows may be supported in the future.~~Yeah, but can it do 10 OS kernels in 30 seconds?!~~ Repeatable Benchmark Harness
bash
bash ./benches/bench_setup_ubuntu.sh # Ubuntu-specific, download/build 10 kernel versions
cargo bench # Grab a coffee, this'll take a while...
bench_setup_ubuntu.sh
downloads and builds 10 consecutive Linux kernels (versions 5.0.1
to 5.0.10
- with x86_64_defconfig
).cargo bench
, among other benchmarks, searches all 10 kernels for common gadgets.gcc
version 8.4.0: the average runtime, to process all ten 54MB kernels simultaneously with a max gadget length of 5 instructions and full-match search for all gadget types (ROP, JOP, and syscall gadgets), is only 26 seconds!xgadget
on the 10 kernels once, use ./benches/run_on_bench_kernels.sh
.--partial-match
flag for the CLI, or via setting a configuration bit, e.g. search_config |= xgadget::SearchConfig::PART
, for the library API. Conversely, removing default options improves performance: searching all 10 kernels for only ROP gadgets (ignoring JOP and syscall gadgets) takes just 17 seconds. xgadget
is designed to scale for large binaries while being easily configurable.Acknowledgements
References
goblin
crate by Lzu Tao, m4b, Philip Craig, seu, Will Glynn](https://crates.io/crates/goblin)rayon
crate by Josh Stone, Niko Matsakis](https://crates.io/crates/rayon)xgadget/.github/workflows
](https://github.com/entropic-security/xgadget/tree/master/.github/workflows)criterion
crate by Brook Heisler, Jorge Aparicio](https://crates.io/crates/criterion)zydis
bindings by Joel Honer, Timo von Hartz](https://crates.io/crates/zydis)