Fast, parallel, cross-variant ROP/JOP gadget search for x86 (32-bit) and x64 (64-bit) binaries. Uses the iced-x86 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;
// Search single binary let searchconfig = xgadget::SearchConfig::DEFAULT; let bin1 = xgadget::Binary::frompathstr("/path/to/binv1").unwrap(); let bins = vec![bin1]; let gadgets = xgadget::findgadgets(&bins, maxgadgetlen, searchconfig).unwrap(); let stackpivotgadgets = xgadget::filterstackpivot(&gadgets);
// Search for cross-variant gadgets, including partial matches let searchconfig = xgadget::SearchConfig::DEFAULT | xgadget::SearchConfig::PART; 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 crossregpopgadgets = xgadget::filterregpoponly(&cross_gadgets); ```
Custom filters can be created using the GadgetAnalysis
object and/or functions from the semantics
module.
How the above filter_stack_pivot
function is implemented:
```rust use rayon::prelude::*; use iced_x86; use xgadget::{Gadget, GadgetAnalysis};
/// Parallel filter to gadgets that write the stack pointer
pub fn filterstackpivot<'a>(gadgets: &[Gadget<'a>]) -> Vec
Run xgadget --help
:
``` xgadget v0.5.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]
-e, --extended-fmt Print in terminal-wide format [default: only used for partial match search]
-h, --help Prints help information
--inc-call Include gadgets containing a call [default: don't include]
--inc-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 [default: color output]
--param-ctrl Filter to gadgets that control function parameters [default: all]
-m, --partial-match Include cross-variant partial matches [default: full matches only]
--reg-pop Filter to 'pop {reg} * 1+, {ret or ctrl-ed jmp/call}' gadgets [default: all]
-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]
-s, --sys Search for SYSCALL gadgets only [default: ROP, JOP, and SYSCALL]
-V, --version Prints version information OPTIONS:
-a, --arch ARGS:
Build a dynamically-linked binary from source and install it locally: Commits to this repo's Unfortunately the statically-linked binary is several times slower on an i7-9700K, likely due to the built-in memory allocator for target Tools that attempt to automate ROP chain generation require heavyweight analysis - typically symbolic execution of an intermediate representation.
While this works well for small binaries and CTF problems, it tends to be slow and difficult to scale for large, real-world programs.
At present, On an i7-9700K (8C/8T, 3.6GHz base, 4.9 GHz max) machine with 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 (Recommended)
bash
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 statically-linked binary for 64-bit Linux.
You can download it here to try out the CLI immediately, instead of building from source.
Static binaries for Windows may also be supported in the future.x86_64-unknown-linux-musl
.
So building a dynamically-linked binary from source with the above cargo install
command is highly recommended for performance (links against your system's allocator).Why No Chain Generation?
xgadget
has a different goal: enable an expert user to manually craft stable exploits by providing fast, accurate gadget discovery.~~Yeah, but can it do 10 OS kernels under 10 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 6.3 seconds! Including partial matches as well takes just 7.9 seconds.Acknowledgements
References