use std::fs;
use std::path::Path;
use std::process::Command;
use cure_engine::Config;
use cure_java_parser::parse;
use cure_java_print::print_unit;
use cure_java_simplify::simplify_unit;
fn javac_available() -> bool {
Command::new("javac")
.arg("-version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn compile_and_run(dir: &Path, class: &str) -> (String, Option<i32>) {
let run = Command::new("java")
.arg("-cp")
.arg(dir)
.arg(class)
.output()
.expect("run java");
(
String::from_utf8_lossy(&run.stdout).to_string(),
run.status.code(),
)
}
const GAUNTLET: &str = r#"
public class Gauntlet {
// ===== [跨方法字符串表 + 解密 helper](string-array 家族)=====
static final String[] T = new String[]{"c3Vw", "ZXI=", "aGVsbG8="};
static String d(int i) {
return new String(java.util.Base64.getDecoder().decode(T[i]));
}
static int mark(int v) {
side += v;
return v;
}
static int side = 0;
public static void main(String[] args) {
// ===== [解密 + 拼接 + 多层字符串]=====
String s = d(0) + d(1) + "!";
System.out.println(s);
// ===== [CFF 状态机:体内含解密调用 + 寄存器噪声 + 不透明谓词]=====
int v24 = 0;
int v25 = 0;
int v7 = 0;
v24 = 1;
while (true) {
switch (v24) {
case 0: {
if (2 > 1) {
v24 = 1;
} else {
v24 = 5;
}
break;
}
case 1: {
if (v25 < 3) {
v24 = 2;
} else {
v24 = 4;
}
break;
}
case 2: {
int v32 = v25 + mark(1);
v25 = v32;
v24 = 1;
break;
}
case 4: {
v7 = v25 * 100;
v24 = 9;
break;
}
case 5: {
v7 = -1;
v24 = 9;
break;
}
}
if (v24 == 9) {
break;
}
}
System.out.println("v7=" + v7);
// ===== [解密 + SB 语句链 + 迭代器组合]=====
java.util.List<String> list = new java.util.ArrayList<>();
list.add(d(2));
list.add("world");
java.util.Iterator it = list.iterator();
String acc = "";
while (true) {
if (!(it.hasNext())) {
break;
} else {
String e = (String) it.next();
StringBuilder sb = new StringBuilder().append(acc);
sb = sb.append(e);
StringBuilder sb2 = sb.append("-");
String acc2 = sb2.toString();
acc = acc2;
continue;
}
}
System.out.println("acc=" + acc);
// ===== [XOR 噪声 + 虚拟执行组合]=====
int r = (mark(5) ^ 0x5A) ^ 0x5A;
String f = String.format("%s=%d", "total", r + "abc".length());
System.out.println(f);
System.out.println("side=" + side);
}
}
"#;
#[test]
fn gauntlet_full_stack_deobfuscation() {
let mut outcome = parse(GAUNTLET);
assert!(outcome.errors.is_empty(), "解析失败:{:?}", outcome.errors);
let report = simplify_unit(&mut outcome.ast, &mut outcome.unit, &Config::default());
let cleaned = print_unit(&outcome.ast, &outcome.unit);
if javac_available() {
let base = std::env::temp_dir().join("cure_gauntlet");
let _ = fs::remove_dir_all(&base);
let orig_dir = base.join("orig");
let clean_dir = base.join("clean");
fs::create_dir_all(&orig_dir).unwrap();
fs::create_dir_all(&clean_dir).unwrap();
fs::write(orig_dir.join("Gauntlet.java"), GAUNTLET).unwrap();
fs::write(clean_dir.join("Gauntlet.java"), &cleaned).unwrap();
for d in [&orig_dir, &clean_dir] {
let out = Command::new("javac")
.arg("-encoding")
.arg("UTF-8")
.arg("-nowarn")
.arg("-d")
.arg(d)
.arg(d.join("Gauntlet.java"))
.output()
.unwrap();
assert!(
out.status.success(),
"javac 失败:\n{}\n== 净化输出 ==\n{cleaned}",
String::from_utf8_lossy(&out.stderr)
);
}
let (a_out, a_code) = compile_and_run(&orig_dir, "Gauntlet");
let (b_out, b_code) = compile_and_run(&clean_dir, "Gauntlet");
assert_eq!(a_code, b_code);
assert_eq!(a_out, b_out, "组合混淆还原改变行为!\n== 净化输出 ==\n{cleaned}");
eprintln!("语义差分通过:{}", a_out.replace('\n', " / ").trim());
}
assert!(cleaned.contains(r#"println("super!")"#), "{cleaned}");
assert!(!cleaned.contains(r#"d(0)"#), "{cleaned}");
assert!(!cleaned.contains(r#"d(1)"#), "{cleaned}");
assert!(!cleaned.contains(r#"d(2)"#), "{cleaned}");
assert!(!cleaned.contains("switch"), "{cleaned}");
assert!(cleaned.contains("while (v25 < 3)"), "{cleaned}");
assert!(cleaned.contains("mark(1)"), "{cleaned}");
assert!(cleaned.contains(r#""v7=" + v25 * 100"#), "{cleaned}");
assert!(cleaned.contains(r#"add("hello")"#), "{cleaned}");
assert!(cleaned.contains(r#"add("hello")"#), "{cleaned}");
assert!(cleaned.contains("mark(5)"), "{cleaned}");
assert!(!cleaned.contains("0x5A"), "{cleaned}");
assert!(cleaned.contains(r#"String.format("%s=%d", "total", r + 3)"#), "{cleaned}");
let a = GAUNTLET.lines().filter(|l| !l.trim().is_empty()).count();
let b = cleaned.lines().filter(|l| !l.trim().is_empty()).count();
eprintln!(
"终极组合:{} 次改写,非空行 {} → {}(-{}%)",
report.edits,
a,
b,
100 - b * 100 / a
);
let mut rules: Vec<_> = report.by_rule.iter().collect();
rules.sort();
for (k, v) in rules {
eprintln!(" {k}: {v}");
}
assert!(report.edits >= 20, "预期大规模净化,实际 {}", report.edits);
}