diff --git a/OCRunner/Util/SymbolSearch.c b/OCRunner/Util/SymbolSearch.c index a714111..a2d1b08 100644 --- a/OCRunner/Util/SymbolSearch.c +++ b/OCRunner/Util/SymbolSearch.c @@ -24,21 +24,18 @@ #include "SymbolSearch.h" #include -#include -#include -#include -#include -#include -#include #include -#include #include #include #include -#include #include -#include -typedef struct load_command load_command_t; +#include +#include +#include +#include +#include +#include + #ifdef __LP64__ typedef struct mach_header_64 mach_header_t; typedef struct segment_command_64 segment_command_t; @@ -54,195 +51,143 @@ typedef struct nlist nlist_t; #endif #ifndef SEG_DATA_CONST -#define SEG_DATA_CONST "__DATA_CONST" +#define SEG_DATA_CONST "__DATA_CONST" #endif #ifndef SEG_AUTH_CONST -#define SEG_AUTH_CONST "__AUTH_CONST" +#define SEG_AUTH_CONST "__AUTH_CONST" #endif struct rebindings_entry { - struct FunctionSearch search; - struct rebindings_entry *next; - size_t link_list_len; + struct FunctionSearch *rebindings; + size_t rebindings_nel; + struct rebindings_entry *next; }; - -struct FunctionSearch makeFunctionSearch(const char *name, void *pointer) { +struct FunctionSearch makeFunctionSearch(const char *name, void *pointer){ + size_t len = strlen(name); + char *str = malloc(len + 1); + strncpy(str, name, len); + str[len] = '\0'; struct FunctionSearch search; - name = name ?: ""; - search.name = strdup(name); + search.name = str; search.pointer = pointer; return search; } - -static struct rebindings_entry *_rebindings_head = NULL; -static bool _is_first_search = true; +static struct rebindings_entry *_rebindings_head; static int prepend_search(struct rebindings_entry **rebindings_head, - struct FunctionSearch rebindings[], - size_t nel) { - for (int i = 0; i < nel; i++) { - struct FunctionSearch search = rebindings[i]; - struct rebindings_entry *new_entry = (struct rebindings_entry *)malloc(sizeof(struct rebindings_entry)); - memcpy(&(new_entry->search), &search, sizeof(struct FunctionSearch)); - new_entry->link_list_len = nel; - if (new_entry->search.name == NULL) continue; - new_entry->next = *rebindings_head; - *rebindings_head = new_entry; - } - return 0; -} - -static bool cstringPrefix(const char *str, const char *pre) { - return strncmp(pre, str, strlen(pre)) == 0; + struct FunctionSearch rebindings[], + size_t nel) { + struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); + if (!new_entry) { + return -1; + } + new_entry->rebindings = (struct FunctionSearch *) malloc(sizeof(struct FunctionSearch) * nel); + if (!new_entry->rebindings) { + free(new_entry); + return -1; + } + memcpy(new_entry->rebindings, rebindings, sizeof(struct FunctionSearch) * nel); + new_entry->rebindings_nel = nel; + new_entry->next = *rebindings_head; + *rebindings_head = new_entry; + return 0; } - -static void forEachLoadCommand(const struct mach_header *header, void (^callback)(const load_command_t *cmd, bool *stop)) { - bool stop = false; - const load_command_t *startCmds = NULL; - if (header->magic == MH_MAGIC_64) - startCmds = (load_command_t *)((char *)header + sizeof(struct mach_header_64)); - else if (header->magic == MH_MAGIC) - startCmds = (load_command_t *)((char *)header + sizeof(struct mach_header)); - else if (header->magic == MH_CIGAM || header->magic == MH_CIGAM_64) - return; // can't process big endian mach-o - else { - return; // not a mach-o file +static void perform_search_with_section(struct rebindings_entry *rebindings, + section_t *section, + intptr_t slide, + nlist_t *symtab, + char *strtab, + uint32_t *indirect_symtab) { + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; } - const load_command_t *const cmdsEnd = (load_command_t *)((char *)startCmds + header->sizeofcmds); - const load_command_t *cmd = startCmds; - for (uint32_t i = 0; i < header->ncmds; ++i) { - const load_command_t *nextCmd = (load_command_t *)((char *)cmd + cmd->cmdsize); - if (cmd->cmdsize < 8) { - return; - } - // FIXME: add check the cmdsize is pointer aligned (might reveal bin compat issues) - if ((nextCmd > cmdsEnd) || (nextCmd < startCmds)) { - return; + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; + bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; + struct rebindings_entry *cur = rebindings; + while (cur) { + for (uint j = 0; j < cur->rebindings_nel; j++) { + if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { + if (cur->rebindings[j].pointer != NULL) { + *(cur->rebindings[j].pointer) = indirect_symbol_bindings[i]; + } + goto symbol_loop; } - callback(cmd, &stop); - if (stop) - return; - cmd = nextCmd; + } + cur = cur->next; } -} + symbol_loop:; + } -static void forEachSection(const struct mach_header *header, void (^callback)(const section_t *sectInfo, bool *stop)) { - __block uint32_t segIndex = 0; - forEachLoadCommand(header, ^(const load_command_t *cmd, bool *stop) { - if (cmd->cmd == LC_SEGMENT_64) { - const segment_command_t *segCmd = (segment_command_t *)cmd; - const section_t *const sectionsStart = (section_t *)((char *)segCmd + sizeof(segment_command_t)); - const section_t *const sectionsEnd = §ionsStart[segCmd->nsects]; - for (const section_t *sect = sectionsStart; !(*stop) && (sect < sectionsEnd); ++sect) { - callback(sect, stop); - } - ++segIndex; - } - }); -} - -//edit from dyld: bool MachOAnalyzer::inCodeSection -static bool inCodeSection(const struct mach_header *header, intptr_t slide, intptr_t address) { - __block bool result = false; - forEachSection(header, ^(const section_t *sectInfo, bool *stop) { - if ((sectInfo->addr + slide <= address) && (address < (sectInfo->addr + sectInfo->size + slide))) { - result = ((sectInfo->flags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo->flags & S_ATTR_SOME_INSTRUCTIONS)); - *stop = true; - } - }); - return result; } static void search_symbols_for_image(struct rebindings_entry *rebindings, const struct mach_header *header, intptr_t slide) { - Dl_info info; - if (dladdr(header, &info) == 0) { - return; - } - - segment_command_t *cur_seg_cmd; - segment_command_t *linkedit_segment = NULL; - struct symtab_command *symtab_cmd = NULL; - - uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); - for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { - cur_seg_cmd = (segment_command_t *)cur; - if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { - if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { - linkedit_segment = cur_seg_cmd; - } - } else if (cur_seg_cmd->cmd == LC_SYMTAB) { - symtab_cmd = (struct symtab_command *)cur_seg_cmd; - } - } - - if (!symtab_cmd || !linkedit_segment) { - return; + Dl_info info; + if (dladdr(header, &info) == 0) { + return; + } + + segment_command_t *cur_seg_cmd; + segment_command_t *linkedit_segment = NULL; + struct symtab_command* symtab_cmd = NULL; + struct dysymtab_command* dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { + linkedit_segment = cur_seg_cmd; + } + } else if (cur_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command*)cur_seg_cmd; + } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; } + } - // Find base symbol/string table addresses - uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; - nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); - char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); - uint32_t cmdsize = symtab_cmd->nsyms; - - void *free_list[rebindings->link_list_len]; - size_t free_list_len = 0; - for (uint32_t i = 0; i < cmdsize; i++) { - nlist_t *nlist = &symtab[i]; - - if ((nlist->n_type & N_STAB) || (nlist->n_type & N_TYPE) != N_SECT) { - continue; - } - - const char *symbol_name = strtab + nlist->n_un.n_strx; - bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; - // - // $ ___Z C++ - // +/- objc - // __swift swift - if (!symbol_name_longer_than_1 - || symbol_name[0] == '<' - || symbol_name[0] == '-' - || symbol_name[0] == '+' - || symbol_name[1] == '$' - || cstringPrefix(symbol_name, "__Z") - || cstringPrefix(symbol_name, "___") - || cstringPrefix(symbol_name, "__swift")) { - continue; - } - - intptr_t functionAddress = (intptr_t)(nlist->n_value + slide); - if (!inCodeSection(header, slide, functionAddress)) { - continue; + if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || + !dysymtab_cmd->nindirectsyms) { + return; + } + + // Find base symbol/string table addresses + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + + // Get indirect symbol table (array of uint32_t indices into symbol table) + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && + strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0 && + strcmp(cur_seg_cmd->segname, SEG_AUTH_CONST) != 0) { + continue; + } + for (uint j = 0; j < cur_seg_cmd->nsects; j++) { + section_t *sect = + (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + perform_search_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); } - - struct rebindings_entry *cur = rebindings; - struct rebindings_entry *prev = NULL; - while (cur) { - if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->search.name) == 0) { - if (cur->search.pointer != NULL) { - *(cur->search.pointer) = (void *)(functionAddress); - if (prev) { - prev->next = cur->next; - free_list[free_list_len++] = cur; - } - } - } - prev = cur; - cur = cur->next; + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + perform_search_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); } + } } - - for (int i = 0; i < free_list_len; i++) { - struct rebindings_entry *cur = free_list[i]; - free((void *)cur->search.name); - free(cur); - } - - return; + } } static void _rebind_symbols_for_image(const struct mach_header *header, @@ -251,20 +196,19 @@ static void _rebind_symbols_for_image(const struct mach_header *header, } int search_symbols(struct FunctionSearch rebindings[], size_t rebindings_nel) { - int retval = prepend_search(&_rebindings_head, rebindings, rebindings_nel); - if (retval < 0) { - return retval; - } - // If this was the first call, register callback for image additions (which is also invoked for - // existing images, otherwise, just run on existing images - if (!_is_first_search) { - _dyld_register_func_for_add_image(_rebind_symbols_for_image); - _is_first_search = false; - } else { - uint32_t c = _dyld_image_count(); - for (uint32_t i = 0; i < c; i++) { - _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); - } - } + int retval = prepend_search(&_rebindings_head, rebindings, rebindings_nel); + if (retval < 0) { return retval; + } + // If this was the first call, register callback for image additions (which is also invoked for + // existing images, otherwise, just run on existing images + if (!_rebindings_head->next) { + _dyld_register_func_for_add_image(_rebind_symbols_for_image); + } else { + uint32_t c = _dyld_image_count(); + for (uint32_t i = 0; i < c; i++) { + _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); + } + } + return retval; }