# -*- coding: utf-8 -*-
# This code is part of Amoco
# Copyright (C) 2020 Axel Tillequin (bdcht3@gmail.com)
# published under GPLv2 license
"""
system/macho.py
================
The system macho module implements the Mach-O executable format parser.
"""
from collections import defaultdict
from amoco.system.core import BinFormat, DataIO
from amoco.system.utils import read_uleb128
from amoco.system.structs import Consts, StructFormatter, default_formatter
from amoco.system.structs import StructDefine, UnionDefine
from amoco.system.structs import token_name_fmt, token_flag_fmt, Token, highlight
from amoco.logger import Log
logger = Log(__name__)
logger.debug("loading module")
[docs]class MachOError(Exception):
"""
MachOError is raised whenever MachO object instance fails
to decode required structures.
"""
def __init__(self, message):
self.message = message
def __str__(self):
return str(self.message)
# ------------------------------------------------------------------------------
[docs]class MachO(BinFormat):
"""
This class takes a DataIO object (ie an opened file of BytesIO instance)
and decodes all Mach-O structures found in it.
Attributes:
entrypoints (list of int): list of entrypoint addresses.
filename (str): binary file name.
header (struct_mach_header): the Mach header structure.
archs (list of MachO): the list of MachO instances in case the
provided binary file is a "fat" format.
cmds (list): the list of all "command" structures.
dynamic (Bool): True if the binary wants to load dynamic libs.
basemap (int): Base address of the binary (or None.)
symtab (list): the symbol table.
dysymtab (list): the dynamic symbol table.
dyld_info (container): a container with dyld_info attributes
rebase, bind, weak_bind, lazy_bind
and export.
function_starts (list,optional): list of function start addresses.
la_symbol_ptr (dict): address to lazy symbol bindings
nl_symbol_ptr (dict): address to non-lazy symbol bindings
"""
is_MachO = True
@property
def entrypoints(self):
if self.__entry:
return [self.__entry]
for c in self.cmds:
if c.cmd in (LC_THREAD, LC_UNIXTHREAD):
try:
self.__entry = c.entrypoint
except AttributeError:
pass
if c.cmd == LC_MAIN:
base = self.basemap
# remplacement for thread commands
self.__entry = base + c.entryoff
break
return [self.__entry]
@property
def filename(self):
return self.__file.name
def __init__(self, f):
self.__file = f
self.__entry = None
self._is_fat = False
try:
self.header = struct_mach_header(f)
except:
raise MachOError("not a Mach-O header")
if self.header.magic == MH_MAGIC_64:
self.header = struct_mach_header_64(f)
elif self.header.magic == FAT_CIGAM:
self.header = struct_fat_header(f)
self._is_fat = True
else:
if not self.header.magic == MH_MAGIC:
raise MachOError("not a Mach-O header")
offset = len(self.header)
if self._is_fat:
self.archs = []
for i in range(self.header.nfat_arch):
a = struct_fat_arch(f, offset)
offset += len(a)
self.archs.append(a)
self.read_fat_arch(a)
else:
self.cmds = self.read_commands(offset)
for c in self.cmds:
if c.cmd in (LC_THREAD, LC_UNIXTHREAD):
c.getstate(self.header.cputype)
elif c.cmd == LC_SYMTAB:
self.symtab = self.__read_symtab(c)
elif c.cmd == LC_DYSYMTAB:
self.dysymtab = self.__read_dysymtab(c)
elif c.cmd in (LC_DYLD_INFO, LC_DYLD_INFO_ONLY):
self.dyld_info = self.__read_dyld_info(c)
elif c.cmd == LC_FUNCTION_STARTS:
self.function_starts = self.__read_funcstarts(c)
self.la_symbol_ptr = self.__la_bindings()
self.nl_symbol_ptr = self.__nl_bindings()
[docs] def read_fat_arch(self, a):
"""
takes a struct_fat_arch instance and sets its 'bin' attribute
to the corresponding MachO instance.
"""
self.__f.seek(a.offset)
data = self.__f.read(a.size)
a.bin = MachO(DataIO(data))
[docs] def read_commands(self, offset):
"returns the list of struct_load_command starting from given offset"
cmds = []
lcsize = 0
f = self.__file
f.seek(0)
while lcsize < self.header.sizeofcmds:
cmd = struct_load_command(f, offset)
data = f[offset : offset + cmd.cmdsize]
offset += cmd.cmdsize
lcsize += cmd.cmdsize
if cmd.cmd in CMD_TABLE:
try:
cmd = CMD_TABLE[cmd.cmd](data)
except:
raise MachOError("bad load command:\n%s" % cmd)
cmds.append(cmd)
return cmds
[docs] def getsize(self):
"total size of LC_SEGMENT/64 commands"
total = 0
for c in self.cmds:
if c.cmd in (LC_SEGMENT, LC_SEGMENT_64,):
total += c.vmsize
return total
[docs] def getinfo(self, target):
"""getinfo return a triplet (s,off,vaddr) with segment, offset
into segment, and segment virtual base address that contains the
target argument.
"""
res = (None, 0, 0)
for c in self.cmds:
if c.cmd in (LC_SEGMENT, LC_SEGMENT_64,):
if c.vmaddr <= target < (c.vmaddr + c.vmsize):
res = (c, (target - c.vmaddr), c.vmaddr)
for s in c.sections:
if s.addr <= target < s.addr+s.size_:
res = (s, (target - s.addr), s.addr)
break
break
return res
[docs] def checksec(self):
"check for usual OSX security features."
R = {}
R['Arc'] = False
R["Canary"] = False
for f in iter(self.la_symbol_ptr.values()):
if f == b"___stack_chk_fail" or\
f == b"___stack_chk_guard":
R["Canary"] = True
elif f==b'_objc_release':
R["Arc"] = True
R['Signature'] = False
R['Encrypted'] = False
R['Restrict'] = False
R['RPath'] = False
for c in self.cmds:
if c.cmd == LC_CODE_SIGNATURE:
R['Signature'] = True
elif c.cmd in (LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64):
R['Encrypted'] = True
elif c.cmd in (LC_SEGMENT, LC_SEGMENT_64):
if c.segname.decode().strip('\0').lower()=="__restrict":
R['Restrict'] = True
elif c.cmd == LC_RPATH:
R['RPath'] = True
R["NX (heap)"] = (self.header.flags & MH_NO_HEAP_EXECUTION)!=0
R["NX (stack)"] = (self.header.flags & MH_ALLOW_STACK_EXECUTION)==0
R["PIE"] = (self.header.flags & MH_PIE)!=0
return R
[docs] def data(self, target, size):
"returns 'size' bytes located at target virtual address"
return self._readcode(target, size)[0]
def _readcode(self, target, size):
s, offset, _ = self.getinfo(target)
data = self.readsegment(s)
return data[offset : offset + size]
[docs] def getfileoffset(self, target):
"converts given target virtual address back to offset in file"
s, offset, _ = self.getinfo(target)
fileoffset = s.fileoffset if hasattr(s,'fileoffset') else s.offset
return s.fileoffset + offset
[docs] def readsegment(self, S):
"returns data of segment/section S"
try:
self.__file.seek(S.fileoffset)
return self.__file.read(S.filesize)
except AttributeError:
self.__file.seek(S.offset)
return self.__file.read(S.size_)
[docs] def loadsegment(self, S, pagesize=None):
"returns padded & aligned data of segment/section S"
s = self.readsegment(S)
if pagesize is None:
if hasattr(S,'vmsize'):
size = pagesize = S.vmsize
else:
size = S.size_
pagesize = S.align
n, r = divmod(size, pagesize)
if r > 0:
n += 1
return s.ljust(n * pagesize, b"\0")
[docs] def readsection(self, sect):
"returns the segment/section data bytes matching given sect name"
if isinstance(sect,bytes):
sect = sect.decode()
if isinstance(sect, str):
for c in self.cmds:
if c.cmd in (LC_SEGMENT,LC_SEGMENT_64):
for s in c.sections:
if s.sectname.decode().strip("\0")==sect:
return self.readsegment(s)
else:
return self.readsegment(sect)
return None
[docs] def getsection(self, sect):
"returns the segment/section matching given sect name"
if isinstance(sect,bytes):
sect = sect.decode()
if isinstance(sect, str):
for c in self.cmds:
if c.cmd in (LC_SEGMENT,LC_SEGMENT_64):
for s in c.sections:
if s.sectname.decode().strip("\0")==sect:
return s
return None
def __read_table(self, off, elt, count, sz=0):
tab = []
if count > 0:
self.__file.seek(off)
if sz == 0:
sz = elt.size()
for n in range(count):
data = self.__file.read(sz)
tab.append(elt(data))
return tab
def __read_symtab(self, s):
if self.header.magic == MH_MAGIC_64:
el = struct_nlist64
elif self.header.magic == MH_MAGIC:
el = struct_nlist
else:
raise NotImplementedError
dylibs = []
for c in self.cmds:
if c.cmd == LC_LOAD_DYLIB:
self.dynamic = True
dylibs.append(c)
symtab = self.__read_table(s.symoff, el, s.nsyms)
strtab = self.__read_strtab(s)
for x in symtab:
sta = x.n_strx
sto = strtab.index(b"\0", sta)
x.strx = strtab[sta:sto]
if self.header.flags & MH_TWOLEVEL:
i = (x.n_desc & 0xFF00) >> 8
if i > 0:
x.strx = b"%s::%s" % (dylibs[i - 1].dylib.name, x.strx)
return symtab
def __read_strtab(self, s):
self.__file.seek(s.stroff)
data = self.__file.read(s.strsize)
return data
def __read_dyld_info(self, cmd):
t = type("container", (object,), {})
# rebase opcodes (raw):
self.__file.seek(cmd.rebase_off)
t.rebase = self.__file.read(cmd.rebase_size)
# bind opcodes (as a list of binding records):
self.__file.seek(cmd.bind_off)
rawbind = self.__file.read(cmd.bind_size)
t.bind = self.__read_bind_opcodes(rawbind)
# weak_bind opcodes (as a list of binding records):
self.__file.seek(cmd.weak_bind_off)
weak_bind = self.__file.read(cmd.weak_bind_size)
t.weak_bind = self.__read_bind_opcodes(weak_bind)
# lazy_bind opcodes (as a list of binding records):
# see source code: the record type is POINTER by default...
self.__file.seek(cmd.lazy_bind_off)
lazy_bind = self.__file.read(cmd.lazy_bind_size)
t.lazy_bind = self.__read_bind_opcodes(lazy_bind, BIND_TYPE_POINTER)
# exports (raw):
self.__file.seek(cmd.export_off)
t.export = self.__file.read(cmd.export_size)
return t
# see dyld source: ImageLoaderMachOCompressed.cpp.
def __read_bind_opcodes(self, raw, default_type=0):
r = record(0, 0, 0, default_type, 0, 0)
L = []
cur = 0
l = 8 if self.header.magic == MH_MAGIC_64 else 4
while cur < len(raw):
op = raw[cur] & BIND_OPCODE_MASK
im = raw[cur] & BIND_IMMEDIATE_MASK
cur += 1
if op == BIND_OPCODE_DONE:
r = record(0, 0, 0, 0, 0, 0)
elif op == BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
r.lib_ordinal = im
elif op == BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
val, cnt = read_uleb128(raw[cur:])
r.lib_ordinal = val
cur += cnt
elif op == BIND_OPCODE_SET_ADDEND_SLEB:
val, cnt = read_sleb128(raw[cur:])
r.addend = val
cur += cnt
elif op == BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
r.lib_ordinal = (0, -1, -2)[im]
elif op == BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
r.flags = im
nulchar = raw.find(b"\0", cur)
if nulchar > cur:
r.symbol = raw[cur:nulchar]
cur = nulchar + 1
elif op == BIND_OPCODE_SET_TYPE_IMM:
r.type = im
elif op == BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
r.seg_index = im
val, cnt = read_uleb128(raw[cur:])
r.seg_offset = val
cur += cnt
elif op == BIND_OPCODE_DO_BIND:
L.append(r.as_list())
r.seg_offset += l
elif op == BIND_OPCODE_ADD_ADDR_ULEB:
val, cnt = read_uleb128(raw[cur:])
r.seg_offset += val
cur += cnt
elif op == BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
L.append(r.as_list())
val, cnt = read_uleb128(raw[cur:])
r.seg_offset += l + val
cur += cnt
elif op == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
L.append(r.as_list())
r.seg_offset += im * l + l
cur += cnt
elif op == BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
count, cnt = read_uleb128(raw[cur:])
skip, cnt2 = read_uleb128(raw[cur + cnt :])
for i in range(count):
L.append(r.as_list())
r.seg_offset += skip + l
cur += cnt + cnt2
else:
raise NotImplementedError
return L
def __read_dysymtab(self, cmd):
t = type("container", (object,), {})
# read table of contents:
t.toc = self.__read_table(cmd.tocoff, struct_dylib_table_of_contents, cmd.ntoc)
# read module table:
if self.header.magic == MH_MAGIC_64:
elt = struct_dylib_module_64
elif self.header.magic == MH_MAGIC:
elt = struct_dylib_module
else:
raise MachOError("module table error")
t.modtab = self.__read_table(cmd.modtaboff, elt, cmd.nmodtab)
t.extrefsym = self.__read_table(
cmd.extrefsymoff, struct_dylib_reference, cmd.nextrefsyms
)
t.indirectsym = self.__read_table(
cmd.indirectsymoff, struct_indirect_entry, cmd.nindirectsyms
)
t.extrel = self.__read_table(cmd.extreloff, struct_relocation_info, cmd.nextrel)
t.locrel = self.__read_table(cmd.locreloff, struct_relocation_info, cmd.nlocrel)
return t
def __la_bindings(self):
segs = [c for c in self.cmds if c.cmd in (LC_SEGMENT, LC_SEGMENT_64)]
libs = [c for c in self.cmds if c.cmd == LC_LOAD_DYLIB]
D = {}
if hasattr(self, "dyld_info"):
for b in self.dyld_info.lazy_bind:
r = record(*b)
addr = segs[r.seg_index].vmaddr + r.seg_offset + r.addend
name = b"%s::%s" % (libs[r.lib_ordinal - 1].dylib.name, r.symbol)
D[addr] = name
return D
def __nl_bindings(self):
segs = [c for c in self.cmds if c.cmd in (LC_SEGMENT, LC_SEGMENT_64)]
libs = [c for c in self.cmds if c.cmd == LC_LOAD_DYLIB]
D = {}
if hasattr(self, "dyld_info"):
for b in self.dyld_info.bind:
r = record(*b)
addr = segs[r.seg_index].vmaddr + r.seg_offset + r.addend
name = b"%s::%s" % (libs[r.lib_ordinal - 1].dylib.name, r.symbol)
D[addr] = name
return D
@property
def basemap(self):
fbaseaddr = None
for c in self.cmds:
if c.cmd in (LC_SEGMENT, LC_SEGMENT_64):
if c.fileoffset == 0 and c.filesize > 0:
fbaseaddr = c.vmaddr
return fbaseaddr
# source: https://opensource.apple.com/source/ld64/ld64-127.2/src/other/dyldinfo.cpp
def __read_funcstarts(self, fs):
fbaseaddr = self.basemap
addr = fbaseaddr
F = []
if fs is not None:
self.__file.seek(fs.dataoff)
data = self.__file.read(fs.datasize)
p = 0
while p < len(data):
delta, shift = 0, 0
more = True
while more:
b = data[p]
p += 1
delta |= (b & 0x7F) << shift
shift += 7
if b < 0x80:
addr += delta
F.append(addr)
more = False
return F
def __str__(self):
S = []
line = "\n" + ("-" * 80)
S.append(str(self.header) + line)
for c in self.cmds:
S.append(str(c) + line)
return "\n".join(S)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I :> cputype
I :> cpusubtype
I :> offset
I :> size
I :> align
"""
)
class struct_fat_arch(StructFormatter):
alt = "mh"
def __init__(self, data=None, offset=0):
self.name_formatter("cputype")
self.func_formatter(cpusubtype=self.subtype_fmt)
if data:
self.unpack(data, offset)
def subtype_fmt(self, k, v, cls):
alt2 = Consts.All["mh.cputype"].get(self.cputype & 0xFF, "")
cls2 = "%s.%s" % (self.alt, alt2)
return token_name_fmt(k, v & 0xFF, cls2)
# ------------------------------------------------------------------------------
with Consts("mh.magic"):
MH_MAGIC = 0xFEEDFACE
MH_CIGAM = 0xCEFAEDFE
MH_MAGIC_64 = 0xFEEDFACF
MH_CIGAM_64 = 0xCFFAEDFE
FAT_MAGIC = 0xCAFEBABE
FAT_CIGAM = 0xBEBAFECA
FAT_MAGIC_64 = 0xCAFEBABF
FAT_CIGAM_64 = 0xBFBAFECA
with Consts("mh.cputype"):
ANY = -1
VAX = 1
MC680x0 = 6
X86 = 7
X86_64 = 16777223
MC98000 = 10
HPPA = 11
ARM = 12
MC88000 = 13
SPARC = 14
I860 = 15
POWERPC = 18
POWERPC64 = 16777234
with Consts("mh.X86.cpusubtype"):
CPU_SUBTYPE_386 = 3
CPU_SUBTYPE_486 = 4
CPU_SUBTYPE_486SX = 0x84
CPU_SUBTYPE_586 = 5
CPU_SUBTYPE_X86_64_H = 8
CPU_SUBTYPE_PENTPRO = 0x16
CPU_SUBTYPE_PENTII_M3 = 0x36
CPU_SUBTYPE_PENTII_M5 = 0x56
CPU_SUBTYPE_CELERON = 0x67
CPU_SUBTYPE_CELERON_MOBILE = 0x77
CPU_SUBTYPE_PENTIUM_3 = 0x08
CPU_SUBTYPE_PENTIUM_3_M = 0x18
CPU_SUBTYPE_PENTIUM_3_XEON = 0x28
CPU_SUBTYPE_PENTIUM_M = 0x09
CPU_SUBTYPE_PENTIUM_4 = 0x0A
CPU_SUBTYPE_PENTIUM_4_M = 0x1A
CPU_SUBTYPE_ITANIUM = 0x0B
CPU_SUBTYPE_ITANIUM_2 = 0x1B
CPU_SUBTYPE_XEON = 0x0C
CPU_SUBTYPE_XEON_MP = 0x1C
with Consts("mh.ARM.cpusubtype"):
CPU_SUBTYPE_ARM_ALL = 0
CPU_SUBTYPE_ARM_V4T = 5
CPU_SUBTYPE_ARM_V6 = 6
CPU_SUBTYPE_ARM_V5 = 7
CPU_SUBTYPE_ARM_XSCALE = 8
CPU_SUBTYPE_ARM_V7 = 9
CPU_SUBTYPE_ARM_V7S = 11
CPU_SUBTYPE_ARM_V7K = 12
CPU_SUBTYPE_ARM_V6M = 14
CPU_SUBTYPE_ARM_V7M = 15
CPU_SUBTYPE_ARM_V7EM = 16
with Consts("mh.POWERPC.cpusubtype"):
CPU_SUBTYPE_POWERPC_ALL = 0
CPU_SUBTYPE_POWERPC_601 = 1
CPU_SUBTYPE_POWERPC_602 = 2
CPU_SUBTYPE_POWERPC_603 = 3
CPU_SUBTYPE_POWERPC_603e = 4
CPU_SUBTYPE_POWERPC_603ev = 5
CPU_SUBTYPE_POWERPC_604 = 6
CPU_SUBTYPE_POWERPC_604e = 7
CPU_SUBTYPE_POWERPC_620 = 8
CPU_SUBTYPE_POWERPC_750 = 9
CPU_SUBTYPE_POWERPC_7400 = 10
CPU_SUBTYPE_POWERPC_7450 = 11
CPU_SUBTYPE_POWERPC_970 = 100
with Consts("mh.filetype"):
MH_OBJECT = 0x1 # relocatable object file
MH_EXECUTE = 0x2 # executable binary
MH_FVMLIB = 0x3
MH_CORE = 0x4 # core dump
MH_PRELOAD = 0x5
MH_DYLIB = 0x6 # dynamic library
MH_DYLINKER = 0x7 # dynamic linker
MH_BUNDLE = 0x8 # Plug-ins
MH_DYLIB_STUB = 0x9
MH_DSYM = 0xA # symbol file & debug info
MH_KEXT_BUNDLE = 0xB # kernel extension
with Consts("mh.flags"):
MH_NOUNDEFS = 0x1
MH_INCRLINK = 0x2
MH_DYLDLINK = 0x4
MH_BINDATLOAD = 0x8
MH_PREBOUND = 0x10
MH_SPLIT_SEGS = 0x20
MH_LAZY_INIT = 0x40
MH_TWOLEVEL = 0x80
MH_FORCE_FLAT = 0x100
MH_NOMULTIDEFS = 0x200
MH_NOFIXPREBINDING = 0x400
MH_PREBINDABLE = 0x800
MH_ALLMODSBOUND = 0x1000
MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000
MH_CANONICAL = 0x4000
MH_WEAK_DEFINES = 0x8000
MH_BINDS_TO_WEAK = 0x10000
MH_ALLOW_STACK_EXECUTION = 0x20000
MH_ROOT_SAFE = 0x40000
MH_SETUID_SAFE = 0x80000
MH_NO_REEXPORTED_DYLIBS = 0x100000
MH_PIE = 0x200000
MH_DEAD_STRIPPABLE_DYLIB = 0x400000
MH_HAS_TLV_DESCRIPTORS = 0x800000
MH_NO_HEAP_EXECUTION = 0x1000000
# ------------------------------------------------------------------------------
def token_cmd_fmt(k, val, cls=None):
s = []
if val & LC_REQ_DYLD:
s.append(highlight([(Token.Name, "LC_REQ_DYLD")]))
s.append(token_name_fmt(k, val, "lc"))
return "+".join(s)
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
"""
)
class struct_load_command(StructFormatter):
alt = "lc"
def __init__(self, data=None, offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
with Consts("lc.cmd"):
LC_REQ_DYLD = 0x80000000
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 # load dynamic lib
LC_ID_DYLIB = 0xD # id of a dynamic lib
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 = 0x18 | LC_REQ_DYLD
LC_SEGMENT_64 = 0x19
LC_ROUTINES_64 = 0x1A
LC_UUID = 0x1B
LC_RPATH = 0x1C | LC_REQ_DYLD
LC_CODE_SIGNATURE = 0x1D
LC_SEGMENT_SPLIT_INFO = 0x1E
LC_REEXPORT_DYLIB = 0x1F | LC_REQ_DYLD
LC_LAZY_LOAD_DYLIB = 0x20 # defered load dynamic lib
LC_ENCRYPTION_INFO = 0x21
LC_DYLD_INFO = 0x22
LC_DYLD_INFO_ONLY = 0x22 | LC_REQ_DYLD
LC_LOAD_UPWARD_DYLIB = 0x23 | LC_REQ_DYLD
LC_VERSION_MIN_MACOSX = 0x24
LC_VERSION_MIN_IPHONEOS = 0x25
LC_FUNCTION_STARTS = 0x26 # compressed table of funcs addr
LC_DYLD_ENVIRONMENT = 0x27
LC_MAIN = 0x28 | LC_REQ_DYLD
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
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
s*16 : segname
I : vmaddr
I : vmsize
I : fileoffset
I : filesize
i : maxprot
i : initprot
I : nsects
I : flags
"""
)
class struct_segment_command(MachoFormatter):
alt = "sg"
def __init__(self, data=None, offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.func_formatter(segname=self.segname_fmt)
self.flag_formatter("flags")
self.address_formatter("vmaddr", "vmsize")
self.address_formatter("fileoffset", "filesize")
if data:
self.unpack(data, offset)
offset += len(self)
self.sections = []
for i in range(self.nsects):
s = struct_section(data, offset)
offset += len(s)
self.sections.append(s)
def segname_fmt(self, k, x, cls=None):
return highlight([(Token.String, str(x.strip(b"\0")))])
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
s*16 : segname
Q : vmaddr
Q : vmsize
Q : fileoffset
Q : filesize
i : maxprot
i : initprot
I : nsects
I : flags
"""
)
class struct_segment_command_64(MachoFormatter):
alt = "sg"
def __init__(self, data=None, offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.func_formatter(segname=self.segname_fmt)
self.flag_formatter("flags")
self.address_formatter("vmaddr", "vmsize")
self.address_formatter("fileoffset", "filesize")
if data:
self.unpack(data, offset)
offset += len(self)
self.sections = []
for i in range(self.nsects):
s = struct_section_64(data, offset)
offset += len(s)
self.sections.append(s)
def segname_fmt(self, k, x, cls=None):
return highlight([(Token.String, str(x.strip(b"\0")))])
# ------------------------------------------------------------------------------
with Consts("sg.flags"):
SG_HIGHVM = 0x1
SG_FVMLIB = 0x2
SG_NORELOC = 0x4
SG_PROTECTED_VERSION_1 = 0x8
[docs]@StructDefine(
"""
B : type
B*3 : attr
"""
)
class SFLG(MachoFormatter):
alt = "sflg"
def __init__(self, data=None, offset=0):
self.name_formatter("type")
self.func_formatter(attr=self.attr_fmt)
if data:
self.unpack(data, offset)
@staticmethod
def attr_fmt(k, val, cls=None):
v = val[0] | (val[1] << 8) | (val[2] << 16)
return token_flag_fmt(k, v, cls)
with Consts("sflg.type"):
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 = 0x15
with Consts("sflg.attr"):
SECTION_ATTRIBUTES_USR = 0xFF000000
S_ATTR_PURE_INSTRUCTIONS = 0x80000000
S_ATTR_NO_TOC = 0x40000000
S_ATTR_STRIP_STATIC_SYMS = 0x20000000
S_ATTR_NO_DEAD_STRIP = 0x10000000
S_ATTR_LIVE_SUPPORT = 0x08000000
S_ATTR_SELF_MODIFYING_CODE = 0x04000000
S_ATTR_DEBUG = 0x02000000
SECTION_ATTRIBUTES_SYS = 0x00FFFF00
S_ATTR_SOME_INSTRUCTIONS = 0x00000400
S_ATTR_EXT_RELOC = 0x00000200
S_ATTR_LOC_RELOC = 0x00000100
[docs]@StructDefine(
"""
s*16 : sectname
s*16 : segname
I : addr
I : size_
I : offset
I : align
I : reloff
I : nreloc
SFLG : flags
I : reserved1
I : reserved2
"""
)
class struct_section(MachoFormatter):
alt = "s"
def __init__(self, data=None, offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("addr")
self.address_formatter("offset")
self.address_formatter("reloff")
if data:
self.unpack(data, offset)
self.name = self.segname.decode().strip('\0')
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
s*16 : sectname
s*16 : segname
Q : addr
Q : size_
I : offset
I : align
I : reloff
I : nreloc
SFLG : flags
I : reserved1
I : reserved2
I : reserved3
"""
)
class struct_section_64(MachoFormatter):
alt = "s"
def __init__(self, data=None, offset=0):
self.func_formatter(cmd=token_cmd_fmt, offset=0)
self.address_formatter("addr")
self.address_formatter("offset")
self.address_formatter("reloff")
if data:
self.unpack(data, offset)
self.name = self.segname.decode().strip('\0')
# ------------------------------------------------------------------------------
[docs]@UnionDefine(
"""
I : offset
P : header_addr
"""
)
class lc_str(MachoFormatter):
def __init__(self, data=None, offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : offset
I : minor_version
I : header_addr
"""
)
class struct_fvmlib(MachoFormatter):
def __init__(self, data=None, offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
struct_fvmlib : fvmlib
"""
)
class struct_fvmlib_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self.fvmlib.offset
i = data.find(b"\0", sta)
if i != -1:
self.fvmlib.name = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : offset
I : timestamp
I : current_version
I : compatibility_version
"""
)
class struct_dylib(MachoFormatter):
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
struct_dylib : dylib
"""
)
class struct_dylib_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self.dylib.offset
i = data.find(b"\0", sta)
if i != -1:
self.dylib.name = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_sub_framework_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.umbrella = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_sub_client_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.client = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_sub_umbrella_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.sub_umbrella = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_sub_library_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.sub_library = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
I : nmodules
I : linked_modules
"""
)
class struct_prebound_dylib_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.name = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_dylinker_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.name = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : flavor
I : count
"""
)
class struct_thread_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.name_formatter("flavor")
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
offset += 16
self.data = data[offset : offset + self.cmdsize - 16]
def getstate(self, cputype):
if cputype in (X86, X86_64):
self.alt = "lc.x86"
if self.flavor == x86_THREAD_STATE32:
self.state = struct_x86_thread_state32(self.data)
self.entrypoint = self.state["eip"]
elif self.flavor == x86_THREAD_STATE64:
self.state = struct_x86_thread_state64(self.data)
self.entrypoint = self.state["rip"]
elif cputype in (ARM,):
self.alt = "lc.arm"
if self.flavor == ARM_THREAD_STATE32:
self.state = struct_x86_thread_state32(self.data)
elif self.flavor == ARM_THREAD_STATE64:
self.state = struct_x86_thread_state64(self.data)
self.entrypoint = self.state["pc"]
return self.state
with Consts("lc.x86.flavor"):
x86_THREAD_STATE32 = 1
x86_FLOAT_STATE32 = 2
x86_EXCEPTION_STATE32 = 3
x86_THREAD_STATE64 = 4
x86_FLOAT_STATE64 = 5
x86_EXCEPTION_STATE64 = 6
x86_THREAD_STATE = 7
x86_FLOAT_STATE = 8
x86_EXCEPTION_STATE = 9
x86_DEBUG_STATE32 = 10
x86_DEBUG_STATE64 = 11
x86_DEBUG_STATE = 12
x86_THREAD_STATE_NONE = 13
x86_AVX_STATE32 = 16
x86_AVX_STATE64 = x86_AVX_STATE32 + 1
x86_AVX_STATE = x86_AVX_STATE32 + 2
x86_AVX512_STATE32 = 19
x86_AVX512_STATE64 = x86_AVX512_STATE32 + 1
x86_AVX512_STATE = x86_AVX512_STATE32 + 2
with Consts("lc.arm.flavor"):
ARM_THREAD_STATE = 1
ARM_VFP_STATE = 2
ARM_EXCEPTION_STATE = 3
ARM_DEBUG_STATE = 4
ARM_THREAD_STATE_NONE = 5
ARM_THREAD_STATE64 = 6
ARM_EXCEPTION_STATE64 = 7
ARM_THREAD_STATE32 = 9
ARM_DEBUG_STATE32 = 14
ARM_DEBUG_STATE64 = 15
ARM_NEON_STATE = 16
ARM_NEON_STATE64 = 17
ARM_CPMU_STATE64 = 18
ARM_SAVED_STATE32 = 20
ARM_SAVED_STATE64 = 21
ARM_NEON_SAVED_STATE32 = 20
ARM_NEON_SAVED_STATE64 = 21
[docs]@StructDefine(
"""
I: eax;
I: ebx;
I: ecx;
I: edx;
I: edi;
I: esi;
I: ebp;
I: esp;
I: e8;
I: e9;
I: e10;
I: e11;
I: e12;
I: e13;
I: e14;
I: e15;
I: eip;
I: eflags;
I: cs;
I: fs;
I: gs;
"""
)
class struct_x86_thread_state32(MachoFormatter):
def __init__(self, data="", offset=0):
for f in self.fields:
self.address_formatter(f.name)
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
Q: rax;
Q: rbx;
Q: rcx;
Q: rdx;
Q: rdi;
Q: rsi;
Q: rbp;
Q: rsp;
Q: r8;
Q: r9;
Q: r10;
Q: r11;
Q: r12;
Q: r13;
Q: r14;
Q: r15;
Q: rip;
Q: rflags;
Q: cs;
Q: fs;
Q: gs;
"""
)
class struct_x86_thread_state64(MachoFormatter):
def __init__(self, data="", offset=0):
for f in self.fields:
self.address_formatter(f.name)
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
I*13: r
I : sp
I : lr
I : pc
I : cpsr
"""
)
class struct_arm_thread_state32(MachoFormatter):
def __init__(self, data="", offset=0):
for f in self.fields:
self.address_formatter(f.name)
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
Q*29: x
Q : fp
Q : lr
Q : sp
Q : pc
I : cpsr
I : pad
"""
)
class struct_arm_thread_state64(MachoFormatter):
def __init__(self, data="", offset=0):
for f in self.fields:
self.address_formatter(f.name)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : init_address
I : init_module
I : reserved1
I : reserved2
I : reserved3
I : reserved4
I : reserved5
I : reserved6
"""
)
class struct_routines_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
Q : init_address
Q : init_module
Q : reserved1
Q : reserved2
Q : reserved3
Q : reserved4
Q : reserved5
Q : reserved6
"""
)
class struct_routines_command_64(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : symoff
I : nsyms
I : stroff
I : strsize
"""
)
class struct_symtab_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.address_formatter("symoff")
self.address_formatter("stroff")
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
I : n_strx
B : n_type
B : n_sect
H : n_desc
I : n_value
"""
)
class struct_nlist(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.address_formatter("n_value")
self.func_formatter(n_type=self.ntype_fmt)
self.func_formatter(n_desc=self.ndesc_fmt)
if data:
self.unpack(data, offset)
def ntype_fmt(self, k, x, cls=None):
if x & N_STAB:
return token_name_fmt(k, x, "lc.stab")
else:
l = []
if x & N_TYPE == 0x0:
l.append("N_UNDF")
elif x & N_TYPE == 0x2:
l.append("N_ABS")
elif x & N_TYPE == 0xE:
l.append("N_SECT")
elif x & N_TYPE == 0xC:
l.append("N_PBUD")
elif x & N_TYPE == 0xA:
l.append("N_INDR")
if x & N_EXT:
l.append("N_PEXT")
s = "+".join(l)
return highlight([(Token.Name, s)])
def ndesc_fmt(self, k, x, cls=None):
s = token_name_fmt(k, x & 0xF, cls)
if x & 0xF0:
s += "+" + token_flag_fmt(k, x & 0xF0, "lc.flag")
return s
[docs]@StructDefine(
"""
I : n_strx
B : n_type
B : n_sect
H : n_desc
Q : n_value
"""
)
class struct_nlist64(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.address_formatter("n_value")
self.func_formatter(n_type=self.ntype_fmt)
self.func_formatter(n_desc=self.ndesc_fmt)
if data:
self.unpack(data, offset)
def ntype_fmt(self, k, x, cls=None):
if x & N_STAB:
return token_name_fmt(k, x, "lc.stab")
else:
l = []
if x & N_PEXT:
l.append("N_PEXT")
if x & N_TYPE == 0x0:
l.append("N_UNDF")
elif x & N_TYPE == 0x2:
l.append("N_ABS")
elif x & N_TYPE == 0xE:
l.append("N_SECT")
elif x & N_TYPE == 0xC:
l.append("N_PBUD")
elif x & N_TYPE == 0xA:
l.append("N_INDR")
if x & N_EXT:
l.append("N_PEXT")
s = "+".join(l)
return highlight([(Token.Name, s)])
def ndesc_fmt(self, k, x, cls=None):
s = token_name_fmt(k, x & 0xF, cls)
if x & 0xF0:
s += "+" + token_flag_fmt(k, x & 0xF0, "lc.flag")
return s
with Consts("lc.n_type"):
N_STAB = 0xE0
N_PEXT = 0x10
N_TYPE = 0x0E
N_EXT = 0x01
N_UNDF = 0x0
N_ABS = 0x2
N_SECT = 0xE
N_PBUD = 0xC
N_INDR = 0xA
with Consts("lc.stab.n_type"):
N_GSYM = 0x20 # global symbol: name,,NO_SECT,type,0
N_FNAME = 0x22 # procedure name (f77 kludge): name,,NO_SECT,0,0
N_FUN = 0x24 # procedure: name,,n_sect,linenumber,address
N_STSYM = 0x26 # static symbol: name,,n_sect,type,address
N_LCSYM = 0x28 # .lcomm symbol: name,,n_sect,type,address
N_BNSYM = 0x2E # begin nsect sym: 0,,n_sect,0,address
N_AST = 0x32 # AST file path: name,,NO_SECT,0,0
N_OPT = 0x3C # emitted with gcc2_compiled and in gcc source
N_RSYM = 0x40 # register sym: name,,NO_SECT,type,register
N_SLINE = 0x44 # src line: 0,,n_sect,linenumber,address
N_ENSYM = 0x4E # end nsect sym: 0,,n_sect,0,address
N_SSYM = 0x60 # structure elt: name,,NO_SECT,type,struct_offset
N_SO = 0x64 # source file name: name,,n_sect,0,address
N_OSO = 0x66 # object file name: name,,0,0,st_mtime
N_LSYM = 0x80 # local sym: name,,NO_SECT,type,offset
N_BINCL = 0x82 # include file beginning: name,,NO_SECT,0,sum
N_SOL = 0x84 # #included file name: name,,n_sect,0,address
N_PARAMS = 0x86 # compiler parameters: name,,NO_SECT,0,0
N_VERSION = 0x88 # compiler version: name,,NO_SECT,0,0
N_OLEVEL = 0x8A # compiler -O level: name,,NO_SECT,0,0
N_PSYM = 0xA0 # parameter: name,,NO_SECT,type,offset
N_EINCL = 0xA2 # include file end: name,,NO_SECT,0,0
N_ENTRY = 0xA4 # alternate entry: name,,n_sect,linenumber,address
N_LBRAC = 0xC0 # left bracket: 0,,NO_SECT,nesting level,address
N_EXCL = 0xC2 # deleted include file: name,,NO_SECT,0,sum
N_RBRAC = 0xE0 # right bracket: 0,,NO_SECT,nesting level,address
N_BCOMM = 0xE2 # begin common: name,,NO_SECT,0,0
N_ECOMM = 0xE4 # end common: name,,n_sect,0,0
N_ECOML = 0xE8 # end common (local name): 0,,n_sect,0,address
N_LENG = 0xFE # second stab entry with length information
N_PC = 0x30 # global pascal symbol: name,,NO_SECT,subtype,line
with Consts("lc.n_desc"):
REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0x0
REFERENCE_FLAG_UNDEFINED_LAZY = 0x1
REFERENCE_FLAG_UNDEFINED = 0x2
REFERENCE_FLAG_PRIVATE_DEFINED = 0x3
REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 0x4
REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 0x5
with Consts("lc.flag.n_desc"):
REFERENCED_DYNAMICALLY = 0x10
N_DESC_DISCARDED = 0x20
N_NO_DEAD_STRIP = 0x20
N_WEAK_REF = 0x40
N_WEAK_DEF = 0x80
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : ilocalsym
I : nlocalsym
I : iextdefsym
I : nextdefsym
I : iundefsym
I : nundefsym
I : tocoff
I : ntoc
I : modtaboff
I : nmodtab
I : extrefsymoff
I : nextrefsyms
I : indirectsymoff
I : nindirectsyms
I : extreloff
I : nextrel
I : locreloff
I : nlocrel
"""
)
class struct_dysymtab_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter(
"tocoff",
"modtaboff",
"extrefsymoff",
"indirectsymoff",
"extreloff",
"locreloff",
)
if data:
self.unpack(data, offset)
INDIRECT_SYMBOL_LOCAL = 0x80000000
INDIRECT_SYMBOL_ABS = 0x40000000
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : symbol_index
I : module_index
"""
)
class struct_dylib_table_of_contents(MachoFormatter):
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : module_name
I : iextdefsym
I : nextdefsym
I : irefsym
I : nrefsym
I : ilocalsym
I : nlocalsym
I : iextrel
I : nextrel
I : iinit_iterm
I : ninit_nterm
I : objc_module_info_addr
I : objc_module_info_size
"""
)
class struct_dylib_module(MachoFormatter):
def __init__(self, data="", offset=0):
self.address_formatter("objc_module_info_addr")
self.address_formatter("objc_module_info_size")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : module_name
I : iextdefsym
I : nextdefsym
I : irefsym
I : nrefsym
I : ilocalsym
I : nlocalsym
I : iextrel
I : nextrel
I : iinit_iterm
I : ninit_nterm
I : objc_module_info_size
Q : objc_module_info_addr
"""
)
class struct_dylib_module_64(MachoFormatter):
def __init__(self, data="", offset=0):
self.address_formatter("objc_module_info_addr")
self.address_formatter("objc_module_info_size")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
B*3 : isym
B : flags
"""
)
class struct_dylib_reference(MachoFormatter):
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
I : nhints
"""
)
class struct_twolevel_hints_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("offset")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
B : isub_image
B*3 : itoc
"""
)
class twolevel_hint(MachoFormatter):
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : cksum
"""
)
class struct_prebind_cksum_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
B*16 : uuid
"""
)
class struct_uuid_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.func_formatter(uuid=self.uuid_fmt)
if data:
self.unpack(data, offset)
def uuid_fmt(self, k, x, cls=None):
return highlight([(Token.String, ".".join(("%02d" % v for v in x)))])
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
"""
)
class struct_rpath_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.path = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : dataoff
I : datasize
"""
)
class struct_linkedit_data_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("dataoff", "datasize")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : cryptoff
I : cryptsize
I : cryptid
"""
)
class struct_encryption_info_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("cryptoff", "cryptsize")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : rebase_off
I : rebase_size
I : bind_off
I : bind_size
I : weak_bind_off
I : weak_bind_size
I : lazy_bind_off
I : lazy_bind_size
I : export_off
I : export_size
"""
)
class struct_dyld_info_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.address_formatter("rebase_off", "rebase_size")
self.address_formatter("bind_off", "bind_size")
self.address_formatter("weak_bind_off", "weak_bind_size")
self.address_formatter("lazy_bind_off", "lazy_bind_size")
self.address_formatter("export_off", "export_size")
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
class record(object):
def __init__(self, indx, off, ordinal, typ, flags, addend, s=None):
self.seg_index = indx
self.seg_offset = off
self.lib_ordinal = ordinal
self.type = typ
self.flags = flags
self.addend = addend
self.symbol = s
def as_list(self):
s = (
self.seg_index,
self.seg_offset,
self.lib_ordinal,
self.type,
self.flags,
self.addend,
self.symbol,
)
return s
with Consts("dyld.rebase"):
REBASE_TYPE_POINTER = 1
REBASE_TYPE_TEXT_ABSOLUTE32 = 2
REBASE_TYPE_TEXT_PCREL32 = 3
REBASE_OPCODE_MASK = 0xF0
REBASE_IMMEDIATE_MASK = 0x0F
REBASE_OPCODE_DONE = 0x00
REBASE_OPCODE_SET_TYPE_IMM = 0x10
REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20
REBASE_OPCODE_ADD_ADDR_ULEB = 0x30
REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40
REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50
REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60
REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70
REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80
with Consts("dyld.bind"):
BIND_TYPE_POINTER = 1
BIND_TYPE_TEXT_ABSOLUTE32 = 2
BIND_TYPE_TEXT_PCREL32 = 3
BIND_SPECIAL_DYLIB_SELF = 0
BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1
BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1
BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8
BIND_OPCODE_MASK = 0xF0
BIND_IMMEDIATE_MASK = 0x0F
BIND_OPCODE_DONE = 0x00
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10
BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40
BIND_OPCODE_SET_TYPE_IMM = 0x50
BIND_OPCODE_SET_ADDEND_SLEB = 0x60
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70
BIND_OPCODE_ADD_ADDR_ULEB = 0x80
BIND_OPCODE_DO_BIND = 0x90
BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0
BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0
BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0
with Consts("dyld.export"):
EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03
EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00
EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01
EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04
EXPORT_SYMBOL_FLAGS_INDIRECT_DEFINITION = 0x08
EXPORT_SYMBOL_FLAGS_HAS_SPECIALIZATIONS = 0x10
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
I : size_
"""
)
class struct_symseg_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
"""
)
class struct_ident_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : offset
I : header_attr
"""
)
class struct_fvmfile_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
if data:
self.unpack(data, offset)
sta = offset + self._v.offset
i = data.find(b"\0", sta)
if i != -1:
self.name = data[sta:i]
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
Q : entryoff
Q : stacksize
"""
)
class struct_entry_point_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("entryoff")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : offset
H : length
H : kind
"""
)
class struct_data_in_code_entry(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.name_formatter("kind")
if data:
self.unpack(data, offset)
with Consts("lc.kind"):
DICE_KIND_DATA = 0x0001
DICE_KIND_JUMP_TABLE8 = 0x0002
DICE_KIND_JUMP_TABLE16 = 0x0003
DICE_KIND_JUMP_TABLE32 = 0x0004
DICE_KIND_ABS_JUMP_TABLE32 = 0x0005
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
s*16 : data_owner
Q : offset
Q : size
"""
)
class struct_note_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.address_formatter("offset")
if data:
self.unpack(data, offset)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
Q : version
"""
)
class struct_source_version_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.func_formatter(version=self.version_fmt)
if data:
self.unpack(data, offset)
def version_fmt(self, k, v, cls=None):
A = v >> 40
B = (v >> 30) & 0x3FF
C = (v >> 20) & 0x3FF
D = (v >> 10) & 0x3FF
E = v & 0x3FF
return highlight([(Token.String, "%d.%d.%d.%d.%d" % (A, B, C, D, E))])
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : version
I : sdk
"""
)
class struct_version_min_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.func_formatter(version=self.version_fmt)
self.func_formatter(sdk=self.version_fmt)
if data:
self.unpack(data, offset)
def version_fmt(self, k, v, cls=None):
x = (v & 0xFFFF0000) >> 16
y = (v & 0x0000FF00) >> 8
z = v & 0xFF
return highlight([(Token.String, "%d.%d.%d" % (x, y, z))])
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
I : cmd
I : cmdsize
I : platform
I : minos
I : sdk
I : ntools
"""
)
class struct_build_version_command(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
self.func_formatter(cmd=token_cmd_fmt)
self.name_formatter("platform")
if data:
self.unpack(data, offset)
offset += len(self)
self.tools = []
for _ in range(self.ntools):
x = struct_build_tool_version(data, offset)
self.tools.append(x)
offset += len(x)
with Consts("lc.platform"):
PLATFORM_MACOS = 1
PLATFORM_IOS = 2
PLATFORM_TVOS = 3
PLATFORM_WATCHOS = 4
with Consts("lc.tool"):
TOOL_CLANG = 1
TOOL_SWIFT = 2
TOOL_LD = 3
CMD_TABLE = {
LC_SEGMENT: struct_segment_command,
LC_SEGMENT_64: struct_segment_command_64,
LC_SYMTAB: struct_symtab_command,
LC_SYMSEG: struct_symseg_command,
LC_THREAD: struct_thread_command,
LC_UNIXTHREAD: struct_thread_command,
LC_LOADFVMLIB: struct_fvmlib_command,
LC_IDFVMLIB: struct_fvmlib_command,
LC_IDENT: struct_ident_command,
LC_FVMFILE: struct_fvmfile_command,
LC_DYSYMTAB: struct_dysymtab_command,
LC_LOAD_DYLIB: struct_dylib_command,
LC_LOAD_WEAK_DYLIB: struct_dylib_command,
LC_ID_DYLIB: struct_dylib_command,
LC_REEXPORT_DYLIB: struct_dylib_command,
LC_LOAD_DYLINKER: struct_dylinker_command,
LC_ID_DYLINKER: struct_dylinker_command,
LC_SUB_FRAMEWORK: struct_sub_framework_command,
LC_SUB_CLIENT: struct_sub_client_command,
LC_SUB_LIBRARY: struct_sub_library_command,
LC_SUB_UMBRELLA: struct_sub_umbrella_command,
LC_TWOLEVEL_HINTS: struct_twolevel_hints_command,
LC_PREBOUND_DYLIB: struct_prebound_dylib_command,
LC_PREBIND_CKSUM: struct_prebind_cksum_command,
LC_UUID: struct_uuid_command,
LC_RPATH: struct_rpath_command,
LC_ROUTINES: struct_routines_command,
LC_ROUTINES_64: struct_routines_command_64,
LC_DYLD_INFO: struct_dyld_info_command,
LC_DYLD_INFO_ONLY: struct_dyld_info_command,
LC_ENCRYPTION_INFO: struct_encryption_info_command,
LC_CODE_SIGNATURE: struct_linkedit_data_command,
LC_SEGMENT_SPLIT_INFO: struct_linkedit_data_command,
LC_FUNCTION_STARTS: struct_linkedit_data_command,
LC_DATA_IN_CODE: struct_linkedit_data_command,
LC_DYLIB_CODE_SIGN_DRS: struct_linkedit_data_command,
LC_VERSION_MIN_MACOSX: struct_version_min_command,
LC_VERSION_MIN_IPHONEOS: struct_version_min_command,
LC_SOURCE_VERSION: struct_source_version_command,
LC_MAIN: struct_entry_point_command,
LC_NOTE: struct_note_command,
LC_BUILD_VERSION: struct_build_version_command,
}
[docs]@StructDefine(
"""
I : r_address
I : r_symbolnum
"""
)
class struct_relocation_info(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
I : index
"""
)
class struct_indirect_entry(MachoFormatter):
alt = "lc"
def __init__(self, data="", offset=0):
if data:
self.unpack(data, offset)