use crate::{
Result,
security::{
options::status::{HasSecurityStatus, YesNoUnknownStatus},
parser::BinaryParser,
},
};
use goblin::mach::{Mach, MachO};
const MH_NOUNDEFS: u32 = 0x0000_0001;
const MH_TWOLEVEL: u32 = 0x0000_0080;
const MH_PIE: u32 = 0x0020_0000;
const MH_NO_HEAP_EXECUTION: u32 = 0x0100_0000;
const VM_PROT_EXECUTE: u32 = 0x04;
pub(crate) fn analyze_binary(
parser: &BinaryParser,
_options: &crate::BinarySecurityCheckOptions,
) -> Result<Vec<Box<dyn HasSecurityStatus>>> {
let mach_obj = match parser.object() {
goblin::Object::Mach(m) => m,
_ => {
return Ok(vec![
Box::new(YesNoUnknownStatus::unknown("MACHO-PARSE")) as Box<dyn HasSecurityStatus>
]);
}
};
match mach_obj {
Mach::Binary(m) => run_checks(m),
Mach::Fat(fat) => {
for (i, _arch) in fat.iter_arches().enumerate() {
if let Ok(goblin::mach::SingleArch::MachO(m)) = fat.get(i) {
return run_checks(&m);
}
}
Ok(vec![
Box::new(YesNoUnknownStatus::unknown("MACHO-PARSE")) as Box<dyn HasSecurityStatus>
])
}
}
}
fn run_checks(slice: &MachO) -> Result<Vec<Box<dyn HasSecurityStatus>>> {
let flags = slice.header.flags;
let pie = YesNoUnknownStatus::new("PIE", flags & MH_PIE != 0);
let nx_flag = flags & MH_NO_HEAP_EXECUTION != 0;
let any_data_exec = slice.segments.iter().any(|seg| {
let name = std::str::from_utf8(&seg.segname)
.unwrap_or("")
.trim_end_matches('\0');
name.starts_with("__DATA") && (seg.initprot & VM_PROT_EXECUTE) != 0
});
let dep = YesNoUnknownStatus::new("DATA-EXEC-PREVENT", nx_flag || !any_data_exec);
let canary = {
let mut found = false;
for sym in slice.symbols().flatten() {
let (name, _nlist) = sym;
if name == "___stack_chk_guard" || name == "___stack_chk_fail" {
found = true;
break;
}
}
YesNoUnknownStatus::new("STACK-CANARY", found)
};
let has_restrict = slice.segments.iter().any(|seg| {
let name = std::str::from_utf8(&seg.segname)
.unwrap_or("")
.trim_end_matches('\0');
name == "__RESTRICT"
});
let restrict = YesNoUnknownStatus::new("RESTRICT", has_restrict);
let signed = slice.load_commands.iter().any(|lc| {
matches!(
&lc.command,
goblin::mach::load_command::CommandVariant::CodeSignature(cs)
if cs.datasize > 0
)
});
let code_sig = YesNoUnknownStatus::new("CODE-SIGNATURE", signed);
let two_level = YesNoUnknownStatus::new("TWO-LEVEL-NAMESPACE", flags & MH_TWOLEVEL != 0);
let no_undef = YesNoUnknownStatus::new("NO-UNDEF-SYMS", flags & MH_NOUNDEFS != 0);
let hardened = YesNoUnknownStatus::unknown("HARDENED-RUNTIME");
let allow_jit = YesNoUnknownStatus::unknown("ALLOW-JIT");
Ok(vec![
Box::new(pie) as Box<dyn HasSecurityStatus>,
Box::new(dep) as Box<dyn HasSecurityStatus>,
Box::new(canary) as Box<dyn HasSecurityStatus>,
Box::new(restrict) as Box<dyn HasSecurityStatus>,
Box::new(code_sig) as Box<dyn HasSecurityStatus>,
Box::new(two_level) as Box<dyn HasSecurityStatus>,
Box::new(no_undef) as Box<dyn HasSecurityStatus>,
Box::new(hardened) as Box<dyn HasSecurityStatus>,
Box::new(allow_jit) as Box<dyn HasSecurityStatus>,
])
}