1#![doc = include_str!("readme.md")]
2
3use gaia_binary::{BigEndian, LittleEndian, ReadBytesExt};
4use gaia_types::helpers::Architecture;
5use serde::{Deserialize, Serialize};
6use std::{
7 fmt::{Display, Formatter},
8 io::Read,
9};
10
11use gaia_types::GaiaError;
12
13#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
17pub enum MachoType {
18 Object,
20 Execute,
22 FvmLib,
24 Core,
26 PreLoad,
28 Dylib,
30 Dylinker,
32 Bundle,
34 DylibStub,
36 Dsym,
38 KextBundle,
40}
41
42impl Display for MachoType {
43 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
44 match self {
45 MachoType::Object => write!(f, "目标文件"),
46 MachoType::Execute => write!(f, "可执行文件"),
47 MachoType::FvmLib => write!(f, "固定虚拟内存共享库"),
48 MachoType::Core => write!(f, "核心转储文件"),
49 MachoType::PreLoad => write!(f, "预加载的可执行文件"),
50 MachoType::Dylib => write!(f, "动态共享库"),
51 MachoType::Dylinker => write!(f, "动态链接器"),
52 MachoType::Bundle => write!(f, "动态加载的包"),
53 MachoType::DylibStub => write!(f, "动态共享库存根"),
54 MachoType::Dsym => write!(f, "配套的调试符号文件"),
55 MachoType::KextBundle => write!(f, "x86_64 kext 包"),
56 }
57 }
58}
59
60impl From<u32> for MachoType {
61 fn from(value: u32) -> Self {
62 match value {
63 1 => MachoType::Object,
64 2 => MachoType::Execute,
65 3 => MachoType::FvmLib,
66 4 => MachoType::Core,
67 5 => MachoType::PreLoad,
68 6 => MachoType::Dylib,
69 7 => MachoType::Dylinker,
70 8 => MachoType::Bundle,
71 9 => MachoType::DylibStub,
72 10 => MachoType::Dsym,
73 11 => MachoType::KextBundle,
74 _ => MachoType::Object,
75 }
76 }
77}
78
79#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
83pub enum CpuType {
84 X86_64,
86 Arm64,
88 Arm64e,
90 I386,
92 Arm,
94}
95
96impl From<u32> for CpuType {
97 fn from(value: u32) -> Self {
98 match value {
99 0x01000007 => CpuType::X86_64,
100 0x0100000c => CpuType::Arm64,
101 0x0200000c => CpuType::Arm64e,
102 0x00000007 => CpuType::I386,
103 0x0000000c => CpuType::Arm,
104 _ => CpuType::X86_64,
105 }
106 }
107}
108
109#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
113pub struct MachoHeader {
114 pub magic: u32,
116 pub cpu_type: u32,
118 pub cpu_subtype: u32,
120 pub file_type: u32,
122 pub ncmds: u32,
124 pub sizeofcmds: u32,
126 pub flags: u32,
128 pub reserved: Option<u32>,
130}
131
132impl MachoHeader {
133 pub fn new(cpu_type: CpuType, file_type: MachoType) -> Self {
135 let (magic, reserved) = match cpu_type {
136 CpuType::X86_64 | CpuType::Arm64 | CpuType::Arm64e => (0xfeedfacf, Some(0)),
137 _ => (0xfeedface, None),
138 };
139
140 Self {
141 magic,
142 cpu_type: cpu_type as u32,
143 cpu_subtype: 0,
144 file_type: file_type as u32,
145 ncmds: 0,
146 sizeofcmds: 0,
147 flags: 0,
148 reserved,
149 }
150 }
151
152 pub fn is_64bit(&self) -> bool {
154 self.magic == 0xfeedfacf || self.magic == 0xcffaedfe
155 }
156
157 pub fn is_little_endian(&self) -> bool {
159 self.magic == 0xfeedfacf || self.magic == 0xfeedface
160 }
161}
162
163#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
165pub enum LoadCommandType {
166 Segment,
168 Segment64,
170 Symtab,
172 Dysymtab,
174 LoadDylib,
176 IdDylib,
178 LoadDylinker,
180 IdDylinker,
182 PreboundDylib,
184 Thread,
186 UnixThread,
188 LoadWeakDylib,
190 Uuid,
192 CodeSignature,
194 SegmentSplitInfo,
196 ReexportDylib,
198 LazyLoadDylib,
200 EncryptionInfo,
202 DylibCodeSignDrs,
204 VersionMinMacosx,
206 VersionMinIphoneos,
208 FunctionStarts,
210 DyldEnvironment,
212 Main,
214 DataInCode,
216 SourceVersion,
218 DylibCodeSignDrs2,
220 EncryptionInfo64,
222 LinkerOption,
224 LinkerOptimizationHint,
226 VersionMinTvos,
228 VersionMinWatchos,
230 Note,
232 BuildVersion,
234 DyldExportsTrie,
236 DyldChainedFixups,
238 FilesetEntry,
240}
241
242impl From<u32> for LoadCommandType {
243 fn from(value: u32) -> Self {
244 match value {
245 0x1 => LoadCommandType::Segment,
246 0x19 => LoadCommandType::Segment64,
247 0x2 => LoadCommandType::Symtab,
248 0xb => LoadCommandType::Dysymtab,
249 0xc => LoadCommandType::LoadDylib,
250 0xd => LoadCommandType::IdDylib,
251 0xe => LoadCommandType::LoadDylinker,
252 0xf => LoadCommandType::IdDylinker,
253 0x10 => LoadCommandType::PreboundDylib,
254 0x4 => LoadCommandType::Thread,
255 0x5 => LoadCommandType::UnixThread,
256 0x18 => LoadCommandType::LoadWeakDylib,
257 0x1b => LoadCommandType::Uuid,
258 0x1d => LoadCommandType::CodeSignature,
259 0x1e => LoadCommandType::SegmentSplitInfo,
260 0x1f => LoadCommandType::ReexportDylib,
261 0x20 => LoadCommandType::LazyLoadDylib,
262 0x21 => LoadCommandType::EncryptionInfo,
263 0x22 => LoadCommandType::DylibCodeSignDrs,
264 0x24 => LoadCommandType::VersionMinMacosx,
265 0x25 => LoadCommandType::VersionMinIphoneos,
266 0x26 => LoadCommandType::FunctionStarts,
267 0x27 => LoadCommandType::DyldEnvironment,
268 0x28 => LoadCommandType::Main,
269 0x29 => LoadCommandType::DataInCode,
270 0x2a => LoadCommandType::SourceVersion,
271 0x2b => LoadCommandType::DylibCodeSignDrs2,
272 0x2c => LoadCommandType::EncryptionInfo64,
273 0x2d => LoadCommandType::LinkerOption,
274 0x2e => LoadCommandType::LinkerOptimizationHint,
275 0x2f => LoadCommandType::VersionMinTvos,
276 0x30 => LoadCommandType::VersionMinWatchos,
277 0x31 => LoadCommandType::Note,
278 0x32 => LoadCommandType::BuildVersion,
279 0x33 => LoadCommandType::DyldExportsTrie,
280 0x34 => LoadCommandType::DyldChainedFixups,
281 0x35 => LoadCommandType::FilesetEntry,
282 _ => LoadCommandType::Segment,
283 }
284 }
285}
286
287#[derive(Clone, Debug, Serialize, Deserialize)]
289pub struct LoadCommand {
290 pub cmd: u32,
292 pub cmdsize: u32,
294 pub data: Vec<u8>,
296}
297
298#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
300pub struct SegmentCommand64 {
301 pub cmd: u32,
303 pub cmdsize: u32,
305 pub segname: [u8; 16],
307 pub vmaddr: u64,
309 pub vmsize: u64,
311 pub fileoff: u64,
313 pub filesize: u64,
315 pub maxprot: u32,
317 pub initprot: u32,
319 pub nsects: u32,
321 pub flags: u32,
323}
324
325#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
327pub struct Section64 {
328 pub sectname: [u8; 16],
330 pub segname: [u8; 16],
332 pub addr: u64,
334 pub size: u64,
336 pub offset: u32,
338 pub align: u32,
340 pub reloff: u32,
342 pub nreloc: u32,
344 pub flags: u32,
346 pub reserved1: u32,
348 pub reserved2: u32,
350 pub reserved3: u32,
352}
353
354#[derive(Debug, Clone, Copy)]
356pub struct MachoReadConfig {
357 pub include_sections: bool,
359 pub parse_symbols: bool,
361 pub parse_dylibs: bool,
363}
364
365impl Default for MachoReadConfig {
366 fn default() -> Self {
367 Self { include_sections: true, parse_symbols: true, parse_dylibs: true }
368 }
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct MachoProgram {
374 pub header: MachoHeader,
376 pub load_commands: Vec<LoadCommand>,
378 pub segments: Vec<SegmentCommand64>,
380 pub sections: Vec<Section64>,
382}
383
384impl MachoProgram {
385 pub fn new(cpu_type: CpuType, file_type: MachoType) -> Self {
387 Self {
388 header: MachoHeader::new(cpu_type, file_type),
389 load_commands: Vec::new(),
390 segments: Vec::new(),
391 sections: Vec::new(),
392 }
393 }
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct MachoInfo {
399 pub target_arch: Architecture,
401 pub file_type: MachoType,
403 pub cpu_type: CpuType,
405 pub ncmds: u32,
407 pub nsegments: u32,
409 pub nsections: u32,
411 pub file_size: u64,
413}
414
415impl MachoHeader {
416 pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
418 let magic = reader.read_u32::<LittleEndian>()?;
419
420 let is_64bit = magic == 0xfeedfacf || magic == 0xcffaedfe;
421 let is_little_endian = magic == 0xfeedfacf || magic == 0xfeedface;
422
423 let cpu_type = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
424
425 let cpu_subtype = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
426
427 let file_type = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
428
429 let ncmds = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
430
431 let sizeofcmds = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
432
433 let flags = if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? };
434
435 let reserved = if is_64bit {
436 Some(if is_little_endian { reader.read_u32::<LittleEndian>()? } else { reader.read_u32::<BigEndian>()? })
437 }
438 else {
439 None
440 };
441
442 Ok(Self { magic, cpu_type, cpu_subtype, file_type, ncmds, sizeofcmds, flags, reserved })
443 }
444}