Library to instrument executable formats including ELF, PE, Mach-O, and Android formats
—
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.
Parse Mach-O files including universal binaries with architecture-specific extraction.
def parse(filename: str, config: ParserConfig = None) -> Optional[FatBinary]:
"""Parse Mach-O file, returns FatBinary even for single architecture."""
def parse(raw: Sequence[int], config: ParserConfig = None) -> Optional[FatBinary]:
"""Parse Mach-O from raw bytes."""
def parse(obj: Union[io.IOBase, os.PathLike], config: ParserConfig = None) -> Optional[FatBinary]:
"""Parse Mach-O from file-like object."""
def parse_from_memory(address: int, config: ParserConfig = None) -> Optional[FatBinary]:
"""Parse Mach-O directly from memory address."""
def is_fat(file: str) -> bool:
"""Check if file is universal binary (fat binary)."""
def is_64(file: str) -> bool:
"""Check if Mach-O is 64-bit architecture."""
class ParserConfig:
parse_dyld_exports: bool
parse_dyld_bindings: bool
parse_dyld_rebases: bool
fix_from_memory: bool
full_dyldinfo: boolUsage example:
import lief.MachO as MachO
# Parse universal binary
fat_binary = MachO.parse("/usr/bin/file")
if MachO.is_fat("/usr/bin/file"):
print("Universal binary detected")
# Check architecture
if MachO.is_64("/usr/bin/file"):
print("64-bit binary")
# Parse with configuration
config = MachO.ParserConfig()
config.parse_dyld_exports = True
config.parse_dyld_bindings = True
fat_binary = MachO.parse("/System/Library/Frameworks/Foundation.framework/Foundation", config)Handle universal binaries (fat binaries) containing multiple architectures.
class FatBinary:
binaries: Iterator[Binary]
def size(self) -> int
def take(self, arch: Header.CPU_TYPE) -> Optional[Binary]
def add(self, binary: Binary) -> None
def write(self, output: str) -> None
def at(self, index: int) -> Binary
def check_layout(binary: Union[Binary, FatBinary]) -> Tuple[bool, str]:
"""Validate Mach-O binary layout and structure."""Usage example:
fat_binary = MachO.parse("/usr/bin/lipo")
print(f"Number of architectures: {fat_binary.size()}")
# List all architectures
for binary in fat_binary.binaries:
print(f"Architecture: {binary.header.cpu_type}")
print(f"Subtype: {binary.header.cpu_subtype}")
# Extract specific architecture
x86_64_binary = fat_binary.take(MachO.Header.CPU_TYPE.X86_64)
if x86_64_binary:
print("Found x86_64 architecture")
# Validate binary layout
is_valid, message = MachO.check_layout(fat_binary)
print(f"Layout valid: {is_valid} - {message}")Mach-O-specific binary manipulation with Apple platform features.
class Binary(lief.Binary):
header: Header
sections: Iterator[Section]
segments: Iterator[Segment]
commands: Iterator[LoadCommand]
symbols: Iterator[Symbol]
relocations: Iterator[Relocation]
dyld_info: Optional[DyldInfo]
uuid: Optional[UUID]
main_command: Optional[Main]
dylinker: Optional[DyLinker]
function_starts: Optional[FunctionStarts]
source_version: Optional[SourceVersion]
version_min: Optional[VersionMin]
thread_command: Optional[ThreadCommand]
rpath: Iterator[RPathCommand]
symbol_command: Optional[SymbolCommand]
dynamic_symbol_command: Optional[DynamicSymbolCommand]
data_in_code: Optional[DataInCode]
segment_split_info: Optional[SegmentSplitInfo]
sub_framework: Optional[SubFramework]
encryption_info: Optional[EncryptionInfo]
build_version: Optional[BuildVersion]
dyld_chained_fixups: Optional[DyldChainedFixups]
dyld_exports_trie: Optional[DyldExportsTrie]
two_level_hints: Optional[TwoLevelHints]
linker_opt_hint: Optional[LinkerOptHint]
def add_section(self, section: Section) -> Section
def remove_section(self, name: str, clear: bool = False) -> None
def add_segment(self, segment: Segment) -> Segment
def remove_signature(self) -> None
def add_library(self, library: str) -> DylibCommand
def remove_library(self, library: str) -> None
def get_segment(self, name: str) -> Optional[Segment]
def get_section(self, segment_name: str, section_name: str) -> Optional[Section]
def has_section(self, section_name: str) -> bool
def has_segment(self, segment_name: str) -> bool
def can_remove(self, command: LoadCommand) -> bool
def can_remove_symbol(self, symbol: Symbol) -> bool
def unexport(self, name: str) -> None
def remove_command(self, index: int) -> None
def extend_segment(self, segment: Segment, size: int) -> Optional[Segment]
def add_exported_function(self, address: int, name: str) -> Symbol
def add_local_symbol(self, address: int, name: str) -> SymbolUsage example:
# Extract single architecture for manipulation
fat_binary = MachO.parse("/usr/lib/libSystem.dylib")
binary = fat_binary.at(0) # Get first architecture
# Add new library dependency
dylib = binary.add_library("libcustom.dylib")
print(f"Added library: {dylib.name}")
# Add new section
section = MachO.Section()
section.name = "__custom"
section.segment_name = "__DATA"
section.content = b"Custom data"
binary.add_section(section)
# Symbol manipulation
symbol = binary.add_exported_function(0x1000, "_custom_function")
print(f"Added symbol: {symbol.name}")
# Remove code signature (for modification)
binary.remove_signature()Analyze and manipulate Mach-O load commands for dynamic linking and loading.
class LoadCommand(lief.Object):
command: LOAD_COMMAND_TYPES
size: int
command_offset: int
class DylibCommand(LoadCommand):
name: str
timestamp: int
current_version: List[int]
compatibility_version: List[int]
class Main(LoadCommand):
entrypoint: int
stack_size: int
class UUID(LoadCommand):
uuid: List[int]
class VersionMin(LoadCommand):
version: List[int] # [major, minor, patch]
sdk: List[int] # [major, minor, patch]
class SourceVersion(LoadCommand):
version: List[int] # [a, b, c, d, e]
class BuildVersion(LoadCommand):
platform: PLATFORMS
minos: List[int] # [major, minor, patch]
sdk: List[int] # [major, minor, patch]
tools: Iterator[BuildToolVersion]
class RPathCommand(LoadCommand):
path: str
class DyLinker(LoadCommand):
name: str
enum LOAD_COMMAND_TYPES:
LC_SEGMENT = 0x1
LC_SYMTAB = 0x2
LC_SYMSEG = 0x3
LC_THREAD = 0x4
LC_UNIXTHREAD = 0x5
LC_LOADFVMLIB = 0x6
LC_IDFVMLIB = 0x7
LC_IDENT = 0x8
LC_FVMFILE = 0x9
LC_PREPAGE = 0xa
LC_DYSYMTAB = 0xb
LC_LOAD_DYLIB = 0xc
LC_ID_DYLIB = 0xd
LC_LOAD_DYLINKER = 0xe
LC_ID_DYLINKER = 0xf
LC_PREBOUND_DYLIB = 0x10
LC_ROUTINES = 0x11
LC_SUB_FRAMEWORK = 0x12
LC_SUB_UMBRELLA = 0x13
LC_SUB_CLIENT = 0x14
LC_SUB_LIBRARY = 0x15
LC_TWOLEVEL_HINTS = 0x16
LC_PREBIND_CKSUM = 0x17
LC_LOAD_WEAK_DYLIB = 0x80000018
LC_SEGMENT_64 = 0x19
LC_ROUTINES_64 = 0x1a
LC_UUID = 0x1b
LC_RPATH = 0x8000001c
LC_CODE_SIGNATURE = 0x1d
LC_SEGMENT_SPLIT_INFO = 0x1e
LC_REEXPORT_DYLIB = 0x8000001f
LC_LAZY_LOAD_DYLIB = 0x20
LC_ENCRYPTION_INFO = 0x21
LC_DYLD_INFO = 0x22
LC_DYLD_INFO_ONLY = 0x80000022
LC_LOAD_UPWARD_DYLIB = 0x80000023
LC_VERSION_MIN_MACOSX = 0x24
LC_VERSION_MIN_IPHONEOS = 0x25
LC_FUNCTION_STARTS = 0x26
LC_DYLD_ENVIRONMENT = 0x27
LC_MAIN = 0x80000028
LC_DATA_IN_CODE = 0x29
LC_SOURCE_VERSION = 0x2A
LC_DYLIB_CODE_SIGN_DRS = 0x2B
LC_ENCRYPTION_INFO_64 = 0x2C
LC_LINKER_OPTION = 0x2D
LC_LINKER_OPTIMIZATION_HINT = 0x2E
LC_VERSION_MIN_TVOS = 0x2F
LC_VERSION_MIN_WATCHOS = 0x30
LC_NOTE = 0x31
LC_BUILD_VERSION = 0x32
LC_DYLD_EXPORTS_TRIE = 0x80000033
LC_DYLD_CHAINED_FIXUPS = 0x80000034Usage example:
binary = fat_binary.at(0)
print("Load commands:")
for command in binary.commands:
print(f" {command.command}: size {command.size}")
# Analyze specific command types
if isinstance(command, MachO.DylibCommand):
print(f" Library: {command.name}")
print(f" Version: {'.'.join(map(str, command.current_version))}")
elif isinstance(command, MachO.UUID):
uuid_str = ''.join(f'{b:02x}' for b in command.uuid)
print(f" UUID: {uuid_str}")
elif isinstance(command, MachO.BuildVersion):
print(f" Platform: {command.platform}")
print(f" Min OS: {'.'.join(map(str, command.minos))}")
print(f" SDK: {'.'.join(map(str, command.sdk))}")Manage Mach-O segments and sections with Apple-specific attributes.
class Segment(lief.Object):
name: str
virtual_address: int
virtual_size: int
file_size: int
file_offset: int
max_protection: int
init_protection: int
number_of_sections: int
flags: int
sections: Iterator[Section]
def add_section(self, section: Section) -> Section
def remove_section(self, name: str) -> None
class Section(lief.Section):
segment_name: str
address: int
alignment: int
relocation_offset: int
number_of_relocations: int
flags: int
type: MACHO_SECTION_TYPES
reserved1: int
reserved2: int
reserved3: int
segment: Segment
relocations: Iterator[Relocation]
def has_segment(self) -> bool
enum MACHO_SECTION_TYPES:
S_REGULAR = 0x0
S_ZEROFILL = 0x1
S_CSTRING_LITERALS = 0x2
S_4BYTE_LITERALS = 0x3
S_8BYTE_LITERALS = 0x4
S_LITERAL_POINTERS = 0x5
S_NON_LAZY_SYMBOL_POINTERS = 0x6
S_LAZY_SYMBOL_POINTERS = 0x7
S_SYMBOL_STUBS = 0x8
S_MOD_INIT_FUNC_POINTERS = 0x9
S_MOD_TERM_FUNC_POINTERS = 0xa
S_COALESCED = 0xb
S_GB_ZEROFILL = 0xc
S_INTERPOSING = 0xd
S_16BYTE_LITERALS = 0xe
S_DTRACE_DOF = 0xf
S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10
S_THREAD_LOCAL_REGULAR = 0x11
S_THREAD_LOCAL_ZEROFILL = 0x12
S_THREAD_LOCAL_VARIABLES = 0x13
S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14
S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15Usage example:
binary = fat_binary.at(0)
# Analyze segments
for segment in binary.segments:
print(f"Segment: {segment.name}")
print(f" Address: 0x{segment.virtual_address:x}")
print(f" Size: {segment.virtual_size}")
print(f" Sections: {segment.number_of_sections}")
# Analyze sections in segment
for section in segment.sections:
print(f" Section: {section.name}")
print(f" Type: {section.type}")
print(f" Address: 0x{section.address:x}")
print(f" Size: {section.size}")
# Find specific segment/section
text_segment = binary.get_segment("__TEXT")
if text_segment:
text_section = binary.get_section("__TEXT", "__text")
if text_section:
print(f"__text section at 0x{text_section.address:x}")Analyze dyld (dynamic linker) information for runtime binding and symbol resolution.
class DyldInfo(LoadCommand):
rebase: memoryview
bind: memoryview
weak_bind: memoryview
lazy_bind: memoryview
export_info: memoryview
def show_rebases(self) -> Iterator[DyldBindingInfo]
def show_bindings(self) -> Iterator[DyldBindingInfo]
def show_lazy_bindings(self) -> Iterator[DyldBindingInfo]
def show_weak_bindings(self) -> Iterator[DyldBindingInfo]
def show_exports(self) -> Iterator[ExportInfo]
class DyldBindingInfo(lief.Object):
address: int
library_ordinal: int
symbol_name: str
symbol_flags: int
original_offset: int
def has_segment(self) -> bool
def has_library(self) -> bool
class ExportInfo(lief.Object):
node_offset: int
flags: int
address: int
other: int
symbol_name: str
class DyldChainedFixups(LoadCommand):
fixups_version: int
starts_offset: int
imports_offset: int
symbols_offset: int
imports_count: int
imports_format: DYLD_CHAINED_FORMAT
symbols_format: int
def bindings(self) -> Iterator[ChainedBindingInfo]
class DyldExportsTrie(LoadCommand):
data_offset: int
data_size: int
def exports(self) -> Iterator[ExportInfo]Usage example:
binary = fat_binary.at(0)
# Analyze dyld information
if binary.dyld_info:
print("Dyld rebases:")
for rebase in binary.dyld_info.show_rebases():
print(f" 0x{rebase.address:x}: {rebase.symbol_name}")
print("Dyld bindings:")
for binding in binary.dyld_info.show_bindings():
print(f" 0x{binding.address:x}: {binding.symbol_name} (lib {binding.library_ordinal})")
print("Dyld exports:")
for export in binary.dyld_info.show_exports():
print(f" {export.symbol_name} @ 0x{export.address:x}")
# Check modern chained fixups
if binary.dyld_chained_fixups:
print("Chained fixups:")
for binding in binary.dyld_chained_fixups.bindings():
print(f" 0x{binding.address:x}: {binding.symbol_name}")Analyze Mach-O code signatures and entitlements for security validation.
class CodeSignature(LoadCommand):
data_offset: int
data_size: int
def content(self) -> memoryview
class LinkerOptHint(LoadCommand):
data_offset: int
data_size: int
class EncryptionInfo(LoadCommand):
crypt_offset: int
crypt_size: int
crypt_id: int
class EncryptionInfo64(EncryptionInfo):
pad: intUsage example:
binary = fat_binary.at(0)
# Find code signature
for command in binary.commands:
if command.command == MachO.LOAD_COMMAND_TYPES.LC_CODE_SIGNATURE:
code_sig = MachO.CodeSignature(command)
print(f"Code signature at offset 0x{code_sig.data_offset:x}")
print(f"Signature size: {code_sig.data_size} bytes")
# Raw signature data analysis would require additional parsing
sig_data = code_sig.content()
print(f"Signature data: {len(sig_data)} bytes")
# Check for encryption
if binary.encryption_info:
if binary.encryption_info.crypt_id != 0:
print("Binary is encrypted")
print(f"Encrypted range: 0x{binary.encryption_info.crypt_offset:x} - {binary.encryption_info.crypt_size}")
else:
print("Binary is not encrypted")class Header(lief.Object):
magic: MACHO_TYPES
cpu_type: CPU_TYPE
cpu_subtype: int
file_type: FILE_TYPE
number_of_commands: int
size_of_commands: int
flags: int
reserved: int
enum MACHO_TYPES:
MH_MAGIC = 0xfeedface
MH_CIGAM = 0xcefaedfe
MH_MAGIC_64 = 0xfeedfacf
MH_CIGAM_64 = 0xcffaedfe
FAT_MAGIC = 0xcafebabe
FAT_CIGAM = 0xbebafeca
enum CPU_TYPE:
ANY = -1
VAX = 1
MC680x0 = 6
X86 = 7
I386 = 7 # Compatibility
X86_64 = 0x01000007
MIPS = 8
MC98000 = 10
HPPA = 11
ARM = 12
ARM64 = 0x0100000c
MC88000 = 13
SPARC = 14
I860 = 15
ALPHA = 16
POWERPC = 18
POWERPC64 = 0x01000012
enum FILE_TYPE:
MH_OBJECT = 0x1
MH_EXECUTE = 0x2
MH_FVMLIB = 0x3
MH_CORE = 0x4
MH_PRELOAD = 0x5
MH_DYLIB = 0x6
MH_DYLINKER = 0x7
MH_BUNDLE = 0x8
MH_DYLIB_STUB = 0x9
MH_COMPANION = 0xa
MH_KEXT_BUNDLE = 0xb
class Symbol(lief.Symbol):
type: int
number_of_sections: int
description: int
def has_export_info(self) -> bool
def has_binding_info(self) -> bool
class FunctionStarts(LoadCommand):
data_offset: int
data_size: int
functions: List[int]
class DataInCode(LoadCommand):
data_offset: int
data_size: int
entries: Iterator[DataInCodeEntry]
class DataInCodeEntry(lief.Object):
offset: int
length: int
kind: DATA_IN_CODE_TYPES
enum DATA_IN_CODE_TYPES:
DICE_KIND_DATA = 0x0001
DICE_KIND_JUMP_TABLE8 = 0x0002
DICE_KIND_JUMP_TABLE16 = 0x0003
DICE_KIND_JUMP_TABLE32 = 0x0004
DICE_KIND_ABS_JUMP_TABLE32 = 0x0005Install with Tessl CLI
npx tessl i tessl/pypi-lief