use std::collections::HashSet;
use crate::{
cilassembly::CleanupRequest,
compiler::PassPhase,
deobfuscation::{
techniques::{Detection, Evidence, Technique, TechniqueCategory},
utils::find_methods_calling_apis,
},
metadata::token::Token,
CilObject,
};
const PAT_VIRTUAL_PROTECT: usize = 0;
const PAT_GET_HINSTANCE: usize = 1;
const PAT_GET_MODULE: usize = 2;
const PAT_MARSHAL_COPY: usize = 3;
const API_PATTERNS: &[&str] = &[
"VirtualProtect",
"GetHINSTANCE",
"get_Module",
"Marshal.Copy",
];
#[derive(Debug)]
pub struct AntiDumpFindings {
pub method_tokens: HashSet<Token>,
}
pub struct GenericAntiDump;
impl Technique for GenericAntiDump {
fn id(&self) -> &'static str {
"generic.dump"
}
fn name(&self) -> &'static str {
"Generic Anti-Dump Detection"
}
fn category(&self) -> TechniqueCategory {
TechniqueCategory::Neutralization
}
fn detect(&self, assembly: &CilObject) -> Detection {
let api_hits = find_methods_calling_apis(assembly, API_PATTERNS);
let method_tokens: HashSet<Token> = api_hits
.into_iter()
.filter(|(_token, indices)| {
let has_virtual_protect = indices.contains(&PAT_VIRTUAL_PROTECT);
let has_module_handle =
indices.contains(&PAT_GET_HINSTANCE) || indices.contains(&PAT_GET_MODULE);
let has_marshal_copy = indices.contains(&PAT_MARSHAL_COPY);
has_virtual_protect && has_module_handle && !has_marshal_copy
})
.map(|(token, _)| token)
.collect();
if method_tokens.is_empty() {
return Detection::new_empty();
}
let count = method_tokens.len();
let mut detection = Detection::new_detected(
vec![Evidence::BytecodePattern(format!(
"{count} methods with anti-dump patterns (VirtualProtect without Marshal.Copy)"
))],
None,
);
for token in &method_tokens {
detection.cleanup_mut().add_method(*token);
}
detection.set_findings(Box::new(AntiDumpFindings { method_tokens }));
detection
}
fn ssa_phase(&self) -> Option<PassPhase> {
Some(PassPhase::Simplify)
}
fn cleanup(&self, detection: &Detection) -> Option<CleanupRequest> {
let findings = detection.findings::<AntiDumpFindings>()?;
if findings.method_tokens.is_empty() {
return None;
}
let mut request = CleanupRequest::new();
for token in &findings.method_tokens {
request.add_method(*token);
}
Some(request)
}
}
#[cfg(test)]
mod tests {
use crate::{deobfuscation::techniques::Technique, test::helpers::load_sample};
#[test]
fn test_detect_no_panic_on_obfuscated() {
let asm = load_sample("tests/samples/packers/confuserex/1.6.0/mkaring_normal.exe");
let technique = super::GenericAntiDump;
let _detection = technique.detect(&asm);
}
#[test]
fn test_detect_negative() {
let asm = load_sample("tests/samples/packers/confuserex/1.6.0/original.exe");
let technique = super::GenericAntiDump;
let detection = technique.detect(&asm);
assert!(!detection.is_detected());
}
}