0
# Mach-O Format
1
2
Native support for Mach-O format used by macOS and iOS executables, frameworks, and universal binaries. Mach-O provides comprehensive Apple platform integration with support for fat binaries, dylib linking, code signing, and Objective-C metadata.
3
4
## Capabilities
5
6
### Mach-O Parsing
7
8
Parse Mach-O files including universal binaries with architecture-specific extraction.
9
10
```python { .api }
11
def parse(filename: str, config: ParserConfig = None) -> Optional[FatBinary]:
12
"""Parse Mach-O file, returns FatBinary even for single architecture."""
13
14
def parse(raw: Sequence[int], config: ParserConfig = None) -> Optional[FatBinary]:
15
"""Parse Mach-O from raw bytes."""
16
17
def parse(obj: Union[io.IOBase, os.PathLike], config: ParserConfig = None) -> Optional[FatBinary]:
18
"""Parse Mach-O from file-like object."""
19
20
def parse_from_memory(address: int, config: ParserConfig = None) -> Optional[FatBinary]:
21
"""Parse Mach-O directly from memory address."""
22
23
def is_fat(file: str) -> bool:
24
"""Check if file is universal binary (fat binary)."""
25
26
def is_64(file: str) -> bool:
27
"""Check if Mach-O is 64-bit architecture."""
28
29
class ParserConfig:
30
parse_dyld_exports: bool
31
parse_dyld_bindings: bool
32
parse_dyld_rebases: bool
33
fix_from_memory: bool
34
full_dyldinfo: bool
35
```
36
37
Usage example:
38
```python
39
import lief.MachO as MachO
40
41
# Parse universal binary
42
fat_binary = MachO.parse("/usr/bin/file")
43
44
if MachO.is_fat("/usr/bin/file"):
45
print("Universal binary detected")
46
47
# Check architecture
48
if MachO.is_64("/usr/bin/file"):
49
print("64-bit binary")
50
51
# Parse with configuration
52
config = MachO.ParserConfig()
53
config.parse_dyld_exports = True
54
config.parse_dyld_bindings = True
55
fat_binary = MachO.parse("/System/Library/Frameworks/Foundation.framework/Foundation", config)
56
```
57
58
### Universal Binary Management
59
60
Handle universal binaries (fat binaries) containing multiple architectures.
61
62
```python { .api }
63
class FatBinary:
64
binaries: Iterator[Binary]
65
66
def size(self) -> int
67
def take(self, arch: Header.CPU_TYPE) -> Optional[Binary]
68
def add(self, binary: Binary) -> None
69
def write(self, output: str) -> None
70
def at(self, index: int) -> Binary
71
72
def check_layout(binary: Union[Binary, FatBinary]) -> Tuple[bool, str]:
73
"""Validate Mach-O binary layout and structure."""
74
```
75
76
Usage example:
77
```python
78
fat_binary = MachO.parse("/usr/bin/lipo")
79
80
print(f"Number of architectures: {fat_binary.size()}")
81
82
# List all architectures
83
for binary in fat_binary.binaries:
84
print(f"Architecture: {binary.header.cpu_type}")
85
print(f"Subtype: {binary.header.cpu_subtype}")
86
87
# Extract specific architecture
88
x86_64_binary = fat_binary.take(MachO.Header.CPU_TYPE.X86_64)
89
if x86_64_binary:
90
print("Found x86_64 architecture")
91
92
# Validate binary layout
93
is_valid, message = MachO.check_layout(fat_binary)
94
print(f"Layout valid: {is_valid} - {message}")
95
```
96
97
### Mach-O Binary Operations
98
99
Mach-O-specific binary manipulation with Apple platform features.
100
101
```python { .api }
102
class Binary(lief.Binary):
103
header: Header
104
sections: Iterator[Section]
105
segments: Iterator[Segment]
106
commands: Iterator[LoadCommand]
107
symbols: Iterator[Symbol]
108
relocations: Iterator[Relocation]
109
dyld_info: Optional[DyldInfo]
110
uuid: Optional[UUID]
111
main_command: Optional[Main]
112
dylinker: Optional[DyLinker]
113
function_starts: Optional[FunctionStarts]
114
source_version: Optional[SourceVersion]
115
version_min: Optional[VersionMin]
116
thread_command: Optional[ThreadCommand]
117
rpath: Iterator[RPathCommand]
118
symbol_command: Optional[SymbolCommand]
119
dynamic_symbol_command: Optional[DynamicSymbolCommand]
120
data_in_code: Optional[DataInCode]
121
segment_split_info: Optional[SegmentSplitInfo]
122
sub_framework: Optional[SubFramework]
123
encryption_info: Optional[EncryptionInfo]
124
build_version: Optional[BuildVersion]
125
dyld_chained_fixups: Optional[DyldChainedFixups]
126
dyld_exports_trie: Optional[DyldExportsTrie]
127
two_level_hints: Optional[TwoLevelHints]
128
linker_opt_hint: Optional[LinkerOptHint]
129
130
def add_section(self, section: Section) -> Section
131
def remove_section(self, name: str, clear: bool = False) -> None
132
def add_segment(self, segment: Segment) -> Segment
133
def remove_signature(self) -> None
134
def add_library(self, library: str) -> DylibCommand
135
def remove_library(self, library: str) -> None
136
def get_segment(self, name: str) -> Optional[Segment]
137
def get_section(self, segment_name: str, section_name: str) -> Optional[Section]
138
def has_section(self, section_name: str) -> bool
139
def has_segment(self, segment_name: str) -> bool
140
def can_remove(self, command: LoadCommand) -> bool
141
def can_remove_symbol(self, symbol: Symbol) -> bool
142
def unexport(self, name: str) -> None
143
def remove_command(self, index: int) -> None
144
def extend_segment(self, segment: Segment, size: int) -> Optional[Segment]
145
def add_exported_function(self, address: int, name: str) -> Symbol
146
def add_local_symbol(self, address: int, name: str) -> Symbol
147
```
148
149
Usage example:
150
```python
151
# Extract single architecture for manipulation
152
fat_binary = MachO.parse("/usr/lib/libSystem.dylib")
153
binary = fat_binary.at(0) # Get first architecture
154
155
# Add new library dependency
156
dylib = binary.add_library("libcustom.dylib")
157
print(f"Added library: {dylib.name}")
158
159
# Add new section
160
section = MachO.Section()
161
section.name = "__custom"
162
section.segment_name = "__DATA"
163
section.content = b"Custom data"
164
binary.add_section(section)
165
166
# Symbol manipulation
167
symbol = binary.add_exported_function(0x1000, "_custom_function")
168
print(f"Added symbol: {symbol.name}")
169
170
# Remove code signature (for modification)
171
binary.remove_signature()
172
```
173
174
### Load Commands
175
176
Analyze and manipulate Mach-O load commands for dynamic linking and loading.
177
178
```python { .api }
179
class LoadCommand(lief.Object):
180
command: LOAD_COMMAND_TYPES
181
size: int
182
command_offset: int
183
184
class DylibCommand(LoadCommand):
185
name: str
186
timestamp: int
187
current_version: List[int]
188
compatibility_version: List[int]
189
190
class Main(LoadCommand):
191
entrypoint: int
192
stack_size: int
193
194
class UUID(LoadCommand):
195
uuid: List[int]
196
197
class VersionMin(LoadCommand):
198
version: List[int] # [major, minor, patch]
199
sdk: List[int] # [major, minor, patch]
200
201
class SourceVersion(LoadCommand):
202
version: List[int] # [a, b, c, d, e]
203
204
class BuildVersion(LoadCommand):
205
platform: PLATFORMS
206
minos: List[int] # [major, minor, patch]
207
sdk: List[int] # [major, minor, patch]
208
tools: Iterator[BuildToolVersion]
209
210
class RPathCommand(LoadCommand):
211
path: str
212
213
class DyLinker(LoadCommand):
214
name: str
215
216
enum LOAD_COMMAND_TYPES:
217
LC_SEGMENT = 0x1
218
LC_SYMTAB = 0x2
219
LC_SYMSEG = 0x3
220
LC_THREAD = 0x4
221
LC_UNIXTHREAD = 0x5
222
LC_LOADFVMLIB = 0x6
223
LC_IDFVMLIB = 0x7
224
LC_IDENT = 0x8
225
LC_FVMFILE = 0x9
226
LC_PREPAGE = 0xa
227
LC_DYSYMTAB = 0xb
228
LC_LOAD_DYLIB = 0xc
229
LC_ID_DYLIB = 0xd
230
LC_LOAD_DYLINKER = 0xe
231
LC_ID_DYLINKER = 0xf
232
LC_PREBOUND_DYLIB = 0x10
233
LC_ROUTINES = 0x11
234
LC_SUB_FRAMEWORK = 0x12
235
LC_SUB_UMBRELLA = 0x13
236
LC_SUB_CLIENT = 0x14
237
LC_SUB_LIBRARY = 0x15
238
LC_TWOLEVEL_HINTS = 0x16
239
LC_PREBIND_CKSUM = 0x17
240
LC_LOAD_WEAK_DYLIB = 0x80000018
241
LC_SEGMENT_64 = 0x19
242
LC_ROUTINES_64 = 0x1a
243
LC_UUID = 0x1b
244
LC_RPATH = 0x8000001c
245
LC_CODE_SIGNATURE = 0x1d
246
LC_SEGMENT_SPLIT_INFO = 0x1e
247
LC_REEXPORT_DYLIB = 0x8000001f
248
LC_LAZY_LOAD_DYLIB = 0x20
249
LC_ENCRYPTION_INFO = 0x21
250
LC_DYLD_INFO = 0x22
251
LC_DYLD_INFO_ONLY = 0x80000022
252
LC_LOAD_UPWARD_DYLIB = 0x80000023
253
LC_VERSION_MIN_MACOSX = 0x24
254
LC_VERSION_MIN_IPHONEOS = 0x25
255
LC_FUNCTION_STARTS = 0x26
256
LC_DYLD_ENVIRONMENT = 0x27
257
LC_MAIN = 0x80000028
258
LC_DATA_IN_CODE = 0x29
259
LC_SOURCE_VERSION = 0x2A
260
LC_DYLIB_CODE_SIGN_DRS = 0x2B
261
LC_ENCRYPTION_INFO_64 = 0x2C
262
LC_LINKER_OPTION = 0x2D
263
LC_LINKER_OPTIMIZATION_HINT = 0x2E
264
LC_VERSION_MIN_TVOS = 0x2F
265
LC_VERSION_MIN_WATCHOS = 0x30
266
LC_NOTE = 0x31
267
LC_BUILD_VERSION = 0x32
268
LC_DYLD_EXPORTS_TRIE = 0x80000033
269
LC_DYLD_CHAINED_FIXUPS = 0x80000034
270
```
271
272
Usage example:
273
```python
274
binary = fat_binary.at(0)
275
276
print("Load commands:")
277
for command in binary.commands:
278
print(f" {command.command}: size {command.size}")
279
280
# Analyze specific command types
281
if isinstance(command, MachO.DylibCommand):
282
print(f" Library: {command.name}")
283
print(f" Version: {'.'.join(map(str, command.current_version))}")
284
285
elif isinstance(command, MachO.UUID):
286
uuid_str = ''.join(f'{b:02x}' for b in command.uuid)
287
print(f" UUID: {uuid_str}")
288
289
elif isinstance(command, MachO.BuildVersion):
290
print(f" Platform: {command.platform}")
291
print(f" Min OS: {'.'.join(map(str, command.minos))}")
292
print(f" SDK: {'.'.join(map(str, command.sdk))}")
293
```
294
295
### Segment and Section Management
296
297
Manage Mach-O segments and sections with Apple-specific attributes.
298
299
```python { .api }
300
class Segment(lief.Object):
301
name: str
302
virtual_address: int
303
virtual_size: int
304
file_size: int
305
file_offset: int
306
max_protection: int
307
init_protection: int
308
number_of_sections: int
309
flags: int
310
sections: Iterator[Section]
311
312
def add_section(self, section: Section) -> Section
313
def remove_section(self, name: str) -> None
314
315
class Section(lief.Section):
316
segment_name: str
317
address: int
318
alignment: int
319
relocation_offset: int
320
number_of_relocations: int
321
flags: int
322
type: MACHO_SECTION_TYPES
323
reserved1: int
324
reserved2: int
325
reserved3: int
326
segment: Segment
327
relocations: Iterator[Relocation]
328
329
def has_segment(self) -> bool
330
331
enum MACHO_SECTION_TYPES:
332
S_REGULAR = 0x0
333
S_ZEROFILL = 0x1
334
S_CSTRING_LITERALS = 0x2
335
S_4BYTE_LITERALS = 0x3
336
S_8BYTE_LITERALS = 0x4
337
S_LITERAL_POINTERS = 0x5
338
S_NON_LAZY_SYMBOL_POINTERS = 0x6
339
S_LAZY_SYMBOL_POINTERS = 0x7
340
S_SYMBOL_STUBS = 0x8
341
S_MOD_INIT_FUNC_POINTERS = 0x9
342
S_MOD_TERM_FUNC_POINTERS = 0xa
343
S_COALESCED = 0xb
344
S_GB_ZEROFILL = 0xc
345
S_INTERPOSING = 0xd
346
S_16BYTE_LITERALS = 0xe
347
S_DTRACE_DOF = 0xf
348
S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10
349
S_THREAD_LOCAL_REGULAR = 0x11
350
S_THREAD_LOCAL_ZEROFILL = 0x12
351
S_THREAD_LOCAL_VARIABLES = 0x13
352
S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14
353
S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15
354
```
355
356
Usage example:
357
```python
358
binary = fat_binary.at(0)
359
360
# Analyze segments
361
for segment in binary.segments:
362
print(f"Segment: {segment.name}")
363
print(f" Address: 0x{segment.virtual_address:x}")
364
print(f" Size: {segment.virtual_size}")
365
print(f" Sections: {segment.number_of_sections}")
366
367
# Analyze sections in segment
368
for section in segment.sections:
369
print(f" Section: {section.name}")
370
print(f" Type: {section.type}")
371
print(f" Address: 0x{section.address:x}")
372
print(f" Size: {section.size}")
373
374
# Find specific segment/section
375
text_segment = binary.get_segment("__TEXT")
376
if text_segment:
377
text_section = binary.get_section("__TEXT", "__text")
378
if text_section:
379
print(f"__text section at 0x{text_section.address:x}")
380
```
381
382
### Dynamic Linking Information
383
384
Analyze dyld (dynamic linker) information for runtime binding and symbol resolution.
385
386
```python { .api }
387
class DyldInfo(LoadCommand):
388
rebase: memoryview
389
bind: memoryview
390
weak_bind: memoryview
391
lazy_bind: memoryview
392
export_info: memoryview
393
394
def show_rebases(self) -> Iterator[DyldBindingInfo]
395
def show_bindings(self) -> Iterator[DyldBindingInfo]
396
def show_lazy_bindings(self) -> Iterator[DyldBindingInfo]
397
def show_weak_bindings(self) -> Iterator[DyldBindingInfo]
398
def show_exports(self) -> Iterator[ExportInfo]
399
400
class DyldBindingInfo(lief.Object):
401
address: int
402
library_ordinal: int
403
symbol_name: str
404
symbol_flags: int
405
original_offset: int
406
407
def has_segment(self) -> bool
408
def has_library(self) -> bool
409
410
class ExportInfo(lief.Object):
411
node_offset: int
412
flags: int
413
address: int
414
other: int
415
symbol_name: str
416
417
class DyldChainedFixups(LoadCommand):
418
fixups_version: int
419
starts_offset: int
420
imports_offset: int
421
symbols_offset: int
422
imports_count: int
423
imports_format: DYLD_CHAINED_FORMAT
424
symbols_format: int
425
426
def bindings(self) -> Iterator[ChainedBindingInfo]
427
428
class DyldExportsTrie(LoadCommand):
429
data_offset: int
430
data_size: int
431
432
def exports(self) -> Iterator[ExportInfo]
433
```
434
435
Usage example:
436
```python
437
binary = fat_binary.at(0)
438
439
# Analyze dyld information
440
if binary.dyld_info:
441
print("Dyld rebases:")
442
for rebase in binary.dyld_info.show_rebases():
443
print(f" 0x{rebase.address:x}: {rebase.symbol_name}")
444
445
print("Dyld bindings:")
446
for binding in binary.dyld_info.show_bindings():
447
print(f" 0x{binding.address:x}: {binding.symbol_name} (lib {binding.library_ordinal})")
448
449
print("Dyld exports:")
450
for export in binary.dyld_info.show_exports():
451
print(f" {export.symbol_name} @ 0x{export.address:x}")
452
453
# Check modern chained fixups
454
if binary.dyld_chained_fixups:
455
print("Chained fixups:")
456
for binding in binary.dyld_chained_fixups.bindings():
457
print(f" 0x{binding.address:x}: {binding.symbol_name}")
458
```
459
460
### Code Signing
461
462
Analyze Mach-O code signatures and entitlements for security validation.
463
464
```python { .api }
465
class CodeSignature(LoadCommand):
466
data_offset: int
467
data_size: int
468
469
def content(self) -> memoryview
470
471
class LinkerOptHint(LoadCommand):
472
data_offset: int
473
data_size: int
474
475
class EncryptionInfo(LoadCommand):
476
crypt_offset: int
477
crypt_size: int
478
crypt_id: int
479
480
class EncryptionInfo64(EncryptionInfo):
481
pad: int
482
```
483
484
Usage example:
485
```python
486
binary = fat_binary.at(0)
487
488
# Find code signature
489
for command in binary.commands:
490
if command.command == MachO.LOAD_COMMAND_TYPES.LC_CODE_SIGNATURE:
491
code_sig = MachO.CodeSignature(command)
492
print(f"Code signature at offset 0x{code_sig.data_offset:x}")
493
print(f"Signature size: {code_sig.data_size} bytes")
494
495
# Raw signature data analysis would require additional parsing
496
sig_data = code_sig.content()
497
print(f"Signature data: {len(sig_data)} bytes")
498
499
# Check for encryption
500
if binary.encryption_info:
501
if binary.encryption_info.crypt_id != 0:
502
print("Binary is encrypted")
503
print(f"Encrypted range: 0x{binary.encryption_info.crypt_offset:x} - {binary.encryption_info.crypt_size}")
504
else:
505
print("Binary is not encrypted")
506
```
507
508
## Types
509
510
```python { .api }
511
class Header(lief.Object):
512
magic: MACHO_TYPES
513
cpu_type: CPU_TYPE
514
cpu_subtype: int
515
file_type: FILE_TYPE
516
number_of_commands: int
517
size_of_commands: int
518
flags: int
519
reserved: int
520
521
enum MACHO_TYPES:
522
MH_MAGIC = 0xfeedface
523
MH_CIGAM = 0xcefaedfe
524
MH_MAGIC_64 = 0xfeedfacf
525
MH_CIGAM_64 = 0xcffaedfe
526
FAT_MAGIC = 0xcafebabe
527
FAT_CIGAM = 0xbebafeca
528
529
enum CPU_TYPE:
530
ANY = -1
531
VAX = 1
532
MC680x0 = 6
533
X86 = 7
534
I386 = 7 # Compatibility
535
X86_64 = 0x01000007
536
MIPS = 8
537
MC98000 = 10
538
HPPA = 11
539
ARM = 12
540
ARM64 = 0x0100000c
541
MC88000 = 13
542
SPARC = 14
543
I860 = 15
544
ALPHA = 16
545
POWERPC = 18
546
POWERPC64 = 0x01000012
547
548
enum FILE_TYPE:
549
MH_OBJECT = 0x1
550
MH_EXECUTE = 0x2
551
MH_FVMLIB = 0x3
552
MH_CORE = 0x4
553
MH_PRELOAD = 0x5
554
MH_DYLIB = 0x6
555
MH_DYLINKER = 0x7
556
MH_BUNDLE = 0x8
557
MH_DYLIB_STUB = 0x9
558
MH_COMPANION = 0xa
559
MH_KEXT_BUNDLE = 0xb
560
561
class Symbol(lief.Symbol):
562
type: int
563
number_of_sections: int
564
description: int
565
566
def has_export_info(self) -> bool
567
def has_binding_info(self) -> bool
568
569
class FunctionStarts(LoadCommand):
570
data_offset: int
571
data_size: int
572
functions: List[int]
573
574
class DataInCode(LoadCommand):
575
data_offset: int
576
data_size: int
577
entries: Iterator[DataInCodeEntry]
578
579
class DataInCodeEntry(lief.Object):
580
offset: int
581
length: int
582
kind: DATA_IN_CODE_TYPES
583
584
enum DATA_IN_CODE_TYPES:
585
DICE_KIND_DATA = 0x0001
586
DICE_KIND_JUMP_TABLE8 = 0x0002
587
DICE_KIND_JUMP_TABLE16 = 0x0003
588
DICE_KIND_JUMP_TABLE32 = 0x0004
589
DICE_KIND_ABS_JUMP_TABLE32 = 0x0005
590
```