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
)
}
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();
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()),
);
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
}
fn instrumentation(&self) -> GenericMap {
GenericMap::new()
}
}