#![doc = include_str!("readme.md")]
use gaia_binary::{BigEndian, LittleEndian, ReadBytesExt};
use gaia_types::helpers::Architecture;
use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
io::Read,
};
use gaia_types::GaiaError;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum MachoType {
Object,
Execute,
FvmLib,
Core,
PreLoad,
Dylib,
Dylinker,
Bundle,
DylibStub,
Dsym,
KextBundle,
}
impl Display for MachoType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
MachoType::Object => write!(f, "目标文件"),
MachoType::Execute => write!(f, "可执行文件"),
MachoType::FvmLib => write!(f, "固定虚拟内存共享库"),
MachoType::Core => write!(f, "核心转储文件"),
MachoType::PreLoad => write!(f, "预加载的可执行文件"),
MachoType::Dylib => write!(f, "动态共享库"),
MachoType::Dylinker => write!(f, "动态链接器"),
MachoType::Bundle => write!(f, "动态加载的包"),
MachoType::DylibStub => write!(f, "动态共享库存根"),
MachoType::Dsym => write!(f, "配套的调试符号文件"),
MachoType::KextBundle => write!(f, "x86_64 kext 包"),
}
}
}
impl From<u32> for MachoType {
fn from(value: u32) -> Self {
match value {
1 => MachoType::Object,
2 => MachoType::Execute,
3 => MachoType::FvmLib,
4 => MachoType::Core,
5 => MachoType::PreLoad,
6 => MachoType::Dylib,
7 => MachoType::Dylinker,
8 => MachoType::Bundle,
9 => MachoType::DylibStub,
10 => MachoType::Dsym,
11 => MachoType::KextBundle,
_ => MachoType::Object,
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum CpuType {
X86_64,
Arm64,
Arm64e,
I386,
Arm,
}
impl From<u32> for CpuType {
fn from(value: u32) -> Self {
match value {
0x01000007 => CpuType::X86_64,
0x0100000c => CpuType::Arm64,
0x0200000c => CpuType::Arm64e,
0x00000007 => CpuType::I386,
0x0000000c => CpuType::Arm,
_ => CpuType::X86_64,
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct MachoHeader {
pub magic: u32,
pub cpu_type: u32,
pub cpu_subtype: u32,
pub file_type: u32,
pub ncmds: u32,
pub sizeofcmds: u32,
pub flags: u32,
pub reserved: Option<u32>,
}
impl MachoHeader {
pub fn new(cpu_type: CpuType, file_type: MachoType) -> Self {
let (magic, reserved) = match cpu_type {
CpuType::X86_64 | CpuType::Arm64 | CpuType::Arm64e => (0xfeedfacf, Some(0)),
_ => (0xfeedface, None),
};
Self {
magic,
cpu_type: cpu_type as u32,
cpu_subtype: 0,
file_type: file_type as u32,
ncmds: 0,
sizeofcmds: 0,
flags: 0,
reserved,
}
}
pub fn is_64bit(&self) -> bool {
self.magic == 0xfeedfacf || self.magic == 0xcffaedfe
}
pub fn is_little_endian(&self) -> bool {
self.magic == 0xfeedfacf || self.magic == 0xfeedface
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum LoadCommandType {
Segment,
Segment64,
Symtab,
Dysymtab,
LoadDylib,
IdDylib,
LoadDylinker,
IdDylinker,
PreboundDylib,
Thread,
UnixThread,
LoadWeakDylib,
Uuid,
CodeSignature,
SegmentSplitInfo,
ReexportDylib,
LazyLoadDylib,
EncryptionInfo,
DylibCodeSignDrs,
VersionMinMacosx,
VersionMinIphoneos,
FunctionStarts,
DyldEnvironment,
Main,
DataInCode,
SourceVersion,
DylibCodeSignDrs2,
EncryptionInfo64,
LinkerOption,
LinkerOptimizationHint,
VersionMinTvos,
VersionMinWatchos,
Note,
BuildVersion,
DyldExportsTrie,
DyldChainedFixups,
FilesetEntry,
}
impl From<u32> for LoadCommandType {
fn from(value: u32) -> Self {
match value {
0x1 => LoadCommandType::Segment,
0x19 => LoadCommandType::Segment64,
0x2 => LoadCommandType::Symtab,
0xb => LoadCommandType::Dysymtab,
0xc => LoadCommandType::LoadDylib,
0xd => LoadCommandType::IdDylib,
0xe => LoadCommandType::LoadDylinker,
0xf => LoadCommandType::IdDylinker,
0x10 => LoadCommandType::PreboundDylib,
0x4 => LoadCommandType::Thread,
0x5 => LoadCommandType::UnixThread,
0x18 => LoadCommandType::LoadWeakDylib,
0x1b => LoadCommandType::Uuid,
0x1d => LoadCommandType::CodeSignature,
0x1e => LoadCommandType::SegmentSplitInfo,
0x1f => LoadCommandType::ReexportDylib,
0x20 => LoadCommandType::LazyLoadDylib,
0x21 => LoadCommandType::EncryptionInfo,
0x22 => LoadCommandType::DylibCodeSignDrs,
0x24 => LoadCommandType::VersionMinMacosx,
0x25 => LoadCommandType::VersionMinIphoneos,
0x26 => LoadCommandType::FunctionStarts,
0x27 => LoadCommandType::DyldEnvironment,
0x28 => LoadCommandType::Main,
0x29 => LoadCommandType::DataInCode,
0x2a => LoadCommandType::SourceVersion,
0x2b => LoadCommandType::DylibCodeSignDrs2,
0x2c => LoadCommandType::EncryptionInfo64,
0x2d => LoadCommandType::LinkerOption,
0x2e => LoadCommandType::LinkerOptimizationHint,
0x2f => LoadCommandType::VersionMinTvos,
0x30 => LoadCommandType::VersionMinWatchos,
0x31 => LoadCommandType::Note,
0x32 => LoadCommandType::BuildVersion,
0x33 => LoadCommandType::DyldExportsTrie,
0x34 => LoadCommandType::DyldChainedFixups,
0x35 => LoadCommandType::FilesetEntry,
_ => LoadCommandType::Segment,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LoadCommand {
pub cmd: u32,
pub cmdsize: u32,
pub data: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
pub struct SegmentCommand64 {
pub cmd: u32,
pub cmdsize: u32,
pub segname: [u8; 16],
pub vmaddr: u64,
pub vmsize: u64,
pub fileoff: u64,
pub filesize: u64,
pub maxprot: u32,
pub initprot: u32,
pub nsects: u32,
pub flags: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
pub struct Section64 {
pub sectname: [u8; 16],
pub segname: [u8; 16],
pub addr: u64,
pub size: u64,
pub offset: u32,
pub align: u32,
pub reloff: u32,
pub nreloc: u32,
pub flags: u32,
pub reserved1: u32,
pub reserved2: u32,
pub reserved3: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct MachoReadConfig {
pub include_sections: bool,
pub parse_symbols: bool,
pub parse_dylibs: bool,
}
impl Default for MachoReadConfig {
fn default() -> Self {
Self { include_sections: true, parse_symbols: true, parse_dylibs: true }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MachoProgram {
pub header: MachoHeader,
pub load_commands: Vec<LoadCommand>,
pub segments: Vec<SegmentCommand64>,
pub sections: Vec<Section64>,
}
impl MachoProgram {
pub fn new(cpu_type: CpuType, file_type: MachoType) -> Self {
Self {
header: MachoHeader::new(cpu_type, file_type),
load_commands: Vec::new(),
segments: Vec::new(),
sections: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MachoInfo {
pub target_arch: Architecture,
pub file_type: MachoType,
pub cpu_type: CpuType,
pub ncmds: u32,
pub nsegments: u32,
pub nsections: u32,
pub file_size: u64,
}
impl MachoHeader {
pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
let magic = reader.read_u32::<LittleEndian>()?;
let is_64bit = magic == 0xfeedfacf || magic == 0xcffaedfe;
let is_little_endian = magic == 0xfeedfacf || magic == 0xfeedface;
let cpu_type = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let cpu_subtype = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let file_type = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let ncmds = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let sizeofcmds = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let flags = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
let reserved = if is_64bit {
Some(if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? })
}
else {
None
};
Ok(Self { magic, cpu_type, cpu_subtype, file_type, ncmds, sizeofcmds, flags, reserved })
}
}