void * macho_find_section_numbered( const void * file_start, const void * file_end, uint8_t sect_num) { _sect_scan sect_data; bzero(§_data, sizeof(sect_data)); sect_data.sect_num = sect_num; if (ISMACHO64(MAGIC32(file_start))) { sect_data.sixtyfourbit = 1; } if (macho_seek_result_found == macho_scan_load_commands( file_start, file_end, &__macho_sect_in_lc, §_data)) { return sect_data.sect_info; } return NULL; }
macho_seek_result macho_find_symbol( const void * file_start, const void * file_end, const char * name, uint8_t * nlist_type, const void ** symbol_address) { macho_seek_result result = macho_seek_result_not_found; macho_seek_result symtab_result = macho_seek_result_not_found; uint8_t swap = 0; char sixtyfourbit = 0; struct symtab_command * symtab = NULL; struct nlist * syms_address; struct nlist_64 * syms_address_64; const void * string_list; char * symbol_name; unsigned int symtab_offset; unsigned int str_offset; unsigned int num_syms; unsigned int syms_bytes; unsigned int sym_index; if (symbol_address) { *symbol_address = 0; } symtab_result = macho_find_symtab(file_start, file_end, &symtab); if (symtab_result != macho_seek_result_found) { goto finish; } if (ISSWAPPEDMACHO(MAGIC32(file_start))) { swap = 1; } if (ISMACHO64(MAGIC32(file_start))) { sixtyfourbit = 1; } symtab_offset = CondSwapInt32(swap, symtab->symoff); str_offset = CondSwapInt32(swap, symtab->stroff); num_syms = CondSwapInt32(swap, symtab->nsyms); syms_address = (struct nlist *)(file_start + symtab_offset); syms_address_64 = (struct nlist_64 *)(file_start + symtab_offset); string_list = file_start + str_offset; if (sixtyfourbit) { syms_bytes = num_syms * sizeof(struct nlist_64); } else { syms_bytes = num_syms * sizeof(struct nlist); } if ((char *)syms_address + syms_bytes > (char *)file_end) { result = macho_seek_result_error; goto finish; } for (sym_index = 0; sym_index < num_syms; sym_index++) { struct nlist * seekptr; struct nlist_64 * seekptr_64; uint32_t string_index; uint8_t n_type; uint8_t n_sect; uint64_t n_value; if (sixtyfourbit) { seekptr_64 = &syms_address_64[sym_index]; string_index = CondSwapInt32(swap, seekptr_64->n_un.n_strx); n_type = seekptr_64->n_type; n_sect = seekptr_64->n_sect; n_value = CondSwapInt64(swap, seekptr_64->n_value); } else { seekptr = &syms_address[sym_index]; string_index = CondSwapInt32(swap, seekptr->n_un.n_strx); n_type = seekptr->n_type; n_sect = seekptr->n_sect; n_value = (uint64_t)CondSwapInt32(swap, seekptr->n_value); } if (string_index == 0 || n_type & N_STAB) { continue; } symbol_name = (char *)(string_list + string_index); if (strcmp(name, symbol_name) == 0) { if (nlist_type) { *nlist_type = n_type; } switch (n_type & N_TYPE) { case N_SECT: { void * v_sect_info = macho_find_section_numbered( file_start, file_end, n_sect); if (!v_sect_info) { break; // out of the switch } if (symbol_address) { if (sixtyfourbit) { struct section_64 * sect_info_64 = (struct section_64 *)v_sect_info; // this isn't right for 64bit? compare below size_t reloffset = (n_value - CondSwapInt64(swap, sect_info_64->addr)); *symbol_address = file_start; *symbol_address += CondSwapInt32(swap, sect_info_64->offset); *symbol_address += reloffset; } else { struct section * sect_info = (struct section *)v_sect_info; size_t reloffset = (n_value - CondSwapInt32(swap, sect_info->addr)); *symbol_address = file_start; *symbol_address += CondSwapInt32(swap, sect_info->offset); *symbol_address += reloffset; } } result = macho_seek_result_found; goto finish; } break; case N_UNDF: result = macho_seek_result_found_no_value; goto finish; break; case N_ABS: result = macho_seek_result_found_no_value; goto finish; break; /* We don't chase indirect symbols as they can be external. */ case N_INDR: result = macho_seek_result_found_no_value; goto finish; break; default: goto finish; break; } } } finish: return result; }
ExitStatus printKextInfo(KctoolArgs * toolArgs) { ExitStatus result = EX_SOFTWARE; CFArrayRef kextPlistArray = NULL; CFIndex i, count; if (CFArrayGetTypeID() == CFGetTypeID(toolArgs->kernelcacheInfoPlist)) { kextPlistArray = (CFArrayRef)toolArgs->kernelcacheInfoPlist; } else if (CFDictionaryGetTypeID() == CFGetTypeID(toolArgs->kernelcacheInfoPlist)){ kextPlistArray = (CFArrayRef)CFDictionaryGetValue(toolArgs->kernelcacheInfoPlist, CFSTR("_PrelinkInfoDictionary")); } else { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Unrecognized kernelcache plist data."); goto finish; } count = CFArrayGetCount(kextPlistArray); for (i = 0; i < count; i++) { CFDictionaryRef kextInfoDict = (CFDictionaryRef)CFArrayGetValueAtIndex(kextPlistArray, i); CFStringRef thisKextID = CFDictionaryGetValue(kextInfoDict, kCFBundleIdentifierKey); if (thisKextID && CFEqual(thisKextID, toolArgs->kextID)) { uint64_t kextAddr = 0; uint64_t kextSize = 0; u_long kextOffset = 0; const UInt8 * kextMachO = NULL; // do not free void * section = NULL; // do not free if (!getKextAddressAndSize(kextInfoDict, &kextAddr, &kextSize)) { goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { section = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)toolArgs->kernelcacheImageBytes, kPrelinkTextSegment, kPrelinkTextSection); } else { section = (void *)macho_get_section_by_name( (struct mach_header *)toolArgs->kernelcacheImageBytes, kPrelinkTextSegment, kPrelinkTextSection); } if (!section) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Cannot find %s,%s in kernelcache.", kPrelinkTextSegment, kPrelinkTextSection); goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { kextOffset = ((struct section_64 *)section)->offset + (u_long)(kextAddr - ((struct section_64 *)section)->addr); } else { kextOffset = ((struct section *)section)->offset + (u_long)(kextAddr - ((struct section *)section)->addr); } kextMachO = toolArgs->kernelcacheImageBytes + kextOffset; /* Find the requested section's file offset and size */ if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { section = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)kextMachO, toolArgs->segmentName, toolArgs->sectionName); } else { /* macho_get_section_by_name doesn't work as the kexts don't have a __TEXT segment. * They just have a single segment named "" with all the sections dumped under it. */ section = (void *)getSectionByName( kextMachO, toolArgs->segmentName, toolArgs->sectionName); } if (!section) { OSKextLogCFString(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, CFSTR("Cannot find %s,%s in kext %@\n"), toolArgs->segmentName, toolArgs->sectionName, toolArgs->kextID); goto finish; } if (ISMACHO64(MAGIC32(toolArgs->kernelcacheImageBytes))) { printf("%#llx %#lx %#llx\n", ((struct section_64 *)section)->addr, kextOffset + ((struct section_64 *)section)->offset, ((struct section_64 *)section)->size); } else { printf("%#x %#lx %#x\n", ((struct section *)section)->addr, kextOffset + ((struct section *)section)->offset, ((struct section *)section)->size); } result = EX_OK; break; } } finish: return result; }
int main(int argc, char * const argv[]) { ExitStatus result = EX_SOFTWARE; KctoolArgs toolArgs; int kernelcache_fd = -1; // must close() void * fat_header = NULL; // must unmapFatHeaderPage() struct fat_arch * fat_arch = NULL; CFDataRef rawKernelcache = NULL; // must release CFDataRef kernelcacheImage = NULL; // must release void * prelinkInfoSect = NULL; const char * prelinkInfoBytes = NULL; CFPropertyListRef prelinkInfoPlist = NULL; // must release bzero(&toolArgs, sizeof(toolArgs)); /***** * Find out what the program was invoked as. */ progname = rindex(argv[0], '/'); if (progname) { progname++; // go past the '/' } else { progname = (char *)argv[0]; } /* Set the OSKext log callback right away. */ OSKextSetLogOutputFunction(&tool_log); /***** * Process args & check for permission to load. */ result = readArgs(&argc, &argv, &toolArgs); if (result != EX_OK) { if (result == kKctoolExitHelp) { result = EX_OK; } goto finish; } kernelcache_fd = open(toolArgs.kernelcachePath, O_RDONLY); if (kernelcache_fd == -1) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't open %s: %s.", toolArgs.kernelcachePath, strerror(errno)); result = EX_OSERR; goto finish; } fat_header = mapAndSwapFatHeaderPage(kernelcache_fd); if (!fat_header) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't map %s: %s.", toolArgs.kernelcachePath, strerror(errno)); result = EX_OSERR; goto finish; } fat_arch = getFirstFatArch(fat_header); if (fat_arch && !toolArgs.archInfo) { toolArgs.archInfo = NXGetArchInfoFromCpuType(fat_arch->cputype, fat_arch->cpusubtype); } rawKernelcache = readMachOSliceForArch(toolArgs.kernelcachePath, toolArgs.archInfo, /* checkArch */ FALSE); if (!rawKernelcache) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't read arch %s from %s.", toolArgs.archInfo->name, toolArgs.kernelcachePath); goto finish; } if (MAGIC32(CFDataGetBytePtr(rawKernelcache)) == OSSwapHostToBigInt32('comp')) { kernelcacheImage = uncompressPrelinkedSlice(rawKernelcache); if (!kernelcacheImage) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't uncompress kernelcache slice."); goto finish; } } else { kernelcacheImage = CFRetain(rawKernelcache); } toolArgs.kernelcacheImageBytes = CFDataGetBytePtr(kernelcacheImage); if (ISMACHO64(MAGIC32(toolArgs.kernelcacheImageBytes))) { prelinkInfoSect = (void *)macho_get_section_by_name_64( (struct mach_header_64 *)toolArgs.kernelcacheImageBytes, "__PRELINK_INFO", "__info"); } else { prelinkInfoSect = (void *)macho_get_section_by_name( (struct mach_header *)toolArgs.kernelcacheImageBytes, "__PRELINK_INFO", "__info"); } if (!prelinkInfoSect) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't find prelink info section."); goto finish; } if (ISMACHO64(MAGIC32(toolArgs.kernelcacheImageBytes))) { prelinkInfoBytes = ((char *)toolArgs.kernelcacheImageBytes) + ((struct section_64 *)prelinkInfoSect)->offset; } else { prelinkInfoBytes = ((char *)toolArgs.kernelcacheImageBytes) + ((struct section *)prelinkInfoSect)->offset; } toolArgs.kernelcacheInfoPlist = (CFPropertyListRef)IOCFUnserialize(prelinkInfoBytes, kCFAllocatorDefault, /* options */ 0, /* errorString */ NULL); if (!toolArgs.kernelcacheInfoPlist) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't unserialize prelink info."); goto finish; } result = printKextInfo(&toolArgs); if (result != EX_OK) { goto finish; } result = EX_OK; finish: SAFE_RELEASE(toolArgs.kernelcacheInfoPlist); SAFE_RELEASE(kernelcacheImage); SAFE_RELEASE(rawKernelcache); if (fat_header) { unmapFatHeaderPage(fat_header); } if (kernelcache_fd != -1) { close(kernelcache_fd); } return result; }