diff options
Diffstat (limited to 'examples/python/mach_o.py')
-rwxr-xr-x | examples/python/mach_o.py | 1845 |
1 files changed, 0 insertions, 1845 deletions
diff --git a/examples/python/mach_o.py b/examples/python/mach_o.py deleted file mode 100755 index faa05ac83078..000000000000 --- a/examples/python/mach_o.py +++ /dev/null @@ -1,1845 +0,0 @@ -#!/usr/bin/python - -import cmd -import dict_utils -import file_extract -import optparse -import re -import struct -import string -import StringIO -import sys -import uuid - -# Mach header "magic" constants -MH_MAGIC = 0xfeedface -MH_CIGAM = 0xcefaedfe -MH_MAGIC_64 = 0xfeedfacf -MH_CIGAM_64 = 0xcffaedfe -FAT_MAGIC = 0xcafebabe -FAT_CIGAM = 0xbebafeca - -# Mach haeder "filetype" constants -MH_OBJECT = 0x00000001 -MH_EXECUTE = 0x00000002 -MH_FVMLIB = 0x00000003 -MH_CORE = 0x00000004 -MH_PRELOAD = 0x00000005 -MH_DYLIB = 0x00000006 -MH_DYLINKER = 0x00000007 -MH_BUNDLE = 0x00000008 -MH_DYLIB_STUB = 0x00000009 -MH_DSYM = 0x0000000a -MH_KEXT_BUNDLE = 0x0000000b - -# Mach haeder "flag" constant bits -MH_NOUNDEFS = 0x00000001 -MH_INCRLINK = 0x00000002 -MH_DYLDLINK = 0x00000004 -MH_BINDATLOAD = 0x00000008 -MH_PREBOUND = 0x00000010 -MH_SPLIT_SEGS = 0x00000020 -MH_LAZY_INIT = 0x00000040 -MH_TWOLEVEL = 0x00000080 -MH_FORCE_FLAT = 0x00000100 -MH_NOMULTIDEFS = 0x00000200 -MH_NOFIXPREBINDING = 0x00000400 -MH_PREBINDABLE = 0x00000800 -MH_ALLMODSBOUND = 0x00001000 -MH_SUBSECTIONS_VIA_SYMBOLS = 0x00002000 -MH_CANONICAL = 0x00004000 -MH_WEAK_DEFINES = 0x00008000 -MH_BINDS_TO_WEAK = 0x00010000 -MH_ALLOW_STACK_EXECUTION = 0x00020000 -MH_ROOT_SAFE = 0x00040000 -MH_SETUID_SAFE = 0x00080000 -MH_NO_REEXPORTED_DYLIBS = 0x00100000 -MH_PIE = 0x00200000 -MH_DEAD_STRIPPABLE_DYLIB = 0x00400000 -MH_HAS_TLV_DESCRIPTORS = 0x00800000 -MH_NO_HEAP_EXECUTION = 0x01000000 - -# Mach load command constants -LC_REQ_DYLD = 0x80000000 -LC_SEGMENT = 0x00000001 -LC_SYMTAB = 0x00000002 -LC_SYMSEG = 0x00000003 -LC_THREAD = 0x00000004 -LC_UNIXTHREAD = 0x00000005 -LC_LOADFVMLIB = 0x00000006 -LC_IDFVMLIB = 0x00000007 -LC_IDENT = 0x00000008 -LC_FVMFILE = 0x00000009 -LC_PREPAGE = 0x0000000a -LC_DYSYMTAB = 0x0000000b -LC_LOAD_DYLIB = 0x0000000c -LC_ID_DYLIB = 0x0000000d -LC_LOAD_DYLINKER = 0x0000000e -LC_ID_DYLINKER = 0x0000000f -LC_PREBOUND_DYLIB = 0x00000010 -LC_ROUTINES = 0x00000011 -LC_SUB_FRAMEWORK = 0x00000012 -LC_SUB_UMBRELLA = 0x00000013 -LC_SUB_CLIENT = 0x00000014 -LC_SUB_LIBRARY = 0x00000015 -LC_TWOLEVEL_HINTS = 0x00000016 -LC_PREBIND_CKSUM = 0x00000017 -LC_LOAD_WEAK_DYLIB = 0x00000018 | LC_REQ_DYLD -LC_SEGMENT_64 = 0x00000019 -LC_ROUTINES_64 = 0x0000001a -LC_UUID = 0x0000001b -LC_RPATH = 0x0000001c | LC_REQ_DYLD -LC_CODE_SIGNATURE = 0x0000001d -LC_SEGMENT_SPLIT_INFO = 0x0000001e -LC_REEXPORT_DYLIB = 0x0000001f | LC_REQ_DYLD -LC_LAZY_LOAD_DYLIB = 0x00000020 -LC_ENCRYPTION_INFO = 0x00000021 -LC_DYLD_INFO = 0x00000022 -LC_DYLD_INFO_ONLY = 0x00000022 | LC_REQ_DYLD -LC_LOAD_UPWARD_DYLIB = 0x00000023 | LC_REQ_DYLD -LC_VERSION_MIN_MACOSX = 0x00000024 -LC_VERSION_MIN_IPHONEOS = 0x00000025 -LC_FUNCTION_STARTS = 0x00000026 -LC_DYLD_ENVIRONMENT = 0x00000027 - -# Mach CPU constants -CPU_ARCH_MASK = 0xff000000 -CPU_ARCH_ABI64 = 0x01000000 -CPU_TYPE_ANY = 0xffffffff -CPU_TYPE_VAX = 1 -CPU_TYPE_MC680x0 = 6 -CPU_TYPE_I386 = 7 -CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64 -CPU_TYPE_MIPS = 8 -CPU_TYPE_MC98000 = 10 -CPU_TYPE_HPPA = 11 -CPU_TYPE_ARM = 12 -CPU_TYPE_MC88000 = 13 -CPU_TYPE_SPARC = 14 -CPU_TYPE_I860 = 15 -CPU_TYPE_ALPHA = 16 -CPU_TYPE_POWERPC = 18 -CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64 - -# VM protection constants -VM_PROT_READ = 1 -VM_PROT_WRITE = 2 -VM_PROT_EXECUTE = 4 - -# VM protection constants -N_STAB = 0xe0 -N_PEXT = 0x10 -N_TYPE = 0x0e -N_EXT = 0x01 - -# Values for nlist N_TYPE bits of the "Mach.NList.type" field. -N_UNDF = 0x0 -N_ABS = 0x2 -N_SECT = 0xe -N_PBUD = 0xc -N_INDR = 0xa - -# Section indexes for the "Mach.NList.sect_idx" fields -NO_SECT = 0 -MAX_SECT = 255 - -# Stab defines -N_GSYM = 0x20 -N_FNAME = 0x22 -N_FUN = 0x24 -N_STSYM = 0x26 -N_LCSYM = 0x28 -N_BNSYM = 0x2e -N_OPT = 0x3c -N_RSYM = 0x40 -N_SLINE = 0x44 -N_ENSYM = 0x4e -N_SSYM = 0x60 -N_SO = 0x64 -N_OSO = 0x66 -N_LSYM = 0x80 -N_BINCL = 0x82 -N_SOL = 0x84 -N_PARAMS = 0x86 -N_VERSION = 0x88 -N_OLEVEL = 0x8A -N_PSYM = 0xa0 -N_EINCL = 0xa2 -N_ENTRY = 0xa4 -N_LBRAC = 0xc0 -N_EXCL = 0xc2 -N_RBRAC = 0xe0 -N_BCOMM = 0xe2 -N_ECOMM = 0xe4 -N_ECOML = 0xe8 -N_LENG = 0xfe - -vm_prot_names = ['---', 'r--', '-w-', 'rw-', '--x', 'r-x', '-wx', 'rwx'] - - -def dump_memory(base_addr, data, hex_bytes_len, num_per_line): - hex_bytes = data.encode('hex') - if hex_bytes_len == -1: - hex_bytes_len = len(hex_bytes) - addr = base_addr - ascii_str = '' - i = 0 - while i < hex_bytes_len: - if ((i / 2) % num_per_line) == 0: - if i > 0: - print ' %s' % (ascii_str) - ascii_str = '' - print '0x%8.8x:' % (addr + i), - hex_byte = hex_bytes[i:i + 2] - print hex_byte, - int_byte = int(hex_byte, 16) - ascii_char = '%c' % (int_byte) - if int_byte >= 32 and int_byte < 127: - ascii_str += ascii_char - else: - ascii_str += '.' - i = i + 2 - if ascii_str: - if (i / 2) % num_per_line: - padding = num_per_line - ((i / 2) % num_per_line) - else: - padding = 0 - print '%*s%s' % (padding * 3 + 1, '', ascii_str) - print - - -class TerminalColors: - '''Simple terminal colors class''' - - def __init__(self, enabled=True): - # TODO: discover terminal type from "file" and disable if - # it can't handle the color codes - self.enabled = enabled - - def reset(self): - '''Reset all terminal colors and formatting.''' - if self.enabled: - return "\x1b[0m" - return '' - - def bold(self, on=True): - '''Enable or disable bold depending on the "on" parameter.''' - if self.enabled: - if on: - return "\x1b[1m" - else: - return "\x1b[22m" - return '' - - def italics(self, on=True): - '''Enable or disable italics depending on the "on" parameter.''' - if self.enabled: - if on: - return "\x1b[3m" - else: - return "\x1b[23m" - return '' - - def underline(self, on=True): - '''Enable or disable underline depending on the "on" parameter.''' - if self.enabled: - if on: - return "\x1b[4m" - else: - return "\x1b[24m" - return '' - - def inverse(self, on=True): - '''Enable or disable inverse depending on the "on" parameter.''' - if self.enabled: - if on: - return "\x1b[7m" - else: - return "\x1b[27m" - return '' - - def strike(self, on=True): - '''Enable or disable strike through depending on the "on" parameter.''' - if self.enabled: - if on: - return "\x1b[9m" - else: - return "\x1b[29m" - return '' - - def black(self, fg=True): - '''Set the foreground or background color to black. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[30m" - else: - return "\x1b[40m" - return '' - - def red(self, fg=True): - '''Set the foreground or background color to red. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[31m" - else: - return "\x1b[41m" - return '' - - def green(self, fg=True): - '''Set the foreground or background color to green. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[32m" - else: - return "\x1b[42m" - return '' - - def yellow(self, fg=True): - '''Set the foreground or background color to yellow. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[43m" - else: - return "\x1b[33m" - return '' - - def blue(self, fg=True): - '''Set the foreground or background color to blue. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[34m" - else: - return "\x1b[44m" - return '' - - def magenta(self, fg=True): - '''Set the foreground or background color to magenta. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[35m" - else: - return "\x1b[45m" - return '' - - def cyan(self, fg=True): - '''Set the foreground or background color to cyan. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[36m" - else: - return "\x1b[46m" - return '' - - def white(self, fg=True): - '''Set the foreground or background color to white. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[37m" - else: - return "\x1b[47m" - return '' - - def default(self, fg=True): - '''Set the foreground or background color to the default. - The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' - if self.enabled: - if fg: - return "\x1b[39m" - else: - return "\x1b[49m" - return '' - - -def swap_unpack_char(): - """Returns the unpack prefix that will for non-native endian-ness.""" - if struct.pack('H', 1).startswith("\x00"): - return '<' - return '>' - - -def dump_hex_bytes(addr, s, bytes_per_line=16): - i = 0 - line = '' - for ch in s: - if (i % bytes_per_line) == 0: - if line: - print line - line = '%#8.8x: ' % (addr + i) - line += "%02X " % ord(ch) - i += 1 - print line - - -def dump_hex_byte_string_diff(addr, a, b, bytes_per_line=16): - i = 0 - line = '' - a_len = len(a) - b_len = len(b) - if a_len < b_len: - max_len = b_len - else: - max_len = a_len - tty_colors = TerminalColors(True) - for i in range(max_len): - ch = None - if i < a_len: - ch_a = a[i] - ch = ch_a - else: - ch_a = None - if i < b_len: - ch_b = b[i] - if not ch: - ch = ch_b - else: - ch_b = None - mismatch = ch_a != ch_b - if (i % bytes_per_line) == 0: - if line: - print line - line = '%#8.8x: ' % (addr + i) - if mismatch: - line += tty_colors.red() - line += "%02X " % ord(ch) - if mismatch: - line += tty_colors.default() - i += 1 - - print line - - -class Mach: - """Class that does everything mach-o related""" - - class Arch: - """Class that implements mach-o architectures""" - - def __init__(self, c=0, s=0): - self.cpu = c - self.sub = s - - def set_cpu_type(self, c): - self.cpu = c - - def set_cpu_subtype(self, s): - self.sub = s - - def set_arch(self, c, s): - self.cpu = c - self.sub = s - - def is_64_bit(self): - return (self.cpu & CPU_ARCH_ABI64) != 0 - - cpu_infos = [ - ["arm", CPU_TYPE_ARM, CPU_TYPE_ANY], - ["arm", CPU_TYPE_ARM, 0], - ["armv4", CPU_TYPE_ARM, 5], - ["armv6", CPU_TYPE_ARM, 6], - ["armv5", CPU_TYPE_ARM, 7], - ["xscale", CPU_TYPE_ARM, 8], - ["armv7", CPU_TYPE_ARM, 9], - ["armv7f", CPU_TYPE_ARM, 10], - ["armv7s", CPU_TYPE_ARM, 11], - ["armv7k", CPU_TYPE_ARM, 12], - ["armv7m", CPU_TYPE_ARM, 15], - ["armv7em", CPU_TYPE_ARM, 16], - ["ppc", CPU_TYPE_POWERPC, CPU_TYPE_ANY], - ["ppc", CPU_TYPE_POWERPC, 0], - ["ppc601", CPU_TYPE_POWERPC, 1], - ["ppc602", CPU_TYPE_POWERPC, 2], - ["ppc603", CPU_TYPE_POWERPC, 3], - ["ppc603e", CPU_TYPE_POWERPC, 4], - ["ppc603ev", CPU_TYPE_POWERPC, 5], - ["ppc604", CPU_TYPE_POWERPC, 6], - ["ppc604e", CPU_TYPE_POWERPC, 7], - ["ppc620", CPU_TYPE_POWERPC, 8], - ["ppc750", CPU_TYPE_POWERPC, 9], - ["ppc7400", CPU_TYPE_POWERPC, 10], - ["ppc7450", CPU_TYPE_POWERPC, 11], - ["ppc970", CPU_TYPE_POWERPC, 100], - ["ppc64", CPU_TYPE_POWERPC64, 0], - ["ppc970-64", CPU_TYPE_POWERPC64, 100], - ["i386", CPU_TYPE_I386, 3], - ["i486", CPU_TYPE_I386, 4], - ["i486sx", CPU_TYPE_I386, 0x84], - ["i386", CPU_TYPE_I386, CPU_TYPE_ANY], - ["x86_64", CPU_TYPE_X86_64, 3], - ["x86_64", CPU_TYPE_X86_64, CPU_TYPE_ANY], - ] - - def __str__(self): - for info in self.cpu_infos: - if self.cpu == info[1] and (self.sub & 0x00ffffff) == info[2]: - return info[0] - return "{0}.{1}".format(self.cpu, self.sub) - - class Magic(dict_utils.Enum): - - enum = { - 'MH_MAGIC': MH_MAGIC, - 'MH_CIGAM': MH_CIGAM, - 'MH_MAGIC_64': MH_MAGIC_64, - 'MH_CIGAM_64': MH_CIGAM_64, - 'FAT_MAGIC': FAT_MAGIC, - 'FAT_CIGAM': FAT_CIGAM - } - - def __init__(self, initial_value=0): - dict_utils.Enum.__init__(self, initial_value, self.enum) - - def is_skinny_mach_file(self): - return self.value == MH_MAGIC or self.value == MH_CIGAM or self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 - - def is_universal_mach_file(self): - return self.value == FAT_MAGIC or self.value == FAT_CIGAM - - def unpack(self, data): - data.set_byte_order('native') - self.value = data.get_uint32() - - def get_byte_order(self): - if self.value == MH_CIGAM or self.value == MH_CIGAM_64 or self.value == FAT_CIGAM: - return swap_unpack_char() - else: - return '=' - - def is_64_bit(self): - return self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 - - def __init__(self): - self.magic = Mach.Magic() - self.content = None - self.path = None - - def extract(self, path, extractor): - self.path = path - self.unpack(extractor) - - def parse(self, path): - self.path = path - try: - f = open(self.path) - file_extractor = file_extract.FileExtract(f, '=') - self.unpack(file_extractor) - # f.close() - except IOError as xxx_todo_changeme: - (errno, strerror) = xxx_todo_changeme.args - print "I/O error({0}): {1}".format(errno, strerror) - except ValueError: - print "Could not convert data to an integer." - except: - print "Unexpected error:", sys.exc_info()[0] - raise - - def compare(self, rhs): - self.content.compare(rhs.content) - - def dump(self, options=None): - self.content.dump(options) - - def dump_header(self, dump_description=True, options=None): - self.content.dump_header(dump_description, options) - - def dump_load_commands(self, dump_description=True, options=None): - self.content.dump_load_commands(dump_description, options) - - def dump_sections(self, dump_description=True, options=None): - self.content.dump_sections(dump_description, options) - - def dump_section_contents(self, options): - self.content.dump_section_contents(options) - - def dump_symtab(self, dump_description=True, options=None): - self.content.dump_symtab(dump_description, options) - - def dump_symbol_names_matching_regex(self, regex, file=None): - self.content.dump_symbol_names_matching_regex(regex, file) - - def description(self): - return self.content.description() - - def unpack(self, data): - self.magic.unpack(data) - if self.magic.is_skinny_mach_file(): - self.content = Mach.Skinny(self.path) - elif self.magic.is_universal_mach_file(): - self.content = Mach.Universal(self.path) - else: - self.content = None - - if self.content is not None: - self.content.unpack(data, self.magic) - - def is_valid(self): - return self.content is not None - - class Universal: - - def __init__(self, path): - self.path = path - self.type = 'universal' - self.file_off = 0 - self.magic = None - self.nfat_arch = 0 - self.archs = list() - - def description(self): - s = '%#8.8x: %s (' % (self.file_off, self.path) - archs_string = '' - for arch in self.archs: - if len(archs_string): - archs_string += ', ' - archs_string += '%s' % arch.arch - s += archs_string - s += ')' - return s - - def unpack(self, data, magic=None): - self.file_off = data.tell() - if magic is None: - self.magic = Mach.Magic() - self.magic.unpack(data) - else: - self.magic = magic - self.file_off = self.file_off - 4 - # Universal headers are always in big endian - data.set_byte_order('big') - self.nfat_arch = data.get_uint32() - for i in range(self.nfat_arch): - self.archs.append(Mach.Universal.ArchInfo()) - self.archs[i].unpack(data) - for i in range(self.nfat_arch): - self.archs[i].mach = Mach.Skinny(self.path) - data.seek(self.archs[i].offset, 0) - skinny_magic = Mach.Magic() - skinny_magic.unpack(data) - self.archs[i].mach.unpack(data, skinny_magic) - - def compare(self, rhs): - print 'error: comparing two universal files is not supported yet' - return False - - def dump(self, options): - if options.dump_header: - print - print "Universal Mach File: magic = %s, nfat_arch = %u" % (self.magic, self.nfat_arch) - print - if self.nfat_arch > 0: - if options.dump_header: - self.archs[0].dump_header(True, options) - for i in range(self.nfat_arch): - self.archs[i].dump_flat(options) - if options.dump_header: - print - for i in range(self.nfat_arch): - self.archs[i].mach.dump(options) - - def dump_header(self, dump_description=True, options=None): - if dump_description: - print self.description() - for i in range(self.nfat_arch): - self.archs[i].mach.dump_header(True, options) - print - - def dump_load_commands(self, dump_description=True, options=None): - if dump_description: - print self.description() - for i in range(self.nfat_arch): - self.archs[i].mach.dump_load_commands(True, options) - print - - def dump_sections(self, dump_description=True, options=None): - if dump_description: - print self.description() - for i in range(self.nfat_arch): - self.archs[i].mach.dump_sections(True, options) - print - - def dump_section_contents(self, options): - for i in range(self.nfat_arch): - self.archs[i].mach.dump_section_contents(options) - print - - def dump_symtab(self, dump_description=True, options=None): - if dump_description: - print self.description() - for i in range(self.nfat_arch): - self.archs[i].mach.dump_symtab(True, options) - print - - def dump_symbol_names_matching_regex(self, regex, file=None): - for i in range(self.nfat_arch): - self.archs[i].mach.dump_symbol_names_matching_regex( - regex, file) - - class ArchInfo: - - def __init__(self): - self.arch = Mach.Arch(0, 0) - self.offset = 0 - self.size = 0 - self.align = 0 - self.mach = None - - def unpack(self, data): - # Universal headers are always in big endian - data.set_byte_order('big') - self.arch.cpu, self.arch.sub, self.offset, self.size, self.align = data.get_n_uint32( - 5) - - def dump_header(self, dump_description=True, options=None): - if options.verbose: - print "CPU SUBTYPE OFFSET SIZE ALIGN" - print "---------- ---------- ---------- ---------- ----------" - else: - print "ARCH FILEOFFSET FILESIZE ALIGN" - print "---------- ---------- ---------- ----------" - - def dump_flat(self, options): - if options.verbose: - print "%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) - else: - print "%-10s %#8.8x %#8.8x %#8.8x" % (self.arch, self.offset, self.size, self.align) - - def dump(self): - print " cputype: %#8.8x" % self.arch.cpu - print "cpusubtype: %#8.8x" % self.arch.sub - print " offset: %#8.8x" % self.offset - print " size: %#8.8x" % self.size - print " align: %#8.8x" % self.align - - def __str__(self): - return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % ( - self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) - - def __repr__(self): - return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % ( - self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) - - class Flags: - - def __init__(self, b): - self.bits = b - - def __str__(self): - s = '' - if self.bits & MH_NOUNDEFS: - s += 'MH_NOUNDEFS | ' - if self.bits & MH_INCRLINK: - s += 'MH_INCRLINK | ' - if self.bits & MH_DYLDLINK: - s += 'MH_DYLDLINK | ' - if self.bits & MH_BINDATLOAD: - s += 'MH_BINDATLOAD | ' - if self.bits & MH_PREBOUND: - s += 'MH_PREBOUND | ' - if self.bits & MH_SPLIT_SEGS: - s += 'MH_SPLIT_SEGS | ' - if self.bits & MH_LAZY_INIT: - s += 'MH_LAZY_INIT | ' - if self.bits & MH_TWOLEVEL: - s += 'MH_TWOLEVEL | ' - if self.bits & MH_FORCE_FLAT: - s += 'MH_FORCE_FLAT | ' - if self.bits & MH_NOMULTIDEFS: - s += 'MH_NOMULTIDEFS | ' - if self.bits & MH_NOFIXPREBINDING: - s += 'MH_NOFIXPREBINDING | ' - if self.bits & MH_PREBINDABLE: - s += 'MH_PREBINDABLE | ' - if self.bits & MH_ALLMODSBOUND: - s += 'MH_ALLMODSBOUND | ' - if self.bits & MH_SUBSECTIONS_VIA_SYMBOLS: - s += 'MH_SUBSECTIONS_VIA_SYMBOLS | ' - if self.bits & MH_CANONICAL: - s += 'MH_CANONICAL | ' - if self.bits & MH_WEAK_DEFINES: - s += 'MH_WEAK_DEFINES | ' - if self.bits & MH_BINDS_TO_WEAK: - s += 'MH_BINDS_TO_WEAK | ' - if self.bits & MH_ALLOW_STACK_EXECUTION: - s += 'MH_ALLOW_STACK_EXECUTION | ' - if self.bits & MH_ROOT_SAFE: - s += 'MH_ROOT_SAFE | ' - if self.bits & MH_SETUID_SAFE: - s += 'MH_SETUID_SAFE | ' - if self.bits & MH_NO_REEXPORTED_DYLIBS: - s += 'MH_NO_REEXPORTED_DYLIBS | ' - if self.bits & MH_PIE: - s += 'MH_PIE | ' - if self.bits & MH_DEAD_STRIPPABLE_DYLIB: - s += 'MH_DEAD_STRIPPABLE_DYLIB | ' - if self.bits & MH_HAS_TLV_DESCRIPTORS: - s += 'MH_HAS_TLV_DESCRIPTORS | ' - if self.bits & MH_NO_HEAP_EXECUTION: - s += 'MH_NO_HEAP_EXECUTION | ' - # Strip the trailing " |" if we have any flags - if len(s) > 0: - s = s[0:-2] - return s - - class FileType(dict_utils.Enum): - - enum = { - 'MH_OBJECT': MH_OBJECT, - 'MH_EXECUTE': MH_EXECUTE, - 'MH_FVMLIB': MH_FVMLIB, - 'MH_CORE': MH_CORE, - 'MH_PRELOAD': MH_PRELOAD, - 'MH_DYLIB': MH_DYLIB, - 'MH_DYLINKER': MH_DYLINKER, - 'MH_BUNDLE': MH_BUNDLE, - 'MH_DYLIB_STUB': MH_DYLIB_STUB, - 'MH_DSYM': MH_DSYM, - 'MH_KEXT_BUNDLE': MH_KEXT_BUNDLE - } - - def __init__(self, initial_value=0): - dict_utils.Enum.__init__(self, initial_value, self.enum) - - class Skinny: - - def __init__(self, path): - self.path = path - self.type = 'skinny' - self.data = None - self.file_off = 0 - self.magic = 0 - self.arch = Mach.Arch(0, 0) - self.filetype = Mach.FileType(0) - self.ncmds = 0 - self.sizeofcmds = 0 - self.flags = Mach.Flags(0) - self.uuid = None - self.commands = list() - self.segments = list() - self.sections = list() - self.symbols = list() - self.sections.append(Mach.Section()) - - def description(self): - return '%#8.8x: %s (%s)' % (self.file_off, self.path, self.arch) - - def unpack(self, data, magic=None): - self.data = data - self.file_off = data.tell() - if magic is None: - self.magic = Mach.Magic() - self.magic.unpack(data) - else: - self.magic = magic - self.file_off = self.file_off - 4 - data.set_byte_order(self.magic.get_byte_order()) - self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, bits = data.get_n_uint32( - 6) - self.flags.bits = bits - - if self.is_64_bit(): - data.get_uint32() # Skip reserved word in mach_header_64 - - for i in range(0, self.ncmds): - lc = self.unpack_load_command(data) - self.commands.append(lc) - - def get_data(self): - if self.data: - self.data.set_byte_order(self.magic.get_byte_order()) - return self.data - return None - - def unpack_load_command(self, data): - lc = Mach.LoadCommand() - lc.unpack(self, data) - lc_command = lc.command.get_enum_value() - if (lc_command == LC_SEGMENT or - lc_command == LC_SEGMENT_64): - lc = Mach.SegmentLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_LOAD_DYLIB or - lc_command == LC_ID_DYLIB or - lc_command == LC_LOAD_WEAK_DYLIB or - lc_command == LC_REEXPORT_DYLIB): - lc = Mach.DylibLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_LOAD_DYLINKER or - lc_command == LC_SUB_FRAMEWORK or - lc_command == LC_SUB_CLIENT or - lc_command == LC_SUB_UMBRELLA or - lc_command == LC_SUB_LIBRARY or - lc_command == LC_ID_DYLINKER or - lc_command == LC_RPATH): - lc = Mach.LoadDYLDLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_DYLD_INFO_ONLY): - lc = Mach.DYLDInfoOnlyLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_SYMTAB): - lc = Mach.SymtabLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_DYSYMTAB): - lc = Mach.DYLDSymtabLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_UUID): - lc = Mach.UUIDLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_CODE_SIGNATURE or - lc_command == LC_SEGMENT_SPLIT_INFO or - lc_command == LC_FUNCTION_STARTS): - lc = Mach.DataBlobLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_UNIXTHREAD): - lc = Mach.UnixThreadLoadCommand(lc) - lc.unpack(self, data) - elif (lc_command == LC_ENCRYPTION_INFO): - lc = Mach.EncryptionInfoLoadCommand(lc) - lc.unpack(self, data) - lc.skip(data) - return lc - - def compare(self, rhs): - print "\nComparing:" - print "a) %s %s" % (self.arch, self.path) - print "b) %s %s" % (rhs.arch, rhs.path) - result = True - if self.type == rhs.type: - for lhs_section in self.sections[1:]: - rhs_section = rhs.get_section_by_section(lhs_section) - if rhs_section: - print 'comparing %s.%s...' % (lhs_section.segname, lhs_section.sectname), - sys.stdout.flush() - lhs_data = lhs_section.get_contents(self) - rhs_data = rhs_section.get_contents(rhs) - if lhs_data and rhs_data: - if lhs_data == rhs_data: - print 'ok' - else: - lhs_data_len = len(lhs_data) - rhs_data_len = len(rhs_data) - # if lhs_data_len < rhs_data_len: - # if lhs_data == rhs_data[0:lhs_data_len]: - # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) - # else: - # # TODO: check padding - # result = False - # elif lhs_data_len > rhs_data_len: - # if lhs_data[0:rhs_data_len] == rhs_data: - # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) - # else: - # # TODO: check padding - # result = False - # else: - result = False - print 'error: sections differ' - # print 'a) %s' % (lhs_section) - # dump_hex_byte_string_diff(0, lhs_data, rhs_data) - # print 'b) %s' % (rhs_section) - # dump_hex_byte_string_diff(0, rhs_data, lhs_data) - elif lhs_data and not rhs_data: - print 'error: section data missing from b:' - print 'a) %s' % (lhs_section) - print 'b) %s' % (rhs_section) - result = False - elif not lhs_data and rhs_data: - print 'error: section data missing from a:' - print 'a) %s' % (lhs_section) - print 'b) %s' % (rhs_section) - result = False - elif lhs_section.offset or rhs_section.offset: - print 'error: section data missing for both a and b:' - print 'a) %s' % (lhs_section) - print 'b) %s' % (rhs_section) - result = False - else: - print 'ok' - else: - result = False - print 'error: section %s is missing in %s' % (lhs_section.sectname, rhs.path) - else: - print 'error: comaparing a %s mach-o file with a %s mach-o file is not supported' % (self.type, rhs.type) - result = False - if not result: - print 'error: mach files differ' - return result - - def dump_header(self, dump_description=True, options=None): - if options.verbose: - print "MAGIC CPU SUBTYPE FILETYPE NUM CMDS SIZE CMDS FLAGS" - print "---------- ---------- ---------- ---------- -------- ---------- ----------" - else: - print "MAGIC ARCH FILETYPE NUM CMDS SIZE CMDS FLAGS" - print "------------ ---------- -------------- -------- ---------- ----------" - - def dump_flat(self, options): - if options.verbose: - print "%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x" % (self.magic, self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, self.flags.bits) - else: - print "%-12s %-10s %-14s %#8u %#8.8x %s" % (self.magic, self.arch, self.filetype, self.ncmds, self.sizeofcmds, self.flags) - - def dump(self, options): - if options.dump_header: - self.dump_header(True, options) - if options.dump_load_commands: - self.dump_load_commands(False, options) - if options.dump_sections: - self.dump_sections(False, options) - if options.section_names: - self.dump_section_contents(options) - if options.dump_symtab: - self.get_symtab() - if len(self.symbols): - self.dump_sections(False, options) - else: - print "No symbols" - if options.find_mangled: - self.dump_symbol_names_matching_regex(re.compile('^_?_Z')) - - def dump_header(self, dump_description=True, options=None): - if dump_description: - print self.description() - print "Mach Header" - print " magic: %#8.8x %s" % (self.magic.value, self.magic) - print " cputype: %#8.8x %s" % (self.arch.cpu, self.arch) - print " cpusubtype: %#8.8x" % self.arch.sub - print " filetype: %#8.8x %s" % (self.filetype.get_enum_value(), self.filetype.get_enum_name()) - print " ncmds: %#8.8x %u" % (self.ncmds, self.ncmds) - print " sizeofcmds: %#8.8x" % self.sizeofcmds - print " flags: %#8.8x %s" % (self.flags.bits, self.flags) - - def dump_load_commands(self, dump_description=True, options=None): - if dump_description: - print self.description() - for lc in self.commands: - print lc - - def get_section_by_name(self, name): - for section in self.sections: - if section.sectname and section.sectname == name: - return section - return None - - def get_section_by_section(self, other_section): - for section in self.sections: - if section.sectname == other_section.sectname and section.segname == other_section.segname: - return section - return None - - def dump_sections(self, dump_description=True, options=None): - if dump_description: - print self.description() - num_sections = len(self.sections) - if num_sections > 1: - self.sections[1].dump_header() - for sect_idx in range(1, num_sections): - print "%s" % self.sections[sect_idx] - - def dump_section_contents(self, options): - saved_section_to_disk = False - for sectname in options.section_names: - section = self.get_section_by_name(sectname) - if section: - sect_bytes = section.get_contents(self) - if options.outfile: - if not saved_section_to_disk: - outfile = open(options.outfile, 'w') - if options.extract_modules: - # print "Extracting modules from mach file..." - data = file_extract.FileExtract( - StringIO.StringIO(sect_bytes), self.data.byte_order) - version = data.get_uint32() - num_modules = data.get_uint32() - # print "version = %u, num_modules = %u" % - # (version, num_modules) - for i in range(num_modules): - data_offset = data.get_uint64() - data_size = data.get_uint64() - name_offset = data.get_uint32() - language = data.get_uint32() - flags = data.get_uint32() - data.seek(name_offset) - module_name = data.get_c_string() - # print "module[%u] data_offset = %#16.16x, - # data_size = %#16.16x, name_offset = - # %#16.16x (%s), language = %u, flags = - # %#x" % (i, data_offset, data_size, - # name_offset, module_name, language, - # flags) - data.seek(data_offset) - outfile.write(data.read_size(data_size)) - else: - print "Saving section %s to '%s'" % (sectname, options.outfile) - outfile.write(sect_bytes) - outfile.close() - saved_section_to_disk = True - else: - print "error: you can only save a single section to disk at a time, skipping section '%s'" % (sectname) - else: - print 'section %s:\n' % (sectname) - section.dump_header() - print '%s\n' % (section) - dump_memory(0, sect_bytes, options.max_count, 16) - else: - print 'error: no section named "%s" was found' % (sectname) - - def get_segment(self, segname): - if len(self.segments) == 1 and self.segments[0].segname == '': - return self.segments[0] - for segment in self.segments: - if segment.segname == segname: - return segment - return None - - def get_first_load_command(self, lc_enum_value): - for lc in self.commands: - if lc.command.value == lc_enum_value: - return lc - return None - - def get_symtab(self): - if self.data and not self.symbols: - lc_symtab = self.get_first_load_command(LC_SYMTAB) - if lc_symtab: - symtab_offset = self.file_off - if self.data.is_in_memory(): - linkedit_segment = self.get_segment('__LINKEDIT') - if linkedit_segment: - linkedit_vmaddr = linkedit_segment.vmaddr - linkedit_fileoff = linkedit_segment.fileoff - symtab_offset = linkedit_vmaddr + lc_symtab.symoff - linkedit_fileoff - symtab_offset = linkedit_vmaddr + lc_symtab.stroff - linkedit_fileoff - else: - symtab_offset += lc_symtab.symoff - - self.data.seek(symtab_offset) - is_64 = self.is_64_bit() - for i in range(lc_symtab.nsyms): - nlist = Mach.NList() - nlist.unpack(self, self.data, lc_symtab) - self.symbols.append(nlist) - else: - print "no LC_SYMTAB" - - def dump_symtab(self, dump_description=True, options=None): - self.get_symtab() - if dump_description: - print self.description() - for i, symbol in enumerate(self.symbols): - print '[%5u] %s' % (i, symbol) - - def dump_symbol_names_matching_regex(self, regex, file=None): - self.get_symtab() - for symbol in self.symbols: - if symbol.name and regex.search(symbol.name): - print symbol.name - if file: - file.write('%s\n' % (symbol.name)) - - def is_64_bit(self): - return self.magic.is_64_bit() - - class LoadCommand: - - class Command(dict_utils.Enum): - enum = { - 'LC_SEGMENT': LC_SEGMENT, - 'LC_SYMTAB': LC_SYMTAB, - 'LC_SYMSEG': LC_SYMSEG, - 'LC_THREAD': LC_THREAD, - 'LC_UNIXTHREAD': LC_UNIXTHREAD, - 'LC_LOADFVMLIB': LC_LOADFVMLIB, - 'LC_IDFVMLIB': LC_IDFVMLIB, - 'LC_IDENT': LC_IDENT, - 'LC_FVMFILE': LC_FVMFILE, - 'LC_PREPAGE': LC_PREPAGE, - 'LC_DYSYMTAB': LC_DYSYMTAB, - 'LC_LOAD_DYLIB': LC_LOAD_DYLIB, - 'LC_ID_DYLIB': LC_ID_DYLIB, - 'LC_LOAD_DYLINKER': LC_LOAD_DYLINKER, - 'LC_ID_DYLINKER': LC_ID_DYLINKER, - 'LC_PREBOUND_DYLIB': LC_PREBOUND_DYLIB, - 'LC_ROUTINES': LC_ROUTINES, - 'LC_SUB_FRAMEWORK': LC_SUB_FRAMEWORK, - 'LC_SUB_UMBRELLA': LC_SUB_UMBRELLA, - 'LC_SUB_CLIENT': LC_SUB_CLIENT, - 'LC_SUB_LIBRARY': LC_SUB_LIBRARY, - 'LC_TWOLEVEL_HINTS': LC_TWOLEVEL_HINTS, - 'LC_PREBIND_CKSUM': LC_PREBIND_CKSUM, - 'LC_LOAD_WEAK_DYLIB': LC_LOAD_WEAK_DYLIB, - 'LC_SEGMENT_64': LC_SEGMENT_64, - 'LC_ROUTINES_64': LC_ROUTINES_64, - 'LC_UUID': LC_UUID, - 'LC_RPATH': LC_RPATH, - 'LC_CODE_SIGNATURE': LC_CODE_SIGNATURE, - 'LC_SEGMENT_SPLIT_INFO': LC_SEGMENT_SPLIT_INFO, - 'LC_REEXPORT_DYLIB': LC_REEXPORT_DYLIB, - 'LC_LAZY_LOAD_DYLIB': LC_LAZY_LOAD_DYLIB, - 'LC_ENCRYPTION_INFO': LC_ENCRYPTION_INFO, - 'LC_DYLD_INFO': LC_DYLD_INFO, - 'LC_DYLD_INFO_ONLY': LC_DYLD_INFO_ONLY, - 'LC_LOAD_UPWARD_DYLIB': LC_LOAD_UPWARD_DYLIB, - 'LC_VERSION_MIN_MACOSX': LC_VERSION_MIN_MACOSX, - 'LC_VERSION_MIN_IPHONEOS': LC_VERSION_MIN_IPHONEOS, - 'LC_FUNCTION_STARTS': LC_FUNCTION_STARTS, - 'LC_DYLD_ENVIRONMENT': LC_DYLD_ENVIRONMENT - } - - def __init__(self, initial_value=0): - dict_utils.Enum.__init__(self, initial_value, self.enum) - - def __init__(self, c=None, l=0, o=0): - if c is not None: - self.command = c - else: - self.command = Mach.LoadCommand.Command(0) - self.length = l - self.file_off = o - - def unpack(self, mach_file, data): - self.file_off = data.tell() - self.command.value, self.length = data.get_n_uint32(2) - - def skip(self, data): - data.seek(self.file_off + self.length, 0) - - def __str__(self): - lc_name = self.command.get_enum_name() - return '%#8.8x: <%#4.4x> %-24s' % (self.file_off, - self.length, lc_name) - - class Section: - - def __init__(self): - self.index = 0 - self.is_64 = False - self.sectname = None - self.segname = None - self.addr = 0 - self.size = 0 - self.offset = 0 - self.align = 0 - self.reloff = 0 - self.nreloc = 0 - self.flags = 0 - self.reserved1 = 0 - self.reserved2 = 0 - self.reserved3 = 0 - - def unpack(self, is_64, data): - self.is_64 = is_64 - self.sectname = data.get_fixed_length_c_string(16, '', True) - self.segname = data.get_fixed_length_c_string(16, '', True) - if self.is_64: - self.addr, self.size = data.get_n_uint64(2) - self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3 = data.get_n_uint32( - 8) - else: - self.addr, self.size = data.get_n_uint32(2) - self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2 = data.get_n_uint32( - 7) - - def dump_header(self): - if self.is_64: - print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 RESERVED3 NAME" - print "===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------" - else: - print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 NAME" - print "===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------" - - def __str__(self): - if self.is_64: - return "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % ( - self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3, self.segname, self.sectname) - else: - return "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % ( - self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.segname, self.sectname) - - def get_contents(self, mach_file): - '''Get the section contents as a python string''' - if self.size > 0 and mach_file.get_segment( - self.segname).filesize > 0: - data = mach_file.get_data() - if data: - section_data_offset = mach_file.file_off + self.offset - # print '%s.%s is at offset 0x%x with size 0x%x' % - # (self.segname, self.sectname, section_data_offset, - # self.size) - data.push_offset_and_seek(section_data_offset) - bytes = data.read_size(self.size) - data.pop_offset_and_seek() - return bytes - return None - - class DylibLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.name = None - self.timestamp = 0 - self.current_version = 0 - self.compatibility_version = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - name_offset, self.timestamp, self.current_version, self.compatibility_version = data.get_n_uint32( - 4) - data.seek(self.file_off + name_offset, 0) - self.name = data.get_fixed_length_c_string(self.length - 24) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "%#8.8x %#8.8x %#8.8x " % (self.timestamp, - self.current_version, - self.compatibility_version) - s += self.name - return s - - class LoadDYLDLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.name = None - - def unpack(self, mach_file, data): - data.get_uint32() - self.name = data.get_fixed_length_c_string(self.length - 12) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "%s" % self.name - return s - - class UnixThreadLoadCommand(LoadCommand): - - class ThreadState: - - def __init__(self): - self.flavor = 0 - self.count = 0 - self.register_values = list() - - def unpack(self, data): - self.flavor, self.count = data.get_n_uint32(2) - self.register_values = data.get_n_uint32(self.count) - - def __str__(self): - s = "flavor = %u, count = %u, regs =" % ( - self.flavor, self.count) - i = 0 - for register_value in self.register_values: - if i % 8 == 0: - s += "\n " - s += " %#8.8x" % register_value - i += 1 - return s - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.reg_sets = list() - - def unpack(self, mach_file, data): - reg_set = Mach.UnixThreadLoadCommand.ThreadState() - reg_set.unpack(data) - self.reg_sets.append(reg_set) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - for reg_set in self.reg_sets: - s += "%s" % reg_set - return s - - class DYLDInfoOnlyLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.rebase_off = 0 - self.rebase_size = 0 - self.bind_off = 0 - self.bind_size = 0 - self.weak_bind_off = 0 - self.weak_bind_size = 0 - self.lazy_bind_off = 0 - self.lazy_bind_size = 0 - self.export_off = 0 - self.export_size = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - self.rebase_off, self.rebase_size, self.bind_off, self.bind_size, self.weak_bind_off, self.weak_bind_size, self.lazy_bind_off, self.lazy_bind_size, self.export_off, self.export_size = data.get_n_uint32( - 10) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "rebase_off = %#8.8x, rebase_size = %u, " % ( - self.rebase_off, self.rebase_size) - s += "bind_off = %#8.8x, bind_size = %u, " % ( - self.bind_off, self.bind_size) - s += "weak_bind_off = %#8.8x, weak_bind_size = %u, " % ( - self.weak_bind_off, self.weak_bind_size) - s += "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % ( - self.lazy_bind_off, self.lazy_bind_size) - s += "export_off = %#8.8x, export_size = %u, " % ( - self.export_off, self.export_size) - return s - - class DYLDSymtabLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.ilocalsym = 0 - self.nlocalsym = 0 - self.iextdefsym = 0 - self.nextdefsym = 0 - self.iundefsym = 0 - self.nundefsym = 0 - self.tocoff = 0 - self.ntoc = 0 - self.modtaboff = 0 - self.nmodtab = 0 - self.extrefsymoff = 0 - self.nextrefsyms = 0 - self.indirectsymoff = 0 - self.nindirectsyms = 0 - self.extreloff = 0 - self.nextrel = 0 - self.locreloff = 0 - self.nlocrel = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - self.ilocalsym, self.nlocalsym, self.iextdefsym, self.nextdefsym, self.iundefsym, self.nundefsym, self.tocoff, self.ntoc, self.modtaboff, self.nmodtab, self.extrefsymoff, self.nextrefsyms, self.indirectsymoff, self.nindirectsyms, self.extreloff, self.nextrel, self.locreloff, self.nlocrel = data.get_n_uint32( - 18) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - # s += "ilocalsym = %u, nlocalsym = %u, " % (self.ilocalsym, self.nlocalsym) - # s += "iextdefsym = %u, nextdefsym = %u, " % (self.iextdefsym, self.nextdefsym) - # s += "iundefsym %u, nundefsym = %u, " % (self.iundefsym, self.nundefsym) - # s += "tocoff = %#8.8x, ntoc = %u, " % (self.tocoff, self.ntoc) - # s += "modtaboff = %#8.8x, nmodtab = %u, " % (self.modtaboff, self.nmodtab) - # s += "extrefsymoff = %#8.8x, nextrefsyms = %u, " % (self.extrefsymoff, self.nextrefsyms) - # s += "indirectsymoff = %#8.8x, nindirectsyms = %u, " % (self.indirectsymoff, self.nindirectsyms) - # s += "extreloff = %#8.8x, nextrel = %u, " % (self.extreloff, self.nextrel) - # s += "locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, - # self.nlocrel) - s += "ilocalsym = %-10u, nlocalsym = %u\n" % ( - self.ilocalsym, self.nlocalsym) - s += " iextdefsym = %-10u, nextdefsym = %u\n" % ( - self.iextdefsym, self.nextdefsym) - s += " iundefsym = %-10u, nundefsym = %u\n" % ( - self.iundefsym, self.nundefsym) - s += " tocoff = %#8.8x, ntoc = %u\n" % ( - self.tocoff, self.ntoc) - s += " modtaboff = %#8.8x, nmodtab = %u\n" % ( - self.modtaboff, self.nmodtab) - s += " extrefsymoff = %#8.8x, nextrefsyms = %u\n" % ( - self.extrefsymoff, self.nextrefsyms) - s += " indirectsymoff = %#8.8x, nindirectsyms = %u\n" % ( - self.indirectsymoff, self.nindirectsyms) - s += " extreloff = %#8.8x, nextrel = %u\n" % ( - self.extreloff, self.nextrel) - s += " locreloff = %#8.8x, nlocrel = %u" % ( - self.locreloff, self.nlocrel) - return s - - class SymtabLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.symoff = 0 - self.nsyms = 0 - self.stroff = 0 - self.strsize = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - self.symoff, self.nsyms, self.stroff, self.strsize = data.get_n_uint32( - 4) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % ( - self.symoff, self.nsyms, self.stroff, self.strsize) - return s - - class UUIDLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.uuid = None - - def unpack(self, mach_file, data): - uuid_data = data.get_n_uint8(16) - uuid_str = '' - for byte in uuid_data: - uuid_str += '%2.2x' % byte - self.uuid = uuid.UUID(uuid_str) - mach_file.uuid = self.uuid - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += self.uuid.__str__() - return s - - class DataBlobLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.dataoff = 0 - self.datasize = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - self.dataoff, self.datasize = data.get_n_uint32(2) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "dataoff = %#8.8x, datasize = %u" % ( - self.dataoff, self.datasize) - return s - - class EncryptionInfoLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.cryptoff = 0 - self.cryptsize = 0 - self.cryptid = 0 - - def unpack(self, mach_file, data): - byte_order_char = mach_file.magic.get_byte_order() - self.cryptoff, self.cryptsize, self.cryptid = data.get_n_uint32(3) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - s += "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % ( - self.cryptoff, self.cryptoff + self.cryptsize, self.cryptsize, self.cryptid) - return s - - class SegmentLoadCommand(LoadCommand): - - def __init__(self, lc): - Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) - self.segname = None - self.vmaddr = 0 - self.vmsize = 0 - self.fileoff = 0 - self.filesize = 0 - self.maxprot = 0 - self.initprot = 0 - self.nsects = 0 - self.flags = 0 - - def unpack(self, mach_file, data): - is_64 = self.command.get_enum_value() == LC_SEGMENT_64 - self.segname = data.get_fixed_length_c_string(16, '', True) - if is_64: - self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint64( - 4) - else: - self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint32( - 4) - self.maxprot, self.initprot, self.nsects, self.flags = data.get_n_uint32( - 4) - mach_file.segments.append(self) - for i in range(self.nsects): - section = Mach.Section() - section.unpack(is_64, data) - section.index = len(mach_file.sections) - mach_file.sections.append(section) - - def __str__(self): - s = Mach.LoadCommand.__str__(self) - if self.command.get_enum_value() == LC_SEGMENT: - s += "%#8.8x %#8.8x %#8.8x %#8.8x " % ( - self.vmaddr, self.vmsize, self.fileoff, self.filesize) - else: - s += "%#16.16x %#16.16x %#16.16x %#16.16x " % ( - self.vmaddr, self.vmsize, self.fileoff, self.filesize) - s += "%s %s %3u %#8.8x" % (vm_prot_names[self.maxprot], vm_prot_names[ - self.initprot], self.nsects, self.flags) - s += ' ' + self.segname - return s - - class NList: - - class Type: - - class Stab(dict_utils.Enum): - enum = { - 'N_GSYM': N_GSYM, - 'N_FNAME': N_FNAME, - 'N_FUN': N_FUN, - 'N_STSYM': N_STSYM, - 'N_LCSYM': N_LCSYM, - 'N_BNSYM': N_BNSYM, - 'N_OPT': N_OPT, - 'N_RSYM': N_RSYM, - 'N_SLINE': N_SLINE, - 'N_ENSYM': N_ENSYM, - 'N_SSYM': N_SSYM, - 'N_SO': N_SO, - 'N_OSO': N_OSO, - 'N_LSYM': N_LSYM, - 'N_BINCL': N_BINCL, - 'N_SOL': N_SOL, - 'N_PARAMS': N_PARAMS, - 'N_VERSION': N_VERSION, - 'N_OLEVEL': N_OLEVEL, - 'N_PSYM': N_PSYM, - 'N_EINCL': N_EINCL, - 'N_ENTRY': N_ENTRY, - 'N_LBRAC': N_LBRAC, - 'N_EXCL': N_EXCL, - 'N_RBRAC': N_RBRAC, - 'N_BCOMM': N_BCOMM, - 'N_ECOMM': N_ECOMM, - 'N_ECOML': N_ECOML, - 'N_LENG': N_LENG - } - - def __init__(self, magic=0): - dict_utils.Enum.__init__(self, magic, self.enum) - - def __init__(self, t=0): - self.value = t - - def __str__(self): - n_type = self.value - if n_type & N_STAB: - stab = Mach.NList.Type.Stab(self.value) - return '%s' % stab - else: - type = self.value & N_TYPE - type_str = '' - if type == N_UNDF: - type_str = 'N_UNDF' - elif type == N_ABS: - type_str = 'N_ABS ' - elif type == N_SECT: - type_str = 'N_SECT' - elif type == N_PBUD: - type_str = 'N_PBUD' - elif type == N_INDR: - type_str = 'N_INDR' - else: - type_str = "??? (%#2.2x)" % type - if n_type & N_PEXT: - type_str += ' | PEXT' - if n_type & N_EXT: - type_str += ' | EXT ' - return type_str - - def __init__(self): - self.index = 0 - self.name_offset = 0 - self.name = 0 - self.type = Mach.NList.Type() - self.sect_idx = 0 - self.desc = 0 - self.value = 0 - - def unpack(self, mach_file, data, symtab_lc): - self.index = len(mach_file.symbols) - self.name_offset = data.get_uint32() - self.type.value, self.sect_idx = data.get_n_uint8(2) - self.desc = data.get_uint16() - if mach_file.is_64_bit(): - self.value = data.get_uint64() - else: - self.value = data.get_uint32() - data.push_offset_and_seek( - mach_file.file_off + - symtab_lc.stroff + - self.name_offset) - # print "get string for symbol[%u]" % self.index - self.name = data.get_c_string() - data.pop_offset_and_seek() - - def __str__(self): - name_display = '' - if len(self.name): - name_display = ' "%s"' % self.name - return '%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s' % (self.name_offset, - self.type.value, self.type, self.sect_idx, self.desc, self.value, name_display) - - class Interactive(cmd.Cmd): - '''Interactive command interpreter to mach-o files.''' - - def __init__(self, mach, options): - cmd.Cmd.__init__(self) - self.intro = 'Interactive mach-o command interpreter' - self.prompt = 'mach-o: %s %% ' % mach.path - self.mach = mach - self.options = options - - def default(self, line): - '''Catch all for unknown command, which will exit the interpreter.''' - print "uknown command: %s" % line - return True - - def do_q(self, line): - '''Quit command''' - return True - - def do_quit(self, line): - '''Quit command''' - return True - - def do_header(self, line): - '''Dump mach-o file headers''' - self.mach.dump_header(True, self.options) - return False - - def do_load(self, line): - '''Dump all mach-o load commands''' - self.mach.dump_load_commands(True, self.options) - return False - - def do_sections(self, line): - '''Dump all mach-o sections''' - self.mach.dump_sections(True, self.options) - return False - - def do_symtab(self, line): - '''Dump all mach-o symbols in the symbol table''' - self.mach.dump_symtab(True, self.options) - return False - -if __name__ == '__main__': - parser = optparse.OptionParser( - description='A script that parses skinny and universal mach-o files.') - parser.add_option( - '--arch', - '-a', - type='string', - metavar='arch', - dest='archs', - action='append', - help='specify one or more architectures by name') - parser.add_option( - '-v', - '--verbose', - action='store_true', - dest='verbose', - help='display verbose debug info', - default=False) - parser.add_option( - '-H', - '--header', - action='store_true', - dest='dump_header', - help='dump the mach-o file header', - default=False) - parser.add_option( - '-l', - '--load-commands', - action='store_true', - dest='dump_load_commands', - help='dump the mach-o load commands', - default=False) - parser.add_option( - '-s', - '--symtab', - action='store_true', - dest='dump_symtab', - help='dump the mach-o symbol table', - default=False) - parser.add_option( - '-S', - '--sections', - action='store_true', - dest='dump_sections', - help='dump the mach-o sections', - default=False) - parser.add_option( - '--section', - type='string', - metavar='sectname', - dest='section_names', - action='append', - help='Specify one or more section names to dump', - default=[]) - parser.add_option( - '-o', - '--out', - type='string', - dest='outfile', - help='Used in conjunction with the --section=NAME option to save a single section\'s data to disk.', - default=False) - parser.add_option( - '-i', - '--interactive', - action='store_true', - dest='interactive', - help='enable interactive mode', - default=False) - parser.add_option( - '-m', - '--mangled', - action='store_true', - dest='find_mangled', - help='dump all mangled names in a mach file', - default=False) - parser.add_option( - '-c', - '--compare', - action='store_true', - dest='compare', - help='compare two mach files', - default=False) - parser.add_option( - '-M', - '--extract-modules', - action='store_true', - dest='extract_modules', - help='Extract modules from file', - default=False) - parser.add_option( - '-C', - '--count', - type='int', - dest='max_count', - help='Sets the max byte count when dumping section data', - default=-1) - - (options, mach_files) = parser.parse_args() - if options.extract_modules: - if options.section_names: - print "error: can't use --section option with the --extract-modules option" - exit(1) - if not options.outfile: - print "error: the --output=FILE option must be specified with the --extract-modules option" - exit(1) - options.section_names.append("__apple_ast") - if options.compare: - if len(mach_files) == 2: - mach_a = Mach() - mach_b = Mach() - mach_a.parse(mach_files[0]) - mach_b.parse(mach_files[1]) - mach_a.compare(mach_b) - else: - print 'error: --compare takes two mach files as arguments' - else: - if not (options.dump_header or options.dump_load_commands or options.dump_symtab or options.dump_sections or options.find_mangled or options.section_names): - options.dump_header = True - options.dump_load_commands = True - if options.verbose: - print 'options', options - print 'mach_files', mach_files - for path in mach_files: - mach = Mach() - mach.parse(path) - if options.interactive: - interpreter = Mach.Interactive(mach, options) - interpreter.cmdloop() - else: - mach.dump(options) |