14 # Mach header "magic" constants
17 MH_MAGIC_64
= 0xFEEDFACF
18 MH_CIGAM_64
= 0xCFFAEDFE
19 FAT_MAGIC
= 0xCAFEBABE
20 FAT_CIGAM
= 0xBEBAFECA
22 # Mach haeder "filetype" constants
23 MH_OBJECT
= 0x00000001
24 MH_EXECUTE
= 0x00000002
25 MH_FVMLIB
= 0x00000003
27 MH_PRELOAD
= 0x00000005
29 MH_DYLINKER
= 0x00000007
30 MH_BUNDLE
= 0x00000008
31 MH_DYLIB_STUB
= 0x00000009
33 MH_KEXT_BUNDLE
= 0x0000000B
35 # Mach haeder "flag" constant bits
36 MH_NOUNDEFS
= 0x00000001
37 MH_INCRLINK
= 0x00000002
38 MH_DYLDLINK
= 0x00000004
39 MH_BINDATLOAD
= 0x00000008
40 MH_PREBOUND
= 0x00000010
41 MH_SPLIT_SEGS
= 0x00000020
42 MH_LAZY_INIT
= 0x00000040
43 MH_TWOLEVEL
= 0x00000080
44 MH_FORCE_FLAT
= 0x00000100
45 MH_NOMULTIDEFS
= 0x00000200
46 MH_NOFIXPREBINDING
= 0x00000400
47 MH_PREBINDABLE
= 0x00000800
48 MH_ALLMODSBOUND
= 0x00001000
49 MH_SUBSECTIONS_VIA_SYMBOLS
= 0x00002000
50 MH_CANONICAL
= 0x00004000
51 MH_WEAK_DEFINES
= 0x00008000
52 MH_BINDS_TO_WEAK
= 0x00010000
53 MH_ALLOW_STACK_EXECUTION
= 0x00020000
54 MH_ROOT_SAFE
= 0x00040000
55 MH_SETUID_SAFE
= 0x00080000
56 MH_NO_REEXPORTED_DYLIBS
= 0x00100000
58 MH_DEAD_STRIPPABLE_DYLIB
= 0x00400000
59 MH_HAS_TLV_DESCRIPTORS
= 0x00800000
60 MH_NO_HEAP_EXECUTION
= 0x01000000
62 # Mach load command constants
63 LC_REQ_DYLD
= 0x80000000
64 LC_SEGMENT
= 0x00000001
65 LC_SYMTAB
= 0x00000002
66 LC_SYMSEG
= 0x00000003
67 LC_THREAD
= 0x00000004
68 LC_UNIXTHREAD
= 0x00000005
69 LC_LOADFVMLIB
= 0x00000006
70 LC_IDFVMLIB
= 0x00000007
72 LC_FVMFILE
= 0x00000009
73 LC_PREPAGE
= 0x0000000A
74 LC_DYSYMTAB
= 0x0000000B
75 LC_LOAD_DYLIB
= 0x0000000C
76 LC_ID_DYLIB
= 0x0000000D
77 LC_LOAD_DYLINKER
= 0x0000000E
78 LC_ID_DYLINKER
= 0x0000000F
79 LC_PREBOUND_DYLIB
= 0x00000010
80 LC_ROUTINES
= 0x00000011
81 LC_SUB_FRAMEWORK
= 0x00000012
82 LC_SUB_UMBRELLA
= 0x00000013
83 LC_SUB_CLIENT
= 0x00000014
84 LC_SUB_LIBRARY
= 0x00000015
85 LC_TWOLEVEL_HINTS
= 0x00000016
86 LC_PREBIND_CKSUM
= 0x00000017
87 LC_LOAD_WEAK_DYLIB
= 0x00000018 | LC_REQ_DYLD
88 LC_SEGMENT_64
= 0x00000019
89 LC_ROUTINES_64
= 0x0000001A
91 LC_RPATH
= 0x0000001C | LC_REQ_DYLD
92 LC_CODE_SIGNATURE
= 0x0000001D
93 LC_SEGMENT_SPLIT_INFO
= 0x0000001E
94 LC_REEXPORT_DYLIB
= 0x0000001F | LC_REQ_DYLD
95 LC_LAZY_LOAD_DYLIB
= 0x00000020
96 LC_ENCRYPTION_INFO
= 0x00000021
97 LC_DYLD_INFO
= 0x00000022
98 LC_DYLD_INFO_ONLY
= 0x00000022 | LC_REQ_DYLD
99 LC_LOAD_UPWARD_DYLIB
= 0x00000023 | LC_REQ_DYLD
100 LC_VERSION_MIN_MACOSX
= 0x00000024
101 LC_VERSION_MIN_IPHONEOS
= 0x00000025
102 LC_FUNCTION_STARTS
= 0x00000026
103 LC_DYLD_ENVIRONMENT
= 0x00000027
106 CPU_ARCH_MASK
= 0xFF000000
107 CPU_ARCH_ABI64
= 0x01000000
108 CPU_TYPE_ANY
= 0xFFFFFFFF
112 CPU_TYPE_X86_64
= CPU_TYPE_I386 | CPU_ARCH_ABI64
114 CPU_TYPE_MC98000
= 10
117 CPU_TYPE_MC88000
= 13
121 CPU_TYPE_POWERPC
= 18
122 CPU_TYPE_POWERPC64
= CPU_TYPE_POWERPC | CPU_ARCH_ABI64
124 # VM protection constants
129 # VM protection constants
135 # Values for nlist N_TYPE bits of the "Mach.NList.type" field.
142 # Section indexes for the "Mach.NList.sect_idx" fields
177 vm_prot_names
= ["---", "r--", "-w-", "rw-", "--x", "r-x", "-wx", "rwx"]
180 def 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
)
187 while i
< hex_bytes_len
:
188 if ((i
/ 2) % num_per_line
) == 0:
190 print(" %s" % (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_char
203 if (i
/ 2) % num_per_line
:
204 padding
= num_per_line
- ((i
/ 2) % num_per_line
)
207 print("%*s%s" % (padding
* 3 + 1, "", ascii_str
))
211 class TerminalColors
:
212 """Simple terminal colors class"""
214 def __init__(self
, enabled
=True):
215 # TODO: discover terminal type from "file" and disable if
216 # it can't handle the color codes
217 self
.enabled
= enabled
220 """Reset all terminal colors and formatting."""
225 def bold(self
, on
=True):
226 """Enable or disable bold depending on the "on" parameter."""
234 def italics(self
, on
=True):
235 """Enable or disable italics depending on the "on" parameter."""
243 def underline(self
, on
=True):
244 """Enable or disable underline depending on the "on" parameter."""
252 def inverse(self
, on
=True):
253 """Enable or disable inverse depending on the "on" parameter."""
261 def strike(self
, on
=True):
262 """Enable or disable strike through depending on the "on" parameter."""
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.
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.
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.
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.
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.
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.
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.
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.
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.
370 def swap_unpack_char():
371 """Returns the unpack prefix that will for non-native endian-ness."""
372 if struct
.pack("H", 1).startswith("\x00"):
377 def dump_hex_bytes(addr
, s
, bytes_per_line
=16):
381 if (i
% bytes_per_line
) == 0:
384 line
= "%#8.8x: " % (addr
+ i
)
385 line
+= "%02X " % ord(ch
)
390 def dump_hex_byte_string_diff(addr
, a
, b
, bytes_per_line
=16):
399 tty_colors
= TerminalColors(True)
400 for i
in range(max_len
):
413 mismatch
= ch_a
!= ch_b
414 if (i
% bytes_per_line
) == 0:
417 line
= "%#8.8x: " % (addr
+ i
)
419 line
+= tty_colors
.red()
420 line
+= "%02X " % ord(ch
)
422 line
+= tty_colors
.default()
429 """Class that does everything mach-o related"""
432 """Class that implements mach-o architectures"""
434 def __init__(self
, c
=0, s
=0):
438 def set_cpu_type(self
, c
):
441 def set_cpu_subtype(self
, s
):
444 def set_arch(self
, c
, s
):
449 return (self
.cpu
& CPU_ARCH_ABI64
) != 0
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
],
489 for info
in self
.cpu_infos
:
490 if self
.cpu
== info
[1] and (self
.sub
& 0x00FFFFFF) == info
[2]:
492 return "{0}.{1}".format(self
.cpu
, self
.sub
)
494 class Magic(dict_utils
.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
,
504 def __init__(self
, initial_value
=0):
505 dict_utils
.Enum
.__init
__(self
, initial_value
, self
.enum
)
507 def is_skinny_mach_file(self
):
509 self
.value
== MH_MAGIC
510 or self
.value
== MH_CIGAM
511 or self
.value
== MH_MAGIC_64
512 or self
.value
== MH_CIGAM_64
515 def is_universal_mach_file(self
):
516 return self
.value
== FAT_MAGIC
or self
.value
== FAT_CIGAM
518 def unpack(self
, data
):
519 data
.set_byte_order("native")
520 self
.value
= data
.get_uint32()
522 def get_byte_order(self
):
524 self
.value
== MH_CIGAM
525 or self
.value
== MH_CIGAM_64
526 or self
.value
== FAT_CIGAM
528 return swap_unpack_char()
533 return self
.value
== MH_MAGIC_64
or self
.value
== MH_CIGAM_64
536 self
.magic
= Mach
.Magic()
540 def extract(self
, path
, extractor
):
542 self
.unpack(extractor
)
544 def parse(self
, path
):
548 file_extractor
= file_extract
.FileExtract(f
, "=")
549 self
.unpack(file_extractor
)
551 except IOError as xxx_todo_changeme
:
552 (errno
, strerror
) = xxx_todo_changeme
.args
553 print("I/O error({0}): {1}".format(errno
, strerror
))
555 print("Could not convert data to an integer.")
557 print("Unexpected error:", sys
.exc_info()[0])
560 def compare(self
, rhs
):
561 self
.content
.compare(rhs
.content
)
563 def dump(self
, options
=None):
564 self
.content
.dump(options
)
566 def dump_header(self
, dump_description
=True, options
=None):
567 self
.content
.dump_header(dump_description
, options
)
569 def dump_load_commands(self
, dump_description
=True, options
=None):
570 self
.content
.dump_load_commands(dump_description
, options
)
572 def dump_sections(self
, dump_description
=True, options
=None):
573 self
.content
.dump_sections(dump_description
, options
)
575 def dump_section_contents(self
, options
):
576 self
.content
.dump_section_contents(options
)
578 def dump_symtab(self
, dump_description
=True, options
=None):
579 self
.content
.dump_symtab(dump_description
, options
)
581 def dump_symbol_names_matching_regex(self
, regex
, file=None):
582 self
.content
.dump_symbol_names_matching_regex(regex
, file)
584 def description(self
):
585 return self
.content
.description()
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
)
596 if self
.content
is not None:
597 self
.content
.unpack(data
, self
.magic
)
600 return self
.content
is not None
603 def __init__(self
, path
):
605 self
.type = "universal"
611 def description(self
):
612 s
= "%#8.8x: %s (" % (self
.file_off
, self
.path
)
614 for arch
in self
.archs
:
615 if len(archs_string
):
617 archs_string
+= "%s" % arch
.arch
622 def unpack(self
, data
, magic
=None):
623 self
.file_off
= data
.tell()
625 self
.magic
= Mach
.Magic()
626 self
.magic
.unpack(data
)
629 self
.file_off
= self
.file_off
- 4
630 # Universal headers are always in big endian
631 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
)
643 def compare(self
, rhs
):
644 print("error: comparing two universal files is not supported yet")
647 def dump(self
, options
):
648 if options
.dump_header
:
651 "Universal Mach File: magic = %s, nfat_arch = %u"
652 % (self
.magic
, self
.nfat_arch
)
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
:
662 for i
in range(self
.nfat_arch
):
663 self
.archs
[i
].mach
.dump(options
)
665 def dump_header(self
, dump_description
=True, options
=None):
667 print(self
.description())
668 for i
in range(self
.nfat_arch
):
669 self
.archs
[i
].mach
.dump_header(True, options
)
672 def dump_load_commands(self
, dump_description
=True, options
=None):
674 print(self
.description())
675 for i
in range(self
.nfat_arch
):
676 self
.archs
[i
].mach
.dump_load_commands(True, options
)
679 def dump_sections(self
, dump_description
=True, options
=None):
681 print(self
.description())
682 for i
in range(self
.nfat_arch
):
683 self
.archs
[i
].mach
.dump_sections(True, options
)
686 def dump_section_contents(self
, options
):
687 for i
in range(self
.nfat_arch
):
688 self
.archs
[i
].mach
.dump_section_contents(options
)
691 def dump_symtab(self
, dump_description
=True, options
=None):
693 print(self
.description())
694 for i
in range(self
.nfat_arch
):
695 self
.archs
[i
].mach
.dump_symtab(True, options
)
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)
704 self
.arch
= Mach
.Arch(0, 0)
710 def unpack(self
, data
):
711 # Universal headers are always in big endian
712 data
.set_byte_order("big")
719 ) = data
.get_n_uint32(5)
721 def dump_header(self
, dump_description
=True, options
=None):
723 print("CPU SUBTYPE OFFSET SIZE ALIGN")
724 print("---------- ---------- ---------- ---------- ----------")
726 print("ARCH FILEOFFSET FILESIZE ALIGN")
727 print("---------- ---------- ---------- ----------")
729 def dump_flat(self
, options
):
732 "%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x"
743 "%-10s %#8.8x %#8.8x %#8.8x"
744 % (self
.arch
, self
.offset
, self
.size
, self
.align
)
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
)
755 return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (
764 return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (
773 def __init__(self
, b
):
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
:
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 flags
833 class FileType(dict_utils
.Enum
):
835 "MH_OBJECT": MH_OBJECT
,
836 "MH_EXECUTE": MH_EXECUTE
,
837 "MH_FVMLIB": MH_FVMLIB
,
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
,
845 "MH_KEXT_BUNDLE": MH_KEXT_BUNDLE
,
848 def __init__(self
, initial_value
=0):
849 dict_utils
.Enum
.__init
__(self
, initial_value
, self
.enum
)
852 def __init__(self
, path
):
858 self
.arch
= Mach
.Arch(0, 0)
859 self
.filetype
= Mach
.FileType(0)
862 self
.flags
= Mach
.Flags(0)
864 self
.commands
= list()
865 self
.segments
= list()
866 self
.sections
= list()
867 self
.symbols
= list()
868 self
.sections
.append(Mach
.Section())
870 def description(self
):
871 return "%#8.8x: %s (%s)" % (self
.file_off
, self
.path
, self
.arch
)
873 def unpack(self
, data
, magic
=None):
875 self
.file_off
= data
.tell()
877 self
.magic
= Mach
.Magic()
878 self
.magic
.unpack(data
)
881 self
.file_off
= self
.file_off
- 4
882 data
.set_byte_order(self
.magic
.get_byte_order())
890 ) = data
.get_n_uint32(6)
891 self
.flags
.bits
= bits
894 data
.get_uint32() # Skip reserved word in mach_header_64
896 for i
in range(0, self
.ncmds
):
897 lc
= self
.unpack_load_command(data
)
898 self
.commands
.append(lc
)
902 self
.data
.set_byte_order(self
.magic
.get_byte_order())
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
)
914 lc_command
== LC_LOAD_DYLIB
915 or lc_command
== LC_ID_DYLIB
916 or lc_command
== LC_LOAD_WEAK_DYLIB
917 or lc_command
== LC_REEXPORT_DYLIB
919 lc
= Mach
.DylibLoadCommand(lc
)
920 lc
.unpack(self
, data
)
922 lc_command
== LC_LOAD_DYLINKER
923 or lc_command
== LC_SUB_FRAMEWORK
924 or lc_command
== LC_SUB_CLIENT
925 or lc_command
== LC_SUB_UMBRELLA
926 or lc_command
== LC_SUB_LIBRARY
927 or lc_command
== LC_ID_DYLINKER
928 or lc_command
== LC_RPATH
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
)
945 lc_command
== LC_CODE_SIGNATURE
946 or lc_command
== LC_SEGMENT_SPLIT_INFO
947 or lc_command
== LC_FUNCTION_STARTS
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
)
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
))
965 if self
.type == rhs
.type:
966 for lhs_section
in self
.sections
[1:]:
967 rhs_section
= rhs
.get_section_by_section(lhs_section
)
971 % (lhs_section
.segname
, lhs_section
.sectname
),
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
:
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)
987 # # TODO: check padding
989 # 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)
993 # # TODO: check padding
997 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
))
1007 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
))
1012 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
))
1022 "error: section %s is missing in %s"
1023 % (lhs_section
.sectname
, rhs
.path
)
1027 "error: comparing a %s mach-o file with a %s mach-o file is not supported"
1028 % (self
.type, rhs
.type)
1032 print("error: mach files differ")
1035 def dump_header(self
, dump_description
=True, options
=None):
1038 "MAGIC CPU SUBTYPE FILETYPE NUM CMDS SIZE CMDS FLAGS"
1041 "---------- ---------- ---------- ---------- -------- ---------- ----------"
1045 "MAGIC ARCH FILETYPE NUM CMDS SIZE CMDS FLAGS"
1048 "------------ ---------- -------------- -------- ---------- ----------"
1051 def dump_flat(self
, options
):
1054 "%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x"
1059 self
.filetype
.value
,
1067 "%-12s %-10s %-14s %#8u %#8.8x %s"
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
:
1089 if len(self
.symbols
):
1090 self
.dump_sections(False, options
)
1093 if options
.find_mangled
:
1094 self
.dump_symbol_names_matching_regex(re
.compile("^_?_Z"))
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
)
1104 " filetype: %#8.8x %s"
1105 % (self
.filetype
.get_enum_value(), self
.filetype
.get_enum_name())
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
))
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
:
1117 def get_section_by_name(self
, name
):
1118 for section
in self
.sections
:
1119 if section
.sectname
and section
.sectname
== name
:
1123 def get_section_by_section(self
, other_section
):
1124 for section
in self
.sections
:
1126 section
.sectname
== other_section
.sectname
1127 and section
.segname
== other_section
.segname
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
])
1141 def dump_section_contents(self
, options
):
1142 saved_section_to_disk
= False
1143 for sectname
in options
.section_names
:
1144 section
= self
.get_section_by_name(sectname
)
1146 sect_bytes
= section
.get_contents(self
)
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_order
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,
1173 data
.seek(data_offset
)
1174 outfile
.write(data
.read_size(data_size
))
1177 "Saving section %s to '%s'"
1178 % (sectname
, options
.outfile
)
1180 outfile
.write(sect_bytes
)
1182 saved_section_to_disk
= True
1185 "error: you can only save a single section to disk at a time, skipping section '%s'"
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)
1194 print('error: no section named "%s" was found' % (sectname
))
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
:
1204 def get_first_load_command(self
, lc_enum_value
):
1205 for lc
in self
.commands
:
1206 if lc
.command
.value
== lc_enum_value
:
1210 def get_symtab(self
):
1211 if self
.data
and not self
.symbols
:
1212 lc_symtab
= self
.get_first_load_command(LC_SYMTAB
)
1214 symtab_offset
= self
.file_off
1215 if self
.data
.is_in_memory():
1216 linkedit_segment
= self
.get_segment("__LINKEDIT")
1217 if linkedit_segment
:
1218 linkedit_vmaddr
= linkedit_segment
.vmaddr
1219 linkedit_fileoff
= linkedit_segment
.fileoff
1221 linkedit_vmaddr
+ lc_symtab
.symoff
- linkedit_fileoff
1224 linkedit_vmaddr
+ lc_symtab
.stroff
- linkedit_fileoff
1227 symtab_offset
+= lc_symtab
.symoff
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
)
1236 print("no LC_SYMTAB")
1238 def dump_symtab(self
, dump_description
=True, options
=None):
1240 if dump_description
:
1241 print(self
.description())
1242 for i
, symbol
in enumerate(self
.symbols
):
1243 print("[%5u] %s" % (i
, symbol
))
1245 def dump_symbol_names_matching_regex(self
, regex
, file=None):
1247 for symbol
in self
.symbols
:
1248 if symbol
.name
and regex
.search(symbol
.name
):
1251 file.write("%s\n" % (symbol
.name
))
1253 def is_64_bit(self
):
1254 return self
.magic
.is_64_bit()
1257 class Command(dict_utils
.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
,
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
,
1301 def __init__(self
, initial_value
=0):
1302 dict_utils
.Enum
.__init
__(self
, initial_value
, self
.enum
)
1304 def __init__(self
, c
=None, l
=0, o
=0):
1308 self
.command
= Mach
.LoadCommand
.Command(0)
1312 def unpack(self
, mach_file
, data
):
1313 self
.file_off
= data
.tell()
1314 self
.command
.value
, self
.length
= data
.get_n_uint32(2)
1316 def skip(self
, data
):
1317 data
.seek(self
.file_off
+ self
.length
, 0)
1320 lc_name
= self
.command
.get_enum_name()
1321 return "%#8.8x: <%#4.4x> %-24s" % (self
.file_off
, self
.length
, lc_name
)
1327 self
.sectname
= None
1340 def unpack(self
, is_64
, data
):
1342 self
.sectname
= data
.get_fixed_length_c_string(16, "", True)
1343 self
.segname
= data
.get_fixed_length_c_string(16, "", True)
1345 self
.addr
, self
.size
= data
.get_n_uint64(2)
1355 ) = data
.get_n_uint32(8)
1357 self
.addr
, self
.size
= data
.get_n_uint32(2)
1366 ) = data
.get_n_uint32(7)
1368 def dump_header(self
):
1371 "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 RESERVED3 NAME"
1374 "===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"
1378 "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 NAME"
1381 "===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"
1387 "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s"
1406 "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s"
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()
1428 section_data_offset
= mach_file
.file_off
+ self
.offset
1429 # print '%s.%s is at offset 0x%x with size 0x%x' %
1430 # (self.segname, self.sectname, section_data_offset,
1432 data
.push_offset_and_seek(section_data_offset
)
1433 bytes
= data
.read_size(self
.size
)
1434 data
.pop_offset_and_seek()
1438 class DylibLoadCommand(LoadCommand
):
1439 def __init__(self
, lc
):
1440 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1443 self
.current_version
= 0
1444 self
.compatibility_version
= 0
1446 def unpack(self
, mach_file
, data
):
1447 byte_order_char
= mach_file
.magic
.get_byte_order()
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)
1458 s
= Mach
.LoadCommand
.__str
__(self
)
1459 s
+= "%#8.8x %#8.8x %#8.8x " % (
1461 self
.current_version
,
1462 self
.compatibility_version
,
1467 class LoadDYLDLoadCommand(LoadCommand
):
1468 def __init__(self
, lc
):
1469 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1472 def unpack(self
, mach_file
, data
):
1474 self
.name
= data
.get_fixed_length_c_string(self
.length
- 12)
1477 s
= Mach
.LoadCommand
.__str
__(self
)
1478 s
+= "%s" % self
.name
1481 class UnixThreadLoadCommand(LoadCommand
):
1486 self
.register_values
= list()
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
)
1493 s
= "flavor = %u, count = %u, regs =" % (self
.flavor
, self
.count
)
1495 for register_value
in self
.register_values
:
1498 s
+= " %#8.8x" % register_value
1502 def __init__(self
, lc
):
1503 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1504 self
.reg_sets
= list()
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
)
1512 s
= Mach
.LoadCommand
.__str
__(self
)
1513 for reg_set
in self
.reg_sets
:
1517 class DYLDInfoOnlyLoadCommand(LoadCommand
):
1518 def __init__(self
, lc
):
1519 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1521 self
.rebase_size
= 0
1524 self
.weak_bind_off
= 0
1525 self
.weak_bind_size
= 0
1526 self
.lazy_bind_off
= 0
1527 self
.lazy_bind_size
= 0
1529 self
.export_size
= 0
1531 def unpack(self
, mach_file
, data
):
1532 byte_order_char
= mach_file
.magic
.get_byte_order()
1539 self
.weak_bind_size
,
1541 self
.lazy_bind_size
,
1544 ) = data
.get_n_uint32(10)
1547 s
= Mach
.LoadCommand
.__str
__(self
)
1548 s
+= "rebase_off = %#8.8x, rebase_size = %u, " % (
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, " % (
1555 self
.weak_bind_size
,
1557 s
+= "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % (
1559 self
.lazy_bind_size
,
1561 s
+= "export_off = %#8.8x, export_size = %u, " % (
1567 class DYLDSymtabLoadCommand(LoadCommand
):
1568 def __init__(self
, lc
):
1569 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1580 self
.extrefsymoff
= 0
1581 self
.nextrefsyms
= 0
1582 self
.indirectsymoff
= 0
1583 self
.nindirectsyms
= 0
1589 def unpack(self
, mach_file
, data
):
1590 byte_order_char
= mach_file
.magic
.get_byte_order()
1604 self
.indirectsymoff
,
1610 ) = data
.get_n_uint32(18)
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,
1624 s
+= "ilocalsym = %-10u, nlocalsym = %u\n" % (
1629 " iextdefsym = %-10u, nextdefsym = %u\n"
1630 % (self
.iextdefsym
, self
.nextdefsym
)
1633 " iundefsym = %-10u, nundefsym = %u\n"
1634 % (self
.iundefsym
, self
.nundefsym
)
1637 " tocoff = %#8.8x, ntoc = %u\n"
1638 % (self
.tocoff
, self
.ntoc
)
1641 " modtaboff = %#8.8x, nmodtab = %u\n"
1642 % (self
.modtaboff
, self
.nmodtab
)
1645 " extrefsymoff = %#8.8x, nextrefsyms = %u\n"
1646 % (self
.extrefsymoff
, self
.nextrefsyms
)
1649 " indirectsymoff = %#8.8x, nindirectsyms = %u\n"
1650 % (self
.indirectsymoff
, self
.nindirectsyms
)
1653 " extreloff = %#8.8x, nextrel = %u\n"
1654 % (self
.extreloff
, self
.nextrel
)
1657 " locreloff = %#8.8x, nlocrel = %u"
1658 % (self
.locreloff
, self
.nlocrel
)
1662 class SymtabLoadCommand(LoadCommand
):
1663 def __init__(self
, lc
):
1664 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
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)
1675 s
= Mach
.LoadCommand
.__str
__(self
)
1676 s
+= "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % (
1684 class UUIDLoadCommand(LoadCommand
):
1685 def __init__(self
, lc
):
1686 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1689 def unpack(self
, mach_file
, data
):
1690 uuid_data
= data
.get_n_uint8(16)
1692 for byte
in uuid_data
:
1693 uuid_str
+= "%2.2x" % byte
1694 self
.uuid
= uuid
.UUID(uuid_str
)
1695 mach_file
.uuid
= self
.uuid
1698 s
= Mach
.LoadCommand
.__str
__(self
)
1699 s
+= self
.uuid
.__str
__()
1702 class DataBlobLoadCommand(LoadCommand
):
1703 def __init__(self
, lc
):
1704 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
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)
1713 s
= Mach
.LoadCommand
.__str
__(self
)
1714 s
+= "dataoff = %#8.8x, datasize = %u" % (self
.dataoff
, self
.datasize
)
1717 class EncryptionInfoLoadCommand(LoadCommand
):
1718 def __init__(self
, lc
):
1719 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
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)
1729 s
= Mach
.LoadCommand
.__str
__(self
)
1730 s
+= "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % (
1732 self
.cryptoff
+ self
.cryptsize
,
1738 class SegmentLoadCommand(LoadCommand
):
1739 def __init__(self
, lc
):
1740 Mach
.LoadCommand
.__init
__(self
, lc
.command
, lc
.length
, lc
.file_off
)
1751 def unpack(self
, mach_file
, data
):
1752 is_64
= self
.command
.get_enum_value() == LC_SEGMENT_64
1753 self
.segname
= data
.get_fixed_length_c_string(16, "", True)
1760 ) = data
.get_n_uint64(4)
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
)
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 " % (
1786 s
+= "%#16.16x %#16.16x %#16.16x %#16.16x " % (
1792 s
+= "%s %s %3u %#8.8x" % (
1793 vm_prot_names
[self
.maxprot
],
1794 vm_prot_names
[self
.initprot
],
1798 s
+= " " + self
.segname
1803 class Stab(dict_utils
.Enum
):
1821 "N_PARAMS": N_PARAMS
,
1822 "N_VERSION": N_VERSION
,
1823 "N_OLEVEL": N_OLEVEL
,
1836 def __init__(self
, magic
=0):
1837 dict_utils
.Enum
.__init
__(self
, magic
, self
.enum
)
1839 def __init__(self
, t
=0):
1845 stab
= Mach
.NList
.Type
.Stab(self
.value
)
1848 type = self
.value
& N_TYPE
1854 elif type == N_SECT
:
1856 elif type == N_PBUD
:
1858 elif type == N_INDR
:
1861 type_str
= "??? (%#2.2x)" % type
1863 type_str
+= " | PEXT"
1865 type_str
+= " | EXT "
1870 self
.name_offset
= 0
1872 self
.type = Mach
.NList
.Type()
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()
1885 self
.value
= data
.get_uint32()
1886 data
.push_offset_and_seek(
1887 mach_file
.file_off
+ symtab_lc
.stroff
+ self
.name_offset
1889 # print "get string for symbol[%u]" % self.index
1890 self
.name
= data
.get_c_string()
1891 data
.pop_offset_and_seek()
1896 name_display
= ' "%s"' % self
.name
1897 return "%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s" % (
1907 class Interactive(cmd
.Cmd
):
1908 """Interactive command interpreter to mach-o files."""
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
.path
1915 self
.options
= options
1917 def default(self
, line
):
1918 """Catch all for unknown command, which will exit the interpreter."""
1919 print("uknown command: %s" % line
)
1922 def do_q(self
, line
):
1926 def do_quit(self
, line
):
1930 def do_header(self
, line
):
1931 """Dump mach-o file headers"""
1932 self
.mach
.dump_header(True, self
.options
)
1935 def do_load(self
, line
):
1936 """Dump all mach-o load commands"""
1937 self
.mach
.dump_load_commands(True, self
.options
)
1940 def do_sections(self
, line
):
1941 """Dump all mach-o sections"""
1942 self
.mach
.dump_sections(True, self
.options
)
1945 def do_symtab(self
, line
):
1946 """Dump all mach-o symbols in the symbol table"""
1947 self
.mach
.dump_symtab(True, self
.options
)
1951 if __name__
== "__main__":
1952 parser
= optparse
.OptionParser(
1953 description
="A script that parses skinny and universal mach-o files."
1962 help="specify one or more architectures by name",
1967 action
="store_true",
1969 help="display verbose debug info",
1975 action
="store_true",
1977 help="dump the mach-o file header",
1983 action
="store_true",
1984 dest
="dump_load_commands",
1985 help="dump the mach-o load commands",
1991 action
="store_true",
1993 help="dump the mach-o symbol table",
1999 action
="store_true",
2000 dest
="dump_sections",
2001 help="dump the mach-o sections",
2008 dest
="section_names",
2010 help="Specify one or more section names to dump",
2018 help="Used in conjunction with the --section=NAME option to save a single section's data to disk.",
2024 action
="store_true",
2026 help="enable interactive mode",
2032 action
="store_true",
2033 dest
="find_mangled",
2034 help="dump all mangled names in a mach file",
2040 action
="store_true",
2042 help="compare two mach files",
2047 "--extract-modules",
2048 action
="store_true",
2049 dest
="extract_modules",
2050 help="Extract modules from file",
2058 help="Sets the max byte count when dumping section data",
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")
2067 if not options
.outfile
:
2069 "error: the --output=FILE option must be specified with the --extract-modules option"
2072 options
.section_names
.append("__apple_ast")
2074 if len(mach_files
) == 2:
2077 mach_a
.parse(mach_files
[0])
2078 mach_b
.parse(mach_files
[1])
2079 mach_a
.compare(mach_b
)
2081 print("error: --compare takes two mach files as arguments")
2085 or options
.dump_load_commands
2086 or options
.dump_symtab
2087 or options
.dump_sections
2088 or options
.find_mangled
2089 or options
.section_names
2091 options
.dump_header
= True
2092 options
.dump_load_commands
= True
2094 print("options", options
)
2095 print("mach_files", mach_files
)
2096 for path
in mach_files
:
2099 if options
.interactive
:
2100 interpreter
= Mach
.Interactive(mach
, options
)
2101 interpreter
.cmdloop()