use std::io::Write;
use assert_cmd::Command;
use predicates::prelude::*;
use tempfile::NamedTempFile;
mod common;
#[cfg(target_arch = "x86")]
static REG_NAME: &str = "eax";
#[cfg(target_arch = "x86_64")]
static REG_NAME: &str = "rax";
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_no_arg_err() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains(
"the following required arguments were not provided:",
));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_file_not_found_err() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin.arg("/usr/bin/some_file_83bb57de34d8713f6e4940b4bdda4bea");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains("No such file or directory"));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_conflicting_flags_rop_jop() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/usr/bin/some_file_83bb57de34d8713f6e4940b4bdda4bea")
.arg("-r")
.arg("-j");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains(
"the argument '--rop' cannot be used with '--jop'",
));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_conflicting_flags_dispatcher_stack_set_reg() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/usr/bin/some_file_83bb57de34d8713f6e4940b4bdda4bea")
.arg("--reg-pop")
.arg("-d");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains(
"the argument '--reg-pop' cannot be used with '--dispatcher'",
));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_conflicting_flags_fess_checksec() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/usr/bin/some_file_83bb57de34d8713f6e4940b4bdda4bea")
.arg("--fess")
.arg("--check-sec");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains(
"the argument '--fess' cannot be used with '--check-sec'",
));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
#[cfg(target_os = "linux")]
fn test_invalid_bad_bytes() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/bin/cat")
.arg("-b")
.arg("0xff")
.arg("0xgg");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains("InvalidDigit"));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
#[cfg(target_os = "linux")]
fn test_invalid_reg_name() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/bin/cat")
.arg("--reg-overwrite")
.arg("r42");
xgadget_bin
.assert()
.failure()
.stderr(predicate::str::contains("Invalid register: \"r42\""));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_raw() {
let mut raw_file = NamedTempFile::new().unwrap();
raw_file.write_all(common::ADJACENT_JMP_X64).unwrap();
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin.arg(raw_file.path());
xgadget_bin
.assert()
.success()
.stdout(predicate::str::contains(
"lea ecx, [rip+0x5ddcb]; jmp qword ptr [rcx];",
));
xgadget_bin
.assert()
.success()
.stdout(predicate::str::contains("jmp rcx;"));
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
#[cfg(target_os = "linux")]
fn test_single_bin() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin.arg("/bin/cat");
xgadget_bin.assert().success();
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_dual_bin() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin.arg("/bin/cat").arg("/bin/cp");
xgadget_bin.assert().success();
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_triple_bin_with_arg() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin
.arg("/bin/cat")
.arg("/bin/ls")
.arg("/bin/cp")
.arg("--att")
.arg("-r");
xgadget_bin.assert().success();
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_checksec() {
let mut xgadget_bin = Command::cargo_bin("xgadget").unwrap();
xgadget_bin.arg("/bin/cat").arg("-c");
xgadget_bin.assert().success();
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_search_args() {
let output_rop_jop_sys = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_rop_jop_sys_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--all")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_rop = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-r")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_jop = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-j")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_sys = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-s")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_stack_pivot = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-p")
.output()
.unwrap()
.stdout,
)
.unwrap();
assert!(output_rop_jop_sys_all.len() >= output_rop_jop_sys.len());
assert!(output_rop_jop_sys.len() >= output_rop.len());
assert!(output_rop_jop_sys.len() >= output_jop.len());
assert!(output_rop_jop_sys.len() >= output_sys.len());
assert!(output_rop_jop_sys.len() >= output_stack_pivot.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_max_len() {
let output_def_len = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_100_len = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-l")
.arg("100")
.output()
.unwrap()
.stdout,
)
.unwrap();
assert!(output_100_len.len() >= output_def_len.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_extended_line_count() {
let output_default = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("OUTPUT_DEFAULT: {}", output_default);
let output_default_line_cnt = output_default.lines().count();
let output_extended = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-e")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("OUTPUT_EXTENDED: {}", output_extended);
let output_extended_line_cnt = output_extended.lines().count();
assert!(output_default_line_cnt == output_extended_line_cnt);
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_att_intel_syntax_line_count() {
let output_intel = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("OUTPUT_INTEL: {}", output_intel);
let output_intel_line_cnt = output_intel.lines().count();
let output_att = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--att")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("OUTPUT_ATT: {}", output_att);
let output_att_line_cnt = output_att.lines().count();
assert!(output_intel_line_cnt == output_att_line_cnt);
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_regex() {
let reg_pop_regex = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-f")
.arg(r"^(?:pop)(?:.*(?:pop))*.*(?:ret|call|jmp)")
.output()
.unwrap()
.stdout,
)
.unwrap();
let reg_pop_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-pop")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("REG_POP_REGEX: {}", reg_pop_regex);
println!("REG_POP_FILTER: {}", reg_pop_filter);
assert!(reg_pop_regex.len() >= reg_pop_filter.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_dispatcher_filter() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_dispatch_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-d")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("DISPATCHER: {}", output_dispatch_filter);
assert!(output_all.len() >= output_dispatch_filter.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_pop_filter() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_reg_pop_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-pop")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("REG_POP: {}", output_reg_pop_filter);
assert!(output_all.len() >= output_reg_pop_filter.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_param_ctrl_filter() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_param_ctrl_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--param-ctrl")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("PARAM_CTRL: {}", output_param_ctrl_filter);
assert!(output_all.len() >= output_param_ctrl_filter.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_no_read_filter_1() {
let output_reg_no_read_rax_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-no-read")
.arg(REG_NAME)
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_reg_no_read_all_regs_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-no-read")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("REG_NO_READ_RAX: {}", output_reg_no_read_rax_filter);
println!("REG_NO_READ: {}", output_reg_no_read_all_regs_filter);
assert!(output_reg_no_read_rax_filter.len() >= output_reg_no_read_all_regs_filter.len());
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_no_read_filter_2() {
let mut raw_file = NamedTempFile::new().unwrap();
raw_file
.write_all(common::FILTERS_REG_NO_DEREF_AND_REG_WRITE)
.unwrap();
let reg_no_read_1_reg = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-no-read")
.arg("rdi")
.output()
.unwrap()
.stdout,
)
.unwrap();
let reg_no_read_2_regs = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-no-read")
.arg("rdi")
.arg("rsi")
.output()
.unwrap()
.stdout,
)
.unwrap();
let reg_no_read_any_regs = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-no-read")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("REG_NO_READ_1_REG: {}", reg_no_read_1_reg);
println!("REG_NO_READ_2_REGS: {}", reg_no_read_2_regs);
println!("REG_NO_READ_ANY_REGS: {}", reg_no_read_any_regs);
assert!(reg_no_read_1_reg.lines().count() >= reg_no_read_2_regs.lines().count());
assert!(reg_no_read_2_regs.lines().count() >= reg_no_read_any_regs.lines().count());
assert!(reg_no_read_2_regs.contains("pop rsi; pop rdi; ret;"));
assert!(reg_no_read_1_reg.contains("pop rsi; pop rdi; ret;"));
assert!(reg_no_read_1_reg.contains("add r8, [rsi]; add r8, [rdx]; pop rsi; pop rdi; ret;"));
assert!(reg_no_read_2_regs.contains("add r8, [rdx]; pop rsi; pop rdi; ret;"));
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_overwrite_filter_1() {
let output_reg_write_rax_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-overwrite")
.arg(REG_NAME)
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_reg_write_all_regs_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-overwrite")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("REG_WRITE_RAX: {}", output_reg_write_rax_filter);
println!("REG_WRITE_ALL: {}", output_reg_write_all_regs_filter);
assert!(output_reg_write_all_regs_filter.len() >= output_reg_write_rax_filter.len());
}
#[test]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_overwrite_filter_2() {
let mut raw_file = NamedTempFile::new().unwrap();
raw_file
.write_all(common::FILTERS_REG_NO_DEREF_AND_REG_WRITE)
.unwrap();
let ctrl_1_reg = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-overwrite")
.arg("rsi")
.arg("--max-len")
.arg("25")
.output()
.unwrap()
.stdout,
)
.unwrap();
let ctrl_2_regs = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-overwrite")
.arg("rsi")
.arg("rdi")
.arg("--max-len")
.arg("25")
.output()
.unwrap()
.stdout,
)
.unwrap();
let ctrl_any_regs = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg(raw_file.path())
.arg("--reg-overwrite")
.arg("--max-len")
.arg("25")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("CTRL_1_REG: {}", ctrl_1_reg);
println!("CTRL_2_REGS: {}", ctrl_2_regs);
println!("CTRL_ANY_REGS: {}", ctrl_any_regs);
assert!(ctrl_any_regs.lines().count() >= ctrl_1_reg.lines().count());
assert!(ctrl_1_reg.lines().count() >= ctrl_2_regs.lines().count());
assert!(ctrl_any_regs.contains("pop rsi; pop rdi; ret;"));
assert!(ctrl_1_reg.contains("pop rsi; pop rdi; ret;"));
assert!(ctrl_2_regs.contains("pop rsi; pop rdi; ret;"));
assert!(ctrl_any_regs
.contains("add r8, [rdi]; add r8, [rsi]; add r8, [rdx]; pop rsi; pop rdi; ret;"));
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_bad_bytes_filter() {
let output_all_bytes = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_bad_bytes = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("-b")
.arg("0x40")
.arg("0x55")
.arg("ff")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL_BYTES: {}", output_all_bytes);
println!("BAD_BYTES: {}", output_bad_bytes);
assert!(output_all_bytes.len() >= output_bad_bytes.len());
}
#[test]
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_reg_equivalence() {
let reg_no_deref_r8l_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-no-read")
.arg("r8l")
.output()
.unwrap()
.stdout,
)
.unwrap();
let reg_no_deref_r8b_filter = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat")
.arg("--reg-no-read")
.arg("r8b")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("REG_NO_DEREF_R8L: {}", reg_no_deref_r8l_filter);
println!("REG_NO_DEREF_R8B: {}", reg_no_deref_r8b_filter);
assert!(reg_no_deref_r8l_filter.lines().count() == reg_no_deref_r8b_filter.lines().count());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_0() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.arg("--reg-only")
.arg("--reg-overwrite")
.arg("rdi")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("README_0: {}", output_readme);
assert!(!output_readme.is_empty());
assert!(output_all.len() >= output_readme.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_1() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.arg("--jop")
.arg("--reg-pop")
.arg("--att")
.arg("--max-len")
.arg("10")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("README_1: {}", output_readme);
assert!(!output_readme.is_empty());
assert!(output_all.len() >= output_readme.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_2() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.arg("--regex-filter")
.arg("^(?:pop)(?:.*(?:pop))*.*(?:call|jmp)")
.arg("--att")
.arg("--max-len")
.arg("10")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("README_2: {}", output_readme);
assert!(!output_readme.is_empty());
assert!(output_all.len() >= output_readme.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_3() {
let output_all = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.output()
.unwrap()
.stdout,
)
.unwrap();
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.arg("--rop")
.arg("--reg-overwrite")
.arg("rdi")
.arg("--reg-no-read")
.arg("rsi")
.arg("rdx")
.arg("--bad-bytes")
.arg("0x32")
.arg("0x0d")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("ALL: {}", output_all);
println!("README_3: {}", output_readme);
assert!(!output_readme.is_empty());
assert!(output_all.len() >= output_readme.len());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_4() {
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/usr/bin/sudo")
.arg("/bin/cat") .arg("--check-sec")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("README_4: {}", output_readme);
assert!(!output_readme.is_empty());
}
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(not(feature = "cli-bin"), ignore)]
fn test_readme_5() {
let output_readme = String::from_utf8(
Command::cargo_bin("xgadget")
.unwrap()
.arg("/bin/cat") .arg("--symbols")
.output()
.unwrap()
.stdout,
)
.unwrap();
println!("README_4: {}", output_readme);
assert!(!output_readme.is_empty());
}