use goblin::mach::{header, MachO};
use serde_json::json;
use crate::check::{Analyze, GenericMap};
use crate::BinResult;
use super::UniversalCompilationProperties;
const MH_PIE: u32 = 0x200000;
const MH_ALLOW_STACK_EXECUTION: u32 = 0x20000;
const MH_NO_HEAP_EXECUTION: u32 = 0x1000000;
impl UniversalCompilationProperties for MachO<'_> {
fn binary_type(&self) -> &str {
header::filetype_to_str(self.header.filetype)
}
fn is_stripped(&self) -> bool {
self.symbols.is_none()
}
fn compiler_runtime(&self, _bytes: &[u8]) -> Option<String> {
None
}
}
trait MachOMitigations {
fn executable_stack(&self) -> bool;
fn executable_heap(&self) -> bool;
fn position_independent(&self) -> bool;
fn stack_canary(&self) -> bool;
fn restricted_segment(&self) -> bool;
fn pagezero_segment(&self) -> bool;
}
impl MachOMitigations for MachO<'_> {
fn executable_stack(&self) -> bool {
matches!(self.header.flags & MH_ALLOW_STACK_EXECUTION, 0)
}
fn executable_heap(&self) -> bool {
matches!(self.header.flags & MH_NO_HEAP_EXECUTION, 0)
}
fn position_independent(&self) -> bool {
matches!(self.header.flags & MH_PIE, 0)
}
fn stack_canary(&self) -> bool {
match self.imports() {
Ok(imports) => imports
.iter()
.any(|x| x.name == "__stack_chk_fail" || x.name == "__stack_chk_guard"),
Err(_) => false,
}
}
fn restricted_segment(&self) -> bool {
self.segments
.iter()
.filter_map(|s| s.name().ok())
.any(|s| s.to_lowercase() == "__restrict")
}
fn pagezero_segment(&self) -> bool {
self.segments
.iter()
.filter_map(|s| s.name().ok())
.any(|s| s.to_lowercase() == "__PAGEZERO")
}
}
impl Analyze for MachO<'_> {
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 mitigate_map: GenericMap = GenericMap::new();
mitigate_map.insert(
"Non-executable Stack".to_string(),
json!(self.executable_stack()),
);
mitigate_map.insert(
"Non-executable Heap".to_string(),
json!(self.executable_heap()),
);
mitigate_map.insert(
"Position Independent Executable / ASLR".to_string(),
json!(self.position_independent()),
);
mitigate_map.insert("Stack Canary".to_string(), json!(self.stack_canary()));
mitigate_map.insert(
"__RESTRICT segment".to_string(),
json!(self.restricted_segment()),
);
mitigate_map.insert(
"__PAGEZERO segment".to_string(),
json!(self.pagezero_segment()),
);
mitigate_map
}
fn instrumentation(&self) -> GenericMap {
let mut instr_map: GenericMap = GenericMap::new();
let asan: bool = match self.imports() {
Ok(imports) => imports.iter().any(|x| x.name.starts_with("__asan")),
Err(_) => false,
};
if asan {
instr_map.insert("Address Sanitizer (ASAN)".to_string(), json!(true));
}
instr_map
}
}