/// Returns a big endian magical number pubfn peek(bytes: &[u8], offset: usize) -> error::Result<u32> {
Ok(bytes.pread_with::<u32>(offset, scroll::BE)?)
}
/// Parses a magic number, and an accompanying mach-o binary parsing context, according to the magic number. pubfn parse_magic_and_ctx(
bytes: &[u8],
offset: usize,
) -> error::Result<(u32, Option<container::Ctx>)> { usecrate::container::Container; usecrate::mach::header::*; let magic = bytes.pread_with::<u32>(offset, BE)?; let ctx = match magic {
MH_CIGAM_64 | MH_CIGAM | MH_MAGIC_64 | MH_MAGIC => { let is_lsb = magic == MH_CIGAM || magic == MH_CIGAM_64; let le = scroll::Endian::from(is_lsb); let container = if magic == MH_MAGIC_64 || magic == MH_CIGAM_64 {
Container::Big
} else {
Container::Little
};
Some(container::Ctx::new(container, le))
}
_ => None,
};
Ok((magic, ctx))
}
/// A cross-platform, zero-copy, endian-aware, 32/64 bit Mach-o binary parser pubstruct MachO<'a> { /// The mach-o header pub header: header::Header, /// The load commands tell the kernel and dynamic linker how to use/interpret this binary pub load_commands: Vec<load_command::LoadCommand>, /// The load command "segments" - typically the pieces of the binary that are loaded into memory pub segments: segment::Segments<'a>, /// The "Nlist" style symbols in this binary - strippable pub symbols: Option<symbols::Symbols<'a>>, /// The dylibs this library depends on pub libs: Vec<&'a str>, /// The runtime search paths for dylibs this library depends on pub rpaths: Vec<&'a str>, /// The entry point (as a virtual memory address), 0 if none pub entry: u64, /// Whether `entry` refers to an older `LC_UNIXTHREAD` instead of the newer `LC_MAIN` entrypoint pub old_style_entry: bool, /// The name of the dylib, if any pub name: Option<&'a str>, /// Are we a little-endian binary? pub little_endian: bool, /// Are we a 64-bit binary pub is_64: bool,
data: &'a [u8],
ctx: container::Ctx,
export_trie: Option<exports::ExportTrie<'a>>,
bind_interpreter: Option<imports::BindInterpreter<'a>>,
}
impl<'a> MachO<'a> { /// Is this a relocatable object file? pubfn is_object_file(&self) -> bool { self.header.filetype == header::MH_OBJECT
} /// Return an iterator over all the symbols in this binary pubfn symbols(&self) -> symbols::SymbolIterator<'a> { iflet Some(ref symbols) = self.symbols {
symbols.into_iter()
} else {
symbols::SymbolIterator::default()
}
} /// Return a vector of the relocations in this binary pubfn relocations(
&self,
) -> error::Result<Vec<(usize, segment::RelocationIterator, segment::Section)>> {
debug!("Iterating relocations"); letmut relocs = Vec::new(); for (_i, segment) in (&self.segments).into_iter().enumerate() { for (j, section) in segment.into_iter().enumerate() { let (section, _data) = section?; if section.nreloc > 0 {
relocs.push((j, section.iter_relocations(self.data, self.ctx), section));
}
}
}
Ok(relocs)
} /// Return the exported symbols in this binary (if any) pubfn exports(&self) -> error::Result<Vec<exports::Export>> { iflet Some(ref trie) = self.export_trie {
trie.exports(self.libs.as_slice())
} else {
Ok(vec![])
}
} /// Return the imported symbols in this binary that dyld knows about (if any) pubfn imports(&self) -> error::Result<Vec<imports::Import>> { iflet Some(ref interpreter) = self.bind_interpreter {
interpreter.imports(self.libs.as_slice(), self.segments.as_slice(), self.ctx)
} else {
Ok(vec![])
}
} /// Parses the Mach-o binary from `bytes` at `offset` pubfn parse(bytes: &'a [u8], offset: usize) -> error::Result<MachO<'a>> { Self::parse_impl(bytes, offset, false)
}
/// Parses the Mach-o binary from `bytes` at `offset` in lossy mode pubfn parse_lossy(bytes: &'a [u8], offset: usize) -> error::Result<MachO<'a>> { Self::parse_impl(bytes, offset, true)
}
/// Parses the Mach-o binary from `bytes` at `offset` in `lossy` mode fn parse_impl(bytes: &'a [u8], mut offset: usize, lossy: bool) -> error::Result<MachO<'a>> { let (magic, maybe_ctx) = parse_magic_and_ctx(bytes, offset)?; let ctx = iflet Some(ctx) = maybe_ctx {
ctx
} else { return Err(error::Error::BadMagic(u64::from(magic)));
};
debug!("Ctx: {:?}", ctx); let offset = &mut offset; let header: header::Header = bytes.pread_with(*offset, ctx)?;
debug!("Mach-o header: {:?}", header); let little_endian = ctx.le.is_little(); let is_64 = ctx.container.is_big();
*offset += header::Header::size_with(&ctx.container); let ncmds = header.ncmds;
let sizeofcmds = header.sizeofcmds as usize; // a load cmd is at least 2 * 4 bytes, (type, sizeof) if ncmds > sizeofcmds / 8 || sizeofcmds > bytes.len() { return Err(error::Error::BufferTooShort(ncmds, "load commands"));
}
letmut cmds: Vec<load_command::LoadCommand> = Vec::with_capacity(ncmds); letmut symbols = None; letmut libs = vec!["self"]; letmut rpaths = vec![]; letmut export_trie = None; letmut bind_interpreter = None; letmut unixthread_entry_address = None; letmut main_entry_offset = None; letmut name = None; letmut segments = segment::Segments::new(ctx); for i in0..ncmds { let cmd = load_command::LoadCommand::parse(bytes, offset, ctx.le)?;
debug!("{} - {:?}", i, cmd); match cmd.command {
load_command::CommandVariant::Segment32(command) => segments.push(
segment::Segment::from_32_impl(bytes, &command, cmd.offset, ctx, lossy)?,
),
load_command::CommandVariant::Segment64(command) => segments.push(
segment::Segment::from_64_impl(bytes, &command, cmd.offset, ctx, lossy)?,
),
load_command::CommandVariant::Symtab(command) => { match symbols::Symbols::parse(bytes, &command, ctx) {
Ok(s) => symbols = Some(s),
Err(e) if lossy => {
debug!("CommandVariant::Symtab failed: {e}");
}
Err(e) => return Err(e),
}
}
load_command::CommandVariant::LoadDylib(command)
| load_command::CommandVariant::LoadUpwardDylib(command)
| load_command::CommandVariant::ReexportDylib(command)
| load_command::CommandVariant::LoadWeakDylib(command)
| load_command::CommandVariant::LazyLoadDylib(command) => { match bytes.pread::<&str>(cmd.offset + command.dylib.name as usize) {
Ok(lib) => libs.push(lib),
Err(e) if lossy => {
debug!("CommandVariant::Load/Reexport Dylib failed: {e}");
}
Err(e) => return Err(e.into()),
}
}
load_command::CommandVariant::Rpath(command) => { match bytes.pread::<&str>(cmd.offset + command.path as usize) {
Ok(rpath) => rpaths.push(rpath),
Err(e) if lossy => {
debug!("CommandVariant::Rpath failed: {e}");
}
Err(e) => return Err(e.into()),
}
}
load_command::CommandVariant::DyldInfo(command)
| load_command::CommandVariant::DyldInfoOnly(command) => {
export_trie = Some(exports::ExportTrie::new(bytes, &command));
bind_interpreter = Some(imports::BindInterpreter::new(bytes, &command));
}
load_command::CommandVariant::DyldExportsTrie(command) => {
export_trie = Some(exports::ExportTrie::new_from_linkedit_data_command(
bytes, &command,
));
}
load_command::CommandVariant::Unixthread(command) => { // dyld cares only about the first LC_UNIXTHREAD if unixthread_entry_address.is_none() {
unixthread_entry_address =
Some(command.instruction_pointer(header.cputype)?);
}
}
load_command::CommandVariant::Main(command) => { // dyld cares only about the first LC_MAIN if main_entry_offset.is_none() {
main_entry_offset = Some(command.entryoff);
}
}
load_command::CommandVariant::IdDylib(command) => { match bytes.pread::<&str>(cmd.offset + command.dylib.name as usize) {
Ok(id) => {
libs[0] = id;
name = Some(id);
}
Err(e) if lossy => {
debug!("CommandVariant::IdDylib failed: {e}");
}
Err(e) => return Err(e.into()),
}
}
_ => (),
}
cmds.push(cmd)
}
// dyld prefers LC_MAIN over LC_UNIXTHREAD // choose the same way here let (entry, old_style_entry) = iflet Some(offset) = main_entry_offset { // map the entrypoint offset to a virtual memory address let base_address = segments
.iter()
.filter(|s| &s.segname[0..7] == b"__TEXT\0")
.map(|s| s.vmaddr - s.fileoff)
.next()
.ok_or_else(|| {
error::Error::Malformed(format!( "image specifies LC_MAIN offset {} but has no __TEXT segment",
offset
))
})?;
letmut arches = Vec::with_capacity(self.narches); for arch inself.iter_arches() {
arches.push(arch?);
}
Ok(arches)
} /// Try to get the Mach-o binary at `index` pubfn get(&self, index: usize) -> error::Result<SingleArch<'a>> { if index >= self.narches { return Err(error::Error::Malformed(format!( "Requested the {}-th binary, but there are only {} architectures in this container",
index, self.narches
)));
} let offset = (index * fat::SIZEOF_FAT_ARCH) + self.start; let arch = self.data.pread_with::<fat::FatArch>(offset, scroll::BE)?; let bytes = arch.slice(self.data);
extract_multi_entry(bytes)
}
pubfn find<F: Fn(error::Result<fat::FatArch>) -> bool>(
&'a self,
f: F,
) -> Option<error::Result<SingleArch<'a>>> { for (i, arch) inself.iter_arches().enumerate() { if f(arch) { return Some(self.get(i));
}
}
None
} /// Try and find the `cputype` in `Self`, if there is one pubfn find_cputype(&self, cputype: u32) -> error::Result<Option<fat::FatArch>> { for arch inself.iter_arches() { let arch = arch?; if arch.cputype == cputype { return Ok(Some(arch));
}
}
Ok(None)
}
}
#[derive(Debug)] #[allow(clippy::large_enum_variant)] /// Either a collection of multiple architectures, or a single mach-o binary pubenum Mach<'a> { /// A "fat" multi-architecture binary container
Fat(MultiArch<'a>), /// A regular Mach-o binary
Binary(MachO<'a>),
}
impl<'a> Mach<'a> { /// Parse from `bytes` either a multi-arch binary or a regular mach-o binary pubfn parse(bytes: &'a [u8]) -> error::Result<Self> { Self::parse_impl(bytes, false)
}
/// Parse from `bytes` either a multi-arch binary or a regular mach-o binary in lossy mode pubfn parse_lossy(bytes: &'a [u8]) -> error::Result<Self> { Self::parse_impl(bytes, true)
}
/// Parse from `bytes` either a multi-arch binary or a regular mach-o binary fn parse_impl(bytes: &'a [u8], lossy: bool) -> error::Result<Self> { let size = bytes.len(); if size < 4 { let error = error::Error::Malformed("size is smaller than a magical number".into()); return Err(error);
} let magic = peek(&bytes, 0)?; match magic {
fat::FAT_MAGIC => { let multi = MultiArch::new(bytes)?;
Ok(Mach::Fat(multi))
} // we might be a regular binary
_ => { let binary = MachO::parse_impl(bytes, 0, lossy)?;
Ok(Mach::Binary(binary))
}
}
}
}
#[cfg(test)] mod test { usesuper::{Mach, SingleArch};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.