use std::any::Any;
use crate::{
compiler::{EventKind, EventLog},
deobfuscation::{
techniques::{
Detection, Detections, Evidence, Technique, TechniqueCategory, WorkingAssembly,
},
utils::resolve_custom_attr_type,
},
metadata::{
tables::{CustomAttributeRaw, TableId},
token::Token,
},
CilObject, Result,
};
#[derive(Debug)]
pub struct IldasmFindings {
pub attribute_token: Token,
pub row_offset: usize,
pub row_size: usize,
}
pub struct GenericIldasm;
impl Technique for GenericIldasm {
fn id(&self) -> &'static str {
"generic.ildasm"
}
fn name(&self) -> &'static str {
"SuppressIldasm Attribute Removal"
}
fn category(&self) -> TechniqueCategory {
TechniqueCategory::Metadata
}
fn detect(&self, assembly: &CilObject) -> Detection {
let Some(tables) = assembly.tables() else {
return Detection::new_empty();
};
let Some(custom_attr_table) = tables.table::<CustomAttributeRaw>() else {
return Detection::new_empty();
};
for attr in custom_attr_table {
let is_module_or_assembly =
attr.parent.tag == TableId::Module || attr.parent.tag == TableId::Assembly;
if !is_module_or_assembly {
continue;
}
let Some(resolved) = resolve_custom_attr_type(assembly, &attr) else {
continue;
};
if let (name, Some(namespace)) = (resolved.name, resolved.namespace) {
if name == "SuppressIldasmAttribute"
&& namespace == "System.Runtime.CompilerServices"
{
let findings = IldasmFindings {
attribute_token: attr.token,
row_offset: attr.offset,
row_size: 0, };
let mut detection = Detection::new_detected(
vec![Evidence::Attribute(format!("{namespace}.{name}"))],
Some(Box::new(findings) as Box<dyn Any + Send + Sync>),
);
detection.cleanup_mut().add_attribute(attr.token);
return detection;
}
}
}
Detection::new_empty()
}
fn byte_transform(
&self,
_assembly: &mut WorkingAssembly,
detection: &Detection,
_detections: &Detections,
) -> Option<Result<EventLog>> {
let events = EventLog::new();
let Some(findings) = detection.findings::<IldasmFindings>() else {
return Some(Ok(events));
};
events.record(EventKind::ArtifactRemoved).message(format!(
"Marked SuppressIldasmAttribute (0x{:08X}) for removal",
findings.attribute_token.value()
));
Some(Ok(events))
}
fn requires_regeneration(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use crate::{deobfuscation::techniques::Technique, test::helpers::load_sample};
#[test]
fn test_detect_positive() {
let asm = load_sample("tests/samples/packers/confuserex/1.6.0/mkaring_normal.exe");
let technique = super::GenericIldasm;
let detection = technique.detect(&asm);
assert!(detection.is_detected());
assert!(!detection.evidence().is_empty());
}
#[test]
fn test_detect_negative() {
let asm = load_sample("tests/samples/packers/confuserex/1.6.0/original.exe");
let technique = super::GenericIldasm;
let detection = technique.detect(&asm);
assert!(!detection.is_detected());
}
}