diff --git a/shadowhook/src/main/cpp/arch/arm/sh_inst.c b/shadowhook/src/main/cpp/arch/arm/sh_inst.c index 27a8999..a044d64 100644 --- a/shadowhook/src/main/cpp/arch/arm/sh_inst.c +++ b/shadowhook/src/main/cpp/arch/arm/sh_inst.c @@ -268,7 +268,7 @@ static int sh_inst_hook_thumb_with_exit(sh_inst_t *self, uintptr_t target_addr, // alloc an exit for absolute jump sh_t32_absolute_jump((uint16_t *)self->exit, true, new_addr); if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit), - sizeof(self->exit), SH_INST_T32_B_RANGE_LOW, SH_INST_T32_B_RANGE_HIGH))) + SH_INST_T32_B_RANGE_LOW, SH_INST_T32_B_RANGE_HIGH))) return r; // rewrite @@ -299,7 +299,7 @@ static int sh_inst_hook_thumb_with_exit(sh_inst_t *self, uintptr_t target_addr, return 0; err: - sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit)); self->exit_addr = 0; // this is a flag for with-exit or without-exit return r; } @@ -395,7 +395,7 @@ static int sh_inst_hook_arm_with_exit(sh_inst_t *self, uintptr_t target_addr, xd // alloc an exit for absolute jump sh_a32_absolute_jump(self->exit, new_addr); if (0 != (r = sh_exit_alloc(&self->exit_addr, &self->exit_type, pc, dlinfo, (uint8_t *)(self->exit), - sizeof(self->exit), SH_INST_A32_B_RANGE_LOW, SH_INST_A32_B_RANGE_HIGH))) + SH_INST_A32_B_RANGE_LOW, SH_INST_A32_B_RANGE_HIGH))) return r; // rewrite @@ -424,7 +424,7 @@ static int sh_inst_hook_arm_with_exit(sh_inst_t *self, uintptr_t target_addr, xd return 0; err: - sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit)); self->exit_addr = 0; // this is a flag for with-exit or without-exit return r; } @@ -511,9 +511,7 @@ int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) { // free memory space for exit if (0 != self->exit_addr) - if (0 != - (r = sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)))) - return r; + if (0 != (r = sh_exit_free(self->exit_addr, self->exit_type, (uint8_t *)(self->exit)))) return r; // free memory space for enter sh_enter_free(self->enter_addr); diff --git a/shadowhook/src/main/cpp/arch/arm64/sh_inst.c b/shadowhook/src/main/cpp/arch/arm64/sh_inst.c index 372b815..5b40f68 100644 --- a/shadowhook/src/main/cpp/arch/arm64/sh_inst.c +++ b/shadowhook/src/main/cpp/arch/arm64/sh_inst.c @@ -88,9 +88,8 @@ static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_in // alloc an exit for absolute jump 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))) + if (0 != (r = sh_exit_alloc(&self->exit_addr, (uint16_t *)&self->exit_type, pc, dlinfo, + (uint8_t *)(self->exit), SH_INST_A64_B_RANGE_LOW, SH_INST_A64_B_RANGE_HIGH))) return r; // rewrite @@ -119,7 +118,7 @@ static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_in return 0; err: - sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), sizeof(self->exit)); + sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit)); self->exit_addr = 0; // this is a flag for with-exit or without-exit return r; } @@ -191,8 +190,7 @@ int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr) { // free memory space for exit if (0 != self->exit_addr) - if (0 != (r = sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit), - sizeof(self->exit)))) + if (0 != (r = sh_exit_free(self->exit_addr, (uint16_t)self->exit_type, (uint8_t *)(self->exit)))) return r; // free memory space for enter diff --git a/shadowhook/src/main/cpp/common/sh_trampo.c b/shadowhook/src/main/cpp/common/sh_trampo.c index a5673b8..98c9a19 100644 --- a/shadowhook/src/main/cpp/common/sh_trampo.c +++ b/shadowhook/src/main/cpp/common/sh_trampo.c @@ -44,50 +44,50 @@ void sh_trampo_init_mgr(sh_trampo_mgr_t *mgr, const char *page_name, size_t tram mgr->delay_sec = delay_sec; } -uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t range_low, uintptr_t range_high) { +uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr) { + return sh_trampo_alloc_near(mgr, 0, 0, 0); +} + +uintptr_t sh_trampo_alloc_near(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t range_low, + uintptr_t range_high) { + uint32_t now = (uint32_t)sh_util_get_stable_timestamp(); + size_t trampo_page_size = sh_util_get_page_size(); + size_t trampo_count = trampo_page_size / mgr->trampo_size; uintptr_t trampo = 0; - uintptr_t new_ptr; + uintptr_t new_ptr = (uintptr_t)MAP_FAILED; uintptr_t new_ptr_prctl = (uintptr_t)MAP_FAILED; - size_t trampo_page_size = sh_util_get_page_size(); - size_t count = trampo_page_size / mgr->trampo_size; if (range_low > hint) range_low = hint; if (range_high > UINTPTR_MAX - hint) range_high = UINTPTR_MAX - hint; - struct timeval now; - if (mgr->delay_sec > 0) gettimeofday(&now, NULL); - pthread_mutex_lock(&mgr->pages_lock); // try to find an unused trampo sh_trampo_page_t *page; SLIST_FOREACH(page, &mgr->pages, link) { - // check hit range - uintptr_t page_trampo_start = page->ptr; - uintptr_t page_trampo_end = page->ptr + trampo_page_size - mgr->trampo_size; - if (hint > 0 && ((page_trampo_end < hint - range_low) || (hint + range_high < page_trampo_start))) - continue; - - for (uintptr_t i = 0; i < count; i++) { - size_t flags_idx = i / 32; - uint32_t mask = (uint32_t)1 << (i % 32); - if (0 == (page->flags[flags_idx] & mask)) // check flag - { - // check timestamp - if (mgr->delay_sec > 0 && - (now.tv_sec <= page->timestamps[i] || now.tv_sec - page->timestamps[i] <= mgr->delay_sec)) - continue; - - // check hit range - uintptr_t cur = page->ptr + (mgr->trampo_size * i); - if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue; - - // OK - page->flags[flags_idx] |= mask; - trampo = cur; - memset((void *)trampo, 0, mgr->trampo_size); - goto end; - } + // check page's hit range: [page_start, page_end) + uintptr_t page_start = page->ptr; + uintptr_t page_end = page->ptr + trampo_count * mgr->trampo_size; + if (hint > 0 && ((page_end <= hint - range_low) || (hint + range_high < page_start))) continue; + + for (uintptr_t i = 0; i < trampo_count; i++) { + // check if used + uint32_t used = page->flags[i] >> 31; + if (used) continue; + + // check timestamp + uint32_t ts = page->flags[i] & 0x7FFFFFFF; + if (now <= ts || now - ts <= (uint32_t)mgr->delay_sec) continue; + + // check current trampo's hit range + uintptr_t cur = page->ptr + (mgr->trampo_size * i); + if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue; + + // OK + page->flags[i] |= 0x80000000; + trampo = cur; + memset((void *)trampo, 0, mgr->trampo_size); + goto end; } } @@ -97,35 +97,27 @@ uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t range_ if ((uintptr_t)MAP_FAILED == new_ptr) goto err; new_ptr_prctl = new_ptr; - // check hit range - if (hint > 0 && - ((hint - range_low >= new_ptr + trampo_page_size - mgr->trampo_size) || (hint + range_high < new_ptr))) - goto err; + // check page's hit range: [page_start, page_end) + uintptr_t page_start = new_ptr; + uintptr_t page_end = new_ptr + trampo_count * mgr->trampo_size; + if (hint > 0 && ((page_end <= hint - range_low) || (hint + range_high < page_start))) goto err; // create a new trampo-page info - if (NULL == (page = calloc(1, sizeof(sh_trampo_page_t)))) goto err; + if (NULL == (page = calloc(1, sizeof(sh_trampo_page_t) + trampo_count * sizeof(uint32_t)))) goto err; memset((void *)new_ptr, 0, trampo_page_size); page->ptr = new_ptr; new_ptr = (uintptr_t)MAP_FAILED; - if (NULL == (page->flags = calloc(1, SH_UTIL_ALIGN_END(count, 32) / 8))) goto err; - page->timestamps = NULL; - if (mgr->delay_sec > 0) { - if (NULL == (page->timestamps = calloc(1, count * sizeof(time_t)))) goto err; - } SLIST_INSERT_HEAD(&mgr->pages, page, link); // alloc trampo from the new memory page - for (uintptr_t i = 0; i < count; i++) { - size_t flags_idx = i / 32; - uint32_t mask = (uint32_t)1 << (i % 32); - - // check hit range - uintptr_t find = page->ptr + (mgr->trampo_size * i); - if (hint > 0 && ((find < hint - range_low) || (hint + range_high < find))) continue; + for (uintptr_t i = 0; i < trampo_count; i++) { + // check current trampo's hit range + uintptr_t cur = page->ptr + (mgr->trampo_size * i); + if (hint > 0 && ((cur < hint - range_low) || (hint + range_high < cur))) continue; // OK - page->flags[flags_idx] |= mask; - trampo = find; + page->flags[i] |= 0x80000000; + trampo = cur; break; } if (0 == trampo) abort(); @@ -140,8 +132,6 @@ uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t range_ pthread_mutex_unlock(&mgr->pages_lock); if (NULL != page) { if (0 != page->ptr) munmap((void *)page->ptr, trampo_page_size); - if (NULL != page->flags) free(page->flags); - if (NULL != page->timestamps) free(page->timestamps); free(page); } if ((uintptr_t)MAP_FAILED != new_ptr) munmap((void *)new_ptr, trampo_page_size); @@ -149,20 +139,20 @@ uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t range_ } void sh_trampo_free(sh_trampo_mgr_t *mgr, uintptr_t trampo) { - struct timeval now; - if (mgr->delay_sec > 0) gettimeofday(&now, NULL); + time_t now = sh_util_get_stable_timestamp(); + size_t trampo_page_size = sh_util_get_page_size(); + size_t trampo_count = trampo_page_size / mgr->trampo_size; pthread_mutex_lock(&mgr->pages_lock); - size_t trampo_page_size = sh_util_get_page_size(); sh_trampo_page_t *page; SLIST_FOREACH(page, &mgr->pages, link) { - if (page->ptr <= trampo && trampo < page->ptr + trampo_page_size) { - uintptr_t i = (trampo - page->ptr) / mgr->trampo_size; - size_t flags_idx = i / 32; - uint32_t mask = (uint32_t)1 << (i % 32); - if (mgr->delay_sec > 0) page->timestamps[i] = now.tv_sec; - page->flags[flags_idx] &= ~mask; + // check page's hit range: [page_start, page_end) + uintptr_t page_start = page->ptr; + uintptr_t page_end = page->ptr + trampo_count * mgr->trampo_size; + if (page_start <= trampo && trampo < page_end) { + uintptr_t i = (trampo - page_start) / mgr->trampo_size; + page->flags[i] = now & 0x7FFFFFFF; break; } } diff --git a/shadowhook/src/main/cpp/common/sh_trampo.h b/shadowhook/src/main/cpp/common/sh_trampo.h index 6b05cf1..bb4fd52 100644 --- a/shadowhook/src/main/cpp/common/sh_trampo.h +++ b/shadowhook/src/main/cpp/common/sh_trampo.h @@ -30,9 +30,8 @@ typedef struct sh_trampo_page { uintptr_t ptr; - uint32_t *flags; - time_t *timestamps; SLIST_ENTRY(sh_trampo_page, ) link; + uint32_t flags[]; // flags for each trampo: 1 bit for used/unused, 31 bits for timestamp } sh_trampo_page_t; typedef SLIST_HEAD(sh_trampo_page_list, sh_trampo_page, ) sh_trampo_page_list_t; @@ -41,10 +40,11 @@ typedef struct sh_trampo_mgr { pthread_mutex_t pages_lock; const char *page_name; size_t trampo_size; - time_t delay_sec; + time_t delay_sec; // must be greater than 0 } sh_trampo_mgr_t; void sh_trampo_init_mgr(sh_trampo_mgr_t *mgr, const char *page_name, size_t trampo_size, time_t delay_sec); - -uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t low_offset, uintptr_t high_offset); +uintptr_t sh_trampo_alloc(sh_trampo_mgr_t *mgr); +uintptr_t sh_trampo_alloc_near(sh_trampo_mgr_t *mgr, uintptr_t hint, uintptr_t low_offset, + uintptr_t high_offset); void sh_trampo_free(sh_trampo_mgr_t *mgr, uintptr_t trampo); diff --git a/shadowhook/src/main/cpp/common/sh_util.c b/shadowhook/src/main/cpp/common/sh_util.c index 2305bf0..6436278 100644 --- a/shadowhook/src/main/cpp/common/sh_util.c +++ b/shadowhook/src/main/cpp/common/sh_util.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -38,9 +39,14 @@ #include "xdl.h" static size_t sh_util_page_size = 0; +static time_t sh_util_system_uptime = 0; __attribute__((constructor)) static void sh_util_ctor(void) { sh_util_page_size = (size_t)getpagesize(); + + struct sysinfo info; + sysinfo(&info); + sh_util_system_uptime = info.uptime; } size_t sh_util_get_page_size(void) { @@ -565,12 +571,13 @@ size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, .. return buffer_len; } -bool sh_util_is_in_elf_pt_load(xdl_info_t *dlinfo, uintptr_t addr) { - if (addr < (uintptr_t)dlinfo->dli_fbase) return false; +bool sh_util_is_in_elf_pt_load(void *dli_fbase, const ElfW(Phdr) *dlpi_phdr, size_t dlpi_phnum, + uintptr_t addr) { + if (addr < (uintptr_t)dli_fbase) return false; - uintptr_t vaddr = addr - (uintptr_t)dlinfo->dli_fbase; - for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) { - const ElfW(Phdr) *phdr = &(dlinfo->dlpi_phdr[i]); + uintptr_t vaddr = addr - (uintptr_t)dli_fbase; + for (size_t i = 0; i < dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &dlpi_phdr[i]; if (PT_LOAD != phdr->p_type) continue; if (phdr->p_vaddr <= vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) return true; @@ -578,3 +585,27 @@ bool sh_util_is_in_elf_pt_load(xdl_info_t *dlinfo, uintptr_t addr) { return false; } + +time_t sh_util_get_process_uptime(void) { + struct sysinfo info; + sysinfo(&info); + if (__predict_false(info.uptime < sh_util_system_uptime)) return 0; + return info.uptime - sh_util_system_uptime; +} + +time_t sh_util_get_stable_timestamp(void) { + // The timestamp returned here is used to "control the delayed release of memory". + // + // First, the system time returned by gettimeofday() should not be used, because the system time can be + // modified at any time. + // Then, the system uptime or process uptime should not be used directly, but a constant time offset value + // should always be added, because uptime may be a very small value, which may be smaller than the "delay", + // and the initial value of the timestamp is generally zero, which will cause bugs. + // + // Another benefit of using the system uptime or process uptime is that we can be sure that uint32_t is + // large enough to hold this value. The system time usually needs to use uint64_t type. This can save memory + // in scenarios where many timestamps need to be stored. + // + // Currently we use the process uptime plus a constant offset. + return sh_util_get_process_uptime() + 24 * 60 * 60; +} diff --git a/shadowhook/src/main/cpp/common/sh_util.h b/shadowhook/src/main/cpp/common/sh_util.h index 8fe3397..afe5829 100644 --- a/shadowhook/src/main/cpp/common/sh_util.h +++ b/shadowhook/src/main/cpp/common/sh_util.h @@ -114,4 +114,8 @@ struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *resu size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args); size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...); -bool sh_util_is_in_elf_pt_load(xdl_info_t *dlinfo, uintptr_t addr); +bool sh_util_is_in_elf_pt_load(void *dli_fbase, const ElfW(Phdr) *dlpi_phdr, size_t dlpi_phnum, + uintptr_t addr); + +time_t sh_util_get_process_uptime(void); +time_t sh_util_get_stable_timestamp(void); diff --git a/shadowhook/src/main/cpp/sh_enter.c b/shadowhook/src/main/cpp/sh_enter.c index 30a3718..65e64ac 100644 --- a/shadowhook/src/main/cpp/sh_enter.c +++ b/shadowhook/src/main/cpp/sh_enter.c @@ -39,7 +39,7 @@ int sh_enter_init(void) { } uintptr_t sh_enter_alloc(void) { - return sh_trampo_alloc(&sh_enter_trampo_mgr, 0, 0, 0); + return sh_trampo_alloc(&sh_enter_trampo_mgr); } void sh_enter_free(uintptr_t enter) { diff --git a/shadowhook/src/main/cpp/sh_exit.c b/shadowhook/src/main/cpp/sh_exit.c index f38084b..0f44d87 100644 --- a/shadowhook/src/main/cpp/sh_exit.c +++ b/shadowhook/src/main/cpp/sh_exit.c @@ -27,10 +27,12 @@ #include #include #include +#include #include #include #include "sh_config.h" +#include "sh_linker.h" #include "sh_log.h" #include "sh_sig.h" #include "sh_trampo.h" @@ -38,47 +40,91 @@ #include "shadowhook.h" #include "xdl.h" +#define SH_EXIT_TYPE_OUT_LIBRARY 0 +#define SH_EXIT_TYPE_IN_LIBRARY 1 + #define SH_EXIT_PAGE_NAME "shadowhook-exit" +#define SH_EXIT_DELAY_SEC 3 #if defined(__arm__) #define SH_EXIT_SZ 8 #elif defined(__aarch64__) #define SH_EXIT_SZ 16 #endif -#define SH_EXIT_DELAY_SEC 2 -#define SH_EXIT_GAPS_CAP 16 -// Used to identify whether the ELF gap has been zero-filled, -// and also serves as a guard instruction. -#if defined(__arm__) -#define SH_EXIT_CLEAR_FLAG 0xE1200070BE00BE00 // BKPT #0(thumb) + BKPT #0(thumb) + BKPT #0(arm) -#elif defined(__aarch64__) -#define SH_EXIT_CLEAR_FLAG 0xD4200000D4200000 // BRK #0 + BRK #0 -#endif +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" -#define SH_EXIT_TYPE_OUT_LIBRARY 0 -#define SH_EXIT_TYPE_IN_LIBRARY 1 +// +// (1) out-library mode: +// +// We store the shellcode for exit in newly mmaped memory near the PC. +// +static sh_trampo_mgr_t sh_exit_trampo_mgr; -extern __attribute((weak)) unsigned long int getauxval(unsigned long int); +static void sh_exit_init_out_library(void) { + sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC); +} -static pthread_mutex_t sh_exit_lock = PTHREAD_MUTEX_INITIALIZER; -static sh_trampo_mgr_t sh_exit_trampo_mgr; +static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, + size_t range_low, size_t range_high) { + (void)dlinfo; + + uintptr_t addr = sh_trampo_alloc_near(&sh_exit_trampo_mgr, pc, range_low, range_high); + if (0 == addr) return -1; -static xdl_info_t sh_exit_app_process_info; -static xdl_info_t sh_exit_linker_info; -static xdl_info_t sh_exit_vdso_info; // vdso may not exist + memcpy((void *)addr, exit, SH_EXIT_SZ); + sh_util_clear_cache(addr, SH_EXIT_SZ); + *exit_addr = addr; + return 0; +} -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-statement-expression" +static void sh_exit_free_out_library(uintptr_t exit_addr) { + sh_trampo_free(&sh_exit_trampo_mgr, exit_addr); +} + +// +// (2) in-library mode: +// +// We store the shellcode for exit in the memory gaps in the current ELF. +// + +// ELF gap, range: [start, end) +typedef struct { + uintptr_t start; + uintptr_t end; + uint32_t *flags; // flags for each trampo: 1 bit for used/unused, 31 bits for timestamp +} sh_exit_elf_gap_t; + +// ELF info +typedef struct sh_exit_elf_info { + void *dli_fbase; + const ElfW(Phdr) *dlpi_phdr; + size_t dlpi_phnum; + sh_exit_elf_gap_t *gaps; + size_t gaps_num; + TAILQ_ENTRY(sh_exit_elf_info, ) link; +} sh_exit_elf_info_t; +typedef TAILQ_HEAD(sh_exit_elf_info_list, sh_exit_elf_info, ) sh_exit_elf_info_list_t; + +// ELF info list +static sh_exit_elf_info_list_t sh_exit_elf_infos = TAILQ_HEAD_INITIALIZER(sh_exit_elf_infos); +static pthread_mutex_t sh_exit_elf_infos_lock = PTHREAD_MUTEX_INITIALIZER; + +static uintptr_t sh_exit_exec_load_bias; +static uintptr_t sh_exit_linker_load_bias; +static uintptr_t sh_exit_vdso_load_bias; -static void sh_exit_init_elfinfo(unsigned long type, xdl_info_t *info) { - if (__predict_false(NULL == getauxval)) goto err; +extern __attribute((weak)) unsigned long int getauxval(unsigned long int); + +static uintptr_t sh_exit_get_load_bias_from_aux(unsigned long type) { + if (__predict_false(NULL == getauxval)) return 0; uintptr_t val = (uintptr_t)getauxval(type); - if (__predict_false(0 == val)) goto err; + if (__predict_false(0 == val)) return 0; // get base uintptr_t base = (AT_PHDR == type ? (val & (~0xffful)) : val); - if (__predict_false(0 != memcmp((void *)base, ELFMAG, SELFMAG))) goto err; + if (__predict_false(0 != memcmp((void *)base, ELFMAG, SELFMAG))) return 0; // ELF info ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; @@ -93,69 +139,52 @@ static void sh_exit_init_elfinfo(unsigned long type, xdl_info_t *info) { if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; } } - if (__predict_false(UINTPTR_MAX == min_vaddr || base < min_vaddr)) goto err; + if (__predict_false(UINTPTR_MAX == min_vaddr || base < min_vaddr)) return 0; uintptr_t load_bias = base - min_vaddr; - info->dli_fbase = (void *)load_bias; - info->dlpi_phdr = dlpi_phdr; - info->dlpi_phnum = dlpi_phnum; - return; - -err: - info->dli_fbase = 0; - info->dlpi_phdr = NULL; - info->dlpi_phnum = 0; + return load_bias; } -void sh_exit_init(void) { - // init for out-library mode - sh_trampo_init_mgr(&sh_exit_trampo_mgr, SH_EXIT_PAGE_NAME, SH_EXIT_SZ, SH_EXIT_DELAY_SEC); - - // init for in-library mode - 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); +static void sh_exit_init_in_library(void) { + sh_exit_exec_load_bias = sh_exit_get_load_bias_from_aux(AT_PHDR); + sh_exit_linker_load_bias = sh_exit_get_load_bias_from_aux(AT_BASE); + sh_exit_vdso_load_bias = sh_exit_get_load_bias_from_aux(AT_SYSINFO_EHDR); } -// out-library mode: -// -// We store the shellcode for exit in mmaped memory near the PC. -// - -static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, - size_t exit_len, size_t range_low, size_t range_high) { - (void)dlinfo; - - uintptr_t addr = sh_trampo_alloc(&sh_exit_trampo_mgr, pc, range_low, range_high); - if (0 == addr) return -1; - - memcpy((void *)addr, exit, exit_len); - sh_util_clear_cache(addr, exit_len); - *exit_addr = addr; - return 0; +static bool sh_exit_is_elf_loaded_by_kernel(uintptr_t load_bias) { + if (0 != sh_exit_exec_load_bias && sh_exit_exec_load_bias == load_bias) return true; + if (0 != sh_exit_linker_load_bias && sh_exit_linker_load_bias == load_bias) return true; + if (0 != sh_exit_vdso_load_bias && sh_exit_vdso_load_bias == load_bias) return true; + return false; } -static void sh_exit_free_out_library(uintptr_t exit_addr) { - sh_trampo_free(&sh_exit_trampo_mgr, exit_addr); +static void sh_exit_destroy_elf_info(sh_exit_elf_info_t *elfinfo) { + for (size_t i = 0; i < elfinfo->gaps_num; i++) { + sh_exit_elf_gap_t *gap = &elfinfo->gaps[i]; + if (NULL != gap->flags) free(gap->flags); + } + if (NULL != elfinfo->gaps) free(elfinfo->gaps); + free(elfinfo); } -// in-library mode: -// -// We store the shellcode for exit in the memory gaps in the ELF. - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -typedef struct { - uintptr_t start; - uintptr_t end; - bool need_fill_zero; - bool readable; -} sh_exit_gap_t; -#pragma clang diagnostic pop +static sh_exit_elf_info_t *sh_exit_create_elf_info(xdl_info_t *dlinfo) { + sh_exit_elf_info_t *elfinfo = calloc(1, sizeof(sh_exit_elf_info_t)); + if (NULL == elfinfo) return NULL; + elfinfo->dli_fbase = dlinfo->dli_fbase; + elfinfo->dlpi_phdr = dlinfo->dlpi_phdr; + elfinfo->dlpi_phnum = dlinfo->dlpi_phnum; + elfinfo->gaps = NULL; + elfinfo->gaps_num = 0; + + size_t gaps_max = 0; + for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) + if (PT_LOAD == dlinfo->dlpi_phdr[i].p_type) gaps_max++; + if (gaps_max > 0) { + elfinfo->gaps = calloc(1, sizeof(sh_exit_elf_gap_t) * gaps_max); + if (NULL == elfinfo->gaps) goto err; + } -static size_t sh_exit_get_gaps(xdl_info_t *dlinfo, sh_exit_gap_t *gaps, size_t gaps_cap, - bool elf_loaded_by_kernel) { - size_t gaps_used = 0; + bool elf_loaded_by_kernel = sh_exit_is_elf_loaded_by_kernel((uintptr_t)dlinfo->dli_fbase); for (size_t i = 0; i < dlinfo->dlpi_phnum; i++) { // current LOAD segment @@ -181,144 +210,140 @@ static size_t sh_exit_get_gaps(xdl_info_t *dlinfo, sh_exit_gap_t *gaps, size_t g (NULL == next_phdr ? cur_page_end : sh_util_page_start((uintptr_t)dlinfo->dli_fbase + next_phdr->p_vaddr)); - sh_exit_gap_t gap = {0, 0, false, false}; + uintptr_t gap_start = 0, gap_end = 0; if (cur_phdr->p_flags & PF_X) { // From: last PF_X page's unused memory tail space. // To: next page start. - gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10); - gap.end = next_page_start; - gap.need_fill_zero = true; - gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start); + gap_start = SH_UTIL_ALIGN_END(cur_end, SH_EXIT_SZ); + gap_end = next_page_start; } else if (cur_page_end > cur_file_page_end) { // From: last .bss page(which must NOT be file backend)'s unused memory tail space. // To: next page start. - gap.start = SH_UTIL_ALIGN_END(cur_end, 0x10); - gap.end = next_page_start; - gap.need_fill_zero = false; - gap.readable = (cur_phdr->p_flags & PF_R && cur_page_end == next_page_start); + gap_start = SH_UTIL_ALIGN_END(cur_end, SH_EXIT_SZ); + gap_end = next_page_start; } else if (next_page_start > cur_page_end) { // Entire unused memory pages. - gap.start = cur_page_end; - gap.end = next_page_start; - gap.need_fill_zero = true; - gap.readable = false; + gap_start = cur_page_end; + gap_end = next_page_start; } - if ((gap.need_fill_zero && gap.end > gap.start + 0x10) || (!gap.need_fill_zero && gap.end > gap.start)) { + if (gap_start < gap_end) { SH_LOG_INFO("exit: gap, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR " - %" PRIxPTR - "), NFZ %d, READABLE %d", - gap.start, gap.end, (uintptr_t)dlinfo->dli_fbase, gap.start - (uintptr_t)dlinfo->dli_fbase, - gap.end - (uintptr_t)dlinfo->dli_fbase, gap.need_fill_zero ? 1 : 0, gap.readable ? 1 : 0); - gaps[gaps_used].start = gap.start; - gaps[gaps_used].end = gap.end; - gaps[gaps_used].need_fill_zero = gap.need_fill_zero; - gaps[gaps_used].readable = gap.readable; - gaps_used++; + ")", + gap_start, gap_end, (uintptr_t)dlinfo->dli_fbase, gap_start - (uintptr_t)dlinfo->dli_fbase, + gap_end - (uintptr_t)dlinfo->dli_fbase); + + sh_exit_elf_gap_t *gap = &elfinfo->gaps[elfinfo->gaps_num]; + elfinfo->gaps_num++; + gap->start = gap_start; + gap->end = gap_end; + size_t item_num = (gap->end - gap->start) / SH_EXIT_SZ; + gap->flags = calloc(1, sizeof(uint32_t) * item_num); + if (NULL == gap->flags) goto err; } - - if (gaps_used >= gaps_cap) break; } - return gaps_used; -} - -static bool sh_exit_is_elf_loaded_by_kernel(uintptr_t pc) { - if (NULL == sh_exit_app_process_info.dlpi_phdr) return true; - if (sh_util_is_in_elf_pt_load(&sh_exit_app_process_info, pc)) return true; + return elfinfo; - if (NULL == sh_exit_linker_info.dlpi_phdr) return true; - if (sh_util_is_in_elf_pt_load(&sh_exit_linker_info, pc)) return true; - - // vdso may not exist - if (NULL != sh_exit_vdso_info.dlpi_phdr) - if (sh_util_is_in_elf_pt_load(&sh_exit_vdso_info, pc)) return true; - - return false; +err: + sh_exit_destroy_elf_info(elfinfo); + return NULL; } -static bool sh_exit_is_zero(uintptr_t buf, size_t buf_len) { - for (uintptr_t i = buf; i < buf + buf_len; i += sizeof(uintptr_t)) - if (*((uintptr_t *)i) != 0) return false; - - return true; +static sh_exit_elf_info_t *sh_exit_find_elf_info_by_pc(uintptr_t pc) { + sh_exit_elf_info_t *elfinfo; + TAILQ_FOREACH(elfinfo, &sh_exit_elf_infos, link) { + if (sh_util_is_in_elf_pt_load(elfinfo->dli_fbase, elfinfo->dlpi_phdr, elfinfo->dlpi_phnum, pc)) + return elfinfo; + } + return NULL; } -static int sh_exit_fill_zero(uintptr_t start, uintptr_t end, bool readable, xdl_info_t *dlinfo) { - size_t size = end - start; - bool set_prot_rwx = false; - - if (!readable) { - if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; - set_prot_rwx = true; - } +static int sh_exit_alloc_in_elf_gap(uintptr_t *exit_addr, uintptr_t pc, sh_exit_elf_info_t *elfinfo, + sh_exit_elf_gap_t *gap, uint8_t *exit, size_t range_low, + size_t range_high, uint32_t now) { + // fix the start of the range according to the "range_low" + uintptr_t start = gap->start; + if (pc >= range_low) start = SH_UTIL_MAX(start, pc - range_low); + start = SH_UTIL_ALIGN_END(start, SH_EXIT_SZ); - if (*((uint64_t *)start) != SH_EXIT_CLEAR_FLAG) { - SH_LOG_INFO("exit: gap fill zero, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR - " - %" PRIxPTR "), READABLE %d", - start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase, - end - (uintptr_t)dlinfo->dli_fbase, readable ? 1 : 0); - if (!set_prot_rwx) - if (0 != sh_util_mprotect(start, size, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; - memset((void *)start, 0, size); - *((uint64_t *)(start)) = SH_EXIT_CLEAR_FLAG; - sh_util_clear_cache(start, size); - } + // fix the end of the range according to the "range_high" + uintptr_t end = gap->end; + if (range_high <= UINTPTR_MAX - pc - SH_EXIT_SZ) end = SH_UTIL_MIN(end, pc + range_high + SH_EXIT_SZ); + end = SH_UTIL_ALIGN_START(end, SH_EXIT_SZ); - return 0; -} + if (start >= end) return -1; -static int sh_exit_try_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, - size_t exit_len, size_t range_low, size_t range_high, uintptr_t start, - uintptr_t end) { - if (pc >= range_low) start = SH_UTIL_MAX(start, pc - range_low); - start = SH_UTIL_ALIGN_END(start, exit_len); + // calculate index range: [start_idx, end_idx) + size_t start_idx = (start - gap->start) / SH_EXIT_SZ; + size_t end_idx = (end - gap->start) / SH_EXIT_SZ; - if (range_high <= UINTPTR_MAX - pc) end = SH_UTIL_MIN(end - exit_len, pc + range_high); - end = SH_UTIL_ALIGN_START(end, exit_len); + SH_LOG_INFO("exit: fixed gap, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR " - %" PRIxPTR + "), idx %zu - %zu", + start, end, (uintptr_t)elfinfo->dli_fbase, start - (uintptr_t)elfinfo->dli_fbase, + end - (uintptr_t)elfinfo->dli_fbase, start_idx, end_idx); - if (end < start) return -1; - SH_LOG_INFO("exit: gap resize, %" PRIxPTR " - %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR - " - %" PRIxPTR ")", - start, end, (uintptr_t)dlinfo->dli_fbase, start - (uintptr_t)dlinfo->dli_fbase, - end - (uintptr_t)dlinfo->dli_fbase); + for (size_t i = start_idx; i < end_idx; i++) { + // check if used + uint32_t used = gap->flags[i] >> 31; + if (used) continue; - for (uintptr_t cur = start; cur <= end; cur += exit_len) { - // check if the current space has been used - if (!sh_exit_is_zero(cur, exit_len)) continue; + // check timestamp + uint32_t ts = gap->flags[i] & 0x7FFFFFFF; + if (now <= ts || now - ts <= SH_EXIT_DELAY_SEC) continue; // write shellcode to the current location - if (0 != sh_util_mprotect(cur, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; - memcpy((void *)cur, exit, exit_len); - sh_util_clear_cache(cur, exit_len); - *exit_addr = cur; + uintptr_t cur = gap->start + i * SH_EXIT_SZ; + if (0 != sh_util_mprotect(cur, SH_EXIT_SZ, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1; + memcpy((void *)cur, exit, SH_EXIT_SZ); + sh_util_clear_cache(cur, SH_EXIT_SZ); + *exit_addr = cur; // OK + + // mark the current item as used + gap->flags[i] |= 0x80000000; + SH_LOG_INFO("exit: in-library alloc, at %" PRIxPTR " (load_bias %" PRIxPTR ", %" PRIxPTR "), len %zu", - cur, (uintptr_t)dlinfo->dli_fbase, cur - (uintptr_t)dlinfo->dli_fbase, exit_len); + cur, (uintptr_t)elfinfo->dli_fbase, cur - (uintptr_t)elfinfo->dli_fbase, (size_t)SH_EXIT_SZ); return 0; } + return -1; } static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, - size_t exit_len, size_t range_low, size_t range_high) { + size_t range_low, size_t range_high) { int r = -1; *exit_addr = 0; - bool elf_loaded_by_kernel = sh_exit_is_elf_loaded_by_kernel(pc); + pthread_mutex_lock(&sh_exit_elf_infos_lock); - pthread_mutex_lock(&sh_exit_lock); + // find or create elfinfo + sh_exit_elf_info_t *elfinfo = sh_exit_find_elf_info_by_pc(pc); + if (NULL == elfinfo) { + // get dlinfo by pc + if (NULL == dlinfo) { + xdl_info_t dlinfo_obj; + dlinfo = &dlinfo_obj; + if (0 != (r = sh_linker_get_dlinfo_by_addr((void *)pc, dlinfo, NULL, 0, NULL, 0, true))) goto end; + } - SH_SIG_TRY(SIGSEGV, SIGBUS) { - sh_exit_gap_t gaps[SH_EXIT_GAPS_CAP]; - size_t gaps_used = sh_exit_get_gaps(dlinfo, gaps, SH_EXIT_GAPS_CAP, elf_loaded_by_kernel); - for (size_t i = 0; i < gaps_used; i++) { - // fill zero - if (gaps[i].need_fill_zero) { - if (0 != sh_exit_fill_zero(gaps[i].start, gaps[i].end, gaps[i].readable, dlinfo)) return -1; - } + // create elfinfo by dlinfo + if (NULL == (elfinfo = sh_exit_create_elf_info(dlinfo))) { + r = SHADOWHOOK_ERRNO_OOM; + goto end; + } + + // save new elfinfo + TAILQ_INSERT_TAIL(&sh_exit_elf_infos, elfinfo, link); + } - if (0 == (r = sh_exit_try_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high, - gaps[i].start, gaps[i].end))) - break; + uint32_t now = (uint32_t)sh_util_get_stable_timestamp(); + SH_SIG_TRY(SIGSEGV, SIGBUS) { + // try to alloc space in each gaps of current ELF + for (size_t i = 0; i < elfinfo->gaps_num; i++) { + if (0 == (r = sh_exit_alloc_in_elf_gap(exit_addr, pc, elfinfo, &elfinfo->gaps[i], exit, range_low, + range_high, now))) + break; // OK } } SH_SIG_CATCH() { @@ -328,51 +353,65 @@ static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info } SH_SIG_EXIT - pthread_mutex_unlock(&sh_exit_lock); +end: + pthread_mutex_unlock(&sh_exit_elf_infos_lock); return r; } -static int sh_exit_free_in_library(uintptr_t exit_addr, uint8_t *exit, size_t exit_len) { - int r; - - pthread_mutex_lock(&sh_exit_lock); - +static int sh_exit_free_in_library(uintptr_t exit_addr, uint8_t *exit) { + // check if the content matches + int r = 0; SH_SIG_TRY(SIGSEGV, SIGBUS) { - if (0 != memcmp((void *)exit_addr, exit, exit_len)) { + if (0 != memcmp((void *)exit_addr, exit, SH_EXIT_SZ)) { r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_MISMATCH; - goto err; - } - if (0 != sh_util_mprotect((uintptr_t)exit_addr, exit_len, PROT_READ | PROT_WRITE | PROT_EXEC)) { - r = SHADOWHOOK_ERRNO_MPROT; - goto err; } - memset((void *)exit_addr, 0, exit_len); - sh_util_clear_cache((uintptr_t)exit_addr, exit_len); r = 0; - err:; } SH_SIG_CATCH() { r = SHADOWHOOK_ERRNO_UNHOOK_EXIT_CRASH; SH_LOG_WARN("exit: free crashed"); } SH_SIG_EXIT + if (0 != r) return r; + + pthread_mutex_lock(&sh_exit_elf_infos_lock); + sh_exit_elf_info_t *elfinfo; + TAILQ_FOREACH(elfinfo, &sh_exit_elf_infos, link) { + for (size_t i = 0; i < elfinfo->gaps_num; i++) { + sh_exit_elf_gap_t *gap = &elfinfo->gaps[i]; + if (gap->start <= exit_addr && exit_addr < gap->end) { + size_t j = (exit_addr - gap->start) / SH_EXIT_SZ; + uint32_t now = (uint32_t)sh_util_get_stable_timestamp(); + gap->flags[j] = now & 0x7FFFFFFF; + goto end; + } + } + } +end: + pthread_mutex_unlock(&sh_exit_elf_infos_lock); + return 0; +} - pthread_mutex_unlock(&sh_exit_lock); - return r; +// +// public APIs +// +void sh_exit_init(void) { + sh_exit_init_out_library(); + sh_exit_init_in_library(); } int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, - size_t exit_len, size_t range_low, size_t range_high) { + size_t range_low, size_t range_high) { int r; // (1) try out-library mode first. Because ELF gaps are a valuable non-renewable resource. *exit_type = SH_EXIT_TYPE_OUT_LIBRARY; - r = sh_exit_alloc_out_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high); + r = sh_exit_alloc_out_library(exit_addr, pc, dlinfo, exit, range_low, range_high); if (0 == r) goto ok; // (2) try in-library mode. *exit_type = SH_EXIT_TYPE_IN_LIBRARY; - r = sh_exit_alloc_in_library(exit_addr, pc, dlinfo, exit, exit_len, range_low, range_high); + r = sh_exit_alloc_in_library(exit_addr, pc, dlinfo, exit, range_low, range_high); if (0 == r) goto ok; return r; @@ -385,13 +424,13 @@ int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_i return 0; } -int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len) { +int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit) { if (SH_EXIT_TYPE_OUT_LIBRARY == exit_type) { - (void)exit, (void)exit_len; + (void)exit; sh_exit_free_out_library(exit_addr); return 0; } else - return sh_exit_free_in_library(exit_addr, exit, exit_len); + return sh_exit_free_in_library(exit_addr, exit); } void sh_exit_free_after_dlclose(uintptr_t exit_addr, uint16_t exit_type) { @@ -400,4 +439,19 @@ void sh_exit_free_after_dlclose(uintptr_t exit_addr, uint16_t exit_type) { } } +void sh_exit_free_after_dlclose_by_dlinfo(xdl_info_t *dlinfo) { + sh_exit_elf_info_t *elfinfo, *elfinfo_tmp; + + pthread_mutex_lock(&sh_exit_elf_infos_lock); + TAILQ_FOREACH_SAFE(elfinfo, &sh_exit_elf_infos, link, elfinfo_tmp) { + if (elfinfo->dli_fbase == dlinfo->dli_fbase) { + TAILQ_REMOVE(&sh_exit_elf_infos, elfinfo, link); + break; + } + } + pthread_mutex_unlock(&sh_exit_elf_infos_lock); + + if (NULL != elfinfo) sh_exit_destroy_elf_info(elfinfo); +} + #pragma clang diagnostic pop diff --git a/shadowhook/src/main/cpp/sh_exit.h b/shadowhook/src/main/cpp/sh_exit.h index 64df18e..c701f0b 100644 --- a/shadowhook/src/main/cpp/sh_exit.h +++ b/shadowhook/src/main/cpp/sh_exit.h @@ -30,6 +30,7 @@ void sh_exit_init(void); int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_info_t *dlinfo, uint8_t *exit, - size_t exit_len, size_t range_low, size_t range_high); -int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit, size_t exit_len); + size_t range_low, size_t range_high); +int sh_exit_free(uintptr_t exit_addr, uint16_t exit_type, uint8_t *exit); void sh_exit_free_after_dlclose(uintptr_t exit_addr, uint16_t exit_type); +void sh_exit_free_after_dlclose_by_dlinfo(xdl_info_t *dlinfo); diff --git a/shadowhook/src/main/cpp/sh_hub.c b/shadowhook/src/main/cpp/sh_hub.c index 3bbcd71..c03c00c 100644 --- a/shadowhook/src/main/cpp/sh_hub.c +++ b/shadowhook/src/main/cpp/sh_hub.c @@ -328,9 +328,10 @@ sh_hub_t *sh_hub_create(uintptr_t *trampo) { SLIST_INIT(&self->proxies); pthread_mutex_init(&self->proxies_lock, NULL); self->orig_addr = 0; + self->destroy_ts = 0; // alloc memory for trampoline - if (0 == (self->trampo = sh_trampo_alloc(&sh_hub_trampo_mgr, 0, 0, 0))) { + if (0 == (self->trampo = sh_trampo_alloc(&sh_hub_trampo_mgr))) { free(self); return NULL; } @@ -381,14 +382,13 @@ static void sh_hub_destroy_inner(sh_hub_t *self) { } void sh_hub_destroy(sh_hub_t *self, bool with_delay) { - struct timeval now; - gettimeofday(&now, NULL); + time_t now = sh_util_get_stable_timestamp(); if (!LIST_EMPTY(&sh_hub_delayed_destroy)) { pthread_mutex_lock(&sh_hub_delayed_destroy_lock); sh_hub_t *hub, *hub_tmp; LIST_FOREACH_SAFE(hub, &sh_hub_delayed_destroy, link, hub_tmp) { - if (now.tv_sec - hub->destroy_ts > SH_HUB_DELAY_SEC) { + if (now - hub->destroy_ts > SH_HUB_DELAY_SEC) { LIST_REMOVE(hub, link); sh_hub_destroy_inner(hub); } @@ -397,7 +397,7 @@ void sh_hub_destroy(sh_hub_t *self, bool with_delay) { } if (with_delay) { - self->destroy_ts = now.tv_sec; + self->destroy_ts = now; sh_trampo_free(&sh_hub_trampo_mgr, self->trampo); self->trampo = 0; diff --git a/shadowhook/src/main/cpp/sh_switch.c b/shadowhook/src/main/cpp/sh_switch.c index 89e22d4..3215875 100644 --- a/shadowhook/src/main/cpp/sh_switch.c +++ b/shadowhook/src/main/cpp/sh_switch.c @@ -33,6 +33,7 @@ #include "sh_config.h" #include "sh_errno.h" +#include "sh_exit.h" #include "sh_hub.h" #include "sh_inst.h" #include "sh_linker.h" @@ -357,7 +358,8 @@ void sh_switch_free_after_dlclose(xdl_info_t *dlinfo) { sh_switch_t *self, *tmp; RB_FOREACH_SAFE(self, sh_switch_tree, &sh_switches, tmp) { - if (sh_util_is_in_elf_pt_load(dlinfo, self->target_addr)) { + if (sh_util_is_in_elf_pt_load(dlinfo->dli_fbase, dlinfo->dlpi_phdr, dlinfo->dlpi_phnum, + self->target_addr)) { RB_REMOVE(sh_switch_tree, &sh_switches, self); sh_inst_free_after_dlclose(&self->inst, self->target_addr); SH_LOG_INFO("switch: free_after_dlclose OK. target_addr %" PRIxPTR, self->target_addr); @@ -366,4 +368,6 @@ void sh_switch_free_after_dlclose(xdl_info_t *dlinfo) { } pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end + + sh_exit_free_after_dlclose_by_dlinfo(dlinfo); } diff --git a/shadowhook/src/main/cpp/sh_task.c b/shadowhook/src/main/cpp/sh_task.c index 8aefb12..100b947 100644 --- a/shadowhook/src/main/cpp/sh_task.c +++ b/shadowhook/src/main/cpp/sh_task.c @@ -210,7 +210,7 @@ static void sh_task_dl_fini_post(struct dl_phdr_info *info, size_t size, void *d sh_task_t *task; TAILQ_FOREACH(task, &sh_tasks, link) { if (task->finished && task->lib_name != NULL && task->sym_name != NULL && 0 != task->target_addr && - sh_util_is_in_elf_pt_load(&dlinfo, task->target_addr)) { + sh_util_is_in_elf_pt_load(dlinfo.dli_fbase, dlinfo.dlpi_phdr, dlinfo.dlpi_phnum, task->target_addr)) { task->target_addr = 0; task->error = false; task->finished = false;