const uint8_t* ksmach_imageUUID(const char* const imageName, bool exactMatch) { const uint32_t iImg = ksmach_imageNamed(imageName, exactMatch); if(iImg != UINT32_MAX) { const struct mach_header* header = _dyld_get_image_header(iImg); if(header != NULL) { uintptr_t cmdPtr = ksmach_firstCmdAfterHeader(header); if(cmdPtr != 0) { for(uint32_t iCmd = 0;iCmd < header->ncmds; iCmd++) { const struct load_command* loadCmd = (struct load_command*)cmdPtr; if(loadCmd->cmd == LC_UUID) { struct uuid_command* uuidCmd = (struct uuid_command*)cmdPtr; return uuidCmd->uuid; } cmdPtr += loadCmd->cmdsize; } } } } return NULL; }
uintptr_t ksbt_segmentBaseOfImageIndex(const uint32_t idx) { const struct mach_header* header = _dyld_get_image_header(idx); // Look for a segment command and return the file image address. uintptr_t cmdPtr = ksmach_firstCmdAfterHeader(header); if(cmdPtr == 0) { return 0; } for(uint32_t i = 0; i < header->ncmds; i++) { const struct load_command* loadCmd = (struct load_command*)cmdPtr; if(loadCmd->cmd == LC_SEGMENT) { const struct segment_command* segmentCmd = (struct segment_command*)cmdPtr; if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { return segmentCmd->vmaddr - segmentCmd->fileoff; } } else if(loadCmd->cmd == LC_SEGMENT_64) { const struct segment_command_64* segmentCmd = (struct segment_command_64*)cmdPtr; if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) { return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff); } } cmdPtr += loadCmd->cmdsize; } return 0; }
uint32_t ksbt_imageIndexContainingAddress(const uintptr_t address) { const uint32_t imageCount = _dyld_image_count(); const struct mach_header* header = 0; for(uint32_t iImg = 0; iImg < imageCount; iImg++) { header = _dyld_get_image_header(iImg); if(header != NULL) { // Look for a segment command with this address within its range. uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg); uintptr_t cmdPtr = ksmach_firstCmdAfterHeader(header); if(cmdPtr == 0) { continue; } for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { const struct load_command* loadCmd = (struct load_command*)cmdPtr; if(loadCmd->cmd == LC_SEGMENT) { const struct segment_command* segCmd = (struct segment_command*)cmdPtr; if(addressWSlide >= segCmd->vmaddr && addressWSlide < segCmd->vmaddr + segCmd->vmsize) { return iImg; } } else if(loadCmd->cmd == LC_SEGMENT_64) { const struct segment_command_64* segCmd = (struct segment_command_64*)cmdPtr; if(addressWSlide >= segCmd->vmaddr && addressWSlide < segCmd->vmaddr + segCmd->vmsize) { return iImg; } } cmdPtr += loadCmd->cmdsize; } } } return UINT_MAX; }
bool ksbt_dladdr(const uintptr_t address, Dl_info* const info) { info->dli_fname = NULL; info->dli_fbase = NULL; info->dli_sname = NULL; info->dli_saddr = NULL; const uint32_t idx = ksbt_imageIndexContainingAddress(address); if(idx == UINT_MAX) { return false; } const struct mach_header* header = _dyld_get_image_header(idx); const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx); const uintptr_t addressWithSlide = address - imageVMAddrSlide; const uintptr_t segmentBase = ksbt_segmentBaseOfImageIndex(idx) + imageVMAddrSlide; if(segmentBase == 0) { return false; } info->dli_fname = _dyld_get_image_name(idx); info->dli_fbase = (void*)header; // Find symbol tables and get whichever symbol is closest to the address. const STRUCT_NLIST* bestMatch = NULL; uintptr_t bestDistance = ULONG_MAX; uintptr_t cmdPtr = ksmach_firstCmdAfterHeader(header); if(cmdPtr == 0) { return false; } for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { const struct load_command* loadCmd = (struct load_command*)cmdPtr; if(loadCmd->cmd == LC_SYMTAB) { const struct symtab_command* symtabCmd = (struct symtab_command*)cmdPtr; const STRUCT_NLIST* symbolTable = (STRUCT_NLIST*)(segmentBase + symtabCmd->symoff); const uintptr_t stringTable = segmentBase + symtabCmd->stroff; for(uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++) { // If n_value is 0, the symbol refers to an external object. if(symbolTable[iSym].n_value != 0) { uintptr_t symbolBase = symbolTable[iSym].n_value; uintptr_t currentDistance = addressWithSlide - symbolBase; if((addressWithSlide >= symbolBase) && (currentDistance <= bestDistance)) { bestMatch = symbolTable + iSym; bestDistance = currentDistance; } } } if(bestMatch != NULL) { info->dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide); info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx); if(*info->dli_sname == '_') { info->dli_sname++; } // This happens if all symbols have been stripped. if(info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) { info->dli_sname = NULL; } break; } } cmdPtr += loadCmd->cmdsize; } return true; }