Skip to content

Commit

Permalink
Optimize the performance of shadowhook_hook_sym_addr function in arm64.
Browse files Browse the repository at this point in the history
  • Loading branch information
caikelun committed Dec 12, 2024
1 parent e9f9f75 commit c9ff65f
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 76 deletions.
9 changes: 7 additions & 2 deletions shadowhook/src/main/cpp/arch/arm/sh_inst.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "sh_config.h"
#include "sh_enter.h"
#include "sh_exit.h"
#include "sh_linker.h"
#include "sh_log.h"
#include "sh_sig.h"
#include "sh_t16.h"
Expand Down Expand Up @@ -462,11 +463,15 @@ static int sh_inst_hook_arm_without_exit(sh_inst_t *self, uintptr_t target_addr,
}

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) {
uintptr_t *orig_addr, uintptr_t *orig_addr2, bool ignore_symbol_check) {
int r;
if (NULL == dlinfo->dli_fbase) {
if (0 != (r = sh_linker_get_dlinfo_by_addr((void *)target_addr, dlinfo, ignore_symbol_check))) return r;
}

self->enter_addr = sh_enter_alloc();
if (0 == self->enter_addr) return SHADOWHOOK_ERRNO_HOOK_ENTER;

int r;
if (SH_UTIL_IS_THUMB(target_addr)) {
#ifdef SH_CONFIG_TRY_WITH_EXIT
if (0 == (r = sh_inst_hook_thumb_with_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
Expand Down
3 changes: 2 additions & 1 deletion shadowhook/src/main/cpp/arch/arm/sh_inst.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// Created by Kelun Cai ([email protected]) on 2021-04-11.

#pragma once
#include <stdbool.h>
#include <stdint.h>

#include "xdl.h"
Expand All @@ -37,7 +38,7 @@ typedef struct {
} sh_inst_t;

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);
uintptr_t *orig_addr, uintptr_t *orig_addr2, bool ignore_symbol_check);
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);

void sh_inst_free_after_dlclose(sh_inst_t *self, uintptr_t target_addr);
15 changes: 13 additions & 2 deletions shadowhook/src/main/cpp/arch/arm64/sh_inst.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "sh_config.h"
#include "sh_enter.h"
#include "sh_exit.h"
#include "sh_linker.h"
#include "sh_log.h"
#include "sh_sig.h"
#include "sh_util.h"
Expand Down Expand Up @@ -84,7 +85,8 @@ static int sh_inst_hook_with_exit(sh_inst_t *self, uintptr_t target_addr, xdl_in
uintptr_t pc = target_addr;
self->backup_len = 4;

if (dlinfo->dli_ssize < self->backup_len) return SHADOWHOOK_ERRNO_HOOK_SYMSZ;
// Any function should have at least one instruction,
// so the arm64 function symbol size must be greater than or equal to 4.

// alloc an exit for absolute jump
sh_a64_absolute_jump_with_br(self->exit, new_addr);
Expand Down Expand Up @@ -155,17 +157,26 @@ static int sh_inst_hook_without_exit(sh_inst_t *self, uintptr_t target_addr, xdl
}

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) {
uintptr_t *orig_addr, uintptr_t *orig_addr2, bool ignore_symbol_check) {
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 (NULL == dlinfo->dli_fbase) {
if (ignore_symbol_check) {
dlinfo->dli_ssize = 1024; // big enough
} else {
if (0 != (r = sh_linker_get_dlinfo_by_addr((void *)target_addr, dlinfo, false))) goto err;
}
}
if (0 == (r = sh_inst_hook_without_exit(self, target_addr, dlinfo, new_addr, orig_addr, orig_addr2)))
return r;

err:
// hook failed
if (NULL != orig_addr) *orig_addr = 0;
if (NULL != orig_addr2) *orig_addr2 = 0;
Expand Down
2 changes: 1 addition & 1 deletion shadowhook/src/main/cpp/arch/arm64/sh_inst.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ typedef struct {
} sh_inst_t;

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);
uintptr_t *orig_addr, uintptr_t *orig_addr2, bool ignore_symbol_check);
int sh_inst_unhook(sh_inst_t *self, uintptr_t target_addr);

void sh_inst_free_after_dlclose(sh_inst_t *self, uintptr_t target_addr);
16 changes: 7 additions & 9 deletions shadowhook/src/main/cpp/sh_exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,8 @@ 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 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;

static int sh_exit_alloc_out_library(uintptr_t *exit_addr, uintptr_t pc, uint8_t *exit, size_t range_low,
size_t range_high) {
uintptr_t addr = sh_trampo_alloc_near(&sh_exit_trampo_mgr, pc, range_low, range_high);
if (0 == addr) return -1;

Expand Down Expand Up @@ -321,10 +319,10 @@ static int sh_exit_alloc_in_library(uintptr_t *exit_addr, uintptr_t pc, xdl_info
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;
if (NULL == dlinfo->dli_fbase || NULL == dlinfo->dlpi_phdr) {
xdl_info_t dlinfo_tmp;
dlinfo = &dlinfo_tmp;
if (0 != (r = sh_linker_get_dlinfo_by_addr((void *)pc, dlinfo, true))) goto end;
}

// create elfinfo by dlinfo
Expand Down Expand Up @@ -406,7 +404,7 @@ int sh_exit_alloc(uintptr_t *exit_addr, uint16_t *exit_type, uintptr_t pc, xdl_i

// (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, range_low, range_high);
r = sh_exit_alloc_out_library(exit_addr, pc, exit, range_low, range_high);
if (0 == r) goto ok;

// (2) try in-library mode.
Expand Down
28 changes: 13 additions & 15 deletions shadowhook/src/main/cpp/sh_linker.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,11 @@ int sh_linker_register_dlopen_post_callback(sh_linker_dlopen_post_t post) {
}

// do hook
int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *) =
int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *, bool) =
SHADOWHOOK_IS_SHARED_MODE ? sh_switch_hook : sh_switch_hook_invisible;
size_t backup_len = 0;
int r = hook((uintptr_t)dlinfo.dli_saddr, (uintptr_t)sh_linker_proxy_dlopen,
(uintptr_t *)&sh_linker_orig_dlopen, &backup_len, &dlinfo);
(uintptr_t *)&sh_linker_orig_dlopen, &backup_len, &dlinfo, false);

// do record
sh_recorder_add_hook(r, true, (uintptr_t)dlinfo.dli_saddr, SH_LINKER_BASENAME, dlinfo.dli_sname,
Expand Down Expand Up @@ -492,7 +492,7 @@ static int sh_linker_hook_call_ctors_dtors(xdl_info_t *call_constructors_dlinfo,
int r_hook_dtors = INT_MAX;
size_t backup_len_ctors = 0;
size_t backup_len_dtors = 0;
int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *) =
int (*hook)(uintptr_t, uintptr_t, uintptr_t *, size_t *, xdl_info_t *, bool) =
SHADOWHOOK_IS_SHARED_MODE ? sh_switch_hook : sh_switch_hook_invisible;

#if !SH_LINKER_HOOK_WITH_DL_MUTEX
Expand All @@ -505,9 +505,9 @@ static int sh_linker_hook_call_ctors_dtors(xdl_info_t *call_constructors_dlinfo,

// hook soinfo::call_constructors()
SH_LOG_INFO("linker: hook soinfo::call_constructors");
r_hook_ctors =
hook((uintptr_t)call_constructors_dlinfo->dli_saddr, (uintptr_t)sh_linker_proxy_soinfo_call_ctors,
(uintptr_t *)&sh_linker_orig_soinfo_call_ctors, &backup_len_ctors, call_constructors_dlinfo);
r_hook_ctors = hook(
(uintptr_t)call_constructors_dlinfo->dli_saddr, (uintptr_t)sh_linker_proxy_soinfo_call_ctors,
(uintptr_t *)&sh_linker_orig_soinfo_call_ctors, &backup_len_ctors, call_constructors_dlinfo, false);
if (__predict_false(0 != r_hook_ctors)) {
#if SH_LINKER_HOOK_WITH_DL_MUTEX
pthread_mutex_unlock(g_dl_mutex);
Expand All @@ -519,7 +519,7 @@ static int sh_linker_hook_call_ctors_dtors(xdl_info_t *call_constructors_dlinfo,
SH_LOG_INFO("linker: hook soinfo::call_destructors");
r_hook_dtors =
hook((uintptr_t)call_destructors_dlinfo->dli_saddr, (uintptr_t)sh_linker_proxy_soinfo_call_dtors,
(uintptr_t *)&sh_linker_orig_soinfo_call_dtors, &backup_len_dtors, call_destructors_dlinfo);
(uintptr_t *)&sh_linker_orig_soinfo_call_dtors, &backup_len_dtors, call_destructors_dlinfo, false);
if (__predict_false(0 != r_hook_dtors)) {
#if SH_LINKER_HOOK_WITH_DL_MUTEX
pthread_mutex_unlock(g_dl_mutex);
Expand Down Expand Up @@ -718,8 +718,9 @@ int sh_linker_unregister_dl_fini_callback(shadowhook_dl_info_t pre, shadowhook_d
return 0;
}

int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
char *sym_name, size_t sym_name_sz, bool ignore_symbol_check) {
int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, bool ignore_symbol_check) {
memset(dlinfo, 0, sizeof(xdl_info_t));

// dladdr()
bool crashed = false;
void *dlcache = NULL;
Expand Down Expand Up @@ -777,18 +778,16 @@ int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name,
r = SHADOWHOOK_ERRNO_HOOK_SYMSZ;
goto end;
}

if (NULL != lib_name) strlcpy(lib_name, dlinfo->dli_fname, lib_name_sz);
if (NULL != sym_name) strlcpy(sym_name, dlinfo->dli_sname, sym_name_sz);
r = 0;

end:
xdl_addr_clean(&dlcache);
return r;
}

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) {
int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo) {
memset(dlinfo, 0, sizeof(xdl_info_t));

// open library
bool crashed = false;
void *handle = NULL;
Expand Down Expand Up @@ -839,6 +838,5 @@ int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name,
dlinfo->dli_sname = sym_name;
dlinfo->dli_saddr = addr;
dlinfo->dli_ssize = sym_size;
if (NULL != real_lib_name) strlcpy(real_lib_name, dlinfo->dli_fname, real_lib_name_sz);
return 0;
}
6 changes: 2 additions & 4 deletions shadowhook/src/main/cpp/sh_linker.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,5 @@ int sh_linker_register_dl_fini_callback(shadowhook_dl_info_t pre, shadowhook_dl_
int sh_linker_unregister_dl_fini_callback(shadowhook_dl_info_t pre, shadowhook_dl_info_t post, void *data);

// linker utils
int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, char *lib_name, size_t lib_name_sz,
char *sym_name, size_t sym_name_sz, bool ignore_symbol_check);
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);
int sh_linker_get_dlinfo_by_addr(void *addr, xdl_info_t *dlinfo, bool ignore_symbol_check);
int sh_linker_get_dlinfo_by_sym_name(const char *lib_name, const char *sym_name, xdl_info_t *dlinfo);
26 changes: 15 additions & 11 deletions shadowhook/src/main/cpp/sh_switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ static void sh_switch_dump_enter(sh_switch_t *self) {
}

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) {
size_t *backup_len, xdl_info_t *dlinfo, bool ignore_symbol_check) {
sh_switch_t *self = sh_switch_find(target_addr);
if (NULL != self) return SHADOWHOOK_ERRNO_HOOK_DUP;

Expand All @@ -148,7 +148,8 @@ static int sh_switch_hook_unique(uintptr_t target_addr, uintptr_t new_addr, uint
(NULL != safe_orig_addr_addr && 0 == __atomic_load_n(safe_orig_addr_addr, __ATOMIC_ACQUIRE))
? safe_orig_addr_addr
: NULL;
if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, orig_addr2))) {
if (0 != (r = sh_inst_hook(&self->inst, target_addr, dlinfo, new_addr, orig_addr, orig_addr2,
ignore_symbol_check))) {
RB_REMOVE(sh_switch_tree, &sh_switches, self);
useless = self;
goto end;
Expand All @@ -163,7 +164,7 @@ static int sh_switch_hook_unique(uintptr_t target_addr, uintptr_t new_addr, uint
}

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) {
size_t *backup_len, xdl_info_t *dlinfo, bool ignore_symbol_check) {
int r;

pthread_rwlock_rdlock(&sh_switches_lock); // SYNC(read) - start
Expand Down Expand Up @@ -201,8 +202,9 @@ static int sh_switch_hook_shared(uintptr_t target_addr, uintptr_t new_addr, uint
} else {
// do hook
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))) {
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, ignore_symbol_check))) {
RB_REMOVE(sh_switch_tree, &sh_switches, self);
useless = self;
goto end;
Expand Down Expand Up @@ -231,12 +233,12 @@ static int sh_switch_hook_shared(uintptr_t target_addr, uintptr_t new_addr, uint
}

int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
xdl_info_t *dlinfo) {
xdl_info_t *dlinfo, bool ignore_symbol_check) {
int r;
if (SHADOWHOOK_IS_UNIQUE_MODE)
r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo);
r = sh_switch_hook_unique(target_addr, new_addr, orig_addr, backup_len, dlinfo, ignore_symbol_check);
else
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo, ignore_symbol_check);

if (0 == r)
SH_LOG_INFO("switch: hook in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
Expand All @@ -251,7 +253,7 @@ static int sh_switch_hook_unique_invisible(uintptr_t target_addr, uintptr_t new_

// do hook
sh_inst_t inst;
int r = sh_inst_hook(&inst, target_addr, dlinfo, new_addr, orig_addr, NULL);
int r = sh_inst_hook(&inst, target_addr, dlinfo, new_addr, orig_addr, NULL, false);

pthread_rwlock_unlock(&sh_switches_lock); // SYNC - end

Expand All @@ -260,12 +262,14 @@ static int sh_switch_hook_unique_invisible(uintptr_t target_addr, uintptr_t new_
}

int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
size_t *backup_len, xdl_info_t *dlinfo) {
size_t *backup_len, xdl_info_t *dlinfo, bool ignore_symbol_check) {
(void)ignore_symbol_check;

int r;
if (SHADOWHOOK_IS_UNIQUE_MODE)
r = sh_switch_hook_unique_invisible(target_addr, new_addr, orig_addr, backup_len, dlinfo);
else
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo);
r = sh_switch_hook_shared(target_addr, new_addr, orig_addr, backup_len, dlinfo, false);

if (0 == r)
SH_LOG_INFO("switch: hook(invisible) in %s mode OK: target_addr %" PRIxPTR ", new_addr %" PRIxPTR,
Expand Down
5 changes: 3 additions & 2 deletions shadowhook/src/main/cpp/sh_switch.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
// Created by Kelun Cai ([email protected]) on 2021-04-11.

#pragma once
#include <stdbool.h>
#include <stdint.h>

#include "xdl.h"

int sh_switch_hook(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr, size_t *backup_len,
xdl_info_t *dlinfo);
xdl_info_t *dlinfo, bool ignore_symbol_check);
int sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr);

int sh_switch_hook_invisible(uintptr_t target_addr, uintptr_t new_addr, uintptr_t *orig_addr,
size_t *backup_len, xdl_info_t *dlinfo);
size_t *backup_len, xdl_info_t *dlinfo, bool ignore_symbol_check);

void sh_switch_free_after_dlclose(xdl_info_t *dlinfo);
Loading

0 comments on commit c9ff65f

Please sign in to comment.