use std::collections::HashSet;
use bstr::{BStr, ByteSlice};
use der_parser::asn1_rs::{FromBer, OptTaggedParser, ParseResult};
use der_parser::ber::{
parse_ber_integer, parse_ber_oid, parse_ber_sequence,
parse_ber_sequence_defined_g, parse_ber_set_of_v,
parse_ber_tagged_explicit_g,
};
use der_parser::error::BerResult;
use der_parser::error::Error::BerValueError;
use itertools::Itertools;
#[cfg(feature = "logging")]
use log::error;
use nom::bytes::complete::{tag, take, take_till};
use nom::combinator::{cond, map, verify};
use nom::error::ErrorKind;
use nom::multi::{count, length_count};
use nom::number::Endianness;
use nom::number::complete::{be_u32, le_u32, u8, u16, u32, u64};
use nom::{Err, IResult, Parser};
use protobuf::MessageField;
use x509_parser::x509::AlgorithmIdentifier;
use crate::modules::protos;
use crate::modules::utils::asn1::SignedData;
use crate::modules::utils::leb128::{sleb128, uleb128};
type NomError<'a> = nom::error::Error<&'a [u8]>;
const MH_MAGIC: u32 = 0xfeedface;
const MH_CIGAM: u32 = 0xcefaedfe;
const MH_MAGIC_64: u32 = 0xfeedfacf;
const MH_CIGAM_64: u32 = 0xcffaedfe;
const FAT_MAGIC: u32 = 0xcafebabe;
const FAT_CIGAM: u32 = 0xbebafeca;
const FAT_MAGIC_64: u32 = 0xcafebabf;
const FAT_CIGAM_64: u32 = 0xbfbafeca;
const _CS_MAGIC_REQUIREMENT: u32 = 0xfade0c00;
const _CS_MAGIC_REQUIREMENTS: u32 = 0xfade0c01;
const _CS_MAGIC_CODEDIRECTORY: u32 = 0xfade0c02;
const _CS_MAGIC_EMBEDDED_SIGNATURE: u32 = 0xfade0cc0;
const _CS_MAGIC_DETACHED_SIGNATURE: u32 = 0xfade0cc1;
const CS_MAGIC_BLOBWRAPPER: u32 = 0xfade0b01;
const CS_MAGIC_EMBEDDED_ENTITLEMENTS: u32 = 0xfade7171;
pub const N_STAB: u8 = 0xe0;
const _N_PEXT: u8 = 0x10;
pub const N_TYPE: u8 = 0x0e;
pub const N_EXT: u8 = 0x01;
const N_UNDF: u8 = 0x0;
const N_ABS: u8 = 0x2;
const N_SECT: u8 = 0xe;
const _N_PBUD: u8 = 0xc;
const N_INDR: u8 = 0xa;
const _EXPORT_SYMBOL_FLAGS_KIND_REGULAR: u64 = 0x00000000;
const _EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION: u64 = 0x00000004;
const EXPORT_SYMBOL_FLAGS_REEXPORT: u64 = 0x00000008;
const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: u64 = 0x00000010;
const BIND_OPCODE_MASK: u8 = 0xF0;
const BIND_IMMEDIATE_MASK: u8 = 0x0F;
const _BIND_OPCODE_DONE: u8 = 0x00;
const _BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10;
const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20;
const _BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30;
const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40;
const _BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50;
const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60;
const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70;
const BIND_OPCODE_ADD_ADDR_ULEB: u8 = 0x80;
const _BIND_OPCODE_DO_BIND: u8 = 0x90;
const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xA0;
const _BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xB0;
const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = 0xC0;
const LC_REQ_DYLD: u32 = 0x80000000;
const LC_SEGMENT: u32 = 0x00000001;
const LC_SYMTAB: u32 = 0x00000002;
const LC_UNIXTHREAD: u32 = 0x00000005;
const LC_DYSYMTAB: u32 = 0x0000000b;
const LC_LOAD_DYLIB: u32 = 0x0000000c;
const LC_ID_DYLIB: u32 = 0x0000000d;
const LC_LOAD_DYLINKER: u32 = 0x0000000e;
const LC_ID_DYLINKER: u32 = 0x0000000f;
const LC_LOAD_WEAK_DYLIB: u32 = 0x18 | LC_REQ_DYLD;
const LC_SEGMENT_64: u32 = 0x00000019;
const LC_UUID: u32 = 0x00000001b;
const LC_RPATH: u32 = 0x1c | LC_REQ_DYLD;
const LC_CODE_SIGNATURE: u32 = 0x0000001d;
const LC_REEXPORT_DYLIB: u32 = 0x1f | LC_REQ_DYLD;
const LC_LAZY_LOAD_DYLIB: u32 = 0x00000020;
const LC_DYLD_INFO: u32 = 0x00000022;
const LC_DYLD_INFO_ONLY: u32 = 0x22 | LC_REQ_DYLD;
const LC_LOAD_UPWARD_DYLIB: u32 = 0x23 | LC_REQ_DYLD;
const LC_VERSION_MIN_MACOSX: u32 = 0x00000024;
const LC_VERSION_MIN_IPHONEOS: u32 = 0x00000025;
const LC_DYLD_ENVIRONMENT: u32 = 0x00000027;
const LC_MAIN: u32 = 0x28 | LC_REQ_DYLD;
const LC_SOURCE_VERSION: u32 = 0x0000002a;
const LC_LINKER_OPTION: u32 = 0x0000002d;
const LC_VERSION_MIN_TVOS: u32 = 0x0000002f;
const LC_VERSION_MIN_WATCHOS: u32 = 0x00000030;
const LC_BUILD_VERSION: u32 = 0x00000032;
const LC_DYLD_EXPORTS_TRIE: u32 = 0x00000033 | LC_REQ_DYLD;
const LC_DYLD_CHAINED_FIXUPS: u32 = 0x00000034 | LC_REQ_DYLD;
const CPU_TYPE_MC680X0: u32 = 0x00000006;
const CPU_TYPE_X86: u32 = 0x00000007;
const CPU_TYPE_X86_64: u32 = 0x01000007;
const CPU_TYPE_ARM: u32 = 0x0000000c;
const CPU_TYPE_ARM64: u32 = 0x0100000c;
const CPU_TYPE_MC88000: u32 = 0x0000000d;
const CPU_TYPE_SPARC: u32 = 0x0000000e;
const CPU_TYPE_POWERPC: u32 = 0x00000012;
const CPU_TYPE_POWERPC64: u32 = 0x01000012;
const DYLD_CHAINED_IMPORT: u32 = 1;
const DYLD_CHAINED_IMPORT_ADDEND: u32 = 2;
const DYLD_CHAINED_IMPORT_ADDEND64: u32 = 3;
pub struct MachO<'a> {
fat_magic: Option<u32>,
archs: Vec<FatArch>,
files: Vec<MachOFile<'a>>,
}
impl<'a> MachO<'a> {
pub fn parse(data: &'a [u8]) -> Result<Self, Err<NomError<'a>>> {
let (_, magic) = le_u32(data)?;
if matches!(magic, FAT_MAGIC | FAT_CIGAM | FAT_MAGIC_64 | FAT_CIGAM_64)
{
Self::parse_fat_macho_file(data)
} else {
Ok(Self {
fat_magic: None,
archs: Vec::new(),
files: vec![Self::parse_macho_file(data)?],
})
}
}
}
impl<'a> MachO<'a> {
fn parse_fat_macho_file(
data: &'a [u8],
) -> Result<Self, Err<NomError<'a>>> {
let (remainder, magic) = verify(be_u32, |magic| {
matches!(
*magic,
FAT_MAGIC | FAT_CIGAM | FAT_MAGIC_64 | FAT_CIGAM_64
)
})
.parse(data)?;
let endianness = match magic {
FAT_MAGIC | FAT_MAGIC_64 => Endianness::Big,
FAT_CIGAM | FAT_CIGAM_64 => Endianness::Little,
_ => unreachable!(),
};
let is_32_bits = match magic {
FAT_MAGIC | FAT_CIGAM => true,
FAT_MAGIC_64 | FAT_CIGAM_64 => false,
_ => unreachable!(),
};
let (_, archs) = length_count(
u32(endianness),
map(
(
u32(endianness), u32(endianness), uint(endianness, is_32_bits), uint(endianness, is_32_bits), u32(endianness), cond(!is_32_bits, u32(endianness)), ),
|(cputype, cpusubtype, offset, size, align, reserved)| {
FatArch {
cputype,
cpusubtype,
offset,
size,
align,
reserved: reserved.unwrap_or_default(),
}
},
),
)
.parse(remainder)?;
let mut files = Vec::new();
for arch in &archs {
let start = arch.offset as usize;
let end = start.saturating_add(arch.size as usize);
if let Some(macho) = data.get(start..end) {
match Self::parse_macho_file(macho) {
Ok(macho) => files.push(macho),
#[cfg(feature = "logging")]
Err(err) => {
error!("Error parsing Mach-O file: {:?}", err);
}
#[cfg(not(feature = "logging"))]
Err(_) => {}
}
};
}
Ok(MachO { fat_magic: Some(magic), archs, files })
}
fn parse_macho_file(
data: &'a [u8],
) -> Result<MachOFile<'a>, Err<NomError<'a>>> {
let (remainder, magic) = verify(be_u32, |magic| {
matches!(*magic, MH_MAGIC | MH_CIGAM | MH_MAGIC_64 | MH_CIGAM_64)
})
.parse(data)?;
let endianness = match magic {
MH_MAGIC | MH_MAGIC_64 => Endianness::Big,
MH_CIGAM | MH_CIGAM_64 => Endianness::Little,
_ => unreachable!(),
};
let is_32_bits = match magic {
MH_MAGIC | MH_CIGAM => true,
MH_MAGIC_64 | MH_CIGAM_64 => false,
_ => unreachable!(),
};
let (mut commands, header) = map(
(
u32(endianness), u32(endianness), u32(endianness), u32(endianness), u32(endianness), u32(endianness), cond(!is_32_bits, u32(endianness)), ),
|(
cputype,
cpusubtype,
filetype,
ncmds,
sizeofcmds,
flags,
reserved,
)| {
MachOHeader {
magic,
cputype,
cpusubtype,
filetype,
ncmds,
sizeofcmds,
flags,
reserved,
}
},
)
.parse(remainder)?;
let mut macho = MachOFile {
endianness,
is_32_bits,
header,
segments: Vec::new(),
dylibs: Vec::new(),
rpaths: Vec::new(),
symtab: None,
dysymtab: None,
dynamic_linker: None,
linker_options: Vec::new(),
dyld_info: None,
dyld_export_trie: None,
dyld_chain_fixups: None,
source_version: None,
entry_point_offset: None,
entry_point_rva: None,
stack_size: None,
code_signature_data: None,
entitlements: Vec::new(),
certificates: Vec::new(),
uuid: None,
build_version: None,
min_version: None,
exports: Vec::new(),
imports: Vec::new(),
};
for _ in 0..macho.header.ncmds as usize {
match macho.command().parse(commands) {
Ok((c, _)) => commands = c,
Err(err) => {
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", err);
if let Err::Error(e) = err
&& e.code == ErrorKind::Eof
{
break;
}
}
}
}
if let Some(ref symtab) = macho.symtab {
let str_offset = symtab.stroff as usize;
let str_end = symtab.strsize as usize;
let sym_offset = symtab.symoff as usize;
let nsyms = symtab.nsyms;
if let Some(string_table) =
data.get(str_offset..str_offset.saturating_add(str_end))
&& let Some(symbol_table) = data.get(sym_offset..)
&& let Err(_err) =
macho.parse_symtab(string_table, symbol_table, nsyms)
{
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", _err);
};
}
if let Some(entry_point_rva) = macho.entry_point_rva {
macho.entry_point_offset = macho.rva_to_offset(entry_point_rva);
}
if let Some(ref code_signature_data) = macho.code_signature_data {
let offset = code_signature_data.dataoff as usize;
let size = code_signature_data.datasize as usize;
if let Some(super_data) =
data.get(offset..offset.saturating_add(size))
&& let Err(_err) = macho.cs_superblob().parse(super_data)
{
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", _err);
};
}
for (offset, size) in [
macho
.dyld_export_trie
.as_ref()
.map(|t| (t.data_off as usize, t.data_size as usize)),
macho
.dyld_info
.as_ref()
.map(|i| (i.export_off as usize, i.export_size as usize)),
]
.into_iter()
.flatten()
{
if let Some(export_data) =
data.get(offset..offset.saturating_add(size))
&& let Err(_err) = macho.parse_exports(export_data)
{
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", _err);
};
}
let mut seen: HashSet<_> = HashSet::new();
for (offset, size) in macho
.dyld_info
.as_ref()
.map(|i| {
[
(i.bind_off as usize, i.bind_size as usize),
(i.lazy_bind_off as usize, i.lazy_bind_size as usize),
(i.weak_bind_off as usize, i.weak_bind_size as usize),
]
})
.into_iter()
.flatten()
{
if let Some(import_data) =
data.get(offset..offset.saturating_add(size))
&& let Err(_err) = macho.parse_imports(import_data, &mut seen)
{
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", _err);
};
}
if let Some(ref chained_fixups) = macho.dyld_chain_fixups {
let offset = chained_fixups.data_off as usize;
let size = chained_fixups.data_size as usize;
if let Some(fixup_data) =
data.get(offset..offset.saturating_add(size))
&& let Err(_err) = macho.parse_chained_fixups(fixup_data)
{
#[cfg(feature = "logging")]
error!("Error parsing Mach-O file: {:?}", _err);
};
}
Ok(macho)
}
}
pub struct MachOFile<'a> {
endianness: Endianness,
is_32_bits: bool,
entry_point_offset: Option<u64>,
entry_point_rva: Option<u64>,
stack_size: Option<u64>,
header: MachOHeader,
segments: Vec<Segment<'a>>,
dylibs: Vec<Dylib<'a>>,
symtab: Option<Symtab<'a>>,
dysymtab: Option<Dysymtab>,
dyld_info: Option<DyldInfo>,
dyld_export_trie: Option<DyldExportTrie>,
dyld_chain_fixups: Option<DyldChainFixups>,
dynamic_linker: Option<&'a [u8]>,
linker_options: Vec<&'a [u8]>,
source_version: Option<String>,
rpaths: Vec<&'a [u8]>,
uuid: Option<&'a [u8]>,
code_signature_data: Option<LinkedItData>,
entitlements: Vec<String>,
certificates: Vec<Certificate>,
build_version: Option<BuildVersionCommand>,
min_version: Option<MinVersion>,
exports: Vec<String>,
imports: Vec<String>,
}
impl MachOFile<'_> {
pub fn rva_to_offset(&self, rva: u64) -> Option<u64> {
for segment in &self.segments {
let start = segment.vmaddr;
let end = segment.vmaddr.checked_add(segment.vmsize)?;
if rva >= start && rva < end {
return segment.fileoff.checked_add(rva.checked_sub(start)?);
}
}
None
}
}
impl<'a> MachOFile<'a> {
fn section(
&self,
) -> impl Parser<&'a [u8], Output = Section<'a>, Error = NomError<'a>> + '_
{
map(
(
map(take(16_usize), |name| {
BStr::new(name).trim_end_with(|c| c == '\0')
}),
map(take(16_usize), |name| {
BStr::new(name).trim_end_with(|c| c == '\0')
}),
uint(self.endianness, self.is_32_bits), uint(self.endianness, self.is_32_bits), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), cond(!self.is_32_bits, u32(self.endianness)), ),
|(
sectname,
segname,
addr,
size,
offset,
align,
reloff,
nreloc,
flags,
reserved1,
reserved2,
reserved3,
)| {
Section {
sectname,
segname,
addr,
size,
offset,
align,
reloff,
nreloc,
flags,
reserved1,
reserved2,
reserved3,
}
},
)
}
fn command(
&mut self,
) -> impl Parser<&'a [u8], Output = (), Error = NomError<'a>> + '_ {
move |input: &'a [u8]| {
let (remainder, (command, command_size)) = (
u32(self.endianness), u32(self.endianness), )
.parse(input)?;
let (remainder, command_data) = take(
command_size.saturating_sub(8),
)(remainder)?;
match command {
LC_MAIN => {
let parsed = self.main_command().parse(command_data);
if let Ok((_, (entry_point_offset, stack_size))) = parsed {
self.entry_point_offset = Some(entry_point_offset);
self.stack_size = Some(stack_size);
}
}
LC_UNIXTHREAD => {
let parsed = self.thread_command().parse(command_data);
if let Ok((_, eip)) = parsed {
self.entry_point_rva = Some(eip);
}
}
LC_SEGMENT | LC_SEGMENT_64 => {
let parsed = self.segment_command().parse(command_data);
if let Ok((_, segment)) = parsed {
self.segments.push(segment);
}
}
LC_RPATH => {
let parsed = self.rpath_command().parse(command_data);
if let Ok((_, rpath)) = parsed {
self.rpaths.push(rpath);
}
}
LC_LOAD_DYLIB | LC_ID_DYLIB | LC_LOAD_WEAK_DYLIB
| LC_REEXPORT_DYLIB | LC_LAZY_LOAD_DYLIB
| LC_LOAD_UPWARD_DYLIB => {
let parsed = self.dylib_command().parse(command_data);
if let Ok((_, dylib)) = parsed {
self.dylibs.push(dylib);
}
}
LC_SOURCE_VERSION => {
let parsed =
self.source_version_command().parse(command_data);
if let Ok((_, ver)) = parsed {
self.source_version =
Some(convert_to_source_version_string(ver));
}
}
LC_ID_DYLINKER | LC_LOAD_DYLINKER | LC_DYLD_ENVIRONMENT => {
let parsed = self.dylinker_command().parse(command_data);
if let Ok((_, dylinker)) = parsed {
self.dynamic_linker = Some(dylinker);
}
}
LC_SYMTAB => {
let parsed = self.symtab_command().parse(command_data);
if let Ok((_, symtab)) = parsed {
self.symtab = Some(symtab);
}
}
LC_DYSYMTAB => {
let parsed = self.dysymtab_command().parse(command_data);
if let Ok((_, dysymtab)) = parsed {
self.dysymtab = Some(dysymtab);
}
}
LC_CODE_SIGNATURE => {
let parsed =
self.linkeditdata_command().parse(command_data);
if let Ok((_, lid)) = parsed {
self.code_signature_data = Some(lid);
}
}
LC_DYLD_EXPORTS_TRIE => {
let parsed =
self.linkeditdata_command().parse(command_data);
if let Ok((_, exports_data)) = parsed {
self.dyld_export_trie = Some(DyldExportTrie {
data_off: exports_data.dataoff,
data_size: exports_data.datasize,
});
}
}
LC_DYLD_CHAINED_FIXUPS => {
let parsed =
self.linkeditdata_command().parse(command_data);
if let Ok((_, imports_data)) = parsed {
self.dyld_chain_fixups = Some(DyldChainFixups {
data_off: imports_data.dataoff,
data_size: imports_data.datasize,
});
}
}
LC_DYLD_INFO | LC_DYLD_INFO_ONLY => {
let parsed = self.dyld_info_command().parse(command_data);
if let Ok((_, dyld_info)) = parsed {
self.dyld_info = Some(dyld_info);
}
}
LC_UUID => {
let parsed = self.uuid_command().parse(command_data);
if let Ok((_, uuid)) = parsed {
self.uuid = Some(uuid);
}
}
LC_BUILD_VERSION => {
let parsed =
self.build_version_command().parse(command_data);
if let Ok((_, bv)) = parsed {
self.build_version = Some(bv);
}
}
LC_VERSION_MIN_MACOSX
| LC_VERSION_MIN_IPHONEOS
| LC_VERSION_MIN_TVOS
| LC_VERSION_MIN_WATCHOS => {
let parsed =
self.min_version_command().parse(command_data);
if let Ok((_, mut mv)) = parsed {
mv.device = command;
self.min_version = Some(mv);
}
}
LC_LINKER_OPTION => {
let parsed =
self.linker_options_command().parse(command_data);
if let Ok((_, linker_options)) = parsed {
self.linker_options.extend(linker_options);
}
}
_ => {}
}
Ok((remainder, ()))
}
}
fn main_command(
&mut self,
) -> impl Parser<&'a [u8], Output = (u64, u64), Error = NomError<'a>> + '_
{
(
u64(self.endianness), u64(self.endianness), )
}
fn thread_command(
&self,
) -> impl Parser<&'a [u8], Output = u64, Error = NomError<'a>> + '_ {
move |input: &'a [u8]| {
let (remainder, (_flavor, _count)) = (
u32(self.endianness), u32(self.endianness), )
.parse(input)?;
match self.header.cputype {
CPU_TYPE_X86 => self.x86_thread_state().parse(remainder),
CPU_TYPE_X86_64 => self.x86_64_thread_state().parse(remainder),
CPU_TYPE_ARM => self.arm_thread_state().parse(remainder),
CPU_TYPE_ARM64 => self.arm64_thread_state().parse(remainder),
CPU_TYPE_POWERPC => self.ppc_thread_state().parse(remainder),
CPU_TYPE_POWERPC64 => {
self.ppc64_thread_state().parse(remainder)
}
CPU_TYPE_MC680X0 => self.m68k_thread_state().parse(remainder),
CPU_TYPE_MC88000 => self.m88k_thread_state().parse(remainder),
CPU_TYPE_SPARC => self.sparc_thread_state().parse(remainder),
_ => Ok((remainder, 0)),
}
}
}
fn segment_command(
&self,
) -> impl Parser<&'a [u8], Output = Segment<'a>, Error = NomError<'a>> + '_
{
move |input: &'a [u8]| {
let (
remainder,
(
segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
nsects,
flags,
),
) = (
map(take(16_usize), |name| {
BStr::new(name).trim_end_with(|c| c == '\0')
}),
uint(self.endianness, self.is_32_bits), uint(self.endianness, self.is_32_bits), uint(self.endianness, self.is_32_bits), uint(self.endianness, self.is_32_bits), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), )
.parse(input)?;
let (remainder, sections) =
count(self.section(), nsects as usize).parse(remainder)?;
Ok((
remainder,
Segment {
segname,
vmaddr,
vmsize,
fileoff,
filesize,
maxprot,
initprot,
nsects,
flags,
sections,
},
))
}
}
fn dylib_command(
&self,
) -> impl Parser<&'a [u8], Output = Dylib<'a>, Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), take_till(|b| b == b'\x00'), ),
|(
_offset,
timestamp,
current_version,
compatibility_version,
name,
)| {
Dylib {
name: BStr::new(name),
timestamp,
current_version,
compatibility_version,
}
},
)
}
fn symtab_command(
&self,
) -> impl Parser<&'a [u8], Output = Symtab<'a>, Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), ),
|(symoff, nsyms, stroff, strsize)| Symtab {
symoff,
nsyms,
stroff,
strsize,
entries: Vec::new(),
nlists: Vec::new(),
},
)
}
fn dysymtab_command(
&self,
) -> impl Parser<&'a [u8], Output = Dysymtab, Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), ),
|(
ilocalsym,
nlocalsym,
iextdefsym,
nextdefsym,
tocoff,
ntoc,
modtaboff,
nmodtab,
extrefsymoff,
nextrefsyms,
indirectsymoff,
nindirectsyms,
extreloff,
nextrel,
locreloff,
nlocrel,
)| {
Dysymtab {
ilocalsym,
nlocalsym,
iextdefsym,
nextdefsym,
tocoff,
ntoc,
modtaboff,
nmodtab,
extrefsymoff,
nextrefsyms,
indirectsymoff,
nindirectsyms,
extreloff,
nextrel,
locreloff,
nlocrel,
}
},
)
}
fn linkeditdata_command(
&self,
) -> impl Parser<&'a [u8], Output = LinkedItData, Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), u32(self.endianness), ),
|(dataoff, datasize)| LinkedItData { dataoff, datasize },
)
}
fn cs_blob(
&self,
) -> impl Parser<&'a [u8], Output = CSBlob, Error = NomError<'a>> + '_
{
map(
(
u32(Endianness::Big), u32(Endianness::Big), ),
|(magic, length)| CSBlob { magic, length },
)
}
fn cs_index(
&self,
) -> impl Parser<&'a [u8], Output = CSBlobIndex, Error = NomError<'a>> + '_
{
map(
(
u32(Endianness::Big), u32(Endianness::Big), ),
|(_blobtype, offset)| CSBlobIndex {
_blobtype,
offset,
blob: None,
},
)
}
fn cs_superblob(
&mut self,
) -> impl Parser<&'a [u8], Output = CSSuperBlob, Error = NomError<'a>> + '_
{
move |input: &'a [u8]| {
let (mut remainder, (_magic, _length, count)) = (
u32(Endianness::Big), u32(Endianness::Big), u32(Endianness::Big), )
.parse(input)?;
let mut super_blob =
CSSuperBlob { _magic, _length, count, index: Vec::new() };
let mut cs_index: CSBlobIndex;
for _ in 0..super_blob.count {
(remainder, cs_index) = self.cs_index().parse(remainder)?;
cs_index.blob = input
.get(cs_index.offset as usize..)
.and_then(|blob_data| self.cs_blob().parse(blob_data).ok())
.map(|(_, blob)| blob);
super_blob.index.push(cs_index);
}
let super_data = input;
let blobs = super_blob.index.iter().filter_map(|blob_index| {
blob_index
.blob
.as_ref()
.map(|blob| (blob_index.offset as usize, blob))
});
for (offset, blob) in blobs {
let length = blob.length as usize;
let size_of_blob = std::mem::size_of::<CSBlob>();
match blob.magic {
CS_MAGIC_EMBEDDED_ENTITLEMENTS => {
let xml_data = match super_data.get(
offset.saturating_add(size_of_blob)
..offset.saturating_add(length),
) {
Some(data) => data,
None => continue,
};
let xml_string =
std::str::from_utf8(xml_data).unwrap_or_default();
let opt = roxmltree::ParsingOptions {
allow_dtd: true,
..roxmltree::ParsingOptions::default()
};
if let Ok(parsed_xml) =
roxmltree::Document::parse_with_options(
xml_string, opt,
)
{
for node in parsed_xml.descendants().filter(|n| {
n.has_tag_name("key")
|| n.has_tag_name("array")
}) {
if let Some(entitlement) = node.text() {
if node.has_tag_name("array") {
node.descendants()
.filter_map(|n| n.text())
.filter(|t| !t.trim().is_empty())
.unique()
.map(|t| t.to_string())
.for_each(|array_entitlement| {
self.entitlements
.push(array_entitlement)
});
} else {
self.entitlements
.push(entitlement.to_string());
}
}
}
}
}
CS_MAGIC_BLOBWRAPPER => {
if let Some(ber_blob) = super_data.get(
offset.saturating_add(size_of_blob)
..offset.saturating_add(length),
) && let Ok((_remainder, certs)) =
parse_certificates(ber_blob)
{
self.certificates.extend(certs);
}
}
_ => {}
}
}
Ok((remainder, super_blob))
}
}
fn dyld_info_command(
&self,
) -> impl Parser<&'a [u8], Output = DyldInfo, Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), ),
|(
rebase_off,
rebase_size,
bind_off,
bind_size,
weak_bind_off,
weak_bind_size,
lazy_bind_off,
lazy_bind_size,
export_off,
export_size,
)| {
DyldInfo {
rebase_off,
rebase_size,
bind_off,
bind_size,
weak_bind_off,
weak_bind_size,
lazy_bind_off,
lazy_bind_size,
export_off,
export_size,
}
},
)
}
fn nlist(
&self,
) -> impl Parser<&'a [u8], Output = Nlist, Error = NomError<'a>> + '_ {
map(
(
u32(self.endianness), u8, u8, u16(self.endianness), uint(self.endianness, self.is_32_bits), ),
|(n_strx, n_type, n_sect, n_desc, n_value)| Nlist {
n_strx,
n_type,
n_sect,
n_desc,
n_value,
},
)
}
fn parse_symtab(
&mut self,
string_table: &'a [u8],
symbol_table: &'a [u8],
count: u32,
) -> IResult<&'a [u8], ()> {
let mut data = symbol_table;
let mut n;
for _ in 0..count {
(data, n) = self.nlist().parse(data)?;
if let Some(symtab) = self.symtab.as_mut()
&& let Some(string_data) =
string_table.get(n.n_strx as usize..)
{
let (_, string_value) = map(
(take_till(|b| b == b'\x00'), tag("\x00")),
|(s, _)| s,
)
.parse(string_data)?;
if !string_value.is_empty() {
symtab.entries.push(string_value);
symtab.nlists.push(n);
}
}
}
Ok((data, ()))
}
fn parse_exports(&mut self, data: &'a [u8]) -> IResult<&'a [u8], ()> {
let mut stack = Vec::<ExportNode>::new();
let mut visited = HashSet::<usize>::new();
stack.push(ExportNode { offset: 0, prefix: "".to_string() });
while !stack.is_empty() && !data.is_empty() {
let export_node = stack.pop().unwrap();
if !visited.insert(export_node.offset) {
continue;
}
let node_data = match data.get(export_node.offset..) {
Some(data) => data,
None => continue,
};
let (mut remaining_data, length) = uleb128(node_data)?;
if length != 0 {
let (remainder, flags) = uleb128(remaining_data)?;
match flags {
EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
let (remainder, (_stub_offset, _resolver_offset)) =
(uleb128, uleb128).parse(remainder)?;
remaining_data = remainder;
}
EXPORT_SYMBOL_FLAGS_REEXPORT => {
let (remainder, _ordinal) = uleb128(remainder)?;
let (remainder, _label) = map(
(take_till(|b| b == b'\x00'), tag("\x00")),
|(s, _)| s,
)
.parse(remainder)?;
remaining_data = remainder;
}
_ => {
let (remainder, _offset) = uleb128(remainder)?;
remaining_data = remainder;
}
}
}
let (mut edge_remainder, edges) = u8(remaining_data)?;
for _ in 0..edges {
let (remainder, edge_label) = map(
(take_till(|b| b == b'\x00'), tag("\x00")),
|(s, _)| BStr::new(s),
)
.parse(edge_remainder)?;
let (remainder, edge_offset) = uleb128(remainder)?;
if let Ok(edge_label_str) = edge_label.to_str() {
stack.push(ExportNode {
offset: edge_offset as usize,
prefix: format!(
"{}{}",
export_node.prefix, edge_label_str
),
});
}
edge_remainder = remainder;
}
if length != 0 {
self.exports.push(export_node.prefix)
}
}
Ok((&[], ()))
}
fn parse_imports(
&mut self,
data: &'a [u8],
seen: &mut HashSet<&'a str>,
) -> IResult<&'a [u8], ()> {
let mut remainder: &[u8] = data;
let mut entry: u8;
while !remainder.is_empty() {
(remainder, entry) = u8(remainder)?;
let opcode = entry & BIND_OPCODE_MASK;
let _immediate = entry & BIND_IMMEDIATE_MASK;
match opcode {
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
| BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
| BIND_OPCODE_ADD_ADDR_ULEB
| BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
(remainder, _) = uleb128(remainder)?;
}
BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
(remainder, _) = uleb128(remainder)?;
(remainder, _) = uleb128(remainder)?;
}
BIND_OPCODE_SET_ADDEND_SLEB => {
(remainder, _) = sleb128(remainder)?;
}
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
let (import_remainder, strr) = map(
(take_till(|b| b == b'\x00'), tag("\x00")),
|(s, _)| BStr::new(s),
)
.parse(remainder)?;
remainder = import_remainder;
if let Ok(import) = strr.to_str()
&& !seen.contains(import)
{
self.imports.push(import.to_string());
seen.insert(import);
}
}
_ => {}
}
}
Ok((remainder, ()))
}
fn chained_fixup_header(
&self,
) -> impl Parser<
&'a [u8],
Output = ChainedFixupsHeader,
Error = NomError<'a>,
> + '_ {
map(
(
u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), u32(self.endianness), ),
|(
fixups_version,
starts_offset,
imports_offset,
symbols_offset,
imports_count,
imports_format,
symbols_format,
)| {
ChainedFixupsHeader {
_fixups_version: fixups_version,
_starts_offset: starts_offset,
imports_offset,
symbols_offset,
imports_count,
imports_format,
_symbols_format: symbols_format,
}
},
)
}
fn parse_chained_fixups(
&mut self,
data: &'a [u8],
) -> IResult<&'a [u8], ()> {
let (_, header) = self.chained_fixup_header().parse(data)?;
if let Some(import_data) = data.get(header.imports_offset as usize..) {
let entry_size = match header.imports_format {
DYLD_CHAINED_IMPORT => 4,
DYLD_CHAINED_IMPORT_ADDEND => 8,
DYLD_CHAINED_IMPORT_ADDEND64 => 16,
_ => 4, };
let is_addend64 =
header.imports_format == DYLD_CHAINED_IMPORT_ADDEND64;
let (shift_symbol, symbol_mask, ordinal_mask, shift_kind) =
if is_addend64 {
(32, 0xFFFFFFFF, 0xFFFF, 16)
} else {
(9, 0x7FFFFF, 0xFF, 8)
};
let imports_size = (header.imports_count as usize) * entry_size;
if let Some(raw_imports_blob) = import_data.get(..imports_size) {
for chunk in raw_imports_blob.chunks_exact(entry_size) {
let chained_import_value = if is_addend64 {
let (_, val) = u64(self.endianness)(chunk)?;
val
} else {
let (_, val) = u32(self.endianness)(chunk)?;
val as u64
};
let _lib_ordinal = chained_import_value & ordinal_mask;
let _import_kind =
(chained_import_value >> shift_kind) & 0x1;
let name_offset =
(chained_import_value >> shift_symbol) & symbol_mask;
if let Some(name_buffer) = data.get(
header
.symbols_offset
.saturating_add(name_offset as u32)
as usize..,
) {
let (_remainder, import_str) = map(
(take_till(|b| b == b'\x00'), tag("\x00")),
|(s, _)| s,
)
.parse(name_buffer)?;
if let Ok(import) = import_str.to_str() {
self.imports.push(import.to_string());
}
}
}
}
}
Ok((&[], ()))
}
fn dylinker_command(
&self,
) -> impl Parser<&'a [u8], Output = &'a [u8], Error = NomError<'a>> + '_
{
map(
(
u32(self.endianness), take_till(|b| b == b'\x00'), ),
|(_offset, command)| command,
)
}
fn linker_options_command(
&self,
) -> impl Parser<&'a [u8], Output = Vec<&'a [u8]>, Error = NomError<'a>> + '_
{
move |input: &'a [u8]| {
let (mut remainder, count) = u32(self.endianness).parse(input)?;
let count = count as usize;
if count > remainder.len() {
return Err(Err::Error(NomError::new(
remainder,
ErrorKind::TooLarge,
)));
}
let mut options = Vec::new();
for _ in 0..count {
let (next, option) =
map((take_till(|b| b == b'