vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! File magic constants and packed-binary detection helpers.

use super::entropy::shannon_bits;
use super::{validate_input, DetectionError};

/// PE executable magic identifier.
pub const MAGIC_PE: u32 = 1;
/// ELF executable magic identifier.
pub const MAGIC_ELF: u32 = 2;
/// Mach-O 32-bit magic identifier.
pub const MAGIC_MACHO32: u32 = 3;
/// Mach-O 64-bit magic identifier.
pub const MAGIC_MACHO64: u32 = 4;
/// Java class file magic identifier.
pub const MAGIC_CLASS: u32 = 5;
/// ZIP archive magic identifier.
pub const MAGIC_ZIP: u32 = 6;
/// GZIP archive magic identifier.
pub const MAGIC_GZIP: u32 = 7;
/// PNG image magic identifier.
pub const MAGIC_PNG: u32 = 8;
/// PDF document magic identifier.
pub const MAGIC_PDF: u32 = 9;

/// File magic identifier and confidence.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MagicFinding {
    /// Stable magic id from the security detection catalog.
    pub magic_id: u32,
    /// Confidence in the range 0..=100.
    pub confidence: u32,
}

/// Push a finding when the input starts with a signature.
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,
        });
    }
}

/// Return known file magic ids and confidence scores.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
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)
}

/// Return true when executable magic combines with high entropy.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
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)
}