Source code for system.macho

# -*- 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 :> magic I :> nfat_arch """ ) class struct_fat_header(StructFormatter): alt = "mh" def __init__(self, data=None): self.name_formatter("magic") if data: self.unpack(data)
[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)
[docs]@StructDefine( """ I : magic i : cputype i : cpusubtype I : filetype I : ncmds I : sizeofcmds I : flags """ ) class struct_mach_header(StructFormatter): alt = "mh" def __init__(self, data=None): self.name_formatter("magic") self.name_formatter("cputype", "filetype") self.func_formatter(cpusubtype=self.subtype_fmt) self.flag_formatter("flags") if data: self.unpack(data) 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)
# ------------------------------------------------------------------------------
[docs]@StructDefine( """ I : magic I : cputype I : cpusubtype I : filetype I : ncmds I : sizeofcmds I : flags I : reserved """ ) class struct_mach_header_64(StructFormatter): alt = "mh" def __init__(self, data=None): self.name_formatter("magic") self.name_formatter("cputype", "filetype") self.func_formatter(cpusubtype=self.subtype_fmt) self.flag_formatter("flags") if data: self.unpack(data) 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]class MachoFormatter(StructFormatter): fkeys = defaultdict(default_formatter) def pack(self, data=None): res = StructFormatter.pack(self, data) return res.ljust(self.cmdsize, b"\0")
[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
[docs]@StructDefine( """ I : tool I : version """ ) class struct_build_tool_version(MachoFormatter): alt = "lc" def __init__(self, data="", offset=0): self.func_formatter(version=self.version_fmt) self.name_formatter("tool") 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))])
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)