Skip to main content

macho_assembler/types/
mod.rs

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/// Mach-O 文件类型枚举
14///
15/// 定义了 Mach-O 文件的不同类型,决定了文件的用途和加载方式。
16#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
17pub enum MachoType {
18    /// 目标文件
19    Object,
20    /// 可执行文件
21    Execute,
22    /// 固定虚拟内存共享库
23    FvmLib,
24    /// 核心转储文件
25    Core,
26    /// 预加载的可执行文件
27    PreLoad,
28    /// 动态共享库
29    Dylib,
30    /// 动态链接器
31    Dylinker,
32    /// 动态加载的包
33    Bundle,
34    /// 动态共享库存根
35    DylibStub,
36    /// 配套的调试符号文件
37    Dsym,
38    /// x86_64 kext 包
39    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/// Mach-O CPU 类型枚举
80///
81/// 定义了 Mach-O 文件支持的处理器架构类型。
82#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
83pub enum CpuType {
84    /// x86_64 架构
85    X86_64,
86    /// ARM64 架构
87    Arm64,
88    /// ARM64e 架构(带指针认证)
89    Arm64e,
90    /// i386 架构
91    I386,
92    /// ARM 架构
93    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/// Mach-O 文件头结构
110///
111/// 包含了 Mach-O 文件的基本信息,如魔数、CPU 类型、文件类型等。
112#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
113pub struct MachoHeader {
114    /// 魔数,用于标识文件格式
115    pub magic: u32,
116    /// CPU 类型
117    pub cpu_type: u32,
118    /// CPU 子类型
119    pub cpu_subtype: u32,
120    /// 文件类型
121    pub file_type: u32,
122    /// 加载命令数量
123    pub ncmds: u32,
124    /// 加载命令总大小
125    pub sizeofcmds: u32,
126    /// 标志位
127    pub flags: u32,
128    /// 保留字段(仅在 64 位格式中存在)
129    pub reserved: Option<u32>,
130}
131
132impl MachoHeader {
133    /// 创建一个新的 Mach-O 文件头
134    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    /// 检查是否为 64 位格式
153    pub fn is_64bit(&self) -> bool {
154        self.magic == 0xfeedfacf || self.magic == 0xcffaedfe
155    }
156
157    /// 检查字节序
158    pub fn is_little_endian(&self) -> bool {
159        self.magic == 0xfeedfacf || self.magic == 0xfeedface
160    }
161}
162
163/// 加载命令类型枚举
164#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
165pub enum LoadCommandType {
166    /// 段加载命令
167    Segment,
168    /// 64位段加载命令
169    Segment64,
170    /// 符号表
171    Symtab,
172    /// 动态符号表
173    Dysymtab,
174    /// 加载动态库
175    LoadDylib,
176    /// ID 动态库
177    IdDylib,
178    /// 加载动态链接器
179    LoadDylinker,
180    /// ID 动态链接器
181    IdDylinker,
182    /// 预绑定动态库
183    PreboundDylib,
184    /// 线程
185    Thread,
186    /// Unix 线程
187    UnixThread,
188    /// 加载弱动态库
189    LoadWeakDylib,
190    /// UUID
191    Uuid,
192    /// 代码签名
193    CodeSignature,
194    /// 段分割信息
195    SegmentSplitInfo,
196    /// 重新导出动态库
197    ReexportDylib,
198    /// 延迟加载动态库
199    LazyLoadDylib,
200    /// 加密信息
201    EncryptionInfo,
202    /// 动态库代码签名目录
203    DylibCodeSignDrs,
204    /// 版本最小 macOS
205    VersionMinMacosx,
206    /// 版本最小 iOS
207    VersionMinIphoneos,
208    /// 函数开始
209    FunctionStarts,
210    /// 动态库环境
211    DyldEnvironment,
212    /// 主程序
213    Main,
214    /// 数据在代码中
215    DataInCode,
216    /// 源版本
217    SourceVersion,
218    /// 动态库代码签名目录
219    DylibCodeSignDrs2,
220    /// 加密信息 64
221    EncryptionInfo64,
222    /// 链接器选项
223    LinkerOption,
224    /// 链接器优化提示
225    LinkerOptimizationHint,
226    /// 版本最小 tvOS
227    VersionMinTvos,
228    /// 版本最小 watchOS
229    VersionMinWatchos,
230    /// 注释
231    Note,
232    /// 构建版本
233    BuildVersion,
234    /// 动态库导出 trie
235    DyldExportsTrie,
236    /// 动态库链式修复
237    DyldChainedFixups,
238    /// 文件集入口
239    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/// 加载命令基础结构
288#[derive(Clone, Debug, Serialize, Deserialize)]
289pub struct LoadCommand {
290    /// 命令类型
291    pub cmd: u32,
292    /// 命令大小
293    pub cmdsize: u32,
294    /// 命令数据
295    pub data: Vec<u8>,
296}
297
298/// 段命令结构(64位)
299#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
300pub struct SegmentCommand64 {
301    /// 命令类型
302    pub cmd: u32,
303    /// 命令大小
304    pub cmdsize: u32,
305    /// 段名称
306    pub segname: [u8; 16],
307    /// 虚拟内存地址
308    pub vmaddr: u64,
309    /// 虚拟内存大小
310    pub vmsize: u64,
311    /// 文件偏移
312    pub fileoff: u64,
313    /// 文件大小
314    pub filesize: u64,
315    /// 最大虚拟内存保护
316    pub maxprot: u32,
317    /// 初始虚拟内存保护
318    pub initprot: u32,
319    /// 节数量
320    pub nsects: u32,
321    /// 标志
322    pub flags: u32,
323}
324
325/// 节结构(64位)
326#[derive(Clone, Debug, Serialize, Deserialize, Copy)]
327pub struct Section64 {
328    /// 节名称
329    pub sectname: [u8; 16],
330    /// 段名称
331    pub segname: [u8; 16],
332    /// 虚拟内存地址
333    pub addr: u64,
334    /// 大小
335    pub size: u64,
336    /// 文件偏移
337    pub offset: u32,
338    /// 对齐
339    pub align: u32,
340    /// 重定位偏移
341    pub reloff: u32,
342    /// 重定位数量
343    pub nreloc: u32,
344    /// 标志
345    pub flags: u32,
346    /// 保留字段1
347    pub reserved1: u32,
348    /// 保留字段2
349    pub reserved2: u32,
350    /// 保留字段3
351    pub reserved3: u32,
352}
353
354/// Mach-O 读取配置
355#[derive(Debug, Clone, Copy)]
356pub struct MachoReadConfig {
357    /// 是否包含节数据
358    pub include_sections: bool,
359    /// 是否解析符号表
360    pub parse_symbols: bool,
361    /// 是否解析动态库
362    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/// Mach-O 程序结构
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct MachoProgram {
374    /// 文件头
375    pub header: MachoHeader,
376    /// 加载命令列表
377    pub load_commands: Vec<LoadCommand>,
378    /// 段列表
379    pub segments: Vec<SegmentCommand64>,
380    /// 节列表
381    pub sections: Vec<Section64>,
382}
383
384impl MachoProgram {
385    /// 创建一个新的 Mach-O 程序
386    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/// Mach-O 文件信息
397#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct MachoInfo {
399    /// 目标架构
400    pub target_arch: Architecture,
401    /// 文件类型
402    pub file_type: MachoType,
403    /// CPU 类型
404    pub cpu_type: CpuType,
405    /// 加载命令数量
406    pub ncmds: u32,
407    /// 段数量
408    pub nsegments: u32,
409    /// 节数量
410    pub nsections: u32,
411    /// 文件大小
412    pub file_size: u64,
413}
414
415impl MachoHeader {
416    /// 从字节流读取 Mach-O 文件头
417    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}