use super::entropy::shannon_bits;
use super::{validate_input, DetectionError};
pub const MAGIC_PE: u32 = 1;
pub const MAGIC_ELF: u32 = 2;
pub const MAGIC_MACHO32: u32 = 3;
pub const MAGIC_MACHO64: u32 = 4;
pub const MAGIC_CLASS: u32 = 5;
pub const MAGIC_ZIP: u32 = 6;
pub const MAGIC_GZIP: u32 = 7;
pub const MAGIC_PNG: u32 = 8;
pub const MAGIC_PDF: u32 = 9;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MagicFinding {
pub magic_id: u32,
pub confidence: u32,
}
pub fn push_if(
input: &[u8],
signature: &[u8],
magic_id: u32,
confidence: u32,
out: &mut Vec<MagicFinding>,
) {
if input.starts_with(signature) {
out.push(MagicFinding {
magic_id,
confidence,
});
}
}
pub fn file_magic_detect(input: &[u8]) -> Result<Vec<MagicFinding>, DetectionError> {
validate_input(input)?;
let mut out = Vec::new();
push_if(input, b"MZ", MAGIC_PE, 80, &mut out);
push_if(input, b"\x7FELF", MAGIC_ELF, 100, &mut out);
push_if(input, b"\xFE\xED\xFA\xCE", MAGIC_MACHO32, 100, &mut out);
push_if(input, b"\xFE\xED\xFA\xCF", MAGIC_MACHO64, 100, &mut out);
push_if(input, b"\xCA\xFE\xBA\xBE", MAGIC_CLASS, 100, &mut out);
push_if(input, b"PK\x03\x04", MAGIC_ZIP, 95, &mut out);
push_if(input, b"\x1F\x8B", MAGIC_GZIP, 95, &mut out);
push_if(input, b"\x89PNG\r\n\x1A\n", MAGIC_PNG, 100, &mut out);
push_if(input, b"%PDF-", MAGIC_PDF, 100, &mut out);
Ok(out)
}
pub fn detect_packed_binary(input: &[u8]) -> Result<bool, DetectionError> {
validate_input(input)?;
let magic = file_magic_detect(input)?;
let executable = magic.iter().any(|finding| {
matches!(
finding.magic_id,
MAGIC_PE | MAGIC_ELF | MAGIC_MACHO32 | MAGIC_MACHO64 | MAGIC_CLASS
)
});
if !executable {
return Ok(false);
}
let sample_len = input.len().min(8192);
if sample_len < 256 {
return Ok(false);
}
Ok(shannon_bits(&input[..sample_len]) >= 7.15)
}