1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//!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/0xd4d/iced).
//!
//!**Current state:** decent test coverage, but still in beta. Issues/PRs welcome :)
//!
//!### About
//!
//!To the best of my knowledge, `xgadget` is the first gadget search tool to have these features:
//!
//!* 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 library API and CLI tool
//!* Supports ELF32, ELF64, PE32, PE32+ \[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:
//!
//!```no_run
//!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`:
//!
//!```ignore
//!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] <FILE(S)>...
//!
//!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 <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]
//!    -l, --max-len <LEN>             Gadgets up to LEN instrs long. If 0: all gadgets, any length [default: 5]
//!        --no-deref <OPT_REG>        Filter to gadgets that don't deref any regs or a specific reg [default: all]
//!        --reg-ctrl <OPT_REG>        Filter to gadgets that control any reg or a specific reg [default: all]
//!    -f, --regex-filter <EXPR>       Filter to gadgets matching a regular expression
//!
//!ARGS:
//!    <FILE(S)>...    1+ binaries to gadget search. If > 1: gadgets common to all
//!```
//!
//!### 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 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, `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.
//!
//!### 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 0xd4d](https://crates.io/crates/iced-x86)
//!* \[6\] ["Practical Binary Analysis" by Dennis Andreisse](https://practicalbinaryanalysis.com/)

// Macro Import --------------------------------------------------------------------------------------------------------

#[macro_use]
extern crate bitflags;

// Exports -------------------------------------------------------------------------------------------------------------

pub mod binary;
pub use crate::binary::*;

pub mod gadget;
pub use crate::gadget::*;

pub mod search;
pub use crate::search::*;

pub mod filters;
pub use crate::filters::*;

pub mod semantics;
pub use crate::semantics::*;