ndaal-binsec 3.2.1

Binary (in)security scanner for ELF/PE/Mach-O with native, strictly-validated SARIF 2.1.0 and Markdown output (ndaal fork of binsec)
Documentation
//! ### PE-Specific Compilation Checks:
//!
//! * TODO
//!
//! ### PE-Specific Exploit Mitigations:
//!
//! * Data Execution Prevention (DEP / NX)
//! * Dynamic Base
//! * Structured Exception Handling (SEH)
//! * Code Integrity
//! * Control Flow Guard

use goblin::pe::characteristic::{IMAGE_FILE_DEBUG_STRIPPED, IMAGE_FILE_RELOCS_STRIPPED};
use goblin::pe::PE;
use serde_json::json;

use crate::check::{Analyze, GenericMap};
use crate::BinResult;

use super::UniversalCompilationProperties;

impl UniversalCompilationProperties for PE<'_> {
    fn binary_type(&self) -> &str {
        match self.is_lib {
            true => "DLL",
            false => "EXE",
        }
    }

    fn is_stripped(&self) -> bool {
        matches!(
            self.header.coff_header.characteristics & IMAGE_FILE_DEBUG_STRIPPED,
            0
        )
    }

    // TODO: PE compiler-runtime detection is not implemented yet.
    fn compiler_runtime(&self, _bytes: &[u8]) -> Option<String> {
        None
    }
}

trait PeMitigations {
    fn dll_characteristics(&self) -> u16;
    fn data_execution_prevention(&self) -> bool;
    fn dynamic_base(&self) -> bool;
    fn structured_exception_handling(&self) -> bool;
    fn isolation_aware_execution(&self) -> bool;
    fn aslr(&self) -> bool;
    fn high_entropy(&self) -> bool;
    fn control_flow_guard(&self) -> bool;
    fn code_integrity(&self) -> bool;
}

impl PeMitigations for PE<'_> {
    fn dll_characteristics(&self) -> u16 {
        if let Some(optional_header) = self.header.optional_header {
            return optional_header.windows_fields.dll_characteristics;
        }
        0
    }

    fn data_execution_prevention(&self) -> bool {
        matches!(self.dll_characteristics() & 0x0100, 0)
    }

    fn dynamic_base(&self) -> bool {
        matches!(self.dll_characteristics() & 0x0040, 0)
    }

    fn structured_exception_handling(&self) -> bool {
        matches!(self.dll_characteristics() & 0x0400, 0)
    }

    fn isolation_aware_execution(&self) -> bool {
        matches!(self.dll_characteristics() & 0x0200, 0)
    }

    fn aslr(&self) -> bool {
        self.dynamic_base()
            && matches!(
                self.header.coff_header.characteristics & IMAGE_FILE_RELOCS_STRIPPED,
                0
            )
    }

    fn high_entropy(&self) -> bool {
        self.aslr() && matches!(self.dll_characteristics() & 0x0020, 0)
    }

    fn control_flow_guard(&self) -> bool {
        self.aslr() && matches!(self.dll_characteristics() & 0x4000, 0)
    }

    fn code_integrity(&self) -> bool {
        self.aslr() && matches!(self.dll_characteristics() & 0x0080, 0)
    }
}

impl Analyze for PE<'_> {
    fn compilation(&self, _bytes: &[u8]) -> BinResult<GenericMap> {
        let mut comp_map = GenericMap::new();
        comp_map.insert("Binary Type".to_string(), json!(self.binary_type()));
        comp_map.insert("Debug Stripped".to_string(), json!(self.is_stripped()));
        Ok(comp_map)
    }

    fn mitigations(&self) -> GenericMap {
        let mut mitigation_checks: GenericMap = GenericMap::new();

        // context independent mitigations
        mitigation_checks.insert(
            "Data Execution Protection (DEP)".to_string(),
            json!(self.data_execution_prevention()),
        );
        mitigation_checks.insert("Dynamic Base".to_string(), json!(self.dynamic_base()));
        mitigation_checks.insert(
            "Structured Exception Handling (SEH)".to_string(),
            json!(!self.structured_exception_handling()),
        );
        mitigation_checks.insert(
            "Isolation-Aware Execution".to_string(),
            json!(!self.isolation_aware_execution()),
        );

        // context dependent mitigations: some don't work without existence of other checks
        mitigation_checks.insert(
            "Address Space Layout Randomization (ASLR)".to_string(),
            json!(self.aslr()),
        );
        mitigation_checks.insert("High Entropy".to_string(), json!(self.high_entropy()));
        mitigation_checks.insert(
            "Control Flow Guard (CFG)".to_string(),
            json!(self.control_flow_guard()),
        );
        mitigation_checks.insert("Code Integrity".to_string(), json!(self.code_integrity()));
        mitigation_checks
    }

    // TODO
    fn instrumentation(&self) -> GenericMap {
        GenericMap::new()
    }
}