#include #include #include #include #include #include #include int indent = 0; #define CHECK(x) do{ \ errno = 0; \ if (!(x)) { \ fprintf(stderr,"%s:%d:%s -- %s\n", __FILE__,__LINE__,#x,strerror(errno)); \ abort(); \ } \ }while(0) char *md_key_string(uint32_t key) { #define _(x,y) if(key==(x)) return (y); _(MH_BINDS_TO_WEAK,"MH_BINDS_TO_WEAK") _(MH_NOUNDEFS,"MH_NOUNDEFS") _(MH_FVMLIB,"MH_FVMLIB") _(MH_DYLDLINK,"MH_DYLDLINK") _(MH_PREBINDABLE,"MH_PREBINDABLE") _(MH_NO_HEAP_EXECUTION,"MH_NO_HEAP_EXECUTION") _(MH_CIGAM,"MH_CIGAM") _(MH_SETUID_SAFE,"MH_SETUID_SAFE") _(MH_PREBOUND,"MH_PREBOUND") _(MH_EXECUTE,"MH_EXECUTE") _(MH_NLIST_OUTOFSYNC_WITH_DYLDINFO,"MH_NLIST_OUTOFSYNC_WITH_DYLDINFO") _(MH_NO_REEXPORTED_DYLIBS,"MH_NO_REEXPORTED_DYLIBS") _(MH_MAGIC,"MH_MAGIC") _(MH_CORE,"MH_CORE") _(MH_SPLIT_SEGS,"MH_SPLIT_SEGS") _(MH_NOMULTIDEFS,"MH_NOMULTIDEFS") _(MH_PRELOAD,"MH_PRELOAD") _(MH_BUNDLE,"MH_BUNDLE") _(MH_KEXT_BUNDLE,"MH_KEXT_BUNDLE") _(MH_SIM_SUPPORT,"MH_SIM_SUPPORT") _(MH_CIGAM_64,"MH_CIGAM_64") _(MH_DEAD_STRIPPABLE_DYLIB,"MH_DEAD_STRIPPABLE_DYLIB") _(MH_DYLINKER,"MH_DYLINKER") _(MH_DYLIB_STUB,"MH_DYLIB_STUB") _(MH_DSYM,"MH_DSYM") _(MH_PIE,"MH_PIE") _(MH_BINDATLOAD,"MH_BINDATLOAD") _(MH_TWOLEVEL,"MH_TWOLEVEL") _(MH_ALLOW_STACK_EXECUTION,"MH_ALLOW_STACK_EXECUTION") _(MH_FORCE_FLAT,"MH_FORCE_FLAT") _(MH_LAZY_INIT,"MH_LAZY_INIT") _(MH_OBJECT,"MH_OBJECT") _(MH_NOFIXPREBINDING,"MH_NOFIXPREBINDING") _(MH_WEAK_DEFINES,"MH_WEAK_DEFINES") _(MH_CANONICAL,"MH_CANONICAL") _(MH_DYLIB,"MH_DYLIB") _(MH_ALLMODSBOUND,"MH_ALLMODSBOUND") _(MH_MAGIC_64,"MH_MAGIC_64") _(MH_INCRLINK,"MH_INCRLINK") _(MH_APP_EXTENSION_SAFE,"MH_APP_EXTENSION_SAFE") _(MH_ROOT_SAFE,"MH_ROOT_SAFE") _(MH_SUBSECTIONS_VIA_SYMBOLS,"MH_SUBSECTIONS_VIA_SYMBOLS") _(MH_HAS_TLV_DESCRIPTORS,"MH_HAS_TLV_DESCRIPTORS") #undef _ return "???"; } char *lc_key_string(unsigned x) { if(LC_UUID == x) return "LC_UUID"; if(LC_RPATH == x) return "LC_RPATH"; if(LC_ID_DYLINKER == x) return "LC_ID_DYLINKER"; if(LC_REEXPORT_DYLIB == x) return "LC_REEXPORT_DYLIB"; if(LC_SEGMENT_SPLIT_INFO == x) return "LC_SEGMENT_SPLIT_INFO"; if(LC_VERSION_MIN_TVOS == x) return "LC_VERSION_MIN_TVOS"; if(LC_LOAD_DYLIB == x) return "LC_LOAD_DYLIB"; if(LC_SUB_LIBRARY == x) return "LC_SUB_LIBRARY"; if(LC_BUILD_VERSION == x) return "LC_BUILD_VERSION"; if(LC_DYLD_INFO_ONLY == x) return "LC_DYLD_INFO_ONLY"; if(LC_MAIN == x) return "LC_MAIN"; if(LC_LOAD_WEAK_DYLIB == x) return "LC_LOAD_WEAK_DYLIB"; if(LC_SUB_FRAMEWORK == x) return "LC_SUB_FRAMEWORK"; if(LC_TWOLEVEL_HINTS == x) return "LC_TWOLEVEL_HINTS"; if(LC_LINKER_OPTIMIZATION_HINT == x) return "LC_LINKER_OPTIMIZATION_HINT"; if(LC_ENCRYPTION_INFO == x) return "LC_ENCRYPTION_INFO"; if(LC_SEGMENT == x) return "LC_SEGMENT"; if(LC_SEGMENT_64 == x) return "LC_SEGMENT_64"; if(LC_THREAD == x) return "LC_THREAD"; if(LC_UNIXTHREAD == x) return "LC_UNIXTHREAD"; if(LC_PREBIND_CKSUM == x) return "LC_PREBIND_CKSUM"; if(LC_FVMFILE == x) return "LC_FVMFILE"; if(LC_SYMTAB == x) return "LC_SYMTAB"; if(LC_LOAD_DYLINKER == x) return "LC_LOAD_DYLINKER"; if(LC_ENCRYPTION_INFO_64 == x) return "LC_ENCRYPTION_INFO_64"; if(LC_SUB_UMBRELLA == x) return "LC_SUB_UMBRELLA"; if(LC_VERSION_MIN_MACOSX == x) return "LC_VERSION_MIN_MACOSX"; if(LC_DYLIB_CODE_SIGN_DRS == x) return "LC_DYLIB_CODE_SIGN_DRS"; if(LC_DYLD_CHAINED_FIXUPS == x) return "LC_DYLD_CHAINED_FIXUPS"; if(LC_DYLD_EXPORTS_TRIE == x) return "LC_DYLD_EXPORTS_TRIE"; if(LC_ROUTINES == x) return "LC_ROUTINES"; if(LC_CODE_SIGNATURE == x) return "LC_CODE_SIGNATURE"; if(LC_SOURCE_VERSION == x) return "LC_SOURCE_VERSION"; if(LC_FUNCTION_STARTS == x) return "LC_FUNCTION_STARTS"; if(LC_REQ_DYLD == x) return "LC_REQ_DYLD"; if(LC_SUB_CLIENT == x) return "LC_SUB_CLIENT"; if(LC_ROUTINES_64 == x) return "LC_ROUTINES_64"; if(LC_DYLD_ENVIRONMENT == x) return "LC_DYLD_ENVIRONMENT"; if(LC_VERSION_MIN_WATCHOS == x) return "LC_VERSION_MIN_WATCHOS"; if(LC_DYSYMTAB == x) return "LC_DYSYMTAB"; if(LC_LOADFVMLIB == x) return "LC_LOADFVMLIB"; if(LC_PREBOUND_DYLIB == x) return "LC_PREBOUND_DYLIB"; if(LC_LINKER_OPTION == x) return "LC_LINKER_OPTION"; if(LC_SYMSEG == x) return "LC_SYMSEG"; if(LC_IDENT == x) return "LC_IDENT"; if(LC_NOTE == x) return "LC_NOTE"; if(LC_LAZY_LOAD_DYLIB == x) return "LC_LAZY_LOAD_DYLIB"; if(LC_PREPAGE == x) return "LC_PREPAGE"; if(LC_DYLD_INFO == x) return "LC_DYLD_INFO"; if(LC_IDFVMLIB == x) return "LC_IDFVMLIB"; if(LC_DATA_IN_CODE == x) return "LC_DATA_IN_CODE"; if(LC_VERSION_MIN_IPHONEOS == x) return "LC_VERSION_MIN_IPHONEOS"; if(LC_ID_DYLIB == x) return "LC_ID_DYLIB"; if(LC_LOAD_UPWARD_DYLIB == x) return "LC_LOAD_UPWARD_DYLIB"; return "???"; } char *section_type_string(unsigned key) { #define _(x) if(key==(x)) return #x; _(S_REGULAR); _(S_ZEROFILL); _(S_CSTRING_LITERALS); _(S_4BYTE_LITERALS); _(S_8BYTE_LITERALS); _(S_LITERAL_POINTERS); _(S_NON_LAZY_SYMBOL_POINTERS); _(S_LAZY_SYMBOL_POINTERS); _(S_SYMBOL_STUBS); _(S_MOD_INIT_FUNC_POINTERS); _(S_MOD_TERM_FUNC_POINTERS); _(S_COALESCED); _(S_GB_ZEROFILL); #undef _ return "???"; } char *section_flags_string(unsigned flags) { char buf [200]; int n = 0; #define _(x) if(flags&(x)) n+=sprintf(buf+n,"%s%s", n?" | ":"",#x); _(S_ATTR_PURE_INSTRUCTIONS); _(S_ATTR_SOME_INSTRUCTIONS); _(S_ATTR_NO_TOC); _(S_ATTR_EXT_RELOC); _(S_ATTR_LOC_RELOC); _(S_ATTR_STRIP_STATIC_SYMS); _(S_ATTR_NO_DEAD_STRIP); _(S_ATTR_LIVE_SUPPORT); #undef _ if(!n) n+=sprintf(buf+n,"0"); return strcpy(malloc(strlen(buf)+1),buf); } // https://alexdremov.me/mystery-of-mach-o-object-file-builders/ #define SHOW0(x) \ _Generic(x, \ uint32_t : fprintf(stderr,"%s = 0x%.8llx",#x,(unsigned long long)(x)), \ uint64_t : fprintf(stderr,"%s = 0x%.16llx",#x,(unsigned long long)(x)), \ int : fprintf(stderr,"%s = %d",#x,(x)), \ char[16] : fprintf(stderr,"%s = '%16.16s'",#x,(x))) #define SHOW(x) do{ \ fprintf(stderr,"%*s",indent,""); \ SHOW0(x);fprintf(stderr,"\n"); \ }while(0) #define SHOW2(x,tr) do{ \ fprintf(stderr,"%*s",indent,""); \ SHOW0(x); \ fprintf(stderr, " (%s)\n",tr(x)); \ }while(0) int main (int argc, char **argv) { FILE *bin; struct mach_header_64 hdr; CHECK(bin = fopen(argv[1],"rb")); CHECK(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), bin)); fprintf(stderr,"hdr.magic = %.8x (%s)\n",hdr.magic,md_key_string(hdr.magic)); fprintf(stderr,"hdr.cputype = %.8x\n",hdr.cputype); fprintf(stderr,"MH_MAGIC_64 = %.8x\n",MH_MAGIC_64); SHOW(hdr.ncmds); CHECK(MH_MAGIC_64 == hdr.magic); fprintf(stderr,"\nCommands\n"); indent = 2; for (uint32_t i = 0; i < hdr.ncmds; i++) { struct load_command cmd_hdr; CHECK(sizeof(cmd_hdr) == fread(&cmd_hdr,1,sizeof(cmd_hdr),bin)); SHOW2(cmd_hdr.cmd,lc_key_string); SHOW(cmd_hdr.cmdsize); { void *buf; CHECK(buf = malloc(cmd_hdr.cmdsize)); memcpy(buf,&cmd_hdr,sizeof(cmd_hdr)); CHECK((cmd_hdr.cmdsize - sizeof(cmd_hdr)) == fread(buf+sizeof(cmd_hdr),1,(cmd_hdr.cmdsize - sizeof(cmd_hdr)),bin)); switch(cmd_hdr.cmd) { case LC_SEGMENT_64: { struct segment_command_64 *q = buf; indent+=2; fprintf(stderr,"%*sq->segname = '%.16s'\n",indent,"",q->segname); //SHOW(q->segname); SHOW(q->vmaddr); SHOW(q->vmsize); SHOW(q->fileoff); SHOW(q->filesize); SHOW(q->maxprot); SHOW(q->initprot); SHOW(q->nsects); SHOW(q->flags); indent += 2; { for (int i = 0; i < q->nsects; i++) { struct section_64 *sect = ((struct section_64*)(q + 1)) + i; fprintf(stderr,"%*ssect->segname = '%.16s'\n",indent,"",sect->segname); fprintf(stderr,"%*ssect->sectname = '%.16s'\n",indent,"",sect->sectname); SHOW(sect->addr); SHOW(sect->size); SHOW(sect->offset); SHOW(sect->reloff); SHOW(sect->nreloc); SHOW2((sect->flags & 0xFF), section_type_string); SHOW2((sect->flags),section_flags_string); printf("\n"); } } indent -= 2; indent-=2; } break; } // for(unsigned i = 0; i < (cmd.cmdsize - sizeof(cmd)); i++) CHECK(fgetc(bin) >= 0); } fprintf(stderr,"\n"); } return 0; }