ShadowHook原理分析
初始化
无论是使用Native初始化还是Java进行初始化。最后其实都会调用到shadowhook_init方法
init的实际初始化逻辑包含如下几个过程
errno init
信号量处理初始化(sigsegv、sigbus)
enter、exit初始化
mode相关初始化
a) shared: safe_init + hub_init
b) unique: linker_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 int shadowhook_init (shadowhook_mode_t mode, bool debuggable) { bool do_init = false ; if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); if (__predict_true(SHADOWHOOK_ERRNO_UNINIT == shadowhook_init_errno)) { do_init = true ; shadowhook_mode = mode; sh_log_set_debuggable(debuggable); #define GOTO_END(errnum) \ do { \ shadowhook_init_errno = errnum; \ goto end; \ } while (0) if (__predict_false(0 != sh_errno_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ERRNO); if (__predict_false(0 != bytesig_init(SIGSEGV))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGSEGV); if (__predict_false(0 != bytesig_init(SIGBUS))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGBUS); if (__predict_false(0 != sh_enter_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_ENTER); sh_exit_init(); if (SHADOWHOOK_MODE_SHARED == shadowhook_mode) { if (__predict_false(0 != sh_safe_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_SAFE); if (__predict_false(0 != sh_hub_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_HUB); } else { if (__predict_false(0 != sh_linker_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_LINKER); } #undef GOTO_END shadowhook_init_errno = SHADOWHOOK_ERRNO_OK; } end: pthread_mutex_unlock(&lock); } SH_LOG_ALWAYS_SHOW("%s: shadowhook init(mode: %s, debuggable: %s), return: %d, real-init: %s" , shadowhook_get_version(), SHADOWHOOK_MODE_SHARED == mode ? "SHARED" : "UNIQUE" , debuggable ? "true" : "false" , shadowhook_init_errno, do_init ? "yes" : "no" ); SH_ERRNO_SET_RET_ERRNUM(shadowhook_init_errno); }
errono init
为多线程环境初始化thread local。
1 2 3 4 5 6 7 8 9 int sh_errno_init (void ) { if (__predict_false(0 != pthread_key_create(&sh_errno_tls_key, NULL ))) { sh_errno_global = SHADOWHOOK_ERRNO_INIT_ERRNO; return -1 ; } sh_errno_global = SHADOWHOOK_ERRNO_OK; return 0 ; }
signal init
信号的初始化包含两个部分
sigsegv:段错误
sigbus:总线错误
1 2 if (__predict_false(0 != bytesig_init(SIGSEGV))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGSEGV);if (__predict_false(0 != bytesig_init(SIGBUS))) GOTO_END(SHADOWHOOK_ERRNO_INIT_SIGBUS);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 int bytesig_init (int signum) { if (__predict_false(signum <= 0 || signum >= __SIGRTMIN || signum == SIGKILL || signum == SIGSTOP)) return -1 ; if (__predict_false(BYTESIG_STATUS_UNAVAILABLE == bytesig_status)) return -1 ; if (__predict_false(NULL != bytesig_signal_array[signum])) return -1 ; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); int ret = -1 ; if (__predict_false(NULL != bytesig_signal_array[signum])) goto end; bytesig_signal_t *sig = calloc (1 , sizeof (bytesig_signal_t )); if (__predict_false(NULL == sig)) goto end; #define SA_EXPOSE_TAGBITS 0x00000800 #define REGISTER_SIGNAL_HANDLER(suffix) \ do { \ struct sigaction##suffix act; \ memset(&act, 0, sizeof(struct sigaction##suffix)); \ sigfillset##suffix(&act.sa_mask); \ act.sa_sigaction = bytesig_handler; \ act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART | SA_EXPOSE_TAGBITS; \ if (__predict_false( \ 0 != \ ((bytesig_sigaction##suffix##_t)bytesig_sigaction)(signum, &act, &sig->prev_action##suffix))) { \ free(sig); \ goto end; \ } \ } while (0) if (BYTESIG_STATUS_SIG64 == bytesig_status) REGISTER_SIGNAL_HANDLER(64 ); else REGISTER_SIGNAL_HANDLER(); bytesig_signal_array[signum] = sig; ret = 0 ; end: pthread_mutex_unlock(&lock); return ret; }
enter & exit init
只是初始化了sh_enter_trampo_mgr(也就是一个单链表)
1 2 3 4 int sh_enter_init (void ) { sh_trampo_init_mgr(&sh_enter_trampo_mgr, SH_ENTER_PAGE_NAME, SH_ENTER_SZ, SH_ENTER_DELAY_SEC); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 void sh_trampo_init_mgr (sh_trampo_mgr_t *mem_mgr, const char *page_name, size_t trampo_size, time_t delay_sec) { SLIST_INIT(&mem_mgr->pages); pthread_mutex_init(&mem_mgr->pages_lock, NULL ); mem_mgr->page_name = page_name; mem_mgr->trampo_size = SH_UTIL_ALIGN_END(trampo_size, SH_TRAMPO_ALIGN); mem_mgr->delay_sec = delay_sec; }
Exit 同Enter逻辑
只是多了一部分用来解析linker elf文件信息
1 2 3 4 5 6 7 8 9 void sh_exit_init (void ) { sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC); sh_exit_init_elfinfo(AT_PHDR, &sh_exit_app_process_info); sh_exit_init_elfinfo(AT_BASE, &sh_exit_linker_info); sh_exit_init_elfinfo(AT_SYSINFO_EHDR, &sh_exit_vdso_info); }
mode init shard
Shard mode需要初始化
safe & hub
1 2 if (__predict_false(0 != sh_safe_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_SAFE);if (__predict_false(0 != sh_hub_init())) GOTO_END(SHADOWHOOK_ERRNO_INIT_HUB);
safe init
确保libc中存有如下方法
pthread_getspecific
pthread_setspecific
abort
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int sh_safe_init (void ) { sh_safe_api_level = sh_util_get_api_level(); void *handle = xdl_open("libc.so" , XDL_DEFAULT); if (NULL == handle) return -1 ; int r = -1 ; if (__predict_false(0 != sh_safe_init_func(handle, "pthread_getspecific" , SH_SAFE_IDX_PTHREAD_GETSPECIFIC))) goto end; if (__predict_false(0 != sh_safe_init_func(handle, "pthread_setspecific" , SH_SAFE_IDX_PTHREAD_SETSPECIFIC))) goto end; if (__predict_false(0 != sh_safe_init_func(handle, "abort" , SH_SAFE_IDX_ABORT))) goto end; r = 0 ; end: xdl_close(handle); return r; }
hub init
初始化tls key
初始化hub stack cache & stack cache used(一个用来保存栈帧、一个用来初始化已使用的换成下标)
初始化trampoline跳板函数。以及trampoline manager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int sh_hub_init (void ) { LIST_INIT(&sh_hub_delayed_destroy); pthread_mutex_init(&sh_hub_delayed_destroy_lock, NULL ); if (__predict_false(0 != pthread_key_create(&sh_hub_stack_tls_key, sh_hub_stack_destroy))) return -1 ; if (__predict_false(NULL == (sh_hub_stack_cache = malloc (SH_HUB_THREAD_MAX * sizeof (sh_hub_stack_t ))))) return -1 ; if (__predict_false(NULL == (sh_hub_stack_cache_used = calloc (SH_HUB_THREAD_MAX, sizeof (uint8_t ))))) return -1 ; size_t code_size = (uintptr_t )(&sh_hub_trampo_template_data) - (uintptr_t )(sh_hub_trampo_template_start()); size_t data_size = sizeof (void *) + sizeof (void *); sh_trampo_init_mgr(&sh_hub_trampo_mgr, SH_HUB_TRAMPO_PAGE_NAME, code_size + data_size, SH_HUB_TRAMPO_DELAY_SEC); return 0 ; }
unique
初始化linker.
根据指令集架构open linker or linker64
获取g_dl_mutex地址
获取do_dlopen地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int sh_linker_init (void ) { memset (&sh_linker_dlopen_dlinfo, 0 , sizeof (sh_linker_dlopen_dlinfo)); int api_level = sh_util_get_api_level(); if (__predict_true(api_level >= __ANDROID_API_L__)) { sh_linker_dlopen_addr = 0 ; void *handle = xdl_open(SH_LINKER_BASENAME, XDL_DEFAULT); if (__predict_false(NULL == handle)) return -1 ; xdl_info(handle, XDL_DI_DLINFO, (void *)&sh_linker_dlopen_dlinfo); sh_linker_dlopen_dlinfo.dli_fname = SH_LINKER_BASENAME; sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX, NULL )); if (NULL == sh_linker_g_dl_mutex && api_level >= __ANDROID_API_U__) sh_linker_g_dl_mutex = (pthread_mutex_t *)(xdl_dsym(handle, SH_LINKER_SYM_G_DL_MUTEX_U_QPR2, NULL )); if (api_level >= __ANDROID_API_O__) sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_O; else if (api_level >= __ANDROID_API_N__) sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_N; else sh_linker_dlopen_dlinfo.dli_sname = SH_LINKER_SYM_DO_DLOPEN_L; sh_linker_dlopen_dlinfo.dli_saddr = xdl_dsym(handle, sh_linker_dlopen_dlinfo.dli_sname, &(sh_linker_dlopen_dlinfo.dli_ssize)); sh_linker_dlopen_addr = (uintptr_t )sh_linker_dlopen_dlinfo.dli_saddr; xdl_close(handle); } return (0 != sh_linker_dlopen_addr && (NULL != sh_linker_g_dl_mutex || api_level < __ANDROID_API_L__)) ? 0 : -1 ; }
Hook shadowhook_hook_func_addr
shadowhook_hook_func_addr——
用于hook已经加载到内存 中的so文件。
只能 hook在符号表 中有记录 的函数
Hookhook流程原理图如下:
图中有一些关键的要素信息,进行介绍
target_addr
被hook的方法的地址
prev func
被hook后返回的方法地址
trampo
hook过程中会覆盖原方法中一定的区域,加上跳转方法
exit
用于跳转到指定hook方法的跳板。
enter
用于返回执行的跳板
可以发现其实Inline Hook其实就是——修改需要Hook函数的前几位汇编代码,直接通过exit跳板跳转到hook方法。必要时再通过enter跳板跳转回来 。
接下来会进行详细的代码讲解。
根据func address hook指定的function
1 2 3 4 void *shadowhook_hook_func_addr (void *func_addr, void *new_addr, void **orig_addr) { const void *caller_addr = __builtin_return_address(0 ); return shadowhook_hook_addr_impl(func_addr, new_addr, orig_addr, true , (uintptr_t )caller_addr); }
内部实现
检查参数
创建task
hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static void *shadowhook_hook_addr_impl (void *sym_addr, void *new_addr, void **orig_addr, bool ignore_symbol_check, uintptr_t caller_addr) { int r; if (NULL == sym_addr || NULL == new_addr) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG); sh_task_t *task = sh_task_create_by_target_addr((uintptr_t )sym_addr, (uintptr_t )new_addr, (uintptr_t *)orig_addr, ignore_symbol_check, (uintptr_t )caller_addr); if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM); r = sh_task_hook(task); if (0 != r) { sh_task_destroy(task); GOTO_ERR(r); } err: }
task创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 sh_task_t *sh_task_create_by_target_addr (uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, bool ignore_symbol_check, uintptr_t caller_addr) { sh_task_t *self = malloc (sizeof (sh_task_t )); if (NULL == self) return NULL ; self->lib_name = NULL ; self->sym_name = NULL ; self->target_addr = target_addr; self->new_addr = new_addr; self->orig_addr = orig_addr; self->hooked = NULL ; self->hooked_arg = NULL ; self->caller_addr = caller_addr; self->finished = false ; self->error = false ; self->ignore_symbol_check = ignore_symbol_check; return self; }
Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 int sh_task_hook (sh_task_t *self) { int r; bool is_hook_sym_addr = true ; char real_lib_name[512 ] = "unknown" ; char real_sym_name[1024 ] = "unknown" ; size_t backup_len = 0 ; xdl_info_t dlinfo; memset (&dlinfo, 0 , sizeof (xdl_info_t )); if (0 == self->target_addr) { is_hook_sym_addr = false ; strlcpy(real_lib_name, self->lib_name, sizeof (real_lib_name)); strlcpy(real_sym_name, self->sym_name, sizeof (real_sym_name)); r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name, sizeof (real_lib_name)); if (SHADOWHOOK_ERRNO_PENDING == r) { if (0 != (r = sh_task_start_monitor(true ))) goto end; r = SHADOWHOOK_ERRNO_PENDING; goto end; } if (0 != r) goto end; self->target_addr = (uintptr_t )dlinfo.dli_saddr; } else { r = sh_linker_get_dlinfo_by_addr((void *)self->target_addr, &dlinfo, real_lib_name, sizeof (real_lib_name), real_sym_name, sizeof (real_sym_name), self->ignore_symbol_check); if (0 != r) goto end; } if (sh_linker_need_to_hook_dlopen(self->target_addr)) { SH_LOG_INFO("task: hook dlopen/do_dlopen internal. target-address %" PRIxPTR, self->target_addr); if (0 != (r = sh_task_start_monitor(false ))) goto end; } r = sh_switch_hook(self->target_addr, self->new_addr, self->orig_addr, &backup_len, &dlinfo); self->finished = true ; end: if (0 == r || SHADOWHOOK_ERRNO_PENDING == r) { pthread_rwlock_wrlock(&sh_tasks_lock); TAILQ_INSERT_TAIL(&sh_tasks, self, link); if (!self->finished) __atomic_add_fetch(&sh_tasks_unfinished_cnt, 1 , __ATOMIC_SEQ_CST); pthread_rwlock_unlock(&sh_tasks_lock); } sh_recorder_add_hook(r, is_hook_sym_addr, self->target_addr, real_lib_name, real_sym_name, self->new_addr, backup_len, (uintptr_t )self, self->caller_addr); return r; }
inline hook
对unique & shared mode单独做处理
unique mode无需跳板函数(trampoline)
shared mode会由跳板函数管理hook请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int sh_switch_hook (uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len, xdl_info_t *dlinfo) { int r; if (SHADOWHOOK_IS_UNIQUE_MODE) r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo); else r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo); if (0 == r) SH_LOG_INFO("switch: hook in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR, SHADOWHOOK_IS_UNIQUE_MODE ? "UNIQUE" : "SHARED" , target_addr, new_addr); return r; }
Unique Mode
hook unique
check是否重复hook
开辟新的switch
插入新的switch 到 switch缓存(红黑树)
进行hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 static int sh_switch_hook_unique (uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len, xdl_info_t *dlinfo) { sh_switch_t *self = sh_switch_find(target_addr); if (NULL != self) return SHADOWHOOK_ERRNO_HOOK_DUP; int r; if (0 != (r = sh_switch_create(&self, target_addr, NULL ))) return r; sh_switch_t *useless = NULL ; pthread_rwlock_wrlock(&sh_switches_lock); if (NULL != RB_INSERT(sh_switch_tree, &sh_switches, self)) { useless = self; r = SHADOWHOOK_ERRNO_HOOK_DUP; goto end; } if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, NULL ))) { RB_REMOVE(sh_switch_tree, &sh_switches, self); useless = self; goto end; } *backup_len = self->inst.backup_len; sh_switch_dump_enter(self); end: pthread_rwlock_unlock(&sh_switches_lock); if (NULL != useless) sh_switch_destroy(useless, false ); return r; }
hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int sh_inst_hook (sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { self->enter_addr = sh_enter_alloc(); if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER; int r; #ifdef SH_CONFIG_TRY_WITH_EXIT if (0 == (r = sh_inst_hook_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) return r; #endif if (0 == (r = sh_inst_hook_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) return r; if (NULL != orig_addr) *orig_addr = 0 ; if (NULL != orig_addr2) *orig_addr2 = 0 ; sh_enter_free(self->enter_addr); return r; }
try with exit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 static int sh_inst_hook_with_exit (sh_inst_t *self, uintptr_t target_addr, xdl_info_t *dlinfo, uintptr_t new_addr, uintptr_t *orig_addr, uintptr_t *orig_addr2) { int r; uintptr_t pc = target_addr; self->backup_len = 4 ; if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ; sh_a64_absolute_jump_with_br(self->exit , new_addr); if (0 != (r = sh_exit_alloc(&self->exit_addr, (uint16_t *)&self->exit_type, pc, dlinfo, (uint8_t *)(self->exit ), sizeof (self->exit ), SH_INST_A64_B_RANGE_LOW, SH_INST_A64_B_RANGE_HIGH))) return r; if (0 != sh_util_mprotect(target_addr, self->backup_len, PROT_READ | PROT_WRITE | PROT_EXEC)) { r = SHADOWHOOK_ERRNO_MPROT; goto err; } SH_SIG_TRY(SIGSEGV, SIGBUS) { r = sh_inst_hook_rewrite(self, target_addr, orig_addr, orig_addr2); } SH_SIG_CATCH() { r = SHADOWHOOK_ERRNO_HOOK_REWRITE_CRASH; goto err; } SH_SIG_EXIT if (0 != r) goto err; sh_a64_relative_jump(self->trampo, self->exit_addr, pc); __atomic_thread_fence(__ATOMIC_SEQ_CST); if (0 != (r = sh_util_write_inst(target_addr, self->trampo, self->backup_len))) goto err; SH_LOG_INFO("a64: hook (WITH EXIT) OK. target %" PRIxPTR " -> exit %" PRIxPTR " -> new %" PRIxPTR " -> enter %" PRIxPTR " -> remaining %" PRIxPTR, target_addr, self->exit_addr, new_addr, self->enter_addr, target_addr + self->backup_len); return 0 ; err: sh_exit_free(self->exit_addr, (uint16_t )self->exit_type, (uint8_t *)(self->exit ), sizeof (self->exit )); self->exit_addr = 0 ; return r; }
可能还是不够直观,我们将一个Hook的示例代码进行演示他的hook原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void *orig;void *stub;typedef void *(*malloc_t )(size_t );void *my_malloc (size_t sz) { return ((malloc_t ) orig)(sz); } void do_hook (void ) { stub = shadowhook_hook_func_addr((void *) malloc , (void *) my_malloc, &orig); }
inline hook覆盖了malloc第一个指令,跳转到了exit跳板
exit跳板,直接跳转到了my_malloc函数地址
my_malloc函数中调用了origin函数指针,函数执行流程返回到了原malloc函数继续执行。
Shared Mode
Shared Mode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 static int sh_switch_hook_shared (uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len, xdl_info_t *dlinfo) { int r; pthread_rwlock_rdlock(&sh_switches_lock); sh_switch_t key = {.target_addr = target_addr}; sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); if (NULL != self) { if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub); r = sh_hub_add_proxy(self->hub, new_addr); pthread_rwlock_unlock(&sh_switches_lock); *backup_len = self->inst.backup_len; return r; } pthread_rwlock_unlock(&sh_switches_lock); uintptr_t hub_trampo; if (0 != (r = sh_switch_create(&self, target_addr, &hub_trampo))) return r; sh_switch_t *useless = NULL ; pthread_rwlock_wrlock(&sh_switches_lock); sh_switch_t *exists; if (NULL != (exists = RB_INSERT(sh_switch_tree, &sh_switches, self))) { useless = self; if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(exists->hub); r = sh_hub_add_proxy(exists->hub, new_addr); *backup_len = exists->inst.backup_len; } else { uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr); if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, hub_trampo, sh_hub_get_orig_addr_addr(self->hub), safe_orig_addr_addr))) { RB_REMOVE(sh_switch_tree, &sh_switches, self); useless = self; goto end; } *backup_len = self->inst.backup_len; sh_switch_dump_enter(self); if (NULL != orig_addr) *orig_addr = sh_hub_get_orig_addr(self->hub); if (0 != (r = sh_hub_add_proxy(self->hub, new_addr))) { sh_inst_unhook(&self->inst, target_addr); *backup_len = 0 ; RB_REMOVE(sh_switch_tree, &sh_switches, self); useless = self; goto end; } } end: pthread_rwlock_unlock(&sh_switches_lock); if (NULL != useless) sh_switch_destroy(useless, false ); return r; }
这里一行代码就可以解释Shared & Unique Mode的差别
unique mode下new_addr传入的用户指定的值,shared mode传入的是hub_trampo的值,即trampoline模板的值
1 2 sh_inst_hook(&self->inst, target_addr, dlinfo, hub_trampo, sh_hub_get_orig_addr_addr(self->hub), safe_orig_addr_addr)
所以我们可以得知,就hook原理上就只有一个区别。
unique mode下函数是没有被包装的,shared mode会包装一层trampoline,由跳转管理我们的函数调用。
如下是template的代码
1.调用sh_hub_push_stack获取proxy函数地址
2.调用sh_hub_push_stack的返回的函数地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 extern void *sh_hub_trampo_template_data __attribute__((visibility("hidden" )));__attribute__((naked)) static void sh_hub_trampo_template (void ) { #if defined(__arm__) __asm__( "push { r0 - r3, lr } \n" "ldr r0, hub_ptr \n" "mov r1, lr \n" "ldr ip, push_stack \n" "blx ip \n" "mov ip, r0 \n" "pop { r0 - r3, lr } \n" "bx ip \n" "sh_hub_trampo_template_data:" ".global sh_hub_trampo_template_data;" "push_stack:" ".word 0;" "hub_ptr:" ".word 0;" ); #elif defined(__aarch64__) __asm__( "stp x0, x1, [sp, #-0xd0]! \n" "stp x2, x3, [sp, #0x10] \n" "stp x4, x5, [sp, #0x20] \n" "stp x6, x7, [sp, #0x30] \n" "stp x8, lr, [sp, #0x40] \n" "stp q0, q1, [sp, #0x50] \n" "stp q2, q3, [sp, #0x70] \n" "stp q4, q5, [sp, #0x90] \n" "stp q6, q7, [sp, #0xb0] \n" "ldr x0, hub_ptr \n" "mov x1, lr \n" "ldr x16, push_stack \n" "blr x16 \n" "mov x16, x0 \n" "ldp q6, q7, [sp, #0xb0] \n" "ldp q4, q5, [sp, #0x90] \n" "ldp q2, q3, [sp, #0x70] \n" "ldp q0, q1, [sp, #0x50] \n" "ldp x8, lr, [sp, #0x40] \n" "ldp x6, x7, [sp, #0x30] \n" "ldp x4, x5, [sp, #0x20] \n" "ldp x2, x3, [sp, #0x10] \n" "ldp x0, x1, [sp], #0xd0 \n" "br x16 \n" "sh_hub_trampo_template_data:" ".global sh_hub_trampo_template_data;" "push_stack:" ".quad 0;" "hub_ptr:" ".quad 0;" ); #endif }
shadowhook_hook_func_addr
我们可以对比之前分析的hook_func_addr
可以发现——除了传入的参数有一个不太一样以外(ignore_symbol_check),其他的过程是一模一样的
(ignore_symbol_check)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static void *shadowhook_hook_addr_impl (void *sym_addr, void *new_addr, void **orig_addr, bool ignore_symbol_check, uintptr_t caller_addr) { } void *shadowhook_hook_func_addr (void *func_addr, void *new_addr, void **orig_addr) { const void *caller_addr = __builtin_return_address(0 ); return shadowhook_hook_addr_impl(func_addr, new_addr, orig_addr, true , (uintptr_t )caller_addr); } void *shadowhook_hook_sym_addr (void *sym_addr, void *new_addr, void **orig_addr) { const void *caller_addr = __builtin_return_address(0 ); return shadowhook_hook_addr_impl(sym_addr, new_addr, orig_addr, false , (uintptr_t )caller_addr); }
在进行sh_linker 调用sh_linker_get_dlinfo_by_addr获取dl_info的时候有一定的差异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 bool crashed = false ; void *dlcache = NULL ; int r = 0 ; if (sh_util_get_api_level() >= __ANDROID_API_L__) { r = xdl_addr((void *)addr, dlinfo, &dlcache); } else { SH_SIG_TRY(SIGSEGV, SIGBUS) { r = xdl_addr((void *)addr, dlinfo, &dlcache); } SH_SIG_CATCH() { crashed = true ; } SH_SIG_EXIT } if (NULL == dlinfo->dli_sname) { if (ignore_symbol_check) { dlinfo->dli_saddr = addr; dlinfo->dli_sname = "unknown" ; dlinfo->dli_ssize = 1024 ; } else { const char *matched_dlfcn_name = NULL ; if (NULL == (matched_dlfcn_name = sh_linker_match_dlfcn((uintptr_t )addr))) { r = SHADOWHOOK_ERRNO_HOOK_DLINFO; goto end; } else { dlinfo->dli_saddr = addr; dlinfo->dli_sname = matched_dlfcn_name; dlinfo->dli_ssize = 4 ; SH_LOG_INFO("task: match dlfcn, target_addr %p, sym_name %s" , addr, matched_dlfcn_name); } } } end: xdl_addr_clean(&dlcache); return r; }
shadowhook_hook_sym_name
逻辑图如下:
这种方式可以 hook “当前已加载到进程中的动态库”,也可以 hook “还没有加载到进程中的动态库”(如果 hook 时动态库还未加载,ShadowHook 内部会记录当前的 hook “诉求”,后续一旦目标动态库被加载到内存中,将立刻执行 hook 操作)。
具体hook代码分析如下:
hook sym name 换了一个方法控制hook逻辑(不同于func_addr、sym_addr)
1 2 3 4 5 void *shadowhook_hook_sym_name (const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr) { const void *caller_addr = __builtin_return_address(0 ); return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, NULL , NULL , (uintptr_t )caller_addr); }
hook大体的逻辑貌似是没什么变化
创建task
调用sh_task_hook进行inline hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static void *shadowhook_hook_sym_name_impl (const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, uintptr_t caller_addr) { sh_task_t *task = sh_task_create_by_sym_name(lib_name, sym_name, (uintptr_t )new_addr, (uintptr_t *)orig_addr, hooked, hooked_arg, (uintptr_t )caller_addr); if (NULL == task) GOTO_ERR(SHADOWHOOK_ERRNO_OOM); r = sh_task_hook(task); if (0 != r && SHADOWHOOK_ERRNO_PENDING != r) { sh_task_destroy(task); GOTO_ERR(r); } SH_LOG_INFO("shadowhook: hook_sym_name(%s, %s, %p) OK. return: %p. %d - %s" , lib_name, sym_name, new_addr, (void *)task, r, sh_errno_to_errmsg(r)); SH_ERRNO_SET_RET(r, (void *)task); err: SH_LOG_ERROR("shadowhook: hook_sym_name(%s, %s, %p) FAILED. %d - %s" , lib_name, sym_name, new_addr, r, sh_errno_to_errmsg(r)); SH_ERRNO_SET_RET_NULL(r); }
创建task,对比上面两种基于addr的hook有如下改变
addr被换成了lb_name和sym_name
添加了参数hooked、hook_args(shadowhook_hook_sym_name这里传入的都是是null)
对于结构体初始化来说,差别也就是初始化不同,之外也没啥好说的。代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 sh_task_t *task = sh_task_create_by_sym_name(lib_name, sym_name, (uintptr_t )new_addr, (uintptr_t *)orig_addr, hooked, hooked_arg, (uintptr_t )caller_addr); sh_task_t *sh_task_create_by_sym_name (const char *lib_name, const char *sym_name, uintptr_t new_addr, uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, uintptr_t caller_addr) { sh_task_t *self = malloc (sizeof (sh_task_t )); if (NULL == self) return NULL ; if (NULL == (self->lib_name = strdup(lib_name))) goto err; if (NULL == (self->sym_name = strdup(sym_name))) goto err; self->target_addr = 0 ; self->new_addr = new_addr; self->orig_addr = orig_addr; self->hooked = hooked; self->hooked_arg = hooked_arg; self->caller_addr = caller_addr; self->finished = false ; self->error = false ; self->ignore_symbol_check = false ; return self; err: if (NULL != self->lib_name) free (self->lib_name); if (NULL != self->sym_name) free (self->sym_name); free (self); return NULL ; }
sh_task_hook我们之前分析过这个代码。
为了减少冗余部分、遇上相似的代码这里就直接跳过了。着重分析于之前hook的差异的地方
从如下代码中可以发现、差别在于下面标记的if else分支,通过lib name和symbol name hook的方案走了单独的分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int sh_task_hook (sh_task_t *self) { if (0 == self->target_addr) { is_hook_sym_addr = false ; strlcpy(real_lib_name, self->lib_name, sizeof (real_lib_name)); strlcpy(real_sym_name, self->sym_name, sizeof (real_sym_name)); r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name, sizeof (real_lib_name)); if (SHADOWHOOK_ERRNO_PENDING == r) { if (0 != (r = sh_task_start_monitor(true ))) goto end; r = SHADOWHOOK_ERRNO_PENDING; goto end; } if (0 != r) goto end; self->target_addr = (uintptr_t )dlinfo.dli_saddr; } else { r = sh_linker_get_dlinfo_by_addr((void *)self->target_addr, &dlinfo, real_lib_name, sizeof (real_lib_name), real_sym_name, sizeof (real_sym_name), self->ignore_symbol_check); if (0 != r) goto end; } }
对分支的内容的单独分析一下
可以得出如下结论:
由于hook_sym_name只有lib name和sym name所以hook逻辑和前两者hook不太一样,走了单独的分支。
hook_sym_name中有两种处理方式:立即hook & 延迟hook,依据是hook的so是否加载
1)对于已经加载到内存的so,通过dl寻找函数的绝对地址,得到地址以后复用直接addr hook的逻辑。
2)对于没有加载到内存的so,会通过延期的方式加载,等到so加载以后会自动触发后续的hook逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 if (0 == self->target_addr) { is_hook_sym_addr = false ; strlcpy(real_lib_name, self->lib_name, sizeof (real_lib_name)); strlcpy(real_sym_name, self->sym_name, sizeof (real_sym_name)); r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name, sizeof (real_lib_name)); if (SHADOWHOOK_ERRNO_PENDING == r) { if (0 != (r = sh_task_start_monitor(true ))) goto end; r = SHADOWHOOK_ERRNO_PENDING; goto end; } if (0 != r) goto end; self->target_addr = (uintptr_t )dlinfo.dli_saddr; }
获取addr
怎么通过lib name & sym name获取的addr呢?
首先通过lib name调用xdl_open打开lib获取elf首个地址
通过elf首地址和函数名称搜索函数绝对地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 int sh_linker_get_dlinfo_by_sym_name (const char *lib_name, const char *sym_name, xdl_info_t *dlinfo, char *real_lib_name, size_t real_lib_name_sz) { bool crashed = false ; void *handle = NULL ; if (sh_util_get_api_level() >= __ANDROID_API_L__) { handle = xdl_open(lib_name, XDL_DEFAULT); } else { SH_SIG_TRY(SIGSEGV, SIGBUS) { handle = xdl_open(lib_name, XDL_DEFAULT); } SH_SIG_CATCH() { crashed = true ; } SH_SIG_EXIT } if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH; if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING; xdl_info(handle, XDL_DI_DLINFO, (void *)dlinfo); if (!sh_linker_check_arch(dlinfo)) { xdl_close(handle); return SHADOWHOOK_ERRNO_ELF_ARCH_MISMATCH; } crashed = false ; void *addr = NULL ; size_t sym_size = 0 ; SH_SIG_TRY(SIGSEGV, SIGBUS) { addr = xdl_sym(handle, sym_name, &sym_size); if (NULL == addr) addr = xdl_dsym(handle, sym_name, &sym_size); } SH_SIG_CATCH() { crashed = true ; } SH_SIG_EXIT return 0 ; }
xdl_open
1.通过auxv系统调用获取(linker, vDSO,app_process)地址
2.通过dl_iterate_phdr便利vmmap表(当前进程的so列表)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 void *xdl_open (const char *filename, int flags) { if (NULL == filename) return NULL ; if (flags & XDL_ALWAYS_FORCE_LOAD) return xdl_open_always_force(filename); else if (flags & XDL_TRY_FORCE_LOAD) return xdl_open_try_force(filename); else return xdl_find(filename); } static xdl_t *xdl_find (const char *filename) { xdl_t *self = NULL ; if (xdl_util_ends_with(filename, XDL_UTIL_LINKER_BASENAME)) self = xdl_find_from_auxv(AT_BASE, XDL_UTIL_LINKER_PATHNAME); else if (xdl_util_ends_with(filename, XDL_UTIL_VDSO_BASENAME)) self = xdl_find_from_auxv(AT_SYSINFO_EHDR, XDL_UTIL_VDSO_BASENAME); const char *basename, *pathname; #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ if (xdl_util_get_api_level() < __ANDROID_API_L__) { basename = XDL_UTIL_APP_PROCESS_BASENAME_K; pathname = XDL_UTIL_APP_PROCESS_PATHNAME_K; } else #endif { basename = XDL_UTIL_APP_PROCESS_BASENAME; pathname = XDL_UTIL_APP_PROCESS_PATHNAME; } if (xdl_util_ends_with(filename, basename)) self = xdl_find_from_auxv(AT_PHDR, pathname); if (NULL != self) return self; uintptr_t pkg[2 ] = {(uintptr_t )&self, (uintptr_t )filename}; xdl_iterate_phdr(xdl_find_iterate_cb, pkg, XDL_DEFAULT); return self; } static int xdl_find_iterate_cb (struct dl_phdr_info *info, size_t size, void *arg) { (void )size; uintptr_t *pkg = (uintptr_t *)arg; xdl_t **self = (xdl_t **)*pkg++; const char *filename = (const char *)*pkg; if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0 ; if ('[' == filename[0 ]) { if (0 != strcmp (info->dlpi_name, filename)) return 0 ; } else if ('/' == filename[0 ]) { if ('/' == info->dlpi_name[0 ]) { if (0 != strcmp (info->dlpi_name, filename)) return 0 ; } else { if (!xdl_util_ends_with(filename, info->dlpi_name)) return 0 ; } } else { if ('/' == info->dlpi_name[0 ]) { if (!xdl_util_ends_with(info->dlpi_name, filename)) return 0 ; } else { if (0 != strcmp (info->dlpi_name, filename)) return 0 ; } } if (NULL == ((*self) = calloc (1 , sizeof (xdl_t )))) return 1 ; if (NULL == ((*self)->pathname = strdup((const char *)info->dlpi_name))) { free (*self); *self = NULL ; return 1 ; } (*self)->load_bias = info->dlpi_addr; (*self)->dlpi_phdr = info->dlpi_phdr; (*self)->dlpi_phnum = info->dlpi_phnum; (*self)->dynsym_try_load = false ; (*self)->symtab_try_load = false ; return 1 ; }
通过sym寻找func addr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void *xdl_sym (void *handle, const char *symbol, size_t *symbol_size) { if (NULL == handle || NULL == symbol) return NULL ; if (NULL != symbol_size) *symbol_size = 0 ; xdl_t *self = (xdl_t *)handle; if (!self->dynsym_try_load) { self->dynsym_try_load = true ; if (0 != xdl_dynsym_load(self)) return NULL ; } if (NULL == self->dynsym) return NULL ; ElfW(Sym) *sym = NULL ; if (self->gnu_hash.buckets_cnt > 0 ) { sym = xdl_dynsym_find_symbol_use_gnu_hash(self, symbol); } if (NULL == sym && self->sysv_hash.buckets_cnt > 0 ) { sym = xdl_dynsym_find_symbol_use_sysv_hash(self, symbol); } if (NULL == sym || !XDL_DYNSYM_IS_EXPORT_SYM(sym->st_shndx)) return NULL ; if (NULL != symbol_size) *symbol_size = sym->st_size; return (void *)(self->load_bias + sym->st_value); }
pending hook
pending hook的原因是so没有加载到内存中,即在xdl_open搜索so以后没有找到符合的地址。
if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int sh_linker_get_dlinfo_by_sym_name (const char *lib_name, const char *sym_name, xdl_info_t *dlinfo, char *real_lib_name, size_t real_lib_name_sz) { bool crashed = false ; void *handle = NULL ; if (sh_util_get_api_level() >= __ANDROID_API_L__) { handle = xdl_open(lib_name, XDL_DEFAULT); } else { SH_SIG_TRY(SIGSEGV, SIGBUS) { handle = xdl_open(lib_name, XDL_DEFAULT); } SH_SIG_CATCH() { crashed = true ; } SH_SIG_EXIT } if (crashed) return SHADOWHOOK_ERRNO_HOOK_DLOPEN_CRASH; if (NULL == handle) return SHADOWHOOK_ERRNO_PENDING; return 0 ; }
继续分析
开启pending以后,首先会开启monitor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (0 == self->target_addr) { is_hook_sym_addr = false ; strlcpy(real_lib_name, self->lib_name, sizeof (real_lib_name)); strlcpy(real_sym_name, self->sym_name, sizeof (real_sym_name)); r = sh_linker_get_dlinfo_by_sym_name(self->lib_name, self->sym_name, &dlinfo, real_lib_name, sizeof (real_lib_name)); if (SHADOWHOOK_ERRNO_PENDING == r) { if (0 != (r = sh_task_start_monitor(true ))) goto end; r = SHADOWHOOK_ERRNO_PENDING; goto end; } if (0 != r) goto end; self->target_addr = (uintptr_t )dlinfo.dli_saddr; }
task monitor
过程如下:
hook dlopen方法,并在每次每次dlopen调用的时候回写/notify eventfd
创建eventfd,创建线程等待evenfd事件触发hook操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static int sh_task_start_monitor (bool start_thread) { static bool thread_started = false ; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_t thread; int r; if (0 != (r = sh_linker_hook_dlopen(sh_task_post_dlopen_callback, NULL ))) return r; if (!start_thread) return 0 ; if (thread_started) return thread_started_result; pthread_mutex_lock(&lock); if (thread_started) goto end; if (0 > (sh_task_eventfd = eventfd(0 , EFD_NONBLOCK | EFD_CLOEXEC))) goto end; if (0 != pthread_create(&thread, NULL , &sh_task_thread_func, NULL )) goto end; thread_started_result = 0 ; end: thread_started = true ; pthread_mutex_unlock(&lock); SH_LOG_INFO("task: start monitor %s, return: %d" , 0 == thread_started_result ? "OK" : "FAILED" , thread_started_result); return thread_started_result; }
hook dlopen.
hook后dlopen会被重定向到sh_linker_proxy_dlopen
1 2 3 4 5 6 7 8 9 10 11 12 static void *sh_linker_proxy_dlopen (const char *filename, int flag) { void *handle; if (SHADOWHOOK_IS_SHARED_MODE) handle = SHADOWHOOK_CALL_PREV(sh_linker_proxy_dlopen, sh_linker_proxy_dlopen_t , filename, flag); else handle = sh_linker_orig_dlopen(filename, flag); if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg); if (SHADOWHOOK_IS_SHARED_MODE) SHADOWHOOK_POP_STACK(); return handle; }
其中
if (NULL != handle) sh_linker_post_dlopen(sh_linker_post_dlopen_arg);
会触发eventfd的回写
1 2 3 4 5 6 7 8 static void sh_task_post_dlopen_callback (void *arg) { (void )arg; if (0 == thread_started_result && __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ) { uint64_t ev_val = 1 ; SH_UTIL_TEMP_FAILURE_RETRY(write(sh_task_eventfd, &ev_val, sizeof (ev_val))); } }
新线程
1 2 3 if (0 > (sh_task_eventfd = eventfd(0 , EFD_NONBLOCK | EFD_CLOEXEC))) goto end;if (0 != pthread_create(&thread, NULL , &sh_task_thread_func, NULL )) goto end;
会死循环poll事件,当有dlopen调用后会被唤醒。之后会进行如下操作
调用xdl_iterate_phdr遍历所有vmmaps
执行hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 __noreturn static void *sh_task_thread_func (void *arg) { (void )arg; pthread_t thread = pthread_self(); pthread_setname_np(thread, SH_TASK_THREAD_NAME); pthread_detach(thread); struct pollfd ev = {.fd = sh_task_eventfd, .events = POLLIN, .revents = 0 }; while (1 ) { int n = SH_UTIL_TEMP_FAILURE_RETRY(poll(&ev, 1 , -1 )); if (n < 0 ) { sleep(1 ); continue ; } else if (n > 0 ) { uint64_t ev_val; SH_UTIL_TEMP_FAILURE_RETRY(read(sh_task_eventfd, &ev_val, sizeof (ev_val))); if (sh_util_get_api_level() >= __ANDROID_API_L__) { xdl_iterate_phdr(sh_task_hook_pending, NULL , XDL_DEFAULT); } else { SH_SIG_TRY(SIGSEGV, SIGBUS) { xdl_iterate_phdr(sh_task_hook_pending, NULL , XDL_DEFAULT); } SH_SIG_CATCH() { SH_LOG_WARN("task: dliterate crashed" ); } SH_SIG_EXIT } } } }
对每一条vmmaps记录进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 static int sh_task_hook_pending (struct dl_phdr_info *info, size_t size, void *arg) { (void )size, (void )arg; pthread_rwlock_rdlock(&sh_tasks_lock); sh_task_t *task; TAILQ_FOREACH(task, &sh_tasks, link) { if (task->finished) continue ; if ('/' == info->dlpi_name[0 ] && NULL == strstr (info->dlpi_name, task->lib_name)) continue ; if ('/' != info->dlpi_name[0 ] && NULL == strstr (task->lib_name, info->dlpi_name)) continue ; xdl_info_t dlinfo; char real_lib_name[512 ]; int r = sh_linker_get_dlinfo_by_sym_name(task->lib_name, task->sym_name, &dlinfo, real_lib_name, sizeof (real_lib_name)); task->target_addr = (uintptr_t )dlinfo.dli_saddr; if (SHADOWHOOK_ERRNO_PENDING != r) { size_t backup_len = 0 ; if (0 == r) { r = sh_switch_hook(task->target_addr, task->new_addr, task->orig_addr, &backup_len, &dlinfo); if (0 != r) task->error = true ; } else { strlcpy(real_lib_name, task->lib_name, sizeof (real_lib_name)); task->error = true ; } sh_recorder_add_hook(r, false , task->target_addr, real_lib_name, task->sym_name, task->new_addr, backup_len, (uintptr_t )task, task->caller_addr); task->finished = true ; sh_task_do_callback(task, r); if (0 == __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1 , __ATOMIC_SEQ_CST)) break ; } } pthread_rwlock_unlock(&sh_tasks_lock); return __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ? 0 : 1 ; }
shadowhook_hook_sym_name_callback
sym_name_callback类似于之前sym_name的回调。
不一样的是多了callback回调,也就是多了两个传入的参数。其他没啥差别。
sym_name_callback与sym_name的差别是:hooked_arg和hooked的差别。
1 2 3 4 5 6 void *shadowhook_hook_sym_name_callback (const char *lib_name, const char *sym_name, void *new_addr, void **orig_addr, shadowhook_hooked_t hooked, void *hooked_arg) { const void *caller_addr = __builtin_return_address(0 ); return shadowhook_hook_sym_name_impl(lib_name, sym_name, new_addr, orig_addr, hooked, hooked_arg, (uintptr_t )caller_addr); }
多传入的参数会在初始化task的时候传入。存在sh_task_t
结构体中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 sh_task_t *sh_task_create_by_sym_name (const char *lib_name, const char *sym_name, uintptr_t new_addr, uintptr_t *orig_addr, shadowhook_hooked_t hooked, void *hooked_arg, uintptr_t caller_addr) { sh_task_t *self = malloc (sizeof (sh_task_t )); if (NULL == self) return NULL ; if (NULL == (self->lib_name = strdup(lib_name))) goto err; if (NULL == (self->sym_name = strdup(sym_name))) goto err; self->target_addr = 0 ; self->new_addr = new_addr; self->orig_addr = orig_addr; self->hooked = hooked; self->hooked_arg = hooked_arg; self->caller_addr = caller_addr; self->finished = false ; self->error = false ; self->ignore_symbol_check = false ; return self; err: if (NULL != self->lib_name) free (self->lib_name); if (NULL != self->sym_name) free (self->sym_name); free (self); return NULL ; }
在hook pending函数调用中,hook后通过调用sh_task_do_callback调用hooked回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static int sh_task_hook_pending (struct dl_phdr_info *info, size_t size, void *arg) { (void )size, (void )arg; sh_task_t *task; TAILQ_FOREACH(task, &sh_tasks, link) { task->target_addr = (uintptr_t )dlinfo.dli_saddr; if (SHADOWHOOK_ERRNO_PENDING != r) { size_t backup_len = 0 ; if (0 == r) { r = sh_switch_hook(task->target_addr, task->new_addr, task->orig_addr, &backup_len, &dlinfo); if (0 != r) task->error = true ; } else { strlcpy(real_lib_name, task->lib_name, sizeof (real_lib_name)); task->error = true ; } sh_recorder_add_hook(r, false , task->target_addr, real_lib_name, task->sym_name, task->new_addr, backup_len, (uintptr_t )task, task->caller_addr); task->finished = true ; sh_task_do_callback(task, r); if (0 == __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1 , __ATOMIC_SEQ_CST)) break ; } } return __atomic_load_n(&sh_tasks_unfinished_cnt, __ATOMIC_SEQ_CST) > 0 ? 0 : 1 ; } static void sh_task_do_callback (sh_task_t *self, int error_number) { if (NULL != self->hooked) self->hooked(error_number, self->lib_name, self->sym_name, (void *)self->target_addr, (void *)self->new_addr, self->orig_addr, self->hooked_arg); }
Unhook
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int shadowhook_unhook (void *stub) ;int shadowhook_unhook (void *stub) { const void *caller_addr = __builtin_return_address(0 ); SH_LOG_INFO("shadowhook: unhook(%p) ..." , stub); sh_errno_reset(); int r; if (NULL == stub) GOTO_ERR(SHADOWHOOK_ERRNO_INVALID_ARG); if (SHADOWHOOK_ERRNO_OK != shadowhook_init_errno) GOTO_ERR(shadowhook_init_errno); sh_task_t *task = (sh_task_t *)stub; r = sh_task_unhook(task, (uintptr_t )caller_addr); sh_task_destroy(task); if (0 != r) GOTO_ERR(r); SH_LOG_INFO("shadowhook: unhook(%p) OK" , stub); SH_ERRNO_SET_RET_ERRNUM(SHADOWHOOK_ERRNO_OK); err: SH_LOG_ERROR("shadowhook: unhook(%p) FAILED. %d - %s" , stub, r, sh_errno_to_errmsg(r)); SH_ERRNO_SET_RET_FAIL(r); }
实际的unhook逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 int sh_task_unhook (sh_task_t *self, uintptr_t caller_addr) { pthread_rwlock_wrlock(&sh_tasks_lock); TAILQ_REMOVE(&sh_tasks, self, link); if (!self->finished) __atomic_sub_fetch(&sh_tasks_unfinished_cnt, 1 , __ATOMIC_SEQ_CST); pthread_rwlock_unlock(&sh_tasks_lock); int r; if (self->error) { r = SHADOWHOOK_ERRNO_UNHOOK_ON_ERROR; goto end; } if (!self->finished) { r = SHADOWHOOK_ERRNO_UNHOOK_ON_UNFINISHED; goto end; } r = sh_switch_unhook(self->target_addr, self->new_addr); end: sh_recorder_add_unhook(r, (uintptr_t )self, caller_addr); return r; } int sh_switch_unhook (uintptr_t target_addr, uintptr_t new_addr) { int r; if (SHADOWHOOK_IS_UNIQUE_MODE) { r = sh_switch_unhook_unique(target_addr); if (0 == r) SH_LOG_INFO("switch: unhook in UNIQUE mode OK: target_addr %" PRIxPTR, target_addr); } else { r = sh_switch_unhook_shared(target_addr, new_addr); if (0 == r) SH_LOG_INFO("switch: unhook in SHARED mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR, target_addr, new_addr); } return r; }
unique mode & shared mode有一定的差别,但是无非就是如下几步
删除数据结构
将inline hook改变的函数地址恢复原状
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 static int sh_switch_unhook_unique (uintptr_t target_addr) { int r; sh_switch_t *useless = NULL ; pthread_rwlock_wrlock(&sh_switches_lock); sh_switch_t key = {.target_addr = target_addr}; sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); if (NULL == self) { r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; goto end; } r = sh_inst_unhook(&self->inst, target_addr); RB_REMOVE(sh_switch_tree, &sh_switches, self); useless = self; end: pthread_rwlock_unlock(&sh_switches_lock); if (NULL != useless) sh_switch_destroy(useless, false ); return r; } static int sh_switch_unhook_shared (uintptr_t target_addr, uintptr_t new_addr) { int r; sh_switch_t *useless = NULL ; pthread_rwlock_wrlock(&sh_switches_lock); sh_switch_t key = {.target_addr = target_addr}; sh_switch_t *self = RB_FIND(sh_switch_tree, &sh_switches, &key); if (NULL == self) { r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; goto end; } bool have_enabled_proxy; if (0 != sh_hub_del_proxy(self->hub, new_addr, &have_enabled_proxy)) { r = SHADOWHOOK_ERRNO_UNHOOK_NOTFOUND; goto end; } r = 0 ; if (!have_enabled_proxy) { r = sh_inst_unhook(&self->inst, target_addr); uintptr_t *safe_orig_addr_addr = sh_safe_get_orig_addr_addr(target_addr); if (NULL != safe_orig_addr_addr) __atomic_store_n(safe_orig_addr_addr, 0 , __ATOMIC_SEQ_CST); RB_REMOVE(sh_switch_tree, &sh_switches, self); useless = self; } end: pthread_rwlock_unlock(&sh_switches_lock); if (NULL != useless) sh_switch_destroy(useless, true ); return r; }
宏
即宏定义
SHADOWHOOK_CALL_PREV
宏定义
(实际是方法的调用)
1 2 3 4 5 6 7 #ifdef __cplusplus #define SHADOWHOOK_CALL_PREV(func, ...) \ ((decltype(&(func)))shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__) #else #define SHADOWHOOK_CALL_PREV(func, func_sig, ...) \ ((func_sig)shadowhook_get_prev_func((void *)(func)))(__VA_ARGS__) #endif
函数原型
1 void *shadowhook_get_prev_func (void *func) ;
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void *shadowhook_get_prev_func (void *func) { if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort (); return sh_hub_get_prev_func(func); } void *sh_hub_get_prev_func (void *func) { sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); if (0 == stack ->frames_cnt) sh_safe_abort(); sh_hub_frame_t *frame = &stack ->frames[stack ->frames_cnt - 1 ]; bool found = false ; sh_hub_proxy_t *proxy; SLIST_FOREACH(proxy, &(frame->proxies), link) { if (!found) { if (proxy->func == func) found = true ; } else { if (proxy->enabled) break ; } } if (NULL != proxy) { SH_LOG_DEBUG("hub: get_prev_func() return next enabled proxy %p" , proxy->func); return proxy->func; } SH_LOG_DEBUG("hub: get_prev_func() return orig_addr %p" , (void *)frame->orig_addr); return (void *)frame->orig_addr; }
SHADOWHOOK_POP_STACK
宏定义。
SHADOWHOOK_IS_SHARED_MODE说明了当前宏定义只适用于,shared mode
1 2 3 4 #define SHADOWHOOK_POP_STACK() \ do { \ if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(__builtin_return_address(0)); \ } while (0)
宏定义最后调用的是如下方法
1 2 3 4 void shadowhook_pop_stack (void *return_address) { if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort (); sh_hub_pop_stack(return_address); }
实现原理很轻松,其实就是把thread local中的调用栈。
减去一层,前提是frame->return_address == return_address。
即当前frame的最后一个函数调用完成了。
1 2 3 4 5 6 7 8 9 10 11 void sh_hub_pop_stack (void *return_address) { sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); if (0 == stack ->frames_cnt) return ; sh_hub_frame_t *frame = &stack ->frames[stack ->frames_cnt - 1 ]; if (frame->return_address == return_address) { stack ->frames_cnt--; SH_LOG_DEBUG("hub: frames_cnt-- = %zu" , stack ->frames_cnt); } }
SHADOWHOOK_STACK_SCOPE
pop stack是用于C++代理函数的一个方法,功能与SHADOWHOOK_POP_STACK完全一致。
唯一的区别就是——只能用于C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #ifdef __cplusplus class ShadowhookStackScope { public: ShadowhookStackScope(void *return_address) : return_address_(return_address) {} ~ShadowhookStackScope() { if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack(return_address_); } private: void *return_address_; }; #define SHADOWHOOK_STACK_SCOPE() ShadowhookStackScope shadowhook_stack_scope_obj(__builtin_return_address(0)) #endif
其实现原理不难,就是利用了C++的析构函数。
宏定义创建了一个栈变量,在函数结束的时候栈变量被摧毁。
自动调用析构函数,析构函数调用了pop_stack减去了,shadow hook自己维护的栈帧。
1 2 3 4 5 ShadowhookStackScope shadowhook_stack_scope_obj (__builtin_return_address(0 )) ;~ShadowhookStackScope () { if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_pop_stack (return_address_); }
SHADOWHOOK_ALLOW_REENTRANT
用于运行proxy 函数的重入
1 2 3 4 5 #define SHADOWHOOK_ALLOW_REENTRANT() \ do { \ if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_allow_reentrant(__builtin_return_address(0)); \ } while (0)
具体逻辑。
只是修改了当前的frame的一个参数。
frame->flags |= SH_HUB_FRAME_FLAG_ALLOW_REENTRANT
1 2 3 4 5 6 7 8 9 10 11 12 void shadowhook_allow_reentrant (void *return_address) { if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort (); sh_hub_allow_reentrant(return_address); } void sh_hub_allow_reentrant (void *return_address) { sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address); if (NULL != frame) { frame->flags |= SH_HUB_FRAME_FLAG_ALLOW_REENTRANT; SH_LOG_DEBUG("hub: allow reentrant frame %p" , return_address); } }
这个参数会在sh_hub_push_stack
时检查递归的过程中使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static void *sh_hub_push_stack (sh_hub_t *self, void *return_address) { bool recursive = false ; for (size_t i = stack ->frames_cnt; i > 0 ; i--) { sh_hub_frame_t *frame = &stack ->frames[i - 1 ]; if (0 == (frame->flags & SH_HUB_FRAME_FLAG_ALLOW_REENTRANT) && (frame->orig_addr == self->orig_addr)) { recursive = true ; break ; } } }
SHADOWHOOK_DISALLOW_REENTRANT
宏定义
1 2 3 4 #define SHADOWHOOK_DISALLOW_REENTRANT() \ do { \ if (SHADOWHOOK_IS_SHARED_MODE) shadowhook_disallow_reentrant(__builtin_return_address(0)); \ } while (0)
同之前的allow,只是设置了相反的参数。
1 2 3 4 5 6 7 8 9 10 11 12 void shadowhook_disallow_reentrant (void *return_address) { if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort (); sh_hub_disallow_reentrant(return_address); } void sh_hub_disallow_reentrant (void *return_address) { sh_hub_frame_t *frame = sh_hub_get_current_frame(return_address); if (NULL != frame) { frame->flags &= ~SH_HUB_FRAME_FLAG_ALLOW_REENTRANT; SH_LOG_DEBUG("hub: disallow reentrant frame %p" , return_address); } }
SHADOWHOOK_RETURN_ADDRESS
宏定义
依旧是对shared mode做的处理。
1 2 3 #define SHADOWHOOK_RETURN_ADDRESS() \ ((void *)(SHADOWHOOK_IS_SHARED_MODE ? shadowhook_get_return_address() : __builtin_return_address(0)))
具体的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 void *shadowhook_get_return_address (void ) { if (__predict_false(SHADOWHOOK_IS_UNIQUE_MODE)) abort (); return sh_hub_get_return_address(); } void *sh_hub_get_return_address (void ) { sh_hub_stack_t *stack = (sh_hub_stack_t *)sh_safe_pthread_getspecific(sh_hub_stack_tls_key); if (0 == stack ->frames_cnt) sh_safe_abort(); sh_hub_frame_t *frame = &stack ->frames[stack ->frames_cnt - 1 ]; return frame->return_address; }