#![cfg(feature = "deobfuscation")]
#![allow(clippy::unwrap_used, clippy::expect_used, missing_docs)]
use std::{hint::black_box, path::PathBuf, time::Duration};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use dotscope::deobfuscation::{DeobfuscationEngine, EngineConfig};
const CONFUSEREX_DIR: &str = "tests/samples/packers/confuserex/1.6.0";
const NETREACTOR_DIR: &str = "tests/samples/packers/netreactor/7.5.0";
const NETREACTOR_SAMPLES: &[(&str, &str)] = &[
("reactor_strings.exe", "strings"),
("reactor_necrobit.exe", "necrobit"),
("reactor_necrobit_strings_cff.exe", "necrobit_strings_cff"),
("reactor_full.exe", "full"),
];
const CONFUSEREX_SAMPLES: &[(&str, &str)] = &[
("mkaring_minimal.exe", "minimal"),
("mkaring_constants.exe", "constants"),
("mkaring_controlflow.exe", "controlflow"),
("mkaring_antitamper.exe", "antitamper"),
("mkaring_constants_cfg_controlflow.exe", "constants_cfg_cf"),
("mkaring_maximum.exe", "maximum"),
];
fn sample_path(dir: &str, filename: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join(dir)
.join(filename)
}
fn bench_confuserex(c: &mut Criterion) {
let mut group = c.benchmark_group("confuserex_deobfuscation");
group.sample_size(10);
group.measurement_time(Duration::from_secs(30));
group.warm_up_time(Duration::from_secs(3));
for (filename, label) in CONFUSEREX_SAMPLES {
let path = sample_path(CONFUSEREX_DIR, filename);
if !path.exists() {
eprintln!("skipping missing sample: {}", path.display());
continue;
}
group.bench_with_input(BenchmarkId::from_parameter(label), &path, |b, path| {
b.iter(|| {
let engine = DeobfuscationEngine::new(EngineConfig::default());
let result = engine.process_file(black_box(path));
black_box(result.is_ok())
});
});
}
group.finish();
}
fn bench_netreactor(c: &mut Criterion) {
let mut group = c.benchmark_group("netreactor_deobfuscation");
group.sample_size(10);
group.measurement_time(Duration::from_secs(30));
group.warm_up_time(Duration::from_secs(3));
for (filename, label) in NETREACTOR_SAMPLES {
let path = sample_path(NETREACTOR_DIR, filename);
if !path.exists() {
eprintln!("skipping missing sample: {}", path.display());
continue;
}
group.bench_with_input(BenchmarkId::from_parameter(label), &path, |b, path| {
b.iter(|| {
let engine = DeobfuscationEngine::new(EngineConfig::default());
let result = engine.process_file(black_box(path));
black_box(result.is_ok())
});
});
}
group.finish();
}
fn bench_detection_only(c: &mut Criterion) {
let mut group = c.benchmark_group("confuserex_detection");
group.sample_size(10);
group.measurement_time(Duration::from_secs(20));
for (filename, label) in CONFUSEREX_SAMPLES {
let path = sample_path(CONFUSEREX_DIR, filename);
if !path.exists() {
continue;
}
let assembly = match dotscope::CilObject::from_path(&path) {
Ok(a) => a,
Err(e) => {
eprintln!("skipping unloadable sample {}: {e}", path.display());
continue;
}
};
group.bench_with_input(
BenchmarkId::from_parameter(label),
&assembly,
|b, assembly| {
let engine = DeobfuscationEngine::default();
b.iter(|| black_box(engine.detect(black_box(assembly))));
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_confuserex,
bench_netreactor,
bench_detection_only
);
criterion_main!(benches);