Skip to content

Commit

Permalink
Fix occasional crash when unhooking. Optimize memory layout of trampo…
Browse files Browse the repository at this point in the history
… module.
  • Loading branch information
caikelun committed Dec 9, 2024
1 parent 7a04676 commit e9f9f75
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 293 deletions.
12 changes: 5 additions & 7 deletions shadowhook/src/main/cpp/arch/arm/sh_inst.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 4 additions & 6 deletions shadowhook/src/main/cpp/arch/arm64/sh_inst.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
116 changes: 53 additions & 63 deletions shadowhook/src/main/cpp/common/sh_trampo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand All @@ -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();
Expand All @@ -140,29 +132,27 @@ 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);
return 0;
}

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;
}
}
Expand Down
10 changes: 5 additions & 5 deletions shadowhook/src/main/cpp/common/sh_trampo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
41 changes: 36 additions & 5 deletions shadowhook/src/main/cpp/common/sh_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>
#include <time.h>
#include <unistd.h>

Expand All @@ -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) {
Expand Down Expand Up @@ -565,16 +571,41 @@ 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;
}

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;
}
6 changes: 5 additions & 1 deletion shadowhook/src/main/cpp/common/sh_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion shadowhook/src/main/cpp/sh_enter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit e9f9f75

Please sign in to comment.