forked from didi/DoKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0d58425
commit 90fdd54
Showing
8 changed files
with
312 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
242 changes: 242 additions & 0 deletions
242
iOS/DoraemonKit/Src/Core/Util/fishhook/doraemon_fishhook.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
// | ||
// doraemon_fishhook.c | ||
// DoraemonKit-DoraemonKit | ||
// | ||
// Created by didi on 2020/3/18. | ||
// | ||
|
||
#include "doraemon_fishhook.h" | ||
|
||
#include <dlfcn.h> | ||
#include <stdbool.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/mman.h> | ||
#include <sys/types.h> | ||
#include <mach/mach.h> | ||
#include <mach/vm_map.h> | ||
#include <mach/vm_region.h> | ||
#include <mach-o/dyld.h> | ||
#include <mach-o/loader.h> | ||
#include <mach-o/nlist.h> | ||
|
||
#ifdef __LP64__ | ||
typedef struct mach_header_64 mach_header_t; | ||
typedef struct segment_command_64 segment_command_t; | ||
typedef struct section_64 section_t; | ||
typedef struct nlist_64 nlist_t; | ||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 | ||
#else | ||
typedef struct mach_header mach_header_t; | ||
typedef struct segment_command segment_command_t; | ||
typedef struct section section_t; | ||
typedef struct nlist nlist_t; | ||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT | ||
#endif | ||
|
||
#ifndef SEG_DATA_CONST | ||
#define SEG_DATA_CONST "__DATA_CONST" | ||
#endif | ||
|
||
struct doraemon_rebindings_entry { | ||
struct doraemon_rebinding *rebindings; | ||
size_t rebindings_nel; | ||
struct doraemon_rebindings_entry *next; | ||
}; | ||
|
||
static struct doraemon_rebindings_entry *_rebindings_head; | ||
|
||
static int doraemon_prepend_rebindings(struct doraemon_rebindings_entry **rebindings_head, | ||
struct doraemon_rebinding rebindings[], | ||
size_t nel) { | ||
struct doraemon_rebindings_entry *new_entry = (struct doraemon_rebindings_entry *) malloc(sizeof(struct doraemon_rebindings_entry)); | ||
if (!new_entry) { | ||
return -1; | ||
} | ||
new_entry->rebindings = (struct doraemon_rebinding *) malloc(sizeof(struct doraemon_rebinding) * nel); | ||
if (!new_entry->rebindings) { | ||
free(new_entry); | ||
return -1; | ||
} | ||
memcpy(new_entry->rebindings, rebindings, sizeof(struct doraemon_rebinding) * nel); | ||
new_entry->rebindings_nel = nel; | ||
new_entry->next = *rebindings_head; | ||
*rebindings_head = new_entry; | ||
return 0; | ||
} | ||
|
||
static vm_prot_t doraemon_get_protection(void *sectionStart) { | ||
mach_port_t task = mach_task_self(); | ||
vm_size_t size = 0; | ||
vm_address_t address = (vm_address_t)sectionStart; | ||
memory_object_name_t object; | ||
#if __LP64__ | ||
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; | ||
vm_region_basic_info_data_64_t info; | ||
kern_return_t info_ret = vm_region_64( | ||
task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object); | ||
#else | ||
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; | ||
vm_region_basic_info_data_t info; | ||
kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object); | ||
#endif | ||
if (info_ret == KERN_SUCCESS) { | ||
return info.protection; | ||
} else { | ||
return VM_PROT_READ; | ||
} | ||
} | ||
|
||
static void doraemon_perform_rebinding_with_section(struct doraemon_rebindings_entry *rebindings, | ||
section_t *section, | ||
intptr_t slide, | ||
nlist_t *symtab, | ||
char *strtab, | ||
uint32_t *indirect_symtab) { | ||
const bool isDataConst = strcmp(section->segname, "__DATA_CONST") == 0; | ||
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; | ||
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); | ||
vm_prot_t oldProtection = VM_PROT_READ; | ||
if (isDataConst) { | ||
oldProtection = doraemon_get_protection(rebindings); | ||
mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE); | ||
} | ||
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; | ||
} | ||
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 doraemon_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].replaced != NULL && | ||
indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { | ||
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; | ||
} | ||
indirect_symbol_bindings[i] = cur->rebindings[j].replacement; | ||
goto symbol_loop; | ||
} | ||
} | ||
cur = cur->next; | ||
} | ||
symbol_loop:; | ||
} | ||
if (isDataConst) { | ||
int protection = 0; | ||
if (oldProtection & VM_PROT_READ) { | ||
protection |= PROT_READ; | ||
} | ||
if (oldProtection & VM_PROT_WRITE) { | ||
protection |= PROT_WRITE; | ||
} | ||
if (oldProtection & VM_PROT_EXECUTE) { | ||
protection |= PROT_EXEC; | ||
} | ||
mprotect(indirect_symbol_bindings, section->size, protection); | ||
} | ||
} | ||
|
||
static void doraemon_rebind_symbols_for_image(struct doraemon_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; | ||
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; | ||
} | ||
} | ||
|
||
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) { | ||
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) { | ||
doraemon_perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); | ||
} | ||
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { | ||
doraemon_perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
static void _doraemon_rebind_symbols_for_image(const struct mach_header *header, | ||
intptr_t slide) { | ||
doraemon_rebind_symbols_for_image(_rebindings_head, header, slide); | ||
} | ||
|
||
int doraemon_rebind_symbols_image(void *header, | ||
intptr_t slide, | ||
struct doraemon_rebinding rebindings[], | ||
size_t rebindings_nel) { | ||
struct doraemon_rebindings_entry *rebindings_head = NULL; | ||
int retval = doraemon_prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); | ||
doraemon_rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); | ||
if (rebindings_head) { | ||
free(rebindings_head->rebindings); | ||
} | ||
free(rebindings_head); | ||
return retval; | ||
} | ||
|
||
int doraemon_rebind_symbols(struct doraemon_rebinding rebindings[], size_t rebindings_nel) { | ||
int retval = doraemon_prepend_rebindings(&_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(_doraemon_rebind_symbols_for_image); | ||
} else { | ||
uint32_t c = _dyld_image_count(); | ||
for (uint32_t i = 0; i < c; i++) { | ||
_doraemon_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); | ||
} | ||
} | ||
return retval; | ||
} |
59 changes: 59 additions & 0 deletions
59
iOS/DoraemonKit/Src/Core/Util/fishhook/doraemon_fishhook.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// doraemon_fishhook.h | ||
// DoraemonKit-DoraemonKit | ||
// | ||
// Created by didi on 2020/3/18. | ||
// | ||
|
||
#ifndef doraemon_fishhook_h | ||
#define doraemon_fishhook_h | ||
|
||
#include <stddef.h> | ||
#include <stdint.h> | ||
|
||
#if !defined(DORAEMON_FISHHOOK_EXPORT) | ||
#define DORAEMON_FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) | ||
#else | ||
#define DORAEMON_FISHHOOK_VISIBILITY __attribute__((visibility("default"))) | ||
#endif | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif //__cplusplus | ||
|
||
/* | ||
* A structure representing a particular intended rebinding from a symbol | ||
* name to its replacement | ||
*/ | ||
struct doraemon_rebinding { | ||
const char *name; | ||
void *replacement; | ||
void **replaced; | ||
}; | ||
|
||
/* | ||
* For each rebinding in rebindings, rebinds references to external, indirect | ||
* symbols with the specified name to instead point at replacement for each | ||
* image in the calling process as well as for all future images that are loaded | ||
* by the process. If rebind_functions is called more than once, the symbols to | ||
* rebind are added to the existing list of rebindingdoraemon_rebind_symbolsl | ||
* is rebound more than once, the later rebinding will take precedence. | ||
*/ | ||
DORAEMON_FISHHOOK_VISIBILITY | ||
int doraemon_rebind_symbols(struct doraemon_rebinding rebindings[], size_t rebindings_nel); | ||
|
||
/* | ||
* Rebinds as above, but only in the specified image. The header should point | ||
* to the mach-o header, the slide should be the slide offset. Others as above. | ||
*/ | ||
DORAEMON_FISHHOOK_VISIBILITY | ||
int doraemon_rebind_symbols_image(void *header, | ||
intptr_t slide, | ||
struct doraemon_rebinding rebindings[], | ||
size_t rebindings_nel); | ||
#ifdef __cplusplus | ||
} | ||
#endif //__cplusplus | ||
|
||
|
||
#endif /* doraemon_fishhook_h */ |
Oops, something went wrong.