use std::sync::Arc;
use crate::{
compiler::SsaPass,
deobfuscation::{
detection::DetectionScore,
findings::DeobfuscationFindings,
obfuscators::{Obfuscator, ObfuscatorRegistry},
},
CilObject,
};
pub struct ObfuscatorDetector {
registry: ObfuscatorRegistry,
}
impl Default for ObfuscatorDetector {
fn default() -> Self {
Self::new()
}
}
impl ObfuscatorDetector {
#[must_use]
pub fn new() -> Self {
Self {
registry: ObfuscatorRegistry::new(),
}
}
#[must_use]
pub fn from_registry(registry: ObfuscatorRegistry) -> Self {
Self { registry }
}
#[must_use]
pub fn registry(&self) -> &ObfuscatorRegistry {
&self.registry
}
pub fn registry_mut(&mut self) -> &mut ObfuscatorRegistry {
&mut self.registry
}
pub fn register(&mut self, obfuscator: Arc<dyn Obfuscator>) {
self.registry.register(obfuscator);
}
pub fn set_threshold(&mut self, threshold: usize) {
self.registry.set_threshold(threshold);
}
#[must_use]
pub fn threshold(&self) -> usize {
self.registry.threshold()
}
pub fn detect(
&self,
assembly: &CilObject,
) -> (Option<Arc<dyn Obfuscator>>, DeobfuscationFindings) {
let all_detected = self.registry.detect(assembly);
let primary_obfuscator = all_detected
.first()
.and_then(|(id, _, _)| self.registry.get(id).cloned());
let mut findings = all_detected
.first()
.map(|(_, _, f)| f.clone())
.unwrap_or_default();
if let Some(obfuscator) = &primary_obfuscator {
findings.obfuscator_name = Some(obfuscator.name());
}
(primary_obfuscator, findings)
}
pub fn best_obfuscator(&self, assembly: &CilObject) -> Option<Arc<dyn Obfuscator>> {
self.registry.detect_best(assembly)
}
pub fn get_passes(&self, assembly: &CilObject) -> Vec<Box<dyn SsaPass>> {
self.registry.get_passes_for_detected(assembly)
}
pub fn quick_detect(&self, assembly: &CilObject) -> Option<String> {
let threshold = self.registry.threshold();
for id in self.registry.obfuscator_ids() {
if let Some(obfuscator) = self.registry.get(id) {
let mut findings = DeobfuscationFindings::new();
let score = obfuscator.detect(assembly, &mut findings);
if score.score() >= threshold {
return Some(id.to_string());
}
}
}
None
}
pub fn all_scores(&self, assembly: &CilObject) -> Vec<(String, DetectionScore)> {
self.registry
.obfuscator_ids()
.iter()
.filter_map(|id| {
self.registry.get(id).map(|o| {
let mut findings = DeobfuscationFindings::new();
((*id).to_string(), o.detect(assembly, &mut findings))
})
})
.collect()
}
}
pub struct DetectorBuilder {
obfuscators: Vec<Arc<dyn Obfuscator>>,
threshold: usize,
}
impl Default for DetectorBuilder {
fn default() -> Self {
Self::new()
}
}
impl DetectorBuilder {
#[must_use]
pub fn new() -> Self {
Self {
obfuscators: Vec::new(),
threshold: 50,
}
}
#[must_use]
pub fn with_obfuscator(mut self, obfuscator: Arc<dyn Obfuscator>) -> Self {
self.obfuscators.push(obfuscator);
self
}
#[must_use]
pub fn threshold(mut self, threshold: usize) -> Self {
self.threshold = threshold;
self
}
#[must_use]
pub fn build(self) -> ObfuscatorDetector {
let mut registry = ObfuscatorRegistry::new();
registry.set_threshold(self.threshold);
for obfuscator in self.obfuscators {
registry.register(obfuscator);
}
ObfuscatorDetector::from_registry(registry)
}
}
#[cfg(test)]
mod tests {
use crate::deobfuscation::{detection::DetectionScore, detector::ObfuscatorDetector};
#[test]
fn test_detector_creation() {
let detector = ObfuscatorDetector::new();
assert!(!detector.registry().is_empty());
assert!(detector.registry().has("ConfuserEx"));
}
#[test]
fn test_detector_threshold() {
let mut detector = ObfuscatorDetector::new();
detector.set_threshold(75);
assert_eq!(detector.threshold(), 75);
}
#[test]
fn test_detection_score_basic() {
let score = DetectionScore::with_score(80);
assert!(score.meets_threshold(50));
assert!(!score.meets_threshold(90));
}
}