brintos

brintos / llvm-project-archived public Read only

0
0
Text · 72.8 KiB · 8c28068 Raw
2104 lines · python
1#!/usr/bin/env python2 3import cmd4import dict_utils5import file_extract6import optparse7import re8import struct9import string10import io11import sys12import uuid13 14# Mach header "magic" constants15MH_MAGIC = 0xFEEDFACE16MH_CIGAM = 0xCEFAEDFE17MH_MAGIC_64 = 0xFEEDFACF18MH_CIGAM_64 = 0xCFFAEDFE19FAT_MAGIC = 0xCAFEBABE20FAT_CIGAM = 0xBEBAFECA21 22# Mach haeder "filetype" constants23MH_OBJECT = 0x0000000124MH_EXECUTE = 0x0000000225MH_FVMLIB = 0x0000000326MH_CORE = 0x0000000427MH_PRELOAD = 0x0000000528MH_DYLIB = 0x0000000629MH_DYLINKER = 0x0000000730MH_BUNDLE = 0x0000000831MH_DYLIB_STUB = 0x0000000932MH_DSYM = 0x0000000A33MH_KEXT_BUNDLE = 0x0000000B34 35# Mach haeder "flag" constant bits36MH_NOUNDEFS = 0x0000000137MH_INCRLINK = 0x0000000238MH_DYLDLINK = 0x0000000439MH_BINDATLOAD = 0x0000000840MH_PREBOUND = 0x0000001041MH_SPLIT_SEGS = 0x0000002042MH_LAZY_INIT = 0x0000004043MH_TWOLEVEL = 0x0000008044MH_FORCE_FLAT = 0x0000010045MH_NOMULTIDEFS = 0x0000020046MH_NOFIXPREBINDING = 0x0000040047MH_PREBINDABLE = 0x0000080048MH_ALLMODSBOUND = 0x0000100049MH_SUBSECTIONS_VIA_SYMBOLS = 0x0000200050MH_CANONICAL = 0x0000400051MH_WEAK_DEFINES = 0x0000800052MH_BINDS_TO_WEAK = 0x0001000053MH_ALLOW_STACK_EXECUTION = 0x0002000054MH_ROOT_SAFE = 0x0004000055MH_SETUID_SAFE = 0x0008000056MH_NO_REEXPORTED_DYLIBS = 0x0010000057MH_PIE = 0x0020000058MH_DEAD_STRIPPABLE_DYLIB = 0x0040000059MH_HAS_TLV_DESCRIPTORS = 0x0080000060MH_NO_HEAP_EXECUTION = 0x0100000061 62# Mach load command constants63LC_REQ_DYLD = 0x8000000064LC_SEGMENT = 0x0000000165LC_SYMTAB = 0x0000000266LC_SYMSEG = 0x0000000367LC_THREAD = 0x0000000468LC_UNIXTHREAD = 0x0000000569LC_LOADFVMLIB = 0x0000000670LC_IDFVMLIB = 0x0000000771LC_IDENT = 0x0000000872LC_FVMFILE = 0x0000000973LC_PREPAGE = 0x0000000A74LC_DYSYMTAB = 0x0000000B75LC_LOAD_DYLIB = 0x0000000C76LC_ID_DYLIB = 0x0000000D77LC_LOAD_DYLINKER = 0x0000000E78LC_ID_DYLINKER = 0x0000000F79LC_PREBOUND_DYLIB = 0x0000001080LC_ROUTINES = 0x0000001181LC_SUB_FRAMEWORK = 0x0000001282LC_SUB_UMBRELLA = 0x0000001383LC_SUB_CLIENT = 0x0000001484LC_SUB_LIBRARY = 0x0000001585LC_TWOLEVEL_HINTS = 0x0000001686LC_PREBIND_CKSUM = 0x0000001787LC_LOAD_WEAK_DYLIB = 0x00000018 | LC_REQ_DYLD88LC_SEGMENT_64 = 0x0000001989LC_ROUTINES_64 = 0x0000001A90LC_UUID = 0x0000001B91LC_RPATH = 0x0000001C | LC_REQ_DYLD92LC_CODE_SIGNATURE = 0x0000001D93LC_SEGMENT_SPLIT_INFO = 0x0000001E94LC_REEXPORT_DYLIB = 0x0000001F | LC_REQ_DYLD95LC_LAZY_LOAD_DYLIB = 0x0000002096LC_ENCRYPTION_INFO = 0x0000002197LC_DYLD_INFO = 0x0000002298LC_DYLD_INFO_ONLY = 0x00000022 | LC_REQ_DYLD99LC_LOAD_UPWARD_DYLIB = 0x00000023 | LC_REQ_DYLD100LC_VERSION_MIN_MACOSX = 0x00000024101LC_VERSION_MIN_IPHONEOS = 0x00000025102LC_FUNCTION_STARTS = 0x00000026103LC_DYLD_ENVIRONMENT = 0x00000027104 105# Mach CPU constants106CPU_ARCH_MASK = 0xFF000000107CPU_ARCH_ABI64 = 0x01000000108CPU_TYPE_ANY = 0xFFFFFFFF109CPU_TYPE_VAX = 1110CPU_TYPE_MC680x0 = 6111CPU_TYPE_I386 = 7112CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64113CPU_TYPE_MIPS = 8114CPU_TYPE_MC98000 = 10115CPU_TYPE_HPPA = 11116CPU_TYPE_ARM = 12117CPU_TYPE_MC88000 = 13118CPU_TYPE_SPARC = 14119CPU_TYPE_I860 = 15120CPU_TYPE_ALPHA = 16121CPU_TYPE_POWERPC = 18122CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64123 124# VM protection constants125VM_PROT_READ = 1126VM_PROT_WRITE = 2127VM_PROT_EXECUTE = 4128 129# VM protection constants130N_STAB = 0xE0131N_PEXT = 0x10132N_TYPE = 0x0E133N_EXT = 0x01134 135# Values for nlist N_TYPE bits of the "Mach.NList.type" field.136N_UNDF = 0x0137N_ABS = 0x2138N_SECT = 0xE139N_PBUD = 0xC140N_INDR = 0xA141 142# Section indexes for the "Mach.NList.sect_idx" fields143NO_SECT = 0144MAX_SECT = 255145 146# Stab defines147N_GSYM = 0x20148N_FNAME = 0x22149N_FUN = 0x24150N_STSYM = 0x26151N_LCSYM = 0x28152N_BNSYM = 0x2E153N_OPT = 0x3C154N_RSYM = 0x40155N_SLINE = 0x44156N_ENSYM = 0x4E157N_SSYM = 0x60158N_SO = 0x64159N_OSO = 0x66160N_LSYM = 0x80161N_BINCL = 0x82162N_SOL = 0x84163N_PARAMS = 0x86164N_VERSION = 0x88165N_OLEVEL = 0x8A166N_PSYM = 0xA0167N_EINCL = 0xA2168N_ENTRY = 0xA4169N_LBRAC = 0xC0170N_EXCL = 0xC2171N_RBRAC = 0xE0172N_BCOMM = 0xE2173N_ECOMM = 0xE4174N_ECOML = 0xE8175N_LENG = 0xFE176 177vm_prot_names = ["---", "r--", "-w-", "rw-", "--x", "r-x", "-wx", "rwx"]178 179 180def dump_memory(base_addr, data, hex_bytes_len, num_per_line):181    hex_bytes = data.encode("hex")182    if hex_bytes_len == -1:183        hex_bytes_len = len(hex_bytes)184    addr = base_addr185    ascii_str = ""186    i = 0187    while i < hex_bytes_len:188        if ((i / 2) % num_per_line) == 0:189            if i > 0:190                print(" %s" % (ascii_str))191                ascii_str = ""192            print("0x%8.8x:" % (addr + i), end=" ")193        hex_byte = hex_bytes[i : i + 2]194        print(hex_byte, end=" ")195        int_byte = int(hex_byte, 16)196        ascii_char = "%c" % (int_byte)197        if int_byte >= 32 and int_byte < 127:198            ascii_str += ascii_char199        else:200            ascii_str += "."201        i = i + 2202    if ascii_str:203        if (i / 2) % num_per_line:204            padding = num_per_line - ((i / 2) % num_per_line)205        else:206            padding = 0207        print("%*s%s" % (padding * 3 + 1, "", ascii_str))208    print()209 210 211class TerminalColors:212    """Simple terminal colors class"""213 214    def __init__(self, enabled=True):215        # TODO: discover terminal type from "file" and disable if216        # it can't handle the color codes217        self.enabled = enabled218 219    def reset(self):220        """Reset all terminal colors and formatting."""221        if self.enabled:222            return "\x1b[0m"223        return ""224 225    def bold(self, on=True):226        """Enable or disable bold depending on the "on" parameter."""227        if self.enabled:228            if on:229                return "\x1b[1m"230            else:231                return "\x1b[22m"232        return ""233 234    def italics(self, on=True):235        """Enable or disable italics depending on the "on" parameter."""236        if self.enabled:237            if on:238                return "\x1b[3m"239            else:240                return "\x1b[23m"241        return ""242 243    def underline(self, on=True):244        """Enable or disable underline depending on the "on" parameter."""245        if self.enabled:246            if on:247                return "\x1b[4m"248            else:249                return "\x1b[24m"250        return ""251 252    def inverse(self, on=True):253        """Enable or disable inverse depending on the "on" parameter."""254        if self.enabled:255            if on:256                return "\x1b[7m"257            else:258                return "\x1b[27m"259        return ""260 261    def strike(self, on=True):262        """Enable or disable strike through depending on the "on" parameter."""263        if self.enabled:264            if on:265                return "\x1b[9m"266            else:267                return "\x1b[29m"268        return ""269 270    def black(self, fg=True):271        """Set the foreground or background color to black.272        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.273        """274        if self.enabled:275            if fg:276                return "\x1b[30m"277            else:278                return "\x1b[40m"279        return ""280 281    def red(self, fg=True):282        """Set the foreground or background color to red.283        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.284        """285        if self.enabled:286            if fg:287                return "\x1b[31m"288            else:289                return "\x1b[41m"290        return ""291 292    def green(self, fg=True):293        """Set the foreground or background color to green.294        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.295        """296        if self.enabled:297            if fg:298                return "\x1b[32m"299            else:300                return "\x1b[42m"301        return ""302 303    def yellow(self, fg=True):304        """Set the foreground or background color to yellow.305        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.306        """307        if self.enabled:308            if fg:309                return "\x1b[43m"310            else:311                return "\x1b[33m"312        return ""313 314    def blue(self, fg=True):315        """Set the foreground or background color to blue.316        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.317        """318        if self.enabled:319            if fg:320                return "\x1b[34m"321            else:322                return "\x1b[44m"323        return ""324 325    def magenta(self, fg=True):326        """Set the foreground or background color to magenta.327        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.328        """329        if self.enabled:330            if fg:331                return "\x1b[35m"332            else:333                return "\x1b[45m"334        return ""335 336    def cyan(self, fg=True):337        """Set the foreground or background color to cyan.338        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.339        """340        if self.enabled:341            if fg:342                return "\x1b[36m"343            else:344                return "\x1b[46m"345        return ""346 347    def white(self, fg=True):348        """Set the foreground or background color to white.349        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.350        """351        if self.enabled:352            if fg:353                return "\x1b[37m"354            else:355                return "\x1b[47m"356        return ""357 358    def default(self, fg=True):359        """Set the foreground or background color to the default.360        The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.361        """362        if self.enabled:363            if fg:364                return "\x1b[39m"365            else:366                return "\x1b[49m"367        return ""368 369 370def swap_unpack_char():371    """Returns the unpack prefix that will for non-native endian-ness."""372    if struct.pack("H", 1).startswith("\x00"):373        return "<"374    return ">"375 376 377def dump_hex_bytes(addr, s, bytes_per_line=16):378    i = 0379    line = ""380    for ch in s:381        if (i % bytes_per_line) == 0:382            if line:383                print(line)384            line = "%#8.8x: " % (addr + i)385        line += "%02X " % ord(ch)386        i += 1387    print(line)388 389 390def dump_hex_byte_string_diff(addr, a, b, bytes_per_line=16):391    i = 0392    line = ""393    a_len = len(a)394    b_len = len(b)395    if a_len < b_len:396        max_len = b_len397    else:398        max_len = a_len399    tty_colors = TerminalColors(True)400    for i in range(max_len):401        ch = None402        if i < a_len:403            ch_a = a[i]404            ch = ch_a405        else:406            ch_a = None407        if i < b_len:408            ch_b = b[i]409            if not ch:410                ch = ch_b411        else:412            ch_b = None413        mismatch = ch_a != ch_b414        if (i % bytes_per_line) == 0:415            if line:416                print(line)417            line = "%#8.8x: " % (addr + i)418        if mismatch:419            line += tty_colors.red()420        line += "%02X " % ord(ch)421        if mismatch:422            line += tty_colors.default()423        i += 1424 425    print(line)426 427 428class Mach:429    """Class that does everything mach-o related"""430 431    class Arch:432        """Class that implements mach-o architectures"""433 434        def __init__(self, c=0, s=0):435            self.cpu = c436            self.sub = s437 438        def set_cpu_type(self, c):439            self.cpu = c440 441        def set_cpu_subtype(self, s):442            self.sub = s443 444        def set_arch(self, c, s):445            self.cpu = c446            self.sub = s447 448        def is_64_bit(self):449            return (self.cpu & CPU_ARCH_ABI64) != 0450 451        cpu_infos = [452            ["arm", CPU_TYPE_ARM, CPU_TYPE_ANY],453            ["arm", CPU_TYPE_ARM, 0],454            ["armv4", CPU_TYPE_ARM, 5],455            ["armv6", CPU_TYPE_ARM, 6],456            ["armv5", CPU_TYPE_ARM, 7],457            ["xscale", CPU_TYPE_ARM, 8],458            ["armv7", CPU_TYPE_ARM, 9],459            ["armv7f", CPU_TYPE_ARM, 10],460            ["armv7s", CPU_TYPE_ARM, 11],461            ["armv7k", CPU_TYPE_ARM, 12],462            ["armv7m", CPU_TYPE_ARM, 15],463            ["armv7em", CPU_TYPE_ARM, 16],464            ["ppc", CPU_TYPE_POWERPC, CPU_TYPE_ANY],465            ["ppc", CPU_TYPE_POWERPC, 0],466            ["ppc601", CPU_TYPE_POWERPC, 1],467            ["ppc602", CPU_TYPE_POWERPC, 2],468            ["ppc603", CPU_TYPE_POWERPC, 3],469            ["ppc603e", CPU_TYPE_POWERPC, 4],470            ["ppc603ev", CPU_TYPE_POWERPC, 5],471            ["ppc604", CPU_TYPE_POWERPC, 6],472            ["ppc604e", CPU_TYPE_POWERPC, 7],473            ["ppc620", CPU_TYPE_POWERPC, 8],474            ["ppc750", CPU_TYPE_POWERPC, 9],475            ["ppc7400", CPU_TYPE_POWERPC, 10],476            ["ppc7450", CPU_TYPE_POWERPC, 11],477            ["ppc970", CPU_TYPE_POWERPC, 100],478            ["ppc64", CPU_TYPE_POWERPC64, 0],479            ["ppc970-64", CPU_TYPE_POWERPC64, 100],480            ["i386", CPU_TYPE_I386, 3],481            ["i486", CPU_TYPE_I386, 4],482            ["i486sx", CPU_TYPE_I386, 0x84],483            ["i386", CPU_TYPE_I386, CPU_TYPE_ANY],484            ["x86_64", CPU_TYPE_X86_64, 3],485            ["x86_64", CPU_TYPE_X86_64, CPU_TYPE_ANY],486        ]487 488        def __str__(self):489            for info in self.cpu_infos:490                if self.cpu == info[1] and (self.sub & 0x00FFFFFF) == info[2]:491                    return info[0]492            return "{0}.{1}".format(self.cpu, self.sub)493 494    class Magic(dict_utils.Enum):495        enum = {496            "MH_MAGIC": MH_MAGIC,497            "MH_CIGAM": MH_CIGAM,498            "MH_MAGIC_64": MH_MAGIC_64,499            "MH_CIGAM_64": MH_CIGAM_64,500            "FAT_MAGIC": FAT_MAGIC,501            "FAT_CIGAM": FAT_CIGAM,502        }503 504        def __init__(self, initial_value=0):505            dict_utils.Enum.__init__(self, initial_value, self.enum)506 507        def is_skinny_mach_file(self):508            return (509                self.value == MH_MAGIC510                or self.value == MH_CIGAM511                or self.value == MH_MAGIC_64512                or self.value == MH_CIGAM_64513            )514 515        def is_universal_mach_file(self):516            return self.value == FAT_MAGIC or self.value == FAT_CIGAM517 518        def unpack(self, data):519            data.set_byte_order("native")520            self.value = data.get_uint32()521 522        def get_byte_order(self):523            if (524                self.value == MH_CIGAM525                or self.value == MH_CIGAM_64526                or self.value == FAT_CIGAM527            ):528                return swap_unpack_char()529            else:530                return "="531 532        def is_64_bit(self):533            return self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64534 535    def __init__(self):536        self.magic = Mach.Magic()537        self.content = None538        self.path = None539 540    def extract(self, path, extractor):541        self.path = path542        self.unpack(extractor)543 544    def parse(self, path):545        self.path = path546        try:547            f = open(self.path)548            file_extractor = file_extract.FileExtract(f, "=")549            self.unpack(file_extractor)550            # f.close()551        except IOError as xxx_todo_changeme:552            (errno, strerror) = xxx_todo_changeme.args553            print("I/O error({0}): {1}".format(errno, strerror))554        except ValueError:555            print("Could not convert data to an integer.")556        except:557            print("Unexpected error:", sys.exc_info()[0])558            raise559 560    def compare(self, rhs):561        self.content.compare(rhs.content)562 563    def dump(self, options=None):564        self.content.dump(options)565 566    def dump_header(self, dump_description=True, options=None):567        self.content.dump_header(dump_description, options)568 569    def dump_load_commands(self, dump_description=True, options=None):570        self.content.dump_load_commands(dump_description, options)571 572    def dump_sections(self, dump_description=True, options=None):573        self.content.dump_sections(dump_description, options)574 575    def dump_section_contents(self, options):576        self.content.dump_section_contents(options)577 578    def dump_symtab(self, dump_description=True, options=None):579        self.content.dump_symtab(dump_description, options)580 581    def dump_symbol_names_matching_regex(self, regex, file=None):582        self.content.dump_symbol_names_matching_regex(regex, file)583 584    def description(self):585        return self.content.description()586 587    def unpack(self, data):588        self.magic.unpack(data)589        if self.magic.is_skinny_mach_file():590            self.content = Mach.Skinny(self.path)591        elif self.magic.is_universal_mach_file():592            self.content = Mach.Universal(self.path)593        else:594            self.content = None595 596        if self.content is not None:597            self.content.unpack(data, self.magic)598 599    def is_valid(self):600        return self.content is not None601 602    class Universal:603        def __init__(self, path):604            self.path = path605            self.type = "universal"606            self.file_off = 0607            self.magic = None608            self.nfat_arch = 0609            self.archs = list()610 611        def description(self):612            s = "%#8.8x: %s (" % (self.file_off, self.path)613            archs_string = ""614            for arch in self.archs:615                if len(archs_string):616                    archs_string += ", "617                archs_string += "%s" % arch.arch618            s += archs_string619            s += ")"620            return s621 622        def unpack(self, data, magic=None):623            self.file_off = data.tell()624            if magic is None:625                self.magic = Mach.Magic()626                self.magic.unpack(data)627            else:628                self.magic = magic629                self.file_off = self.file_off - 4630            # Universal headers are always in big endian631            data.set_byte_order("big")632            self.nfat_arch = data.get_uint32()633            for i in range(self.nfat_arch):634                self.archs.append(Mach.Universal.ArchInfo())635                self.archs[i].unpack(data)636            for i in range(self.nfat_arch):637                self.archs[i].mach = Mach.Skinny(self.path)638                data.seek(self.archs[i].offset, 0)639                skinny_magic = Mach.Magic()640                skinny_magic.unpack(data)641                self.archs[i].mach.unpack(data, skinny_magic)642 643        def compare(self, rhs):644            print("error: comparing two universal files is not supported yet")645            return False646 647        def dump(self, options):648            if options.dump_header:649                print()650                print(651                    "Universal Mach File: magic = %s, nfat_arch = %u"652                    % (self.magic, self.nfat_arch)653                )654                print()655            if self.nfat_arch > 0:656                if options.dump_header:657                    self.archs[0].dump_header(True, options)658                    for i in range(self.nfat_arch):659                        self.archs[i].dump_flat(options)660                if options.dump_header:661                    print()662                for i in range(self.nfat_arch):663                    self.archs[i].mach.dump(options)664 665        def dump_header(self, dump_description=True, options=None):666            if dump_description:667                print(self.description())668            for i in range(self.nfat_arch):669                self.archs[i].mach.dump_header(True, options)670                print()671 672        def dump_load_commands(self, dump_description=True, options=None):673            if dump_description:674                print(self.description())675            for i in range(self.nfat_arch):676                self.archs[i].mach.dump_load_commands(True, options)677                print()678 679        def dump_sections(self, dump_description=True, options=None):680            if dump_description:681                print(self.description())682            for i in range(self.nfat_arch):683                self.archs[i].mach.dump_sections(True, options)684                print()685 686        def dump_section_contents(self, options):687            for i in range(self.nfat_arch):688                self.archs[i].mach.dump_section_contents(options)689                print()690 691        def dump_symtab(self, dump_description=True, options=None):692            if dump_description:693                print(self.description())694            for i in range(self.nfat_arch):695                self.archs[i].mach.dump_symtab(True, options)696                print()697 698        def dump_symbol_names_matching_regex(self, regex, file=None):699            for i in range(self.nfat_arch):700                self.archs[i].mach.dump_symbol_names_matching_regex(regex, file)701 702        class ArchInfo:703            def __init__(self):704                self.arch = Mach.Arch(0, 0)705                self.offset = 0706                self.size = 0707                self.align = 0708                self.mach = None709 710            def unpack(self, data):711                # Universal headers are always in big endian712                data.set_byte_order("big")713                (714                    self.arch.cpu,715                    self.arch.sub,716                    self.offset,717                    self.size,718                    self.align,719                ) = data.get_n_uint32(5)720 721            def dump_header(self, dump_description=True, options=None):722                if options.verbose:723                    print("CPU        SUBTYPE    OFFSET     SIZE       ALIGN")724                    print("---------- ---------- ---------- ---------- ----------")725                else:726                    print("ARCH       FILEOFFSET FILESIZE   ALIGN")727                    print("---------- ---------- ---------- ----------")728 729            def dump_flat(self, options):730                if options.verbose:731                    print(732                        "%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x"733                        % (734                            self.arch.cpu,735                            self.arch.sub,736                            self.offset,737                            self.size,738                            self.align,739                        )740                    )741                else:742                    print(743                        "%-10s %#8.8x %#8.8x %#8.8x"744                        % (self.arch, self.offset, self.size, self.align)745                    )746 747            def dump(self):748                print("   cputype: %#8.8x" % self.arch.cpu)749                print("cpusubtype: %#8.8x" % self.arch.sub)750                print("    offset: %#8.8x" % self.offset)751                print("      size: %#8.8x" % self.size)752                print("     align: %#8.8x" % self.align)753 754            def __str__(self):755                return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (756                    self.arch.cpu,757                    self.arch.sub,758                    self.offset,759                    self.size,760                    self.align,761                )762 763            def __repr__(self):764                return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (765                    self.arch.cpu,766                    self.arch.sub,767                    self.offset,768                    self.size,769                    self.align,770                )771 772    class Flags:773        def __init__(self, b):774            self.bits = b775 776        def __str__(self):777            s = ""778            if self.bits & MH_NOUNDEFS:779                s += "MH_NOUNDEFS | "780            if self.bits & MH_INCRLINK:781                s += "MH_INCRLINK | "782            if self.bits & MH_DYLDLINK:783                s += "MH_DYLDLINK | "784            if self.bits & MH_BINDATLOAD:785                s += "MH_BINDATLOAD | "786            if self.bits & MH_PREBOUND:787                s += "MH_PREBOUND | "788            if self.bits & MH_SPLIT_SEGS:789                s += "MH_SPLIT_SEGS | "790            if self.bits & MH_LAZY_INIT:791                s += "MH_LAZY_INIT | "792            if self.bits & MH_TWOLEVEL:793                s += "MH_TWOLEVEL | "794            if self.bits & MH_FORCE_FLAT:795                s += "MH_FORCE_FLAT | "796            if self.bits & MH_NOMULTIDEFS:797                s += "MH_NOMULTIDEFS | "798            if self.bits & MH_NOFIXPREBINDING:799                s += "MH_NOFIXPREBINDING | "800            if self.bits & MH_PREBINDABLE:801                s += "MH_PREBINDABLE | "802            if self.bits & MH_ALLMODSBOUND:803                s += "MH_ALLMODSBOUND | "804            if self.bits & MH_SUBSECTIONS_VIA_SYMBOLS:805                s += "MH_SUBSECTIONS_VIA_SYMBOLS | "806            if self.bits & MH_CANONICAL:807                s += "MH_CANONICAL | "808            if self.bits & MH_WEAK_DEFINES:809                s += "MH_WEAK_DEFINES | "810            if self.bits & MH_BINDS_TO_WEAK:811                s += "MH_BINDS_TO_WEAK | "812            if self.bits & MH_ALLOW_STACK_EXECUTION:813                s += "MH_ALLOW_STACK_EXECUTION | "814            if self.bits & MH_ROOT_SAFE:815                s += "MH_ROOT_SAFE | "816            if self.bits & MH_SETUID_SAFE:817                s += "MH_SETUID_SAFE | "818            if self.bits & MH_NO_REEXPORTED_DYLIBS:819                s += "MH_NO_REEXPORTED_DYLIBS | "820            if self.bits & MH_PIE:821                s += "MH_PIE | "822            if self.bits & MH_DEAD_STRIPPABLE_DYLIB:823                s += "MH_DEAD_STRIPPABLE_DYLIB | "824            if self.bits & MH_HAS_TLV_DESCRIPTORS:825                s += "MH_HAS_TLV_DESCRIPTORS | "826            if self.bits & MH_NO_HEAP_EXECUTION:827                s += "MH_NO_HEAP_EXECUTION | "828            # Strip the trailing " |" if we have any flags829            if len(s) > 0:830                s = s[0:-2]831            return s832 833    class FileType(dict_utils.Enum):834        enum = {835            "MH_OBJECT": MH_OBJECT,836            "MH_EXECUTE": MH_EXECUTE,837            "MH_FVMLIB": MH_FVMLIB,838            "MH_CORE": MH_CORE,839            "MH_PRELOAD": MH_PRELOAD,840            "MH_DYLIB": MH_DYLIB,841            "MH_DYLINKER": MH_DYLINKER,842            "MH_BUNDLE": MH_BUNDLE,843            "MH_DYLIB_STUB": MH_DYLIB_STUB,844            "MH_DSYM": MH_DSYM,845            "MH_KEXT_BUNDLE": MH_KEXT_BUNDLE,846        }847 848        def __init__(self, initial_value=0):849            dict_utils.Enum.__init__(self, initial_value, self.enum)850 851    class Skinny:852        def __init__(self, path):853            self.path = path854            self.type = "skinny"855            self.data = None856            self.file_off = 0857            self.magic = 0858            self.arch = Mach.Arch(0, 0)859            self.filetype = Mach.FileType(0)860            self.ncmds = 0861            self.sizeofcmds = 0862            self.flags = Mach.Flags(0)863            self.uuid = None864            self.commands = list()865            self.segments = list()866            self.sections = list()867            self.symbols = list()868            self.sections.append(Mach.Section())869 870        def description(self):871            return "%#8.8x: %s (%s)" % (self.file_off, self.path, self.arch)872 873        def unpack(self, data, magic=None):874            self.data = data875            self.file_off = data.tell()876            if magic is None:877                self.magic = Mach.Magic()878                self.magic.unpack(data)879            else:880                self.magic = magic881                self.file_off = self.file_off - 4882            data.set_byte_order(self.magic.get_byte_order())883            (884                self.arch.cpu,885                self.arch.sub,886                self.filetype.value,887                self.ncmds,888                self.sizeofcmds,889                bits,890            ) = data.get_n_uint32(6)891            self.flags.bits = bits892 893            if self.is_64_bit():894                data.get_uint32()  # Skip reserved word in mach_header_64895 896            for i in range(0, self.ncmds):897                lc = self.unpack_load_command(data)898                self.commands.append(lc)899 900        def get_data(self):901            if self.data:902                self.data.set_byte_order(self.magic.get_byte_order())903                return self.data904            return None905 906        def unpack_load_command(self, data):907            lc = Mach.LoadCommand()908            lc.unpack(self, data)909            lc_command = lc.command.get_enum_value()910            if lc_command == LC_SEGMENT or lc_command == LC_SEGMENT_64:911                lc = Mach.SegmentLoadCommand(lc)912                lc.unpack(self, data)913            elif (914                lc_command == LC_LOAD_DYLIB915                or lc_command == LC_ID_DYLIB916                or lc_command == LC_LOAD_WEAK_DYLIB917                or lc_command == LC_REEXPORT_DYLIB918            ):919                lc = Mach.DylibLoadCommand(lc)920                lc.unpack(self, data)921            elif (922                lc_command == LC_LOAD_DYLINKER923                or lc_command == LC_SUB_FRAMEWORK924                or lc_command == LC_SUB_CLIENT925                or lc_command == LC_SUB_UMBRELLA926                or lc_command == LC_SUB_LIBRARY927                or lc_command == LC_ID_DYLINKER928                or lc_command == LC_RPATH929            ):930                lc = Mach.LoadDYLDLoadCommand(lc)931                lc.unpack(self, data)932            elif lc_command == LC_DYLD_INFO_ONLY:933                lc = Mach.DYLDInfoOnlyLoadCommand(lc)934                lc.unpack(self, data)935            elif lc_command == LC_SYMTAB:936                lc = Mach.SymtabLoadCommand(lc)937                lc.unpack(self, data)938            elif lc_command == LC_DYSYMTAB:939                lc = Mach.DYLDSymtabLoadCommand(lc)940                lc.unpack(self, data)941            elif lc_command == LC_UUID:942                lc = Mach.UUIDLoadCommand(lc)943                lc.unpack(self, data)944            elif (945                lc_command == LC_CODE_SIGNATURE946                or lc_command == LC_SEGMENT_SPLIT_INFO947                or lc_command == LC_FUNCTION_STARTS948            ):949                lc = Mach.DataBlobLoadCommand(lc)950                lc.unpack(self, data)951            elif lc_command == LC_UNIXTHREAD:952                lc = Mach.UnixThreadLoadCommand(lc)953                lc.unpack(self, data)954            elif lc_command == LC_ENCRYPTION_INFO:955                lc = Mach.EncryptionInfoLoadCommand(lc)956                lc.unpack(self, data)957            lc.skip(data)958            return lc959 960        def compare(self, rhs):961            print("\nComparing:")962            print("a) %s %s" % (self.arch, self.path))963            print("b) %s %s" % (rhs.arch, rhs.path))964            result = True965            if self.type == rhs.type:966                for lhs_section in self.sections[1:]:967                    rhs_section = rhs.get_section_by_section(lhs_section)968                    if rhs_section:969                        print(970                            "comparing %s.%s..."971                            % (lhs_section.segname, lhs_section.sectname),972                            end=" ",973                        )974                        sys.stdout.flush()975                        lhs_data = lhs_section.get_contents(self)976                        rhs_data = rhs_section.get_contents(rhs)977                        if lhs_data and rhs_data:978                            if lhs_data == rhs_data:979                                print("ok")980                            else:981                                lhs_data_len = len(lhs_data)982                                rhs_data_len = len(rhs_data)983                                # if lhs_data_len < rhs_data_len:984                                #     if lhs_data == rhs_data[0:lhs_data_len]:985                                #         print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len)986                                #     else:987                                #         # TODO: check padding988                                #         result = False989                                # elif lhs_data_len > rhs_data_len:990                                #     if lhs_data[0:rhs_data_len] == rhs_data:991                                #         print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len)992                                #     else:993                                #         # TODO: check padding994                                #         result = False995                                # else:996                                result = False997                                print("error: sections differ")998                                # print 'a) %s' % (lhs_section)999                                # dump_hex_byte_string_diff(0, lhs_data, rhs_data)1000                                # print 'b) %s' % (rhs_section)1001                                # dump_hex_byte_string_diff(0, rhs_data, lhs_data)1002                        elif lhs_data and not rhs_data:1003                            print("error: section data missing from b:")1004                            print("a) %s" % (lhs_section))1005                            print("b) %s" % (rhs_section))1006                            result = False1007                        elif not lhs_data and rhs_data:1008                            print("error: section data missing from a:")1009                            print("a) %s" % (lhs_section))1010                            print("b) %s" % (rhs_section))1011                            result = False1012                        elif lhs_section.offset or rhs_section.offset:1013                            print("error: section data missing for both a and b:")1014                            print("a) %s" % (lhs_section))1015                            print("b) %s" % (rhs_section))1016                            result = False1017                        else:1018                            print("ok")1019                    else:1020                        result = False1021                        print(1022                            "error: section %s is missing in %s"1023                            % (lhs_section.sectname, rhs.path)1024                        )1025            else:1026                print(1027                    "error: comparing a %s mach-o file with a %s mach-o file is not supported"1028                    % (self.type, rhs.type)1029                )1030                result = False1031            if not result:1032                print("error: mach files differ")1033            return result1034 1035        def dump_header(self, dump_description=True, options=None):1036            if options.verbose:1037                print(1038                    "MAGIC      CPU        SUBTYPE    FILETYPE   NUM CMDS SIZE CMDS  FLAGS"1039                )1040                print(1041                    "---------- ---------- ---------- ---------- -------- ---------- ----------"1042                )1043            else:1044                print(1045                    "MAGIC        ARCH       FILETYPE       NUM CMDS SIZE CMDS  FLAGS"1046                )1047                print(1048                    "------------ ---------- -------------- -------- ---------- ----------"1049                )1050 1051        def dump_flat(self, options):1052            if options.verbose:1053                print(1054                    "%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x"1055                    % (1056                        self.magic,1057                        self.arch.cpu,1058                        self.arch.sub,1059                        self.filetype.value,1060                        self.ncmds,1061                        self.sizeofcmds,1062                        self.flags.bits,1063                    )1064                )1065            else:1066                print(1067                    "%-12s %-10s %-14s %#8u %#8.8x %s"1068                    % (1069                        self.magic,1070                        self.arch,1071                        self.filetype,1072                        self.ncmds,1073                        self.sizeofcmds,1074                        self.flags,1075                    )1076                )1077 1078        def dump(self, options):1079            if options.dump_header:1080                self.dump_header(True, options)1081            if options.dump_load_commands:1082                self.dump_load_commands(False, options)1083            if options.dump_sections:1084                self.dump_sections(False, options)1085            if options.section_names:1086                self.dump_section_contents(options)1087            if options.dump_symtab:1088                self.get_symtab()1089                if len(self.symbols):1090                    self.dump_sections(False, options)1091                else:1092                    print("No symbols")1093            if options.find_mangled:1094                self.dump_symbol_names_matching_regex(re.compile("^_?_Z"))1095 1096        def dump_header(self, dump_description=True, options=None):1097            if dump_description:1098                print(self.description())1099            print("Mach Header")1100            print("       magic: %#8.8x %s" % (self.magic.value, self.magic))1101            print("     cputype: %#8.8x %s" % (self.arch.cpu, self.arch))1102            print("  cpusubtype: %#8.8x" % self.arch.sub)1103            print(1104                "    filetype: %#8.8x %s"1105                % (self.filetype.get_enum_value(), self.filetype.get_enum_name())1106            )1107            print("       ncmds: %#8.8x %u" % (self.ncmds, self.ncmds))1108            print("  sizeofcmds: %#8.8x" % self.sizeofcmds)1109            print("       flags: %#8.8x %s" % (self.flags.bits, self.flags))1110 1111        def dump_load_commands(self, dump_description=True, options=None):1112            if dump_description:1113                print(self.description())1114            for lc in self.commands:1115                print(lc)1116 1117        def get_section_by_name(self, name):1118            for section in self.sections:1119                if section.sectname and section.sectname == name:1120                    return section1121            return None1122 1123        def get_section_by_section(self, other_section):1124            for section in self.sections:1125                if (1126                    section.sectname == other_section.sectname1127                    and section.segname == other_section.segname1128                ):1129                    return section1130            return None1131 1132        def dump_sections(self, dump_description=True, options=None):1133            if dump_description:1134                print(self.description())1135            num_sections = len(self.sections)1136            if num_sections > 1:1137                self.sections[1].dump_header()1138                for sect_idx in range(1, num_sections):1139                    print("%s" % self.sections[sect_idx])1140 1141        def dump_section_contents(self, options):1142            saved_section_to_disk = False1143            for sectname in options.section_names:1144                section = self.get_section_by_name(sectname)1145                if section:1146                    sect_bytes = section.get_contents(self)1147                    if options.outfile:1148                        if not saved_section_to_disk:1149                            outfile = open(options.outfile, "w")1150                            if options.extract_modules:1151                                # print "Extracting modules from mach file..."1152                                data = file_extract.FileExtract(1153                                    io.BytesIO(sect_bytes), self.data.byte_order1154                                )1155                                version = data.get_uint32()1156                                num_modules = data.get_uint32()1157                                # print "version = %u, num_modules = %u" %1158                                # (version, num_modules)1159                                for i in range(num_modules):1160                                    data_offset = data.get_uint64()1161                                    data_size = data.get_uint64()1162                                    name_offset = data.get_uint32()1163                                    language = data.get_uint32()1164                                    flags = data.get_uint32()1165                                    data.seek(name_offset)1166                                    module_name = data.get_c_string()1167                                    # print "module[%u] data_offset = %#16.16x,1168                                    # data_size = %#16.16x, name_offset =1169                                    # %#16.16x (%s), language = %u, flags =1170                                    # %#x" % (i, data_offset, data_size,1171                                    # name_offset, module_name, language,1172                                    # flags)1173                                    data.seek(data_offset)1174                                    outfile.write(data.read_size(data_size))1175                            else:1176                                print(1177                                    "Saving section %s to '%s'"1178                                    % (sectname, options.outfile)1179                                )1180                                outfile.write(sect_bytes)1181                            outfile.close()1182                            saved_section_to_disk = True1183                        else:1184                            print(1185                                "error: you can only save a single section to disk at a time, skipping section '%s'"1186                                % (sectname)1187                            )1188                    else:1189                        print("section %s:\n" % (sectname))1190                        section.dump_header()1191                        print("%s\n" % (section))1192                        dump_memory(0, sect_bytes, options.max_count, 16)1193                else:1194                    print('error: no section named "%s" was found' % (sectname))1195 1196        def get_segment(self, segname):1197            if len(self.segments) == 1 and self.segments[0].segname == "":1198                return self.segments[0]1199            for segment in self.segments:1200                if segment.segname == segname:1201                    return segment1202            return None1203 1204        def get_first_load_command(self, lc_enum_value):1205            for lc in self.commands:1206                if lc.command.value == lc_enum_value:1207                    return lc1208            return None1209 1210        def get_symtab(self):1211            if self.data and not self.symbols:1212                lc_symtab = self.get_first_load_command(LC_SYMTAB)1213                if lc_symtab:1214                    symtab_offset = self.file_off1215                    if self.data.is_in_memory():1216                        linkedit_segment = self.get_segment("__LINKEDIT")1217                        if linkedit_segment:1218                            linkedit_vmaddr = linkedit_segment.vmaddr1219                            linkedit_fileoff = linkedit_segment.fileoff1220                            symtab_offset = (1221                                linkedit_vmaddr + lc_symtab.symoff - linkedit_fileoff1222                            )1223                            symtab_offset = (1224                                linkedit_vmaddr + lc_symtab.stroff - linkedit_fileoff1225                            )1226                    else:1227                        symtab_offset += lc_symtab.symoff1228 1229                    self.data.seek(symtab_offset)1230                    is_64 = self.is_64_bit()1231                    for i in range(lc_symtab.nsyms):1232                        nlist = Mach.NList()1233                        nlist.unpack(self, self.data, lc_symtab)1234                        self.symbols.append(nlist)1235                else:1236                    print("no LC_SYMTAB")1237 1238        def dump_symtab(self, dump_description=True, options=None):1239            self.get_symtab()1240            if dump_description:1241                print(self.description())1242            for i, symbol in enumerate(self.symbols):1243                print("[%5u] %s" % (i, symbol))1244 1245        def dump_symbol_names_matching_regex(self, regex, file=None):1246            self.get_symtab()1247            for symbol in self.symbols:1248                if symbol.name and regex.search(symbol.name):1249                    print(symbol.name)1250                    if file:1251                        file.write("%s\n" % (symbol.name))1252 1253        def is_64_bit(self):1254            return self.magic.is_64_bit()1255 1256    class LoadCommand:1257        class Command(dict_utils.Enum):1258            enum = {1259                "LC_SEGMENT": LC_SEGMENT,1260                "LC_SYMTAB": LC_SYMTAB,1261                "LC_SYMSEG": LC_SYMSEG,1262                "LC_THREAD": LC_THREAD,1263                "LC_UNIXTHREAD": LC_UNIXTHREAD,1264                "LC_LOADFVMLIB": LC_LOADFVMLIB,1265                "LC_IDFVMLIB": LC_IDFVMLIB,1266                "LC_IDENT": LC_IDENT,1267                "LC_FVMFILE": LC_FVMFILE,1268                "LC_PREPAGE": LC_PREPAGE,1269                "LC_DYSYMTAB": LC_DYSYMTAB,1270                "LC_LOAD_DYLIB": LC_LOAD_DYLIB,1271                "LC_ID_DYLIB": LC_ID_DYLIB,1272                "LC_LOAD_DYLINKER": LC_LOAD_DYLINKER,1273                "LC_ID_DYLINKER": LC_ID_DYLINKER,1274                "LC_PREBOUND_DYLIB": LC_PREBOUND_DYLIB,1275                "LC_ROUTINES": LC_ROUTINES,1276                "LC_SUB_FRAMEWORK": LC_SUB_FRAMEWORK,1277                "LC_SUB_UMBRELLA": LC_SUB_UMBRELLA,1278                "LC_SUB_CLIENT": LC_SUB_CLIENT,1279                "LC_SUB_LIBRARY": LC_SUB_LIBRARY,1280                "LC_TWOLEVEL_HINTS": LC_TWOLEVEL_HINTS,1281                "LC_PREBIND_CKSUM": LC_PREBIND_CKSUM,1282                "LC_LOAD_WEAK_DYLIB": LC_LOAD_WEAK_DYLIB,1283                "LC_SEGMENT_64": LC_SEGMENT_64,1284                "LC_ROUTINES_64": LC_ROUTINES_64,1285                "LC_UUID": LC_UUID,1286                "LC_RPATH": LC_RPATH,1287                "LC_CODE_SIGNATURE": LC_CODE_SIGNATURE,1288                "LC_SEGMENT_SPLIT_INFO": LC_SEGMENT_SPLIT_INFO,1289                "LC_REEXPORT_DYLIB": LC_REEXPORT_DYLIB,1290                "LC_LAZY_LOAD_DYLIB": LC_LAZY_LOAD_DYLIB,1291                "LC_ENCRYPTION_INFO": LC_ENCRYPTION_INFO,1292                "LC_DYLD_INFO": LC_DYLD_INFO,1293                "LC_DYLD_INFO_ONLY": LC_DYLD_INFO_ONLY,1294                "LC_LOAD_UPWARD_DYLIB": LC_LOAD_UPWARD_DYLIB,1295                "LC_VERSION_MIN_MACOSX": LC_VERSION_MIN_MACOSX,1296                "LC_VERSION_MIN_IPHONEOS": LC_VERSION_MIN_IPHONEOS,1297                "LC_FUNCTION_STARTS": LC_FUNCTION_STARTS,1298                "LC_DYLD_ENVIRONMENT": LC_DYLD_ENVIRONMENT,1299            }1300 1301            def __init__(self, initial_value=0):1302                dict_utils.Enum.__init__(self, initial_value, self.enum)1303 1304        def __init__(self, c=None, l=0, o=0):1305            if c is not None:1306                self.command = c1307            else:1308                self.command = Mach.LoadCommand.Command(0)1309            self.length = l1310            self.file_off = o1311 1312        def unpack(self, mach_file, data):1313            self.file_off = data.tell()1314            self.command.value, self.length = data.get_n_uint32(2)1315 1316        def skip(self, data):1317            data.seek(self.file_off + self.length, 0)1318 1319        def __str__(self):1320            lc_name = self.command.get_enum_name()1321            return "%#8.8x: <%#4.4x> %-24s" % (self.file_off, self.length, lc_name)1322 1323    class Section:1324        def __init__(self):1325            self.index = 01326            self.is_64 = False1327            self.sectname = None1328            self.segname = None1329            self.addr = 01330            self.size = 01331            self.offset = 01332            self.align = 01333            self.reloff = 01334            self.nreloc = 01335            self.flags = 01336            self.reserved1 = 01337            self.reserved2 = 01338            self.reserved3 = 01339 1340        def unpack(self, is_64, data):1341            self.is_64 = is_641342            self.sectname = data.get_fixed_length_c_string(16, "", True)1343            self.segname = data.get_fixed_length_c_string(16, "", True)1344            if self.is_64:1345                self.addr, self.size = data.get_n_uint64(2)1346                (1347                    self.offset,1348                    self.align,1349                    self.reloff,1350                    self.nreloc,1351                    self.flags,1352                    self.reserved1,1353                    self.reserved2,1354                    self.reserved3,1355                ) = data.get_n_uint32(8)1356            else:1357                self.addr, self.size = data.get_n_uint32(2)1358                (1359                    self.offset,1360                    self.align,1361                    self.reloff,1362                    self.nreloc,1363                    self.flags,1364                    self.reserved1,1365                    self.reserved2,1366                ) = data.get_n_uint32(7)1367 1368        def dump_header(self):1369            if self.is_64:1370                print(1371                    "INDEX ADDRESS            SIZE               OFFSET     ALIGN      RELOFF     NRELOC     FLAGS      RESERVED1  RESERVED2  RESERVED3  NAME"1372                )1373                print(1374                    "===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"1375                )1376            else:1377                print(1378                    "INDEX ADDRESS    SIZE       OFFSET     ALIGN      RELOFF     NRELOC     FLAGS      RESERVED1  RESERVED2  NAME"1379                )1380                print(1381                    "===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"1382                )1383 1384        def __str__(self):1385            if self.is_64:1386                return (1387                    "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s"1388                    % (1389                        self.index,1390                        self.addr,1391                        self.size,1392                        self.offset,1393                        self.align,1394                        self.reloff,1395                        self.nreloc,1396                        self.flags,1397                        self.reserved1,1398                        self.reserved2,1399                        self.reserved3,1400                        self.segname,1401                        self.sectname,1402                    )1403                )1404            else:1405                return (1406                    "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s"1407                    % (1408                        self.index,1409                        self.addr,1410                        self.size,1411                        self.offset,1412                        self.align,1413                        self.reloff,1414                        self.nreloc,1415                        self.flags,1416                        self.reserved1,1417                        self.reserved2,1418                        self.segname,1419                        self.sectname,1420                    )1421                )1422 1423        def get_contents(self, mach_file):1424            """Get the section contents as a python string"""1425            if self.size > 0 and mach_file.get_segment(self.segname).filesize > 0:1426                data = mach_file.get_data()1427                if data:1428                    section_data_offset = mach_file.file_off + self.offset1429                    # print '%s.%s is at offset 0x%x with size 0x%x' %1430                    # (self.segname, self.sectname, section_data_offset,1431                    # self.size)1432                    data.push_offset_and_seek(section_data_offset)1433                    bytes = data.read_size(self.size)1434                    data.pop_offset_and_seek()1435                    return bytes1436            return None1437 1438    class DylibLoadCommand(LoadCommand):1439        def __init__(self, lc):1440            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1441            self.name = None1442            self.timestamp = 01443            self.current_version = 01444            self.compatibility_version = 01445 1446        def unpack(self, mach_file, data):1447            byte_order_char = mach_file.magic.get_byte_order()1448            (1449                name_offset,1450                self.timestamp,1451                self.current_version,1452                self.compatibility_version,1453            ) = data.get_n_uint32(4)1454            data.seek(self.file_off + name_offset, 0)1455            self.name = data.get_fixed_length_c_string(self.length - 24)1456 1457        def __str__(self):1458            s = Mach.LoadCommand.__str__(self)1459            s += "%#8.8x %#8.8x %#8.8x " % (1460                self.timestamp,1461                self.current_version,1462                self.compatibility_version,1463            )1464            s += self.name1465            return s1466 1467    class LoadDYLDLoadCommand(LoadCommand):1468        def __init__(self, lc):1469            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1470            self.name = None1471 1472        def unpack(self, mach_file, data):1473            data.get_uint32()1474            self.name = data.get_fixed_length_c_string(self.length - 12)1475 1476        def __str__(self):1477            s = Mach.LoadCommand.__str__(self)1478            s += "%s" % self.name1479            return s1480 1481    class UnixThreadLoadCommand(LoadCommand):1482        class ThreadState:1483            def __init__(self):1484                self.flavor = 01485                self.count = 01486                self.register_values = list()1487 1488            def unpack(self, data):1489                self.flavor, self.count = data.get_n_uint32(2)1490                self.register_values = data.get_n_uint32(self.count)1491 1492            def __str__(self):1493                s = "flavor = %u, count = %u, regs =" % (self.flavor, self.count)1494                i = 01495                for register_value in self.register_values:1496                    if i % 8 == 0:1497                        s += "\n                                            "1498                    s += " %#8.8x" % register_value1499                    i += 11500                return s1501 1502        def __init__(self, lc):1503            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1504            self.reg_sets = list()1505 1506        def unpack(self, mach_file, data):1507            reg_set = Mach.UnixThreadLoadCommand.ThreadState()1508            reg_set.unpack(data)1509            self.reg_sets.append(reg_set)1510 1511        def __str__(self):1512            s = Mach.LoadCommand.__str__(self)1513            for reg_set in self.reg_sets:1514                s += "%s" % reg_set1515            return s1516 1517    class DYLDInfoOnlyLoadCommand(LoadCommand):1518        def __init__(self, lc):1519            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1520            self.rebase_off = 01521            self.rebase_size = 01522            self.bind_off = 01523            self.bind_size = 01524            self.weak_bind_off = 01525            self.weak_bind_size = 01526            self.lazy_bind_off = 01527            self.lazy_bind_size = 01528            self.export_off = 01529            self.export_size = 01530 1531        def unpack(self, mach_file, data):1532            byte_order_char = mach_file.magic.get_byte_order()1533            (1534                self.rebase_off,1535                self.rebase_size,1536                self.bind_off,1537                self.bind_size,1538                self.weak_bind_off,1539                self.weak_bind_size,1540                self.lazy_bind_off,1541                self.lazy_bind_size,1542                self.export_off,1543                self.export_size,1544            ) = data.get_n_uint32(10)1545 1546        def __str__(self):1547            s = Mach.LoadCommand.__str__(self)1548            s += "rebase_off = %#8.8x, rebase_size = %u, " % (1549                self.rebase_off,1550                self.rebase_size,1551            )1552            s += "bind_off = %#8.8x, bind_size = %u, " % (self.bind_off, self.bind_size)1553            s += "weak_bind_off = %#8.8x, weak_bind_size = %u, " % (1554                self.weak_bind_off,1555                self.weak_bind_size,1556            )1557            s += "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % (1558                self.lazy_bind_off,1559                self.lazy_bind_size,1560            )1561            s += "export_off = %#8.8x, export_size = %u, " % (1562                self.export_off,1563                self.export_size,1564            )1565            return s1566 1567    class DYLDSymtabLoadCommand(LoadCommand):1568        def __init__(self, lc):1569            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1570            self.ilocalsym = 01571            self.nlocalsym = 01572            self.iextdefsym = 01573            self.nextdefsym = 01574            self.iundefsym = 01575            self.nundefsym = 01576            self.tocoff = 01577            self.ntoc = 01578            self.modtaboff = 01579            self.nmodtab = 01580            self.extrefsymoff = 01581            self.nextrefsyms = 01582            self.indirectsymoff = 01583            self.nindirectsyms = 01584            self.extreloff = 01585            self.nextrel = 01586            self.locreloff = 01587            self.nlocrel = 01588 1589        def unpack(self, mach_file, data):1590            byte_order_char = mach_file.magic.get_byte_order()1591            (1592                self.ilocalsym,1593                self.nlocalsym,1594                self.iextdefsym,1595                self.nextdefsym,1596                self.iundefsym,1597                self.nundefsym,1598                self.tocoff,1599                self.ntoc,1600                self.modtaboff,1601                self.nmodtab,1602                self.extrefsymoff,1603                self.nextrefsyms,1604                self.indirectsymoff,1605                self.nindirectsyms,1606                self.extreloff,1607                self.nextrel,1608                self.locreloff,1609                self.nlocrel,1610            ) = data.get_n_uint32(18)1611 1612        def __str__(self):1613            s = Mach.LoadCommand.__str__(self)1614            # s += "ilocalsym = %u, nlocalsym = %u, " % (self.ilocalsym, self.nlocalsym)1615            # s += "iextdefsym = %u, nextdefsym = %u, " % (self.iextdefsym, self.nextdefsym)1616            # s += "iundefsym %u, nundefsym = %u, " % (self.iundefsym, self.nundefsym)1617            # s += "tocoff = %#8.8x, ntoc = %u, " % (self.tocoff, self.ntoc)1618            # s += "modtaboff = %#8.8x, nmodtab = %u, " % (self.modtaboff, self.nmodtab)1619            # s += "extrefsymoff = %#8.8x, nextrefsyms = %u, " % (self.extrefsymoff, self.nextrefsyms)1620            # s += "indirectsymoff = %#8.8x, nindirectsyms = %u, " % (self.indirectsymoff, self.nindirectsyms)1621            # s += "extreloff = %#8.8x, nextrel = %u, " % (self.extreloff, self.nextrel)1622            # s += "locreloff = %#8.8x, nlocrel = %u" % (self.locreloff,1623            # self.nlocrel)1624            s += "ilocalsym      = %-10u, nlocalsym     = %u\n" % (1625                self.ilocalsym,1626                self.nlocalsym,1627            )1628            s += (1629                "                                             iextdefsym     = %-10u, nextdefsym    = %u\n"1630                % (self.iextdefsym, self.nextdefsym)1631            )1632            s += (1633                "                                             iundefsym      = %-10u, nundefsym     = %u\n"1634                % (self.iundefsym, self.nundefsym)1635            )1636            s += (1637                "                                             tocoff         = %#8.8x, ntoc          = %u\n"1638                % (self.tocoff, self.ntoc)1639            )1640            s += (1641                "                                             modtaboff      = %#8.8x, nmodtab       = %u\n"1642                % (self.modtaboff, self.nmodtab)1643            )1644            s += (1645                "                                             extrefsymoff   = %#8.8x, nextrefsyms   = %u\n"1646                % (self.extrefsymoff, self.nextrefsyms)1647            )1648            s += (1649                "                                             indirectsymoff = %#8.8x, nindirectsyms = %u\n"1650                % (self.indirectsymoff, self.nindirectsyms)1651            )1652            s += (1653                "                                             extreloff      = %#8.8x, nextrel       = %u\n"1654                % (self.extreloff, self.nextrel)1655            )1656            s += (1657                "                                             locreloff      = %#8.8x, nlocrel       = %u"1658                % (self.locreloff, self.nlocrel)1659            )1660            return s1661 1662    class SymtabLoadCommand(LoadCommand):1663        def __init__(self, lc):1664            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1665            self.symoff = 01666            self.nsyms = 01667            self.stroff = 01668            self.strsize = 01669 1670        def unpack(self, mach_file, data):1671            byte_order_char = mach_file.magic.get_byte_order()1672            self.symoff, self.nsyms, self.stroff, self.strsize = data.get_n_uint32(4)1673 1674        def __str__(self):1675            s = Mach.LoadCommand.__str__(self)1676            s += "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % (1677                self.symoff,1678                self.nsyms,1679                self.stroff,1680                self.strsize,1681            )1682            return s1683 1684    class UUIDLoadCommand(LoadCommand):1685        def __init__(self, lc):1686            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1687            self.uuid = None1688 1689        def unpack(self, mach_file, data):1690            uuid_data = data.get_n_uint8(16)1691            uuid_str = ""1692            for byte in uuid_data:1693                uuid_str += "%2.2x" % byte1694            self.uuid = uuid.UUID(uuid_str)1695            mach_file.uuid = self.uuid1696 1697        def __str__(self):1698            s = Mach.LoadCommand.__str__(self)1699            s += self.uuid.__str__()1700            return s1701 1702    class DataBlobLoadCommand(LoadCommand):1703        def __init__(self, lc):1704            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1705            self.dataoff = 01706            self.datasize = 01707 1708        def unpack(self, mach_file, data):1709            byte_order_char = mach_file.magic.get_byte_order()1710            self.dataoff, self.datasize = data.get_n_uint32(2)1711 1712        def __str__(self):1713            s = Mach.LoadCommand.__str__(self)1714            s += "dataoff = %#8.8x, datasize = %u" % (self.dataoff, self.datasize)1715            return s1716 1717    class EncryptionInfoLoadCommand(LoadCommand):1718        def __init__(self, lc):1719            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1720            self.cryptoff = 01721            self.cryptsize = 01722            self.cryptid = 01723 1724        def unpack(self, mach_file, data):1725            byte_order_char = mach_file.magic.get_byte_order()1726            self.cryptoff, self.cryptsize, self.cryptid = data.get_n_uint32(3)1727 1728        def __str__(self):1729            s = Mach.LoadCommand.__str__(self)1730            s += "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % (1731                self.cryptoff,1732                self.cryptoff + self.cryptsize,1733                self.cryptsize,1734                self.cryptid,1735            )1736            return s1737 1738    class SegmentLoadCommand(LoadCommand):1739        def __init__(self, lc):1740            Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off)1741            self.segname = None1742            self.vmaddr = 01743            self.vmsize = 01744            self.fileoff = 01745            self.filesize = 01746            self.maxprot = 01747            self.initprot = 01748            self.nsects = 01749            self.flags = 01750 1751        def unpack(self, mach_file, data):1752            is_64 = self.command.get_enum_value() == LC_SEGMENT_641753            self.segname = data.get_fixed_length_c_string(16, "", True)1754            if is_64:1755                (1756                    self.vmaddr,1757                    self.vmsize,1758                    self.fileoff,1759                    self.filesize,1760                ) = data.get_n_uint64(4)1761            else:1762                (1763                    self.vmaddr,1764                    self.vmsize,1765                    self.fileoff,1766                    self.filesize,1767                ) = data.get_n_uint32(4)1768            self.maxprot, self.initprot, self.nsects, self.flags = data.get_n_uint32(4)1769            mach_file.segments.append(self)1770            for i in range(self.nsects):1771                section = Mach.Section()1772                section.unpack(is_64, data)1773                section.index = len(mach_file.sections)1774                mach_file.sections.append(section)1775 1776        def __str__(self):1777            s = Mach.LoadCommand.__str__(self)1778            if self.command.get_enum_value() == LC_SEGMENT:1779                s += "%#8.8x %#8.8x %#8.8x %#8.8x " % (1780                    self.vmaddr,1781                    self.vmsize,1782                    self.fileoff,1783                    self.filesize,1784                )1785            else:1786                s += "%#16.16x %#16.16x %#16.16x %#16.16x " % (1787                    self.vmaddr,1788                    self.vmsize,1789                    self.fileoff,1790                    self.filesize,1791                )1792            s += "%s %s %3u %#8.8x" % (1793                vm_prot_names[self.maxprot],1794                vm_prot_names[self.initprot],1795                self.nsects,1796                self.flags,1797            )1798            s += " " + self.segname1799            return s1800 1801    class NList:1802        class Type:1803            class Stab(dict_utils.Enum):1804                enum = {1805                    "N_GSYM": N_GSYM,1806                    "N_FNAME": N_FNAME,1807                    "N_FUN": N_FUN,1808                    "N_STSYM": N_STSYM,1809                    "N_LCSYM": N_LCSYM,1810                    "N_BNSYM": N_BNSYM,1811                    "N_OPT": N_OPT,1812                    "N_RSYM": N_RSYM,1813                    "N_SLINE": N_SLINE,1814                    "N_ENSYM": N_ENSYM,1815                    "N_SSYM": N_SSYM,1816                    "N_SO": N_SO,1817                    "N_OSO": N_OSO,1818                    "N_LSYM": N_LSYM,1819                    "N_BINCL": N_BINCL,1820                    "N_SOL": N_SOL,1821                    "N_PARAMS": N_PARAMS,1822                    "N_VERSION": N_VERSION,1823                    "N_OLEVEL": N_OLEVEL,1824                    "N_PSYM": N_PSYM,1825                    "N_EINCL": N_EINCL,1826                    "N_ENTRY": N_ENTRY,1827                    "N_LBRAC": N_LBRAC,1828                    "N_EXCL": N_EXCL,1829                    "N_RBRAC": N_RBRAC,1830                    "N_BCOMM": N_BCOMM,1831                    "N_ECOMM": N_ECOMM,1832                    "N_ECOML": N_ECOML,1833                    "N_LENG": N_LENG,1834                }1835 1836                def __init__(self, magic=0):1837                    dict_utils.Enum.__init__(self, magic, self.enum)1838 1839            def __init__(self, t=0):1840                self.value = t1841 1842            def __str__(self):1843                n_type = self.value1844                if n_type & N_STAB:1845                    stab = Mach.NList.Type.Stab(self.value)1846                    return "%s" % stab1847                else:1848                    type = self.value & N_TYPE1849                    type_str = ""1850                    if type == N_UNDF:1851                        type_str = "N_UNDF"1852                    elif type == N_ABS:1853                        type_str = "N_ABS "1854                    elif type == N_SECT:1855                        type_str = "N_SECT"1856                    elif type == N_PBUD:1857                        type_str = "N_PBUD"1858                    elif type == N_INDR:1859                        type_str = "N_INDR"1860                    else:1861                        type_str = "??? (%#2.2x)" % type1862                    if n_type & N_PEXT:1863                        type_str += " | PEXT"1864                    if n_type & N_EXT:1865                        type_str += " | EXT "1866                    return type_str1867 1868        def __init__(self):1869            self.index = 01870            self.name_offset = 01871            self.name = 01872            self.type = Mach.NList.Type()1873            self.sect_idx = 01874            self.desc = 01875            self.value = 01876 1877        def unpack(self, mach_file, data, symtab_lc):1878            self.index = len(mach_file.symbols)1879            self.name_offset = data.get_uint32()1880            self.type.value, self.sect_idx = data.get_n_uint8(2)1881            self.desc = data.get_uint16()1882            if mach_file.is_64_bit():1883                self.value = data.get_uint64()1884            else:1885                self.value = data.get_uint32()1886            data.push_offset_and_seek(1887                mach_file.file_off + symtab_lc.stroff + self.name_offset1888            )1889            # print "get string for symbol[%u]" % self.index1890            self.name = data.get_c_string()1891            data.pop_offset_and_seek()1892 1893        def __str__(self):1894            name_display = ""1895            if len(self.name):1896                name_display = ' "%s"' % self.name1897            return "%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s" % (1898                self.name_offset,1899                self.type.value,1900                self.type,1901                self.sect_idx,1902                self.desc,1903                self.value,1904                name_display,1905            )1906 1907    class Interactive(cmd.Cmd):1908        """Interactive command interpreter to mach-o files."""1909 1910        def __init__(self, mach, options):1911            cmd.Cmd.__init__(self)1912            self.intro = "Interactive mach-o command interpreter"1913            self.prompt = "mach-o: %s %% " % mach.path1914            self.mach = mach1915            self.options = options1916 1917        def default(self, line):1918            """Catch all for unknown command, which will exit the interpreter."""1919            print("uknown command: %s" % line)1920            return True1921 1922        def do_q(self, line):1923            """Quit command"""1924            return True1925 1926        def do_quit(self, line):1927            """Quit command"""1928            return True1929 1930        def do_header(self, line):1931            """Dump mach-o file headers"""1932            self.mach.dump_header(True, self.options)1933            return False1934 1935        def do_load(self, line):1936            """Dump all mach-o load commands"""1937            self.mach.dump_load_commands(True, self.options)1938            return False1939 1940        def do_sections(self, line):1941            """Dump all mach-o sections"""1942            self.mach.dump_sections(True, self.options)1943            return False1944 1945        def do_symtab(self, line):1946            """Dump all mach-o symbols in the symbol table"""1947            self.mach.dump_symtab(True, self.options)1948            return False1949 1950 1951if __name__ == "__main__":1952    parser = optparse.OptionParser(1953        description="A script that parses skinny and universal mach-o files."1954    )1955    parser.add_option(1956        "--arch",1957        "-a",1958        type="string",1959        metavar="arch",1960        dest="archs",1961        action="append",1962        help="specify one or more architectures by name",1963    )1964    parser.add_option(1965        "-v",1966        "--verbose",1967        action="store_true",1968        dest="verbose",1969        help="display verbose debug info",1970        default=False,1971    )1972    parser.add_option(1973        "-H",1974        "--header",1975        action="store_true",1976        dest="dump_header",1977        help="dump the mach-o file header",1978        default=False,1979    )1980    parser.add_option(1981        "-l",1982        "--load-commands",1983        action="store_true",1984        dest="dump_load_commands",1985        help="dump the mach-o load commands",1986        default=False,1987    )1988    parser.add_option(1989        "-s",1990        "--symtab",1991        action="store_true",1992        dest="dump_symtab",1993        help="dump the mach-o symbol table",1994        default=False,1995    )1996    parser.add_option(1997        "-S",1998        "--sections",1999        action="store_true",2000        dest="dump_sections",2001        help="dump the mach-o sections",2002        default=False,2003    )2004    parser.add_option(2005        "--section",2006        type="string",2007        metavar="sectname",2008        dest="section_names",2009        action="append",2010        help="Specify one or more section names to dump",2011        default=[],2012    )2013    parser.add_option(2014        "-o",2015        "--out",2016        type="string",2017        dest="outfile",2018        help="Used in conjunction with the --section=NAME option to save a single section's data to disk.",2019        default=False,2020    )2021    parser.add_option(2022        "-i",2023        "--interactive",2024        action="store_true",2025        dest="interactive",2026        help="enable interactive mode",2027        default=False,2028    )2029    parser.add_option(2030        "-m",2031        "--mangled",2032        action="store_true",2033        dest="find_mangled",2034        help="dump all mangled names in a mach file",2035        default=False,2036    )2037    parser.add_option(2038        "-c",2039        "--compare",2040        action="store_true",2041        dest="compare",2042        help="compare two mach files",2043        default=False,2044    )2045    parser.add_option(2046        "-M",2047        "--extract-modules",2048        action="store_true",2049        dest="extract_modules",2050        help="Extract modules from file",2051        default=False,2052    )2053    parser.add_option(2054        "-C",2055        "--count",2056        type="int",2057        dest="max_count",2058        help="Sets the max byte count when dumping section data",2059        default=-1,2060    )2061 2062    (options, mach_files) = parser.parse_args()2063    if options.extract_modules:2064        if options.section_names:2065            print("error: can't use --section option with the --extract-modules option")2066            exit(1)2067        if not options.outfile:2068            print(2069                "error: the --output=FILE option must be specified with the --extract-modules option"2070            )2071            exit(1)2072        options.section_names.append("__apple_ast")2073    if options.compare:2074        if len(mach_files) == 2:2075            mach_a = Mach()2076            mach_b = Mach()2077            mach_a.parse(mach_files[0])2078            mach_b.parse(mach_files[1])2079            mach_a.compare(mach_b)2080        else:2081            print("error: --compare takes two mach files as arguments")2082    else:2083        if not (2084            options.dump_header2085            or options.dump_load_commands2086            or options.dump_symtab2087            or options.dump_sections2088            or options.find_mangled2089            or options.section_names2090        ):2091            options.dump_header = True2092            options.dump_load_commands = True2093        if options.verbose:2094            print("options", options)2095            print("mach_files", mach_files)2096        for path in mach_files:2097            mach = Mach()2098            mach.parse(path)2099            if options.interactive:2100                interpreter = Mach.Interactive(mach, options)2101                interpreter.cmdloop()2102            else:2103                mach.dump(options)2104