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