use std::any::Any;
use crate::{
assembly::{Immediate, Operand},
compiler::{EventKind, EventLog},
deobfuscation::techniques::{
Detection, Detections, Evidence, Technique, TechniqueCategory, WorkingAssembly,
},
CilObject, Result,
};
#[derive(Debug)]
pub struct JunkFindings {
pub affected_methods: usize,
}
pub struct BitMonoJunk;
impl Technique for BitMonoJunk {
fn id(&self) -> &'static str {
"bitmono.junk"
}
fn name(&self) -> &'static str {
"BitMono Junk Prefix Detection"
}
fn category(&self) -> TechniqueCategory {
TechniqueCategory::Metadata
}
fn detect(&self, assembly: &CilObject) -> Detection {
let mut junk_method_count = 0usize;
for method_entry in assembly.methods() {
let method = method_entry.value();
let instructions: Vec<_> = method.instructions().collect();
if instructions.len() < 2 {
continue;
}
if instructions[0].mnemonic == "br.s" {
let is_small_forward_jump = matches!(
instructions[0].operand,
Operand::Immediate(Immediate::Int8(1..=10))
);
if is_small_forward_jump {
junk_method_count += 1;
}
}
}
if junk_method_count == 0 {
return Detection::new_empty();
}
Detection::new_detected(
vec![Evidence::BytecodePattern(format!(
"{junk_method_count} methods with br.s junk prefix (BitMono BitMethodDotnet)"
))],
Some(Box::new(JunkFindings {
affected_methods: junk_method_count,
}) as Box<dyn Any + Send + Sync>),
)
}
fn byte_transform(
&self,
_assembly: &mut WorkingAssembly,
detection: &Detection,
_detections: &Detections,
) -> Option<Result<EventLog>> {
let events = EventLog::new();
let Some(findings) = detection.findings::<JunkFindings>() else {
return Some(Ok(events));
};
if findings.affected_methods > 0 {
events.record(EventKind::ArtifactRemoved)
.message(format!(
"BitMono junk prefix: {} methods detected, removal deferred to SSA BlockMergingPass",
findings.affected_methods,
));
}
Some(Ok(events))
}
}
#[cfg(test)]
mod tests {
use crate::deobfuscation::techniques::{bitmono::BitMonoJunk, Technique};
use crate::test::helpers::load_sample;
#[test]
fn test_detect_positive() {
let assembly = load_sample("tests/samples/packers/bitmono/0.39.0/bitmono_junk.exe");
let technique = BitMonoJunk;
let detection = technique.detect(&assembly);
assert!(
detection.is_detected(),
"BitMonoJunk should detect junk prefix in bitmono_junk.exe"
);
assert!(
!detection.evidence().is_empty(),
"Detection should include evidence"
);
}
#[test]
fn test_detect_negative() {
let assembly = load_sample("tests/samples/packers/confuserex/1.6.0/original.exe");
let technique = BitMonoJunk;
let detection = technique.detect(&assembly);
assert!(
!detection.is_detected(),
"BitMonoJunk should not detect junk prefix in a non-BitMono assembly"
);
}
}