xgadget 0.6.0

Fast, parallel, cross-variant ROP/JOP gadget search for x86/x64 binaries.
Documentation
# xgadget

![crates.io](https://img.shields.io/crates/v/xgadget.svg)
![GitHub Actions](https://github.com/entropic-security/xgadget/workflows/test/badge.svg)

Fast, parallel, cross-variant ROP/JOP gadget search for x86 (32-bit) and x64 (64-bit) binaries.
Uses the [iced-x86 disassembler library](https://github.com/icedland/iced).

**Current state:** decent test coverage, but still in beta. Issues/PRs welcome :)

### Quickstart

Install the CLI tool and show its help menu:

```bash
cargo install xgadget --features cli-bin    # Build on host (pre-req: https://www.rust-lang.org/tools/install)
xgadget --help                              # List available commandline options
```
### About

`xgadget` is a tool for Return-Oriented Programming (ROP) and Jump-Oriented Programming (JOP) exploit development.
It's a fast, multi-threaded alternative to awesome tools like [`ROPGadget`](https://github.com/JonathanSalwan/ROPgadget), [`Ropper`](https://github.com/sashs/Ropper), and [`rp`](https://github.com/0vercl0k/rp).

Though not yet as mature as some of its contemporaries, it contains unique and experimental functionality.
To the best of our knowledge, `xgadget` is the first gadget search tool to have these features:

* Finds registers that can be controlled (overwritten) - not just those that match a user-provided regex
    * Use the `--reg-ctrl <optional_register_name>` flag
* JOP search uses instruction semantics - not hardcoded regex for individual encodings
    * Optionally filter to JOP "dispatcher" gadgets with flag `--dispatcher`
* Finds gadgets that work across multiple variants of a binary (e.g. different program or compiler versions)
    * **Full-match** - Same instruction sequence, same program counter: gadget fully re-usable.
        * E.g. `pop rsp; add [rax-0x77], cl; ret ------------------------------------- [ 0xc748d ]`
    * **Partial-match** - Same instruction sequence, different program counter: gadget logic portable.
        * E.g. `pop rsp; add [rax-0x77], cl; ret; --- [ 'bin_v1.1': 0xc748d, 'bin_v1.2': 0xc9106 ]`
    * This is entirely optional, you're free to run this tool on a single binary.
* The stack pointer is explicitly colored in terminal output, for workflow convenience.

Other features include:

* Both a library API and CLI tool
* Supports ELF32, ELF64, PE32, PE32+, Mach-O \[1\], and raw files
* Parallel across available cores \[2\], whether searching a single binary or multiple variants
* CI/CD for automated integration test and binary releases (Linux, 64-bit) \[3\]
* Statistical benchmark harness for performance tuning \[4\]
* 8086/x86/x64 only, uses a speed-optimized disassembly backend \[5\]

### API Usage

Find gadgets:

```rust
use xgadget;

let max_gadget_len = 5;

// Search single binary
let search_config = xgadget::SearchConfig::DEFAULT;
let bin_1 = xgadget::Binary::from_path_str("/path/to/bin_v1").unwrap();
let bins = vec![bin_1];
let gadgets = xgadget::find_gadgets(&bins, max_gadget_len, search_config).unwrap();
let stack_pivot_gadgets = xgadget::filter_stack_pivot(&gadgets);

// Search for cross-variant gadgets, including partial matches
let search_config = xgadget::SearchConfig::DEFAULT | xgadget::SearchConfig::PART;
let bin_1 = xgadget::Binary::from_path_str("/path/to/bin_v1").unwrap();
let bin_2 = xgadget::Binary::from_path_str("/path/to/bin_v2").unwrap();
let bins = vec![bin_1, bin_2];
let cross_gadgets = xgadget::find_gadgets(&bins, max_gadget_len, search_config).unwrap();
let cross_reg_pop_gadgets = xgadget::filter_reg_pop_only(&cross_gadgets);
```

Custom filters can be created using the [`GadgetAnalysis`](crate::gadget::GadgetAnalysis) object and/or functions from the [`semantics`](crate::semantics) module.
How the above [`filter_stack_pivot`](crate::filters::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 filter_stack_pivot<'a>(gadgets: &[Gadget<'a>]) -> Vec<Gadget<'a>> {
    gadgets
        .par_iter()
        .filter(|g| {
            let regs_overwritten = GadgetAnalysis::new(&g).regs_overwritten();
            if regs_overwritten.contains(&iced_x86::Register::RSP)
                || regs_overwritten.contains(&iced_x86::Register::ESP)
                || regs_overwritten.contains(&iced_x86::Register::SP)
            {
                return true;
            }
            false
        })
        .cloned()
        .collect()
}
```

### CLI Usage

Run `xgadget --help`:

```
xgadget v0.6.0

About:  Fast, parallel, cross-variant ROP/JOP gadget search for x86/x64 binaries.
Cores:  8 logical, 8 physical

USAGE:
    xgadget [OPTIONS] <FILE(S)>...

ARGS:
    <FILE(S)>...    1+ binaries to gadget search. If > 1: gadgets common to all

OPTIONS:
    -a, --arch <ARCH>               For raw (no header) files: specify arch ('x8086', 'x86', or 'x64') [default: x64]
    -b, --bad-bytes <BYTE(S)>...    Filter to gadgets whose addrs don't contain given bytes [default: all]
    -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]
    -f, --regex-filter <EXPR>       Filter to gadgets matching a regular expression
    -h, --help                      Print 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]
    -l, --max-len <LEN>             Gadgets up to LEN instrs long. If 0: all gadgets, any length [default: 5]
    -m, --partial-match             Include cross-variant partial matches [default: full matches only]
    -n, --no-color                  Don't color output [default: color output]
        --no-deref [<OPT_REG>]      Filter to gadgets that don't deref any regs or a specific reg [default: all]
    -p, --stack-pivot               Filter to gadgets that write the stack ptr [default: all]
        --param-ctrl                Filter to gadgets that control function parameters [default: all]
    -r, --rop                       Search for ROP gadgets only [default: ROP, JOP, and SYSCALL]
        --reg-ctrl [<OPT_REG>]      Filter to gadgets that control any reg or a specific reg [default: all]
        --reg-pop                   Filter to 'pop {reg} * 1+, {ret or ctrl-ed jmp/call}' gadgets [default: all]
    -s, --sys                       Search for SYSCALL gadgets only [default: ROP, JOP, and SYSCALL]
    -t, --att                       Display gadgets using AT&T syntax [default: Intel syntax]
    -V, --version                   Print version information
```

### CLI Build and Install (Recommended)

Build a dynamically-linked binary from source and install it locally:

```bash
cargo install xgadget --features cli-bin    # Build on host (pre-req: https://www.rust-lang.org/tools/install)
```

### CLI Binary Releases for Linux

Commits to this repo's `master` branch automatically run integration tests and build a statically-linked binary for 64-bit Linux.
You can [download it here](https://github.com/entropic-security/xgadget/releases) to try out the CLI immediately, instead of building from source.
Static binaries for Windows may also be supported in the future.

Unfortunately the statically-linked binary is several times slower on an i7-9700K, likely due to the built-in memory allocator for target `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?

Tools that attempt to automate ROP/JOP chain generation require heavyweight analysis - typically symbolic execution of an intermediate representation.
While this works well for small binaries and CTF problems, but tends to be error-prone and difficult to scale for large, real-world programs.
At present, `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.

On an i7-9700K (8C/8T, 3.6GHz base, 4.9 GHz max) machine with `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

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.

### License and Contributing

Licensed under the [MIT license](https://github.com/entropic-security/xgadget/blob/master/LICENSE).
[Contributions](https://github.com/entropic-security/xgadget/blob/master/CONTRIBUTING.md) are welcome!

### References

* \[1\] [`goblin` crate by Lzu Tao, m4b, Philip Craig, seu, Will Glynn]https://crates.io/crates/goblin
* \[2\] [`rayon` crate by Josh Stone, Niko Matsakis]https://crates.io/crates/rayon
* \[3\] [`xgadget/.github/workflows`]https://github.com/entropic-security/xgadget/tree/master/.github/workflows
* \[4\] [`criterion` crate by Brook Heisler, Jorge Aparicio]https://crates.io/crates/criterion
* \[5\] [`iced-x86` crate by wtfsck]https://crates.io/crates/iced-x86
* \[6\] ["Practical Binary Analysis" by Dennis Andreisse]https://practicalbinaryanalysis.com/