//------------------------------------------------ //--- 010 Editor v3.2.2 Binary Template // // File: MachO.bt // Authors: Tim "diff" Strazzere, Harli Aquino // E-mail: diff@lookout.com, strazz@gmail.com // Version: 1.9 // Purpose: Quick template for parsing Mach-o binaries, // including Mac OS X executables, .o and .dylib files. // Category: Executable // File Mask: *,*.o,*.dylib // ID Bytes: CF FA ED FE, CE FA ED FE, BE BA FE CA, CA FE BA BE // History: // 1.9 2023-09-12 Harli Aquino : add indirect symbols parsing // - parsed strings from string related sections // - moved macho header and load command structs into an macho object struct // - added symbol table and indirect symbols parser // - added export trie parser // 1.8 2022-07-19 Minyoung Sim: Add code signature // 1.7 2019-03-05 D.Miller: Fixed ARM64, Added CpuType enum, Changed Load Handling to MAIN, MAIN|DYLD // - Changed load command handling to always seek to orig offset + command size after switch // 1.6 2019-02-19 nathan@lanza.io: LC_MAIN is 0x28 | REQ_DYLD - adjust this template accordingly and implement in switch // 1.5 2018-11-09 swigger at gmail.com: support LC_BUILD_VERSION load command. // 1.4 2017-03-17 swigger at gmail.com: enable encryption segment 64. // 1.3 2016-06-08 N Moinvaziri: Fixed definition of section_64. Offset should have been uint64 and reserved3 missing. // 1.2 2016-02-12 SweetScape Software: Updated header for repository submission. // 1.1 T Strazzere: - Minimum version load command now properly outputs the format for better readability // - Added a readvalue function for the header, helps understand headers at a glance // 1.0 T Strazzere: - Correctly parses FAT headers and will continue to parse the rest of the combined // binary // - Added many todo's to make the output more pretty // - Fixed some broken LoadCommands (64bit ones mainly), will gracefully fail if unknown // LoadCommand is hit // - Found some bugs in 010Editor and added fixes to try to avoid those // 1.0 T Strazzere: - First stab it this, lots of issues - FAT binaries don't work at all // // Known issues: // - Needs optimized structures otherwise anything of a decent size will kill it // (Related to an 010Editor template bug) //------------------------------------------------ // Mach-o's should be Little Endian only -- except for the fat_header/fat_arch LittleEndian(); typedef enum { MACHO_32 = 0xFEEDFACE, // 32-bit mach object file MACHO_64 = 0xFEEDFACF, // 64-bit mach object file MACHO_FAT = 0xCAFEBABE, // Universal object file / FAT_MAGIC MACHO_FAT_CIGAM = 0xBEBAFECA } Magic ; #define CPU_ARCH_MASK 0xff000000 #define CPU_ARCH_ABI64 0x01000000 // Changed to enum, more info and handles constant | macro fine // // typedef enum { CPU_TYPE_X86 = 0x07, CPU_TYPE_X64 = 0x07 | CPU_ARCH_ABI64, CPU_TYPE_ARM = 0x0C, CPU_TYPE_ARM64 = 0x0C | CPU_ARCH_ABI64, CPU_TYPE_PPC = 0x12, CPU_TYPE_PPC64 = 0x12 | CPU_ARCH_ABI64, CPU_TYPE_I386 = CPU_TYPE_X86, CPU_TYPE_X86_64 = CPU_TYPE_X64, CPU_TYPE_POWERPC = CPU_TYPE_PPC, CPU_TYPE_POWERPC64 = CPU_TYPE_PPC64, } CpuType ; typedef enum { MACH_OBJECT = 0x1, MACH_EXECUTE = 0x2, MACH_FVMLIB = 0x3, MACH_CORE = 0x4, MACH_PRELOAD = 0x5, MACH_DYLIB = 0x6, MACH_DYLINKER = 0x7, MACH_BUNDLE = 0x8, MACH_DYLIB_STUB = 0x9, MACH_DSYM = 0xA, MACH_KEXT_BUNDLE = 0xB, } FileType; typedef enum { i386_THREAD_STATE = 0x1, i386_FLOAT_STATE = 0x2, i386_EXCEPTION_STATE = 0x3 } i386ThreadFlavor ; typedef enum { x86_THREAD_STATE32 = 0x1, x86_FLOAT_STATE32 = 0x2, x86_EXCEPTION_STATE32 = 0x3, x86_THREAD_STATE64 = 0x4, x86_FLOAT_STATE64 = 0x5, x86_EXCEPTION_STATE64 = 0x6, x86_THREAD_STATE = 0x7, x86_FLOAT_STATE = 0x8, x86_EXCEPTION_STATE = 0x9, x86_DEBUG_STATE32 = 0xA, x86_DEBUG_STATE64 = 0xB, x86_DEBUG_STATE = 0xC, THREAD_STATE_NONE = 0xD } x86ThreadFlavor ; typedef enum { PPC_THREAD_STATE = 0x1, PPC_FLOAT_STATE = 0x2, PPC_EXCEPTION_STATE = 0x3, PPC_VECTOR_STATE = 0x4, PPC_THREAD_STATE64 = 0x5, PPC_EXCEPTION_STATE64 = 0x6 } PPCThreadFlavor ; typedef enum { MACH_NOUNDEFS = 0x1, MACH_INCRLINK = 0x2, MACH_DYLDLINK = 0x4, MACH_BINDATLOAD = 0x8, MACH_PREBOUND = 0x10, MACH_SPLIT_SEGS = 0x20, MACH_LAZY_INIT = 0x40, MACH_TWOLEVEL = 0x80, MACH_FORCE_FLAT = 0x100, MACH_NOMULTIDEFS = 0x200, MACH_NOFIXPREBINDING = 0x400, MACH_PREBINDABLE = 0x800, MACH_ALLMODSBOUND = 0x1000, MACH_SUBSECTIONS_VIA_SYMBOLS = 0x2000, MACH_CANONICAL = 0x4000, MACH_WEAK_DEFINES = 0x8000, MACH_BINDS_TO_WEAK = 0x10000, MACH_ALLOW_STACK_EXECUTION = 0x20000, MACH_ROOT_SAFE = 0x40000, MACH_SETUID_SAFE = 0x80000, MACH_NO_REEXPORTED_DYLIBS = 0x100000, MACH_PIE = 0x200000, MACH_DEAD_STRIPPABLE_DYLIB = 0x400000, MACH_HAS_TLV_DESCRIPTORS = 0x800000, MACH_NO_HEAP_EXECUTION = 0x1000000 } Flags; #define REQ_DYLD (0x80000000) typedef enum { SEGMENT = 0x1, SYM_TAB = 0x2, SYM_SEG = 0x3, THREAD = 0x4, UNIX_THREAD = 0x5, LOAD_FVM_LIB = 0x6, ID_FVM_LIB = 0x7, IDENT = 0x8, FVM_FILE = 0x9, PREPAGE = 0xA, DY_SYM_TAB = 0xB, LOAD_DYLIB = 0xC, ID_DYLIB = 0xD, LOAD_DYLINKER = 0xE, ID_DYLINKER = 0xF, PREBOUND_DYLIB = 0x10, ROUTINES = 0x11, SUB_FRAMEWORK = 0x12, SUB_UMBRELLA = 0x13, SUB_CLIENT = 0x14, SUB_LIBRARY = 0x15, TWOLEVEL_HINTS = 0x16, PREBIND_CKSUM = 0x17, LOAD_WEAK_DYLIB = 0x18 | REQ_DYLD, SEGMENT_64 = 0x19, ROUTINES_64 = 0x1A, UUID = 0x1B, RPATH = 0x1C | REQ_DYLD, CODE_SIGNATURE = 0x1D, SEGMENT_SPLIT_INFO = 0x1E, REEXPORT_DYLIB = 0x1F | REQ_DYLD, LAZY_LOAD_DYLIB = 0x20, ENCRYPTION_INFO = 0x21, DYLD_INFO = 0x22, DYLD_INFO_ONLY = 0x22 | REQ_DYLD, LOAD_UPWARD_DYLIB = 0x23 | REQ_DYLD, VERSION_MIN_MAC_OSX = 0x24, VERSION_MIN_IPHONE_OS = 0x25, FUNCTION_STARTS = 0x26, DYLD_ENVIRONMENT = 0x27, MAIN = 0x28, MAIN_DYLIB = 0x28 | REQ_DYLD, // Idk, ios app in arm64 uses DATA_IN_CODE = 0x29, SOURCE_VERSION = 0x2A, DYLIB_CODE_SIGN_DRS = 0x2B, ENCRYPTION_INFO_64 = 0x2c, LC_BUILD_VERSION = 0x32, LC_DYLD_EXPORTS_TRIE = 0x33 | REQ_DYLD, // https://go-review.googlesource.com/c/go/+/312729/2/src/cmd/link/internal/ld/macho.go#183 LC_DYLD_CHAINED_FIXUPS = 0x34 | REQ_DYLD } LoadCommandType ; typedef uint vm_proc; typedef enum { HIGH_VM = 0x1, FVM_LIB = 0x2, NO_RELOC = 0x4, PROTECTION_VERSION_1 = 0x8 } SegmentFlags ; typedef enum { N_UNDF = 0x00, N_ABS = 0x02, N_INDR = 0x0a, N_SECT = 0x0e, N_PBUD = 0x0c, } NType; typedef enum { N_EXT = 0x01, N_TYPE = 0x0e, N_PEXT = 0x10, N_STAB = 0xe0 } SType; typedef enum { REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0x00, REFERENCE_FLAG_UNDEFINED_LAZY = 0x01, REFERENCE_FLAG_DEFINED = 0x02, REFERENCE_FLAG_PRIVATE_DEFINED = 0x03, REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 0x04, REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 0x05, REFERENCED_DYNAMICALLY = 0x10, N_NO_DEAD_STRIP = 0x20, N_WEAK_REF = 0x40, N_WEAK_DEF = 0x80 } ReferenceType; typedef enum { EXPORT_SYMBOL_FLAG_KIND_REGULAR, EXPORT_SYMBOL_FLAG_KIND_THREAD_LOCAL, EXPORT_SYMBOL_FLAG_KIND_ABSOLUTE, EXPORT_SYMBOL_FLAG_KIND_MASK } ExportSymbolFlagKind; typedef enum { CSMAGIC_CODEDIRECTORY = 0xFADE0C02, CSMAGIC_EMBEDDED_SIGNATURE = 0xFADE0CC0, // embedded form of signature data CSMAGIC_BLOBWRAPPER = 0xFADE0B01, } CS_Magic ; typedef enum { CSSLOT_CODEDIRECTORY = 0, CSSLOT_INFOSLOT = 1, CSSLOT_REQUIREMENTS = 2, CSSLOT_RESOURCEDIR = 3, CSSLOT_APPLICATION = 4, CSSLOT_ENTITLEMENTS = 5, CSSLOT_ENTITLEMENTS_DER = 7, CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, CSSLOT_CMS_SIGNATURE = 0x10000, } CS_Slot ; typedef struct { uint32 address; local uint32 file_offset = address - text_section_vm_address32; } i386_rip; typedef struct { uint32 eax ; uint32 ebx ; uint32 ecx ; uint32 edx ; uint32 edi ; uint32 esi ; uint32 ebp ; uint32 esp ; uint32 ss ; uint32 eflags ; uint32 eip ; uint32 cs ; uint32 ds ; uint32 es ; uint32 fs ; uint32 gs ; } i386ThreadState; typedef struct { uint64 address; local uint64 file_offset = address - text_section_vm_address; } x86_rip; typedef struct { uint64 rax ; uint64 rbx ; uint64 rcx ; uint64 rdx ; uint64 rdi ; uint64 rsi ; uint64 rbp ; uint64 rsp ; uint64 r8 ; uint64 r9 ; uint64 r10 ; uint64 r11 ; uint64 r12 ; uint64 r13 ; uint64 r14 ; uint64 r15 ; x86_rip rip ; uint64 rflags ; uint64 cs ; uint64 fs ; uint64 gs ; } x86ThreadState; typedef struct { uint32 r0 ; uint32 r1 ; uint32 r2 ; uint32 r3 ; uint32 r4 ; uint32 r5 ; uint32 r6 ; uint32 r7 ; uint32 r8 ; uint32 r9 ; uint32 r10 ; uint32 r11 ; uint32 r12 ; uint32 r13 ; uint32 r14 ; uint32 r15 ; uint32 r16 ; } ARMThreadState; typedef struct { uint32 __srr0 ; uint32 __srr1 ; uint32 __r0; uint32 __r1; uint32 __r2; uint32 __r3; uint32 __r4; uint32 __r5; uint32 __r6; uint32 __r7; uint32 __r8; uint32 __r9; uint32 __r10; uint32 __r11; uint32 __r12; uint32 __r13; uint32 __r14; uint32 __r15; uint32 __r16; uint32 __r17; uint32 __r18; uint32 __r19; uint32 __r20; uint32 __r21; uint32 __r22; uint32 __r23; uint32 __r24; uint32 __r25; uint32 __r26; uint32 __r27; uint32 __r28; uint32 __r29; uint32 __r30; uint32 __r31; uint32 __cr ; uint32 __xer ; uint32 __lr ; uint32 __ctr ; uint32 __mq ; uint32 __vrsave ; } PPCThreadState; typedef struct { // TODO : Extract out capabilities here CpuType cpu_type ; uint32 cpu_sub_type ; uint32 file_offset ; uint32 size ; uint32 align ; } Fat_Arch; typedef struct { Magic magic ; if(magic == MACHO_FAT || magic == MACHO_FAT_CIGAM) { // Need to switch to BigEndian! BigEndian(); uint32 fat_arch_size ; Fat_Arch fat_arch[fat_arch_size]; // Switch back to LittleEndian for rest of parsing LittleEndian(); } else { CpuType cpu_type ; uint32 cpu_sub_type ; FileType file_type; uint32 num_load_commands; uint32 size_of_load_commands; Flags flags; } if(magic == MACHO_64) { uint32 reserved; } } Header ; string HeaderRead(Header &header) { local string header_string; switch(header.magic) { case MACHO_FAT : case MACHO_FAT_CIGAM : header_string = "FAT header"; break; case MACHO_32 : header_string = "32bit Mach-O header"; break; case MACHO_64 : header_string = "64bit Mach-O header"; break; default : header_string = "Unknown header!"; } return header_string; } string LoadCommandTypeRead(LoadCommandType &load_commandType) { return EnumToString(load_commandType); } typedef struct (uint32 address, uint32 length){ local uint64 pos = FTell(); //Printf("text_section_vm_address32: 0x%08x, data_address: 0x%08x; data_length: 0x%08x\n", text_section_vm_address32, address, length); FSeek(address-text_section_vm_address32); byte data[length]; FSeek(pos); } CString32 ; string CString32Read(CString32 &cstring32) { Printf("%s", cstring32.data); return _cstring32.data; } typedef struct { uint32 isa; uint32 info; uint32 data_address; uint32 data_length; //Printf("data_address: 0x%08x; data_length: 0x%08x\n", data_address, data_length); if (data_length > 0) { CString32 cstring(data_address, data_length); } } CFString32 ; string CFString32Read(CFString32 &cfstring32) { if (cfstring32.data_length > 0x00) return cfstring32.cstring.data; return ""; } typedef struct (uint32 offset, uint32 size) { local uint64 cfstring_count = size / 0x10; local uint64 idx = 0; for (idx=0; idx < cfstring_count - 1; idx++) { CFString32 cfstring; } } pCFString32; typedef struct { string data; } StringData ; string StringDataRead(StringData &string_data) { return string_data.data; } typedef struct (char section_name[], uint32 offset) { local int ptr = offset; local int str_count = 0; local string this_section_name = section_name; for (ptr=0; ptr < size; ptr++) { if (ReadByte(offset+ptr) == 0x00) { str_count++; } } //Printf("%s count: %d\n", section_name, str_count); for (ptr=0; ptr < str_count; ptr++) { StringData section_string; } } SectionStrings ; typedef struct { local uint64 offset = FTell(); byte opcode[2]; uint32 delta; local uint64 address; if (is_32bit) address = offset + delta + text_section_vm_address32 + 6; else address = offset + delta + text_section_vm_address + 6; } StubData; typedef struct (uint32 size) { local int i ; for (i=0; i < size/6; i++){ StubData stub_data; } } Stubs; typedef struct { char section_name[16]; char segment_name[16]; uint32 address ; uint32 size ; uint32 offset; uint32 section_alignment; uint32 relocation_entry_offset; uint32 number_of_relocation_entries; uint32 flags ; uint32 reserved1; uint32 reserved2; if (offset > 0x00 && ((offset + size) < FileSize())) { local uint64 pos = FTell(); FSeek(offset); if (Strcmp(section_name, "__cfstring") == 0) { FSeek(offset + macho_offset); pCFString32 cfstring32(offset, size); } else if (Strcmp(section_name, "__cstring") == 0 || Strcmp(section_name, "__objc_classname") == 0 || Strcmp(section_name, "__objc_methname") == 0 || Strcmp(section_name, "__objc_methtype") == 0 ) { FSeek(offset + macho_offset); SectionStrings strings(section_name, offset + macho_offset); } else if (Strstr(section_name, "__symbol_stub") >= 0) { symbol_stubs_offset = offset; symbol_stubs_size = size; Stubs stubs(size); } else byte section_data[size]; FSeek(pos); } } Section ; string SectionRead(Section §ion) { return section.section_name; } typedef struct (uint64 address, uint64 length) { local uint64 pos = FTell(); FSeek(address-text_section_vm_address); byte data[length]; FSeek(pos); } CString64 ; string CString64Read(CString64 &cstring64) { return cstring64.data; } typedef struct { uint64 isa; uint64 info; uint64 data_address; uint64 data_length; //Printf("data_address: 0x%10x; data_length: 0x%10x\n", data_address, data_length); if (data_length > 0) { CString64 cstring(data_address, data_length); } } CFString64 ; string CFString64Read(CFString64 &cfstring64) { if (cfstring64.data_length > 0x00) return cfstring64.cstring.data; return ""; } typedef struct (uint64 offset, uint64 size) { local uint64 cfstring_count = size / 0x20; local uint64 idx = 0; for (idx=0; idx < cfstring_count - 1; idx++) { CFString64 cfstring; } } pCFString64; string SectionStringsRead(SectionStrings §ion_strings){ local string message; SPrintf(message, "count: %d [%04Xh]", section_strings.str_count, section_strings.str_count); return message; } typedef struct { char section_name[16]; char segment_name[16]; uint64 address ; uint64 size ; uint32 offset; uint32 section_alignment; uint32 relocation_entry_offset; uint32 number_of_relocation_entries; uint32 flags ; uint32 reserved1; uint32 reserved2; uint32 reserved3; local uint64 pos = FTell(); //Printf("%s\n", section_name); if (Strcmp(section_name, "__cfstring") == 0) { FSeek(offset + macho_offset); pCFString64 cfstring64(offset, size); } else if (Strcmp(section_name, "__cstring") == 0 || Strcmp(section_name, "__objc_classname") == 0 || Strcmp(section_name, "__objc_methname") == 0 || Strcmp(section_name, "__objc_methtype") == 0 ) { FSeek(offset + macho_offset); SectionStrings strings(section_name, offset + macho_offset); } else if (Strstr(section_name, "_ptr") > -1 || Strstr(section_name, "_vars") > -1 || Strstr(section_name, "list") > -1 || Strstr(section_name, "refs") > -1 ) { FSeek(offset + macho_offset); uint64 address_ptr[size / 0x08]; } else if (Strstr(section_name, "__symbol_stub") >= 0) { FSeek(offset + macho_offset); symbol_stubs_offset = offset; symbol_stubs_size = size; Stubs stubs(size); } else if (size && offset) { FSeek(offset + macho_offset); byte section_data[size]; } FSeek(pos); } Section64 ; string Section64Read(Section64 §ion64) { return section64.section_name; } typedef struct { uint32 load_command_string_offset ; local int64 pos = FTell(); // We need to goto beginning of LoadCommand, then goto the offset FSeek(FTell() - (sizeof(uint32) * 3) + load_command_string_offset); string string_data ; FSeek(pos); } LoadCommandString ; string LoadCommandStringRead(LoadCommandString &load_command_string) { return load_command_string.string_data; }; typedef ubyte Uuid[16] ; // TODO : Clean this ugly thing up string UuidRead(Uuid uuid) { local string ret, tmp; local int i; for(i = 0; i<4; i++) { SPrintf(tmp, "%.2X", uuid[i]); ret += tmp; } ret += "-"; for(i = 0; i<2; i++) { SPrintf(tmp, "%.2X", uuid[i+4]); ret += tmp; } ret += "-"; for(i = 0; i<2; i++) { SPrintf(tmp, "%.2X", uuid[i+6]); ret += tmp; } ret += "-"; for(i = 0; i<2; i++) { SPrintf(tmp, "%.2X", uuid[i+8]); ret += tmp; } ret += "-"; for(i = 0; i<6; i++) { SPrintf(tmp, "%.2X", uuid[i+10]); ret += tmp; } return ret; } typedef struct { uint32 version; } Version ; string VersionRead(Version &version) { local string version_string; if(version.version & 0xFF == 0) { SPrintf(version_string, "%u.%u", version.version >> 16, (version.version >> 8) & 0xFF); } else { SPrintf(version_string, "%u.%u.%u", version.version >> 16, (version.version >> 8) & 0xFF, version.version & 0xFF); } return version_string; } typedef struct { uint32 tool; // enum for the tool Version version; // version of the tool }build_tool_version; typedef struct { SType type; }s_type ; string s_type_read(s_type &_s_type){ local string stype; if (_s_type.type & N_STAB) SPrintf(stype, EnumToString(N_STAB)); else if (_s_type.type & N_PEXT) SPrintf(stype, EnumToString(N_PEXT)); else if (_s_type.type & N_EXT) SPrintf(stype, EnumToString(N_EXT)); else if (_s_type.type & N_TYPE) SPrintf(stype, EnumToString(N_TYPE)); if ((_s_type.type & N_TYPE) && !(_s_type.type & N_STAB)) { if (_s_type.type & N_SECT) SPrintf(stype, "%s|%s", stype, EnumToString(N_SECT)); else if (_s_type.type & N_PBUD) SPrintf(stype, "%s|%s", stype, EnumToString(N_PBUD)); else if (_s_type.type & N_INDR) SPrintf(stype, "%s|%s", stype, EnumToString(N_INDR)); else if (_s_type.type & N_ABS) SPrintf(stype, "%s|%s", stype, EnumToString(N_ABS)); } return stype; } typedef struct { ReferenceType ref_type; ubyte lib_ordinal; } Description ; string DescriptionRead(Description &description){ local string comment = EnumToString(description.ref_type); SPrintf(comment, "%s; Library Ordinal: 0x%08x", comment, description.lib_ordinal); return comment; } typedef struct(uint32 string_table_offset) { uint32 n_name_ptr; s_type n_type; ubyte n_sect; Description n_desc; if (magic == MACHO_32) uint32 n_value; else uint64 n_value; local uint64 pos = FTell(); FSeek(string_table_offset+n_name_ptr); string name; if (n_value > 0x00) local uint64 file_offset = n_value - text_section_vm_address; FSeek(pos); } Symbol ; string SymbolRead(Symbol &symbol) { if (symbol.n_sect == 0x00) return symbol.name; local string comment = ""; if (macho_array == 1) SPrintf(comment, "%s [%s]", symbol.name, macho[macho_index].indexed_sections.indexed_section[symbol.n_sect-1].name); else SPrintf(comment, "%s [%s]", symbol.name, macho.indexed_sections.indexed_section[symbol.n_sect-1].name); return comment; } typedef struct(uint32 symbol_table_offset, uint32 string_table_offset, uint32 entry_count) { local uint64 pos = FTell(); local uint32 count = 0x00; FSeek(symbol_table_offset); for (count=0x00; count < entry_count; count++){ Symbol symbol(string_table_offset); } FSeek(pos); } Symbols; uint64 ReadULEB128(){ local uint64 result = 0x00; local uint64 size = 0x00; local uint64 base_offset = FTell(); local ubyte ulebyte = 0x00; do { ulebyte = ReadByte(base_offset+size) & 0x7f; result |= ((uint64)ulebyte << (size*7)); size++; } while (ReadByte(base_offset + size - 1) & 0x80); FSeek(base_offset+size); return result; } typedef struct { local uint64 o_pos = FTell(); //LoadCommandHead load_commandHead ; LoadCommandType command; uint command_size; // Process rest of load command based on command type switch(command) { case ID_DYLIB : case LOAD_DYLIB : case LOAD_WEAK_DYLIB : case REEXPORT_DYLIB : LoadCommandString name; // TODO : Pretty print this uint32 timestamp; // TODO : Pretty print this uint32 current_version; // TODO : Pretty print this uint32 compatibility_version; break; case SYM_TAB : uint32 symbol_table_offset ; uint32 number_of_symbol_table_entries ; uint32 string_table_offset ; uint32 string_table_size ; gsymbol_table_offset = symbol_table_offset; gnumber_of_symbol_table_entries = number_of_symbol_table_entries; gstring_table_offset = string_table_offset; gstring_table_size = string_table_size; break; case DYLD_INFO : case DYLD_INFO_ONLY : uint32 rebase_offset; uint32 rebase_size; uint32 bind_offset; uint32 bind_size; uint32 weak_bind_offset; uint32 weak_bind_size; uint32 lazy_bind_offset; uint32 lazy_bind_size; uint32 export_offset; uint32 export_size; export_trie_offset = export_offset; export_trie_size = export_size; break; case DY_SYM_TAB : uint32 index_local_symbols; uint32 local_symbols_size; uint32 index_externally_defined_symbols; uint32 externally_defined_symbols_size; uint32 index_undefined_symbols; uint32 undefined_symbols_size; uint32 table_contents_offset; uint32 enteries_toc_size; uint32 file_offset_module_table; uint32 module_table_entries_size; uint32 external_references_symbol_table_offset; uint32 external_references_symbol_table_size; uint32 indirect_symbol_table_offset; uint32 indirect_symbol_table_size; uint32 external_relocation_entries_offset; uint32 external_relocation_entries_size; uint32 local_relocation_entries_offset; uint32 local_relocation_entries_size; break; case UUID : Uuid uuid; break; case VERSION_MIN_MAC_OSX : case VERSION_MIN_IPHONE_OS : // TODO : Pretty print this Version version; uint32 reserved ; break; case FUNCTION_STARTS : case CODE_SIGNATURE : case SEGMENT_SPLIT_INFO: case DATA_IN_CODE: uint32 data_offset; uint32 data_size; local uint64 cur_offset1 = FTell(); if (data_size > 0 && data_offset < FileSize()) { FSeek(data_offset); byte data[data_size]; FSeek(cur_offset1); } break; case UNIX_THREAD : case THREAD : switch(cpu_typer) { case CPU_TYPE_X86 : case CPU_TYPE_I386 : i386ThreadFlavor flavor; // TODO : Pretty print this uint32 count; switch(flavor) { case i386_THREAD_STATE : i386ThreadState thread_state; // TODO : Flesh these guys out case i386_FLOAT_STATE : case i386_EXCEPTION_STATE : } break; case CPU_TYPE_X86_64 : x86ThreadFlavor flavor; // TODO : Pretty print this uint32 count; switch(flavor) { case x86_THREAD_STATE64 : x86ThreadState thread_state; break; // TODO : Flesh these guys out case x86_FLOAT_STATE64 : case x86_EXCEPTION_STATE64 : case x86_DEBUG_STATE64 : } break; case CPU_TYPE_POWERPC : case CPU_TYPE_POWERPC64 : PPCThreadFlavor flavor; // TODO : Pretty print this uint32 count; switch(flavor) { case PPC_THREAD_STATE : PPCThreadState thread_state; break; // TODO : Flesh these guys out case PPC_FLOAT_STATE : case PPC_EXCEPTION_STATE : case PPC_VECTOR_STATE : case PPC_THREAD_STATE64 : case PPC_EXCEPTION_STATE64 : } break; case CPU_TYPE_ARM : // TODO: Unsure if this is correct ? // uint32 flavor; // uint32 count; ARMThreadState thread_state; break; } break; case FVM_LIB : case ID_FVM_LIB : LoadCommandString name ; uint32 minor_version ; uint32 header_address ; break; case SUB_FRAMEWORK : LoadCommandString umbrella ; break; case SUB_CLIENT : LoadCommandString client ; break; case SUB_UMBRELLA : LoadCommandString sub_umbrella ; break; case SUB_LIBRARY : LoadCommandString sub_library ; break; case PREBOUND_DYLIB : LoadCommandString name ; uint32 modules_size ; LoadCommandString linked_modules ; break; case ID_DYLINKER : case LOAD_DYLINKER : LoadCommandString name ; break; case ROUTINES_64 : uint64 init_address ; uint64 init_module ; uint32 reversed_1; uint32 reversed_2; uint32 reversed_3; uint32 reversed_4; uint32 reversed_5; uint32 reversed_6; break; case ROUTINES : uint32 init_address ; uint32 init_module ; uint32 reversed_1; uint32 reversed_2; uint32 reversed_3; uint32 reversed_4; uint32 reversed_5; uint32 reversed_6; break; case TWOLEVEL_HINTS : uint32 offset ; uint32 hints_size ; break; case PREBIND_CKSUM : uint32 cksum ; break; case RPATH: LoadCommandString path ; break; case ENCRYPTION_INFO : uint32 crypt_offset ; uint32 crypt_size ; uint32 crypt_id ; break; case IDENT : break; case FVM_FILE : LoadCommandString name ; uint32 header_address ; break; case SEGMENT_64 : char segment_name[16]; uint64 vm_address ; if (Strcmp(segment_name, "__TEXT")==0x00) text_section_vm_address = vm_address; uint64 vm_size ; uint64 file_offset; uint64 file_size; vm_proc maximum_protection ; vm_proc initial_protection ; uint32 number_of_sections; // TODO : Fix this enum SegmentFlags flags; // Having this if statement will prevent warnings in 010Editor if(number_of_sections > 0) { Section64 section[number_of_sections]; } break; case SEGMENT : char segment_name[16]; uint32 vm_address ; if (Strcmp(segment_name, "__TEXT")==0x00) text_section_vm_address32 = vm_address; uint32 vm_size ; uint32 file_offset; uint32 file_size; vm_proc maximum_protection ; vm_proc initial_protection ; uint32 number_of_sections; // TODO : Fix this enum SegmentFlags flags; // Having this if statement will prevent warnings in 010Editor if(number_of_sections > 0) { Section section[number_of_sections]; } break; case SOURCE_VERSION: uint32 unka; uint32 unkb; break; case ENCRYPTION_INFO_64: uint32 cryptoff ; /* file offset of encrypted range */ uint32 cryptsize ; /* file size of encrypted range */ uint32 cryptid; /* which enryption system, 0 means not-encrypted yet */ uint32 pad; /* padding to make this struct's size a multiple of 8 */ break; case LC_BUILD_VERSION: uint32 platform; // platform Version minos; // X.Y.Z is encoded in nibbles xxxx.yy.zz Version sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz uint32 ntools; if (ntools > 0) { build_tool_version tools[ntools]; } break; case MAIN: case MAIN_DYLIB: uint64 entryoff ; uint64 stacksize ; break; default : Warning("Hit an unknown or unsupported load command : [%d]", command); break; //Exit(-1); } FSeek(o_pos + command_size); // AIO solution, fixes default case, need for unk/pad, and all of the LoadCommandString + Seeks all over // } LoadCommand ; string LoadCommandRead(LoadCommand &load_command) { return "load_command[" + LoadCommandTypeRead(load_command.command) + "]"; } // https://opensource.apple.com/source/dyld/dyld-551.4/dyld3/CodeSigningTypes.h typedef struct { CS_Slot type ; uint32 offset ; } CS_BlobIndex ; string CS_BlobIndexRead(CS_BlobIndex &cs_blob_index){ return EnumToString(cs_blob_index.type); } typedef struct { BigEndian(); CS_Magic magic ; uint32 length ; uint32 count ; CS_BlobIndex blob_index[count]; LittleEndian(); } CS_SuperBlob; typedef struct { BigEndian(); CS_Magic magic ; uint32 length ; uint32 version ; uint32 flags ; uint32 hash_offset ; uint32 ident_offset ; uint32 n_special_slots ; uint32 n_code_slots ; uint32 code_limit ; ubyte hash_size ; ubyte hash_type ; ubyte spare1 ; ubyte page_size infinite", format=hex>; uint32 spare2 ; local int struct_size = 0x2c; ubyte data[length-struct_size]; LittleEndian(); } CS_CodeDirectory; typedef struct { BigEndian(); CS_Magic magic ; uint32 length ; local int struct_size = 0x08; ubyte data[length-struct_size]; LittleEndian(); } CS_Blob; void parse_code_signature(int offset) { local int i; if (offset < FileSize()) { FSeek(offset); CS_SuperBlob code_signature; for(i = 0; i < code_signature.count; i++) { FSeek(offset + code_signature.blob_index[i].offset); switch(code_signature.blob_index[i].type) { case CSSLOT_CODEDIRECTORY: case CSSLOT_ALTERNATE_CODEDIRECTORIES: CS_CodeDirectory code_directory; break; case CSSLOT_CMS_SIGNATURE : CS_Blob cms_signature; break; } } } } void parse_code_signature_in_load_command(int offset, int load_command_idx, int num) { local int i; for(i = load_command_idx; i < load_command_idx + num; i++) { switch(load_commands.load_command[i].command) { case CODE_SIGNATURE : parse_code_signature(offset + load_commands.load_command[i].data_offset); } } } typedef struct { uint32 name_index; local string name; if (name_index < 0x40000000) { if (macho_array == 1) name = macho[macho_index].symbols.symbol[name_index].name; else name = macho.symbols.symbol[name_index].name; } else SPrintf(name, "Oridinal: 0x%08x", name_index); } IndirectSymbol ; string IndirectSymbolRead(IndirectSymbol &indirect_symbol){ return indirect_symbol.name; } typedef struct(int sym_tab_idx, int dy_sym_tab_idx) { local uint32 entry_count ; if (macho_array == 1) entry_count = macho[macho_index].load_command[dy_sym_tab_idx].indirect_symbol_table_size; else entry_count = macho.load_commands.load_command[dy_sym_tab_idx].indirect_symbol_table_size; //local uint32 parsedCount = entry_count / 2; // skip duplicate entries IndirectSymbol indirect_symbol[entry_count] ; } IndirectSymbols; void parse_indirect_symbols(int load_command_num) { local int idx ; local int sym_tab_idx ; local int dy_sym_tab_idx ; for (idx=0; idx < load_command_num; idx++) { if (macho_array ==1) { switch(macho[macho_index].load_commands.load_command[idx].command) { case SYM_TAB: sym_tab_idx=idx; break; case DY_SYM_TAB: dy_sym_tab_idx=idx; break; } } else { switch(macho.load_commands.load_command[idx].command) { case SYM_TAB: sym_tab_idx=idx; break; case DY_SYM_TAB: dy_sym_tab_idx=idx; break; } } } local uint64 curPos = FTell(); if (dy_sym_tab_idx > 0) { if (macho_array == 1) FSeek(macho[macho_index].load_commands.load_command[dy_sym_tab_idx].indirect_symbol_table_offset+macho_offset); else FSeek(macho.load_commands.load_command[dy_sym_tab_idx].indirect_symbol_table_offset+macho_offset); IndirectSymbols indirect_symbols(sym_tab_idx, dy_sym_tab_idx); FSeek(curPos); } } void parse_symbol_table(){ Symbols symbols(gsymbol_table_offset+macho_offset, gstring_table_offset+macho_offset, gnumber_of_symbol_table_entries); } typedef struct(uint load_command_num) { LoadCommand load_command[load_command_num]; } LoadCommands; typedef struct { string label; local uint64 next_node = ReadULEB128(); local uint64 next_node_offset = export_trie_offset+next_node; } ExportChild ; string ExportChildRead(ExportChild &exportChild) { return exportChild.label; } typedef struct { local uint64 node_offset = FTell(); ubyte terminal_size; if (terminal_size > 0x00) { ExportSymbolFlagKind flags; local uint64 symbol_offset = ReadULEB128(); } ubyte child_count; local int idx = 0x00; for (idx=0; idx< child_count; idx++) { ExportChild child ; } } ExportNode ; int end_node(){ local int nulls = 0x00; local int i = 0x00; local uchar terminal_check[0x10] ; ReadBytes(terminal_check, FTell(), 0x10); for (i=0x00;i<0x10;i++){ if (!terminal_check[i]) nulls++; } return nulls == 0x10; } typedef struct { local uint64 pos = FTell(); local uint count = 0x00; FSeek(export_trie_offset); while (FTell() < export_trie_offset + export_trie_size) { ExportNode node; count++; if (end_node()) break; } FSeek(pos); } ExportNodes; int get_node_index(ExportNodes &export_nodes, uint64 node_offset){ local int i ; for (i=0x00; i < export_nodes.count; i++) { if (export_nodes.node[i].node_offset == node_offset) break; } return i; } typedef struct (string ename, ExportSymbolFlagKind eflags, uint64 eoffset){ local string name = ename; local ExportSymbolFlagKind flags = eflags; local uint64 symbol_offset = eoffset; } Export ; string ExportRead(Export &export) { local string label = ""; SPrintf(label, "[%s] %s", EnumToString(export.flags), export.name); return label; } void parse_export_nodes(ExportNodes &export_nodes, int node_index, string label){ local string export_name = label; if (export_nodes.node[node_index].child_count > 0x00) { local int child_index ; for (child_index=0x00; child_index < export_nodes.node[node_index].child_count; child_index++) { if (export_nodes.node[node_index].child[child_index].next_node_offset > 0x00) { SPrintf(export_name, "%s%s", export_name, export_nodes.node[node_index].child[child_index].label); parse_export_nodes(export_nodes, get_node_index(export_nodes, export_nodes.node[node_index].child[child_index].next_node_offset), export_name); export_name = label; } } } else { Printf("[%s] %s\n", EnumToString(export_nodes.node[node_index].flags), export_name); Export export(export_name, export_nodes.node[node_index].flags, export_nodes.node[node_index].symbol_offset); } } typedef struct(ExportNodes &export_nodes) { parse_export_nodes(export_nodes, 0x00, ""); }Exports; typedef struct { if (export_trie_offset > 0x00 && export_trie_size > 0x00) { ExportNodes export_nodes; Exports exports(export_nodes); } } ExportTrie; typedef struct (LoadCommand &dylib){ local string name = dylib.name.string_data; } LibName ; string LibNameRead(LibName &libname){ return libname.name; } typedef struct (LoadCommands &load_commands, int load_command_num){ local int i = 0x00; for (i=0x00; i < load_command_num; i++){ if (load_commands.load_command[i].command == LOAD_DYLIB) { LibName lib_name(load_commands.load_command[i]); } } } DyLibs; void parse_lib_names(LoadCommands &load_commands, int load_command_num){ DyLibs dylibs(load_commands, load_command_num); } typedef struct (string import_name, Description &import_desc, DyLibs &dylibs){ local string name=import_name; local string lib_name=dylibs.lib_name[import_desc.lib_ordinal-1].name; local ReferenceType ref_type = import_desc.ref_type; } Import ; string base_name(string path){ local int pos ; for (pos = Strlen(path)-1; pos >= 0x00; pos--){ if (Strcmp(SubStr(path, pos-1, 1), "/")==0x00) break; } return SubStr(path, pos); } string ImportRead(Import &import){ local string comment = ""; SPrintf(comment, "%s.%s", base_name(import.lib_name), import.name); return comment; } typedef struct (string import_name, Description &import_desc) { local string name=import_name; local ReferenceType ref_type = import_desc.ref_type; } UndefinedImport ; string UndefinedImportRead(UndefinedImport &undefined_import) { local string comment = ""; SPrintf(comment, "(local) %s", undefined_import.name); return comment; } typedef struct(Symbols &symbols, DyLibs &dylibs){ local int i = 0x00; for (i=0x00; i; string IndexedSectionRead(IndexedSection &_section){ return (string)_section.name; } typedef struct (string _name, uint32 _offset, uint64 _size) { local string name = _name; local uint32 offset = _offset; local uint64 size = _size; } IndexedSection64 ; string IndexedSection64Read(IndexedSection64 &_section){ local string comment = ""; SPrintf(comment, "%s", _section.name); return comment; } typedef struct (LoadCommands &load_commands, int load_command_num){ local int i = 0; local int j = 0; for (i=0; i < load_command_num; i++) { if ((load_commands.load_command[i].command == SEGMENT_64 || load_commands.load_command[i].command == SEGMENT) && load_commands.load_command[i].number_of_sections > 0) { for (j=0; j < load_commands.load_command[i].number_of_sections; j++) { //Printf("Indexing %s->%s\n", load_commands.load_command[i].segment_name, load_commands.load_command[i].section[j].section_name); if (is_32bit) { IndexedSection indexed_section((string)load_commands.load_command[i].section[j].section_name, load_commands.load_command[i].section[j].offset, load_commands.load_command[i].section[j].size); } else { IndexedSection64 indexed_section((string)load_commands.load_command[i].section[j].section_name, load_commands.load_command[i].section[j].offset, load_commands.load_command[i].section[j].size); } } } } } IndexedSections; typedef struct (int index){ local uint64 offset = FTell() + text_section_vm_address; if (is_32bit) offset = FTell() + text_section_vm_address32; byte opcode[2]; uint32 delta; local string name = ""; if (macho_array == 1) name = macho[macho_index].indirect_symbols.indirect_symbol[index].name; else name = macho.indirect_symbols.indirect_symbol[index].name; } SymbolStub ; string SymbolStubRead(SymbolStub &symbol_stub) { local string comment = ""; SPrintf(comment, "0x%08x -> %s", symbol_stub.offset + symbol_stub.delta + 6, symbol_stub.name); return comment; } typedef struct { if (symbol_stubs_offset > 0x00 && symbol_stubs_size > 0x00) { local uint64 pos = FTell(); FSeek(symbol_stubs_offset); local int i = 0; for (i=0; i < symbol_stubs_size/6; i++) { SymbolStub symbol_stub(i); } FSeek(pos); } } SymbolStubs; typedef struct { Header header ; local uint32 cpu_typer = header.cpu_type; local int load_command_num = header.num_load_commands; LoadCommands load_commands(load_command_num); parse_code_signature_in_load_command(macho_offset, 0, load_command_num); parse_lib_names(load_commands, load_command_num); IndexedSections indexed_sections(load_commands, load_command_num); parse_symbol_table(); parse_indirect_symbols(load_command_num); SymbolStubs symbol_stubs; Imports imports(symbols, dylibs); ExportTrie export_trie; } MachO; local uint32 magic = ReadInt(); local uint32 is_32bit = (magic == MACHO_32); local uint64 macho_offset = FTell(); local uint32 macho_array = 0; local uint32 macho_index ; local uint64 text_section_vm_address32 = 0x00; local uint64 text_section_vm_address = 0x00; local uint64 gsymbol_table_offset = 0x00; local uint64 gnumber_of_symbol_table_entries = 0x00; local uint64 gstring_table_offset = 0x00; local uint64 gstring_table_size = 0x00; local uint64 export_trie_offset = 0x00; local uint64 export_trie_size = 0x00; local uint64 symbol_stubs_offset = 0x00; local uint64 symbol_stubs_size = 0x00; if(magic == MACHO_32 || magic == MACHO_64) { MachO macho ; } else { Header fatHeader ; local int load_command_idx = 0; local int load_command_num ; local uint64 load_command_offset ; for(macho_index = 0; macho_index < fatHeader.fat_arch_size; macho_index++) { if (macho_index > 0) { macho_array = 1; } macho_offset = fatHeader.fat_arch[macho_index].file_offset; FSeek(macho_offset); MachO macho ; load_command_idx = load_command_idx + load_command_num; } }