use std::collections::HashMap;
use object::{
macho::{
self, FatHeader, MachHeader32, MachHeader64, SegmentCommand32, SegmentCommand64,
ThreadCommand,
},
read::macho::{FatArch, LoadCommandData, MachHeader, Section, Segment},
BigEndian, Bytes, Endianness, FileKind, U32, U64,
};
use super::{Module, ModuleData, ScanContext, StaticValue, Type, Value};
#[derive(Debug)]
pub struct MachO;
impl Module for MachO {
fn get_name(&self) -> &'static str {
"macho"
}
fn get_static_values(&self) -> HashMap<&'static str, StaticValue> {
[
("MH_MAGIC", StaticValue::Integer(macho::MH_MAGIC.into())),
("MH_CIGAM", StaticValue::Integer(macho::MH_CIGAM.into())),
(
"MH_MAGIC_64",
StaticValue::Integer(macho::MH_MAGIC_64.into()),
),
(
"MH_CIGAM_64",
StaticValue::Integer(macho::MH_CIGAM_64.into()),
),
("FAT_MAGIC", StaticValue::Integer(macho::FAT_MAGIC.into())),
("FAT_CIGAM", StaticValue::Integer(macho::FAT_CIGAM.into())),
(
"FAT_MAGIC_64",
StaticValue::Integer(macho::FAT_MAGIC_64.into()),
),
(
"FAT_CIGAM_64",
StaticValue::Integer(macho::FAT_CIGAM_64.into()),
),
(
"CPU_ARCH_ABI64",
StaticValue::Integer(macho::CPU_ARCH_ABI64.into()),
),
(
"CPU_SUBTYPE_LIB64",
StaticValue::Integer(macho::CPU_SUBTYPE_LIB64.into()),
),
(
"CPU_TYPE_MC680X0",
StaticValue::Integer(macho::CPU_TYPE_MC680X0.into()),
),
(
"CPU_TYPE_X86",
StaticValue::Integer(macho::CPU_TYPE_X86.into()),
),
(
"CPU_TYPE_I386",
StaticValue::Integer(macho::CPU_TYPE_X86.into()),
),
(
"CPU_TYPE_X86_64",
StaticValue::Integer(macho::CPU_TYPE_X86_64.into()),
),
(
"CPU_TYPE_MIPS",
StaticValue::Integer(macho::CPU_TYPE_MIPS.into()),
),
(
"CPU_TYPE_MC98000",
StaticValue::Integer(macho::CPU_TYPE_MC98000.into()),
),
(
"CPU_TYPE_ARM",
StaticValue::Integer(macho::CPU_TYPE_ARM.into()),
),
(
"CPU_TYPE_ARM64",
StaticValue::Integer(macho::CPU_TYPE_ARM64.into()),
),
(
"CPU_TYPE_MC88000",
StaticValue::Integer(macho::CPU_TYPE_MC88000.into()),
),
(
"CPU_TYPE_SPARC",
StaticValue::Integer(macho::CPU_TYPE_SPARC.into()),
),
(
"CPU_TYPE_POWERPC",
StaticValue::Integer(macho::CPU_TYPE_POWERPC.into()),
),
(
"CPU_TYPE_POWERPC64",
StaticValue::Integer(macho::CPU_TYPE_POWERPC64.into()),
),
(
"CPU_SUBTYPE_INTEL_MODEL_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_INTEL_MODEL_ALL.into()),
),
(
"CPU_SUBTYPE_386",
StaticValue::Integer(macho::CPU_SUBTYPE_386.into()),
),
(
"CPU_SUBTYPE_I386_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_I386_ALL.into()),
),
(
"CPU_SUBTYPE_X86_64_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_X86_64_ALL.into()),
),
(
"CPU_SUBTYPE_486",
StaticValue::Integer(macho::CPU_SUBTYPE_486.into()),
),
(
"CPU_SUBTYPE_486SX",
StaticValue::Integer(macho::CPU_SUBTYPE_486SX.into()),
),
(
"CPU_SUBTYPE_586",
StaticValue::Integer(macho::CPU_SUBTYPE_586.into()),
),
(
"CPU_SUBTYPE_PENT",
StaticValue::Integer(macho::CPU_SUBTYPE_PENT.into()),
),
(
"CPU_SUBTYPE_PENTPRO",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTPRO.into()),
),
(
"CPU_SUBTYPE_PENTII_M3",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTII_M3.into()),
),
(
"CPU_SUBTYPE_PENTII_M5",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTII_M5.into()),
),
(
"CPU_SUBTYPE_CELERON",
StaticValue::Integer(macho::CPU_SUBTYPE_CELERON.into()),
),
(
"CPU_SUBTYPE_CELERON_MOBILE",
StaticValue::Integer(macho::CPU_SUBTYPE_CELERON_MOBILE.into()),
),
(
"CPU_SUBTYPE_PENTIUM_3",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_3.into()),
),
(
"CPU_SUBTYPE_PENTIUM_3_M",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_3_M.into()),
),
(
"CPU_SUBTYPE_PENTIUM_3_XEON",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_3_XEON.into()),
),
(
"CPU_SUBTYPE_PENTIUM_M",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_M.into()),
),
(
"CPU_SUBTYPE_PENTIUM_4",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_4.into()),
),
(
"CPU_SUBTYPE_PENTIUM_4_M",
StaticValue::Integer(macho::CPU_SUBTYPE_PENTIUM_4_M.into()),
),
(
"CPU_SUBTYPE_ITANIUM",
StaticValue::Integer(macho::CPU_SUBTYPE_ITANIUM.into()),
),
(
"CPU_SUBTYPE_ITANIUM_2",
StaticValue::Integer(macho::CPU_SUBTYPE_ITANIUM_2.into()),
),
(
"CPU_SUBTYPE_XEON",
StaticValue::Integer(macho::CPU_SUBTYPE_XEON.into()),
),
(
"CPU_SUBTYPE_XEON_MP",
StaticValue::Integer(macho::CPU_SUBTYPE_XEON_MP.into()),
),
(
"CPU_SUBTYPE_ARM_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_ALL.into()),
),
(
"CPU_SUBTYPE_ARM_V4T",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V4T.into()),
),
(
"CPU_SUBTYPE_ARM_V6",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V6.into()),
),
(
"CPU_SUBTYPE_ARM_V5",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V5TEJ.into()),
),
(
"CPU_SUBTYPE_ARM_V5TEJ",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V5TEJ.into()),
),
(
"CPU_SUBTYPE_ARM_XSCALE",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_XSCALE.into()),
),
(
"CPU_SUBTYPE_ARM_V7",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7.into()),
),
(
"CPU_SUBTYPE_ARM_V7F",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7F.into()),
),
(
"CPU_SUBTYPE_ARM_V7S",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7S.into()),
),
(
"CPU_SUBTYPE_ARM_V7K",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7K.into()),
),
(
"CPU_SUBTYPE_ARM_V6M",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V6M.into()),
),
(
"CPU_SUBTYPE_ARM_V7M",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7M.into()),
),
(
"CPU_SUBTYPE_ARM_V7EM",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM_V7EM.into()),
),
(
"CPU_SUBTYPE_ARM64_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_ARM64_ALL.into()),
),
(
"CPU_SUBTYPE_SPARC_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_SPARC_ALL.into()),
),
(
"CPU_SUBTYPE_POWERPC_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_ALL.into()),
),
(
"CPU_SUBTYPE_MC980000_ALL",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_ALL.into()),
),
(
"CPU_SUBTYPE_POWERPC_601",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_601.into()),
),
(
"CPU_SUBTYPE_MC98601",
StaticValue::Integer(macho::CPU_SUBTYPE_MC98601.into()),
),
(
"CPU_SUBTYPE_POWERPC_602",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_602.into()),
),
(
"CPU_SUBTYPE_POWERPC_603",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_603.into()),
),
(
"CPU_SUBTYPE_POWERPC_603e",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_603E.into()),
),
(
"CPU_SUBTYPE_POWERPC_603ev",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_603EV.into()),
),
(
"CPU_SUBTYPE_POWERPC_604",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_604.into()),
),
(
"CPU_SUBTYPE_POWERPC_604e",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_604E.into()),
),
(
"CPU_SUBTYPE_POWERPC_620",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_620.into()),
),
(
"CPU_SUBTYPE_POWERPC_750",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_750.into()),
),
(
"CPU_SUBTYPE_POWERPC_7400",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_7400.into()),
),
(
"CPU_SUBTYPE_POWERPC_7450",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_7450.into()),
),
(
"CPU_SUBTYPE_POWERPC_970",
StaticValue::Integer(macho::CPU_SUBTYPE_POWERPC_970.into()),
),
("MH_OBJECT", StaticValue::Integer(macho::MH_OBJECT.into())),
("MH_EXECUTE", StaticValue::Integer(macho::MH_EXECUTE.into())),
("MH_FVMLIB", StaticValue::Integer(macho::MH_FVMLIB.into())),
("MH_CORE", StaticValue::Integer(macho::MH_CORE.into())),
("MH_PRELOAD", StaticValue::Integer(macho::MH_PRELOAD.into())),
("MH_DYLIB", StaticValue::Integer(macho::MH_DYLIB.into())),
(
"MH_DYLINKER",
StaticValue::Integer(macho::MH_DYLINKER.into()),
),
("MH_BUNDLE", StaticValue::Integer(macho::MH_BUNDLE.into())),
(
"MH_DYLIB_STUB",
StaticValue::Integer(macho::MH_DYLIB_STUB.into()),
),
("MH_DSYM", StaticValue::Integer(macho::MH_DSYM.into())),
(
"MH_KEXT_BUNDLE",
StaticValue::Integer(macho::MH_KEXT_BUNDLE.into()),
),
(
"MH_NOUNDEFS",
StaticValue::Integer(macho::MH_NOUNDEFS.into()),
),
(
"MH_INCRLINK",
StaticValue::Integer(macho::MH_INCRLINK.into()),
),
(
"MH_DYLDLINK",
StaticValue::Integer(macho::MH_DYLDLINK.into()),
),
(
"MH_BINDATLOAD",
StaticValue::Integer(macho::MH_BINDATLOAD.into()),
),
(
"MH_PREBOUND",
StaticValue::Integer(macho::MH_PREBOUND.into()),
),
(
"MH_SPLIT_SEGS",
StaticValue::Integer(macho::MH_SPLIT_SEGS.into()),
),
(
"MH_LAZY_INIT",
StaticValue::Integer(macho::MH_LAZY_INIT.into()),
),
(
"MH_TWOLEVEL",
StaticValue::Integer(macho::MH_TWOLEVEL.into()),
),
(
"MH_FORCE_FLAT",
StaticValue::Integer(macho::MH_FORCE_FLAT.into()),
),
(
"MH_NOMULTIDEFS",
StaticValue::Integer(macho::MH_NOMULTIDEFS.into()),
),
(
"MH_NOFIXPREBINDING",
StaticValue::Integer(macho::MH_NOFIXPREBINDING.into()),
),
(
"MH_PREBINDABLE",
StaticValue::Integer(macho::MH_PREBINDABLE.into()),
),
(
"MH_ALLMODSBOUND",
StaticValue::Integer(macho::MH_ALLMODSBOUND.into()),
),
(
"MH_SUBSECTIONS_VIA_SYMBOLS",
StaticValue::Integer(macho::MH_SUBSECTIONS_VIA_SYMBOLS.into()),
),
(
"MH_CANONICAL",
StaticValue::Integer(macho::MH_CANONICAL.into()),
),
(
"MH_WEAK_DEFINES",
StaticValue::Integer(macho::MH_WEAK_DEFINES.into()),
),
(
"MH_BINDS_TO_WEAK",
StaticValue::Integer(macho::MH_BINDS_TO_WEAK.into()),
),
(
"MH_ALLOW_STACK_EXECUTION",
StaticValue::Integer(macho::MH_ALLOW_STACK_EXECUTION.into()),
),
(
"MH_ROOT_SAFE",
StaticValue::Integer(macho::MH_ROOT_SAFE.into()),
),
(
"MH_SETUID_SAFE",
StaticValue::Integer(macho::MH_SETUID_SAFE.into()),
),
(
"MH_NO_REEXPORTED_DYLIBS",
StaticValue::Integer(macho::MH_NO_REEXPORTED_DYLIBS.into()),
),
("MH_PIE", StaticValue::Integer(macho::MH_PIE.into())),
(
"MH_DEAD_STRIPPABLE_DYLIB",
StaticValue::Integer(macho::MH_DEAD_STRIPPABLE_DYLIB.into()),
),
(
"MH_HAS_TLV_DESCRIPTORS",
StaticValue::Integer(macho::MH_HAS_TLV_DESCRIPTORS.into()),
),
(
"MH_NO_HEAP_EXECUTION",
StaticValue::Integer(macho::MH_NO_HEAP_EXECUTION.into()),
),
(
"MH_APP_EXTENSION_SAFE",
StaticValue::Integer(macho::MH_APP_EXTENSION_SAFE.into()),
),
("SG_HIGHVM", StaticValue::Integer(macho::SG_HIGHVM.into())),
("SG_FVMLIB", StaticValue::Integer(macho::SG_FVMLIB.into())),
("SG_NORELOC", StaticValue::Integer(macho::SG_NORELOC.into())),
(
"SG_PROTECTED_VERSION_1",
StaticValue::Integer(macho::SG_PROTECTED_VERSION_1.into()),
),
(
"SECTION_TYPE",
StaticValue::Integer(macho::SECTION_TYPE.into()),
),
(
"SECTION_ATTRIBUTES",
StaticValue::Integer(macho::SECTION_ATTRIBUTES.into()),
),
("S_REGULAR", StaticValue::Integer(macho::S_REGULAR.into())),
("S_ZEROFILL", StaticValue::Integer(macho::S_ZEROFILL.into())),
(
"S_CSTRING_LITERALS",
StaticValue::Integer(macho::S_CSTRING_LITERALS.into()),
),
(
"S_4BYTE_LITERALS",
StaticValue::Integer(macho::S_4BYTE_LITERALS.into()),
),
(
"S_8BYTE_LITERALS",
StaticValue::Integer(macho::S_8BYTE_LITERALS.into()),
),
(
"S_LITERAL_POINTERS",
StaticValue::Integer(macho::S_LITERAL_POINTERS.into()),
),
(
"S_NON_LAZY_SYMBOL_POINTERS",
StaticValue::Integer(macho::S_NON_LAZY_SYMBOL_POINTERS.into()),
),
(
"S_LAZY_SYMBOL_POINTERS",
StaticValue::Integer(macho::S_LAZY_SYMBOL_POINTERS.into()),
),
(
"S_SYMBOL_STUBS",
StaticValue::Integer(macho::S_SYMBOL_STUBS.into()),
),
(
"S_MOD_INIT_FUNC_POINTERS",
StaticValue::Integer(macho::S_MOD_INIT_FUNC_POINTERS.into()),
),
(
"S_MOD_TERM_FUNC_POINTERS",
StaticValue::Integer(macho::S_MOD_TERM_FUNC_POINTERS.into()),
),
(
"S_COALESCED",
StaticValue::Integer(macho::S_COALESCED.into()),
),
(
"S_GB_ZEROFILL",
StaticValue::Integer(macho::S_GB_ZEROFILL.into()),
),
(
"S_INTERPOSING",
StaticValue::Integer(macho::S_INTERPOSING.into()),
),
(
"S_16BYTE_LITERALS",
StaticValue::Integer(macho::S_16BYTE_LITERALS.into()),
),
(
"S_DTRACE_DOF",
StaticValue::Integer(macho::S_DTRACE_DOF.into()),
),
(
"S_LAZY_DYLIB_SYMBOL_POINTERS",
StaticValue::Integer(macho::S_LAZY_DYLIB_SYMBOL_POINTERS.into()),
),
(
"S_THREAD_LOCAL_REGULAR",
StaticValue::Integer(macho::S_THREAD_LOCAL_REGULAR.into()),
),
(
"S_THREAD_LOCAL_ZEROFILL",
StaticValue::Integer(macho::S_THREAD_LOCAL_ZEROFILL.into()),
),
(
"S_THREAD_LOCAL_VARIABLES",
StaticValue::Integer(macho::S_THREAD_LOCAL_VARIABLES.into()),
),
(
"S_THREAD_LOCAL_VARIABLE_POINTERS",
StaticValue::Integer(macho::S_THREAD_LOCAL_VARIABLE_POINTERS.into()),
),
(
"S_THREAD_LOCAL_INIT_FUNCTION_POINTERS",
StaticValue::Integer(macho::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS.into()),
),
(
"S_ATTR_PURE_INSTRUCTIONS",
StaticValue::Integer(macho::S_ATTR_PURE_INSTRUCTIONS.into()),
),
(
"S_ATTR_NO_TOC",
StaticValue::Integer(macho::S_ATTR_NO_TOC.into()),
),
(
"S_ATTR_STRIP_STATIC_SYMS",
StaticValue::Integer(macho::S_ATTR_STRIP_STATIC_SYMS.into()),
),
(
"S_ATTR_NO_DEAD_STRIP",
StaticValue::Integer(macho::S_ATTR_NO_DEAD_STRIP.into()),
),
(
"S_ATTR_LIVE_SUPPORT",
StaticValue::Integer(macho::S_ATTR_LIVE_SUPPORT.into()),
),
(
"S_ATTR_SELF_MODIFYING_CODE",
StaticValue::Integer(macho::S_ATTR_SELF_MODIFYING_CODE.into()),
),
(
"S_ATTR_DEBUG",
StaticValue::Integer(macho::S_ATTR_DEBUG.into()),
),
(
"S_ATTR_SOME_INSTRUCTIONS",
StaticValue::Integer(macho::S_ATTR_SOME_INSTRUCTIONS.into()),
),
(
"S_ATTR_EXT_RELOC",
StaticValue::Integer(macho::S_ATTR_EXT_RELOC.into()),
),
(
"S_ATTR_LOC_RELOC",
StaticValue::Integer(macho::S_ATTR_LOC_RELOC.into()),
),
(
"file_index_for_arch",
StaticValue::function(
Self::file_index_for_arch,
vec![vec![Type::Integer], vec![Type::Integer, Type::Integer]],
Type::Integer,
),
),
(
"entry_point_for_arch",
StaticValue::function(
Self::entry_point_for_arch,
vec![vec![Type::Integer], vec![Type::Integer, Type::Integer]],
Type::Integer,
),
),
]
.into()
}
fn get_dynamic_types(&self) -> HashMap<&'static str, Type> {
let file_types = [
("magic", Type::Integer),
("cputype", Type::Integer),
("cpusubtype", Type::Integer),
("filetype", Type::Integer),
("ncmds", Type::Integer),
("sizeofcmds", Type::Integer),
("flags", Type::Integer),
("reserved", Type::Integer),
("number_of_segments", Type::Integer),
(
"segments",
Type::array(Type::object([
("segname", Type::Bytes),
("vmaddr", Type::Integer),
("vmsize", Type::Integer),
("fileoff", Type::Integer),
("fsize", Type::Integer),
("maxprot", Type::Integer),
("initprot", Type::Integer),
("nsects", Type::Integer),
("flags", Type::Integer),
(
"sections",
Type::array(Type::object([
("sectname", Type::Bytes),
("segname", Type::Bytes),
("addr", Type::Integer),
("size", Type::Integer),
("offset", Type::Integer),
("align", Type::Integer),
("reloff", Type::Integer),
("nreloc", Type::Integer),
("flags", Type::Integer),
("reserved1", Type::Integer),
("reserved2", Type::Integer),
("reserved3", Type::Integer),
])),
),
])),
),
("entry_point", Type::Integer),
("stack_size", Type::Integer),
];
let mut out: HashMap<_, _> = [
("fat_magic", Type::Integer),
("nfat_arch", Type::Integer),
(
"fat_arch",
Type::array(Type::object([
("cputype", Type::Integer),
("cpusubtype", Type::Integer),
("offset", Type::Integer),
("size", Type::Integer),
("align", Type::Integer),
])),
),
]
.into();
let _r = out.insert("file", Type::array(Type::object(file_types.clone())));
out.extend(file_types);
out
}
fn get_dynamic_values(&self, ctx: &mut ScanContext) -> HashMap<&'static str, Value> {
let mut data = Data::default();
let res = parse_file(ctx.mem, &mut data, false).unwrap_or_default();
ctx.module_data.insert::<Self>(data);
res
}
}
#[derive(Debug, Default)]
pub struct Data {
files: Vec<FileData>,
arch_offsets: Vec<u64>,
}
#[derive(Debug, Default)]
struct FileData {
cputype: u32,
cpusubtype: u32,
entry_point: Option<u64>,
}
impl ModuleData for MachO {
type Data = Data;
}
impl MachO {
fn file_index_for_arch(ctx: &ScanContext, args: Vec<Value>) -> Option<Value> {
let mut args = args.into_iter();
let v1: i64 = args.next()?.try_into().ok()?;
let v2 = args.next().and_then(|v| i64::try_from(v).ok());
let data = ctx.module_data.get::<Self>()?;
for (i, file) in data.files.iter().enumerate() {
if i64::from(file.cputype) != v1 {
continue;
}
if let Some(v2) = v2 {
if i64::from(file.cpusubtype) != v2 {
continue;
}
}
return Some(Value::Integer(i as i64));
}
None
}
fn entry_point_for_arch(ctx: &ScanContext, args: Vec<Value>) -> Option<Value> {
let mut args = args.into_iter();
let v1: i64 = args.next()?.try_into().ok()?;
let v2 = args.next().and_then(|v| i64::try_from(v).ok());
let data = ctx.module_data.get::<Self>()?;
for (i, file) in data.files.iter().enumerate() {
if i64::from(file.cputype) != v1 {
continue;
}
if let Some(v2) = v2 {
if i64::from(file.cpusubtype) != v2 {
continue;
}
}
let offset = data.arch_offsets.get(i)?;
let entry_point = file.entry_point?;
return offset.saturating_add(entry_point).try_into().ok();
}
None
}
}
fn parse_file(
mem: &[u8],
data: &mut Data,
add_file_to_data: bool,
) -> Option<HashMap<&'static str, Value>> {
match FileKind::parse(mem).ok()? {
FileKind::MachO32 => {
let header = MachHeader32::parse(mem, 0).ok()?;
let e = header.endian().ok()?;
Some(parse_header(
header,
e,
mem,
None,
add_file_to_data.then_some(data),
))
}
FileKind::MachO64 => {
let header = MachHeader64::parse(mem, 0).ok()?;
let e = header.endian().ok()?;
Some(parse_header(
header,
e,
mem,
Some(header.reserved.get(e)),
add_file_to_data.then_some(data),
))
}
FileKind::MachOFat32 => parse_fat(mem, data, false),
FileKind::MachOFat64 => parse_fat(mem, data, true),
_ => None,
}
}
fn parse_header<Mach: MachHeader<Endian = Endianness>>(
header: &Mach,
e: Endianness,
mem: &[u8],
reserved: Option<u32>,
data: Option<&mut Data>,
) -> HashMap<&'static str, Value> {
let magic = header.magic().to_be().into();
let cputype = header.cputype(e);
let cpusubtype = header.cpusubtype(e);
let filetype = header.filetype(e).into();
let ncmds = header.ncmds(e).into();
let sizeofcmds = header.sizeofcmds(e).into();
let flags = header.flags(e).into();
let segments = segments(header, e, mem);
let nb_segments = segments.as_ref().and_then(|v| v.len().try_into().ok());
let (entry_point, stack_size) = entry_point_data(header, e, mem, cputype);
if let Some(data) = data {
data.files.push(FileData {
cputype,
cpusubtype,
entry_point,
});
}
[
("magic", Some(magic)),
("cputype", Some(cputype.into())),
("cpusubtype", Some(cpusubtype.into())),
("filetype", Some(filetype)),
("ncmds", Some(ncmds)),
("sizeofcmds", Some(sizeofcmds)),
("flags", Some(flags)),
("reserved", reserved.map(Into::into)),
("segments", segments.map(Value::Array)),
("number_of_segments", nb_segments),
("entry_point", entry_point.and_then(|v| v.try_into().ok())),
("stack_size", stack_size.and_then(|v| v.try_into().ok())),
]
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect()
}
fn segments<Mach: MachHeader<Endian = Endianness>>(
header: &Mach,
e: Endianness,
mem: &[u8],
) -> Option<Vec<Value>> {
let mut segments = Vec::new();
let mut cmds = header.load_commands(e, mem, 0).ok()?;
while let Ok(Some(cmd)) = cmds.next() {
if let Ok(Some((segment32, section_data))) = cmd.segment_32() {
let mut map = segment_to_map(segment32, e);
match sections32(segment32, e, section_data) {
Some(sections) if !sections.is_empty() => {
let _r = map.insert("sections", Value::Array(sections));
}
_ => (),
}
segments.push(Value::Object(map));
} else if let Ok(Some((segment64, section_data))) = cmd.segment_64() {
let mut map = segment_to_map(segment64, e);
match sections64(segment64, e, section_data) {
Some(sections) if !sections.is_empty() => {
let _r = map.insert("sections", Value::Array(sections));
}
_ => (),
}
segments.push(Value::Object(map));
}
}
Some(segments)
}
fn entry_point_data<Mach: MachHeader<Endian = Endianness>>(
header: &Mach,
e: Endianness,
mem: &[u8],
cputype: u32,
) -> (Option<u64>, Option<u64>) {
if let Ok(mut cmds) = header.load_commands(e, mem, 0) {
while let Ok(Some(cmd)) = cmds.next() {
if let Ok(Some(entry)) = cmd.entry_point() {
return (Some(entry.entryoff.get(e)), Some(entry.stacksize.get(e)));
} else if cmd.cmd() == macho::LC_UNIXTHREAD {
match handle_unix_thread(cmd, e, cputype) {
Some(ep) => {
return (va_to_file_offset(header, e, mem, ep), None);
}
None => return (None, None),
}
}
}
}
(None, None)
}
fn va_to_file_offset<Mach: MachHeader<Endian = Endianness>>(
header: &Mach,
e: Endianness,
mem: &[u8],
ep: u64,
) -> Option<u64> {
let mut cmds = header.load_commands(e, mem, 0).ok()?;
while let Ok(Some(cmd)) = cmds.next() {
if let Ok(Some((segment, _))) = Mach::Segment::from_command(cmd) {
let vmaddr: u64 = segment.vmaddr(e).into();
let vmsize: u64 = segment.vmsize(e).into();
if ep >= vmaddr && ep < vmaddr.saturating_add(vmsize) {
let fileoff: u64 = segment.fileoff(e).into();
return Some(fileoff.saturating_add(ep - vmaddr));
}
}
}
None
}
fn handle_unix_thread(
cmd: LoadCommandData<Endianness>,
e: Endianness,
cputype: u32,
) -> Option<u64> {
let thread_cmd: &ThreadCommand<Endianness> = cmd.data().ok()?;
let cmdsize = thread_cmd.cmdsize.get(e) as usize;
let get_at = |mut offset: usize, is64: bool| {
offset += 16;
if (is64 && offset + 8 > cmdsize) || (!is64 && offset + 4 > cmdsize) {
return None;
}
let mut data = Bytes(cmd.raw_data());
data.skip(offset).ok()?;
if is64 {
data.read::<U64<Endianness>>().ok().map(|v| v.get(e))
} else {
data.read::<U32<Endianness>>().ok().map(|v| v.get(e).into())
}
};
match cputype {
macho::CPU_TYPE_MC680X0 => {
get_at(16 * 4 + 2 * 2, false)
}
macho::CPU_TYPE_MC88000 => {
get_at(31 * 4, false)
}
macho::CPU_TYPE_SPARC => {
get_at(4, false)
}
macho::CPU_TYPE_POWERPC => {
get_at(0, false)
}
macho::CPU_TYPE_X86 => {
get_at(10 * 4, false)
}
macho::CPU_TYPE_ARM => {
get_at(15 * 4, false)
}
macho::CPU_TYPE_X86_64 => {
get_at(16 * 8, true)
}
macho::CPU_TYPE_ARM64 => {
get_at(32 * 8, true)
}
macho::CPU_TYPE_POWERPC64 => {
get_at(0, true)
}
_ => None,
}
}
fn segment_to_map<S: Segment<Endian = Endianness>>(
segment: &S,
e: Endianness,
) -> HashMap<&'static str, Value> {
let vmaddr: u64 = segment.vmaddr(e).into();
let vmsize: u64 = segment.vmsize(e).into();
let fileoff: u64 = segment.fileoff(e).into();
let fsize: u64 = segment.filesize(e).into();
let segname = segment.name().to_vec();
[
("segname", Some(segname.into())),
("vmaddr", vmaddr.try_into().ok()),
("vmsize", vmsize.try_into().ok()),
("fileoff", fileoff.try_into().ok()),
("fsize", fsize.try_into().ok()),
("maxprot", Some(segment.maxprot(e).into())),
("initprot", Some(segment.initprot(e).into())),
("nsects", Some(segment.nsects(e).into())),
("flags", Some(segment.flags(e).into())),
]
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect()
}
fn sections_to_map<S: Section<Endian = Endianness>>(
section: &S,
e: Endianness,
) -> HashMap<&'static str, Value> {
let addr: u64 = section.addr(e).into();
let size: u64 = section.size(e).into();
let segname = section.segment_name().to_vec();
let sectname = section.name().to_vec();
[
("segname", Some(segname.into())),
("sectname", Some(sectname.into())),
("addr", addr.try_into().ok()),
("size", size.try_into().ok()),
("offset", Some(section.offset(e).into())),
("align", Some(section.align(e).into())),
("reloff", Some(section.reloff(e).into())),
("nreloc", Some(section.nreloc(e).into())),
("flags", Some(section.flags(e).into())),
]
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect()
}
fn sections32(
segment: &SegmentCommand32<Endianness>,
e: Endianness,
section_data: &[u8],
) -> Option<Vec<Value>> {
Some(
segment
.sections(e, section_data)
.ok()?
.iter()
.map(|section| {
let mut map = sections_to_map(section, e);
let _r = map.insert("reserved1", section.reserved1.get(e).into());
let _r = map.insert("reserved2", section.reserved2.get(e).into());
Value::Object(map)
})
.collect(),
)
}
fn sections64(
segment: &SegmentCommand64<Endianness>,
e: Endianness,
section_data: &[u8],
) -> Option<Vec<Value>> {
Some(
segment
.sections(e, section_data)
.ok()?
.iter()
.map(|section| {
let mut map = sections_to_map(section, e);
let _r = map.insert("reserved1", section.reserved1.get(e).into());
let _r = map.insert("reserved2", section.reserved2.get(e).into());
let _r = map.insert("reserved3", section.reserved3.get(e).into());
Value::Object(map)
})
.collect(),
)
}
fn parse_fat(mem: &[u8], data: &mut Data, is64: bool) -> Option<HashMap<&'static str, Value>> {
let (magic, nfat_arch) = match FatHeader::parse(mem) {
Ok(header) => (
Some(header.magic.get(BigEndian).into()),
Some(header.nfat_arch.get(BigEndian).into()),
),
Err(_) => (None, None),
};
let mut archs = Vec::new();
let mut files = Vec::new();
if is64 {
for arch in FatHeader::parse_arch64(mem).ok()? {
archs.push(fat_arch_to_value(
arch,
data,
Some(arch.reserved.get(BigEndian)),
));
files.push(fat_arch_to_file_value(arch, data, mem));
}
} else {
for arch in FatHeader::parse_arch32(mem).ok()? {
archs.push(fat_arch_to_value(arch, data, None));
files.push(fat_arch_to_file_value(arch, data, mem));
}
}
Some(
[
("fat_magic", magic),
("nfat_arch", nfat_arch),
("fat_arch", Some(Value::Array(archs))),
("file", Some(Value::Array(files))),
]
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect(),
)
}
fn fat_arch_to_file_value<A: FatArch>(arch: &A, data: &mut Data, mem: &[u8]) -> Value {
Value::Object(
arch.data(mem)
.ok()
.and_then(|new_mem| parse_file(new_mem, data, true))
.unwrap_or_default(),
)
}
fn fat_arch_to_value<A: FatArch>(arch: &A, data: &mut Data, reserved: Option<u32>) -> Value {
let cputype = arch.cputype().into();
let cpusubtype = arch.cpusubtype().into();
let offset = arch.offset().into();
let size = arch.size().into();
let align = arch.align().into();
data.arch_offsets.push(offset);
Value::Object(
[
("cputype", Some(cputype)),
("cpusubtype", Some(cpusubtype)),
("offset", offset.try_into().ok()),
("size", size.try_into().ok()),
("align", Some(align)),
("reserved", reserved.map(Into::into)),
]
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect(),
)
}