diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index d72e3dc5..a97cc67b 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -10,28 +10,22 @@ elseif (${ANDROID_ABI} STREQUAL "armeabi-v7a") set(ARCH "arm") endif () -file(GLOB SHADOWHOOK_SRC - shadowhook/*.c - shadowhook/arch/${ARCH}/*.c - shadowhook/common/*.c - shadowhook/third_party/xdl/*.c +file(GLOB BYTEHOOK_SRC + bytehook/*.c ) add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp cJSON/cJSON.c - ${SHADOWHOOK_SRC} + ${BYTEHOOK_SRC} ) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE cJSON - shadowhook/. - shadowhook/arch/${ARCH} - shadowhook/include - shadowhook/common - shadowhook/third_party/bsd - shadowhook/third_party/lss - shadowhook/third_party/xdl + bytehook/. + bytehook/include + bytehook/third_party/bsd + bytehook/third_party/lss ) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log cxx::cxx) diff --git a/app/src/main/cpp/bytehook/bh_cfi.c b/app/src/main/cpp/bytehook/bh_cfi.c new file mode 100644 index 00000000..415ca840 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_cfi.c @@ -0,0 +1,84 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Li Han (hanli.lee@bytedance.com) on 2020-11-04. + +#include "bh_cfi.h" + +#if defined(__aarch64__) + +#include +#include +#include +#include + +#include "bh_util.h" +#include "bytesig.h" + +#define BH_CFI_LIB_DL "libdl.so" +#define BH_CFI_SLOWPATH "__cfi_slowpath" +#define BH_CFI_SLOWPATH_DIAG "__cfi_slowpath_diag" +#define BH_CFI_ARM64_RET_INST 0xd65f03c0 + +static void *bh_cfi_slowpath = NULL; +static void *bh_cfi_slowpath_diag = NULL; + +__attribute__((constructor)) static void bh_cfi_ctor(void) { + void *handle = dlopen(BH_CFI_LIB_DL, RTLD_NOW); + if (NULL != handle) { + bh_cfi_slowpath = dlsym(handle, BH_CFI_SLOWPATH); + bh_cfi_slowpath_diag = dlsym(handle, BH_CFI_SLOWPATH_DIAG); + dlclose(handle); + } +} + +int bh_cfi_disable_slowpath(void) { + if (bh_util_get_api_level() < __ANDROID_API_O__) return 0; + + if (NULL == bh_cfi_slowpath || NULL == bh_cfi_slowpath_diag) return -1; + + void *start = bh_cfi_slowpath <= bh_cfi_slowpath_diag ? bh_cfi_slowpath : bh_cfi_slowpath_diag; + void *end = bh_cfi_slowpath <= bh_cfi_slowpath_diag ? bh_cfi_slowpath_diag : bh_cfi_slowpath; + if (0 != bh_util_set_protect(start, (void *)((uintptr_t)end + sizeof(uint32_t)), + PROT_READ | PROT_WRITE | PROT_EXEC)) + return -1; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + *((uint32_t *)bh_cfi_slowpath) = BH_CFI_ARM64_RET_INST; + *((uint32_t *)bh_cfi_slowpath_diag) = BH_CFI_ARM64_RET_INST; + } + BYTESIG_CATCH() { + return -1; + } + BYTESIG_EXIT + + __builtin___clear_cache(start, (void *)((size_t)end + sizeof(uint32_t))); + + return 0; +} + +#else + +int bh_cfi_disable_slowpath(void) { + return 0; +} + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h b/app/src/main/cpp/bytehook/bh_cfi.h similarity index 75% rename from app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h rename to app/src/main/cpp/bytehook/bh_cfi.h index 2d40cf4f..01e1a195 100644 --- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h +++ b/app/src/main/cpp/bytehook/bh_cfi.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 HexHacking Team +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,22 +19,8 @@ // SOFTWARE. // -// Created by caikelun on 2021-02-21. +// Created by Li Han (hanli.lee@bytedance.com) on 2020-11-04. -#ifndef IO_GITHUB_HEXHACKING_XDL_LINKER -#define IO_GITHUB_HEXHACKING_XDL_LINKER +#pragma once -#ifdef __cplusplus -extern "C" { -#endif - -void xdl_linker_lock(void); -void xdl_linker_unlock(void); - -void *xdl_linker_force_dlopen(const char *filename); - -#ifdef __cplusplus -} -#endif - -#endif +int bh_cfi_disable_slowpath(void); diff --git a/app/src/main/cpp/bytehook/bh_const.h b/app/src/main/cpp/bytehook/bh_const.h new file mode 100644 index 00000000..9628853b --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_const.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once + +#ifndef __LP64__ +#define BH_CONST_PATHNAME_LINKER "/system/bin/linker" +#define BH_CONST_BASENAME_LINKER "linker" +#define BH_CONST_BASENAME_APP_PROCESS "app_process32" +#else +#define BH_CONST_PATHNAME_LINKER "/system/bin/linker64" +#define BH_CONST_BASENAME_LINKER "linker64" +#define BH_CONST_BASENAME_APP_PROCESS "app_process64" +#endif + +#define BH_CONST_BASENAME_DL "libdl.so" +#define BH_CONST_BASENAME_BYTEHOOK "libbytehook.so" + +#define BH_CONST_SYM_DLCLOSE "dlclose" +#define BH_CONST_SYM_LOADER_DLCLOSE "__loader_dlclose" +#define BH_CONST_SYM_DLOPEN "dlopen" +#define BH_CONST_SYM_ANDROID_DLOPEN_EXT "android_dlopen_ext" +#define BH_CONST_SYM_DLOPEN_EXT "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv" +#define BH_CONST_SYM_LOADER_DLOPEN "__loader_dlopen" +#define BH_CONST_SYM_LOADER_ANDROID_DLOPEN_EXT "__loader_android_dlopen_ext" +#define BH_CONST_SYM_DO_DLOPEN "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv" +#define BH_CONST_SYM_G_DL_MUTEX "__dl__ZL10g_dl_mutex" +#define BH_CONST_SYM_G_DL_MUTEX_U_QPR2 "__dl_g_dl_mutex" +#define BH_CONST_SYM_LINKER_GET_ERROR_BUFFER "__dl__Z23linker_get_error_bufferv" +#define BH_CONST_SYM_BIONIC_FORMAT_DLERROR "__dl__ZL23__bionic_format_dlerrorPKcS0_" +#define BH_CONST_SYM_CFI_SLOWPATH "__cfi_slowpath" +#define BH_CONST_SYM_CFI_SLOWPATH_DIAG "__cfi_slowpath_diag" diff --git a/app/src/main/cpp/bytehook/bh_core.c b/app/src/main/cpp/bytehook/bh_core.c new file mode 100644 index 00000000..c384c541 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_core.c @@ -0,0 +1,221 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_core.h" + +#include +#include +#include +#include +#include + +#include "bh_cfi.h" +#include "bh_dl_monitor.h" +#include "bh_elf.h" +#include "bh_elf_manager.h" +#include "bh_linker.h" +#include "bh_log.h" +#include "bh_recorder.h" +#include "bh_task.h" +#include "bh_task_manager.h" +#include "bh_trampo.h" +#include "bh_util.h" +#include "bytehook.h" +#include "bytesig.h" + +static bh_core_t bh_core = {.init_status = BYTEHOOK_STATUS_CODE_UNINIT, + .mode = -1, + .task_mgr = NULL, + .hook_mgr = NULL, + .elf_mgr = NULL}; + +bh_core_t *bh_core_global(void) { + return &bh_core; +} + +int bh_core_init(int mode, bool debug) { + // Do not repeat the initialization. + if (BYTEHOOK_STATUS_CODE_UNINIT != bh_core.init_status) { + BH_LOG_SHOW("bytehook already inited, return: %d", bh_core.init_status); + return bh_core.init_status; + } + + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&lock); + if (__predict_true(BYTEHOOK_STATUS_CODE_UNINIT == bh_core.init_status)) { + int status; + + bh_log_set_debug(debug); + if (BYTEHOOK_MODE_AUTOMATIC != mode && BYTEHOOK_MODE_MANUAL != mode) { + status = BYTEHOOK_STATUS_CODE_INITERR_INVALID_ARG; + goto end; + } + bh_core.mode = mode; + if (0 != bh_linker_init()) { + status = BYTEHOOK_STATUS_CODE_INITERR_SYM; + goto end; + } + if (NULL == (bh_core.task_mgr = bh_task_manager_create())) { + status = BYTEHOOK_STATUS_CODE_INITERR_TASK; + goto end; + } + if (NULL == (bh_core.hook_mgr = bh_hook_manager_create())) { + status = BYTEHOOK_STATUS_CODE_INITERR_HOOK; + goto end; + } + if (NULL == (bh_core.elf_mgr = bh_elf_manager_create())) { + status = BYTEHOOK_STATUS_CODE_INITERR_ELF; + goto end; + } + if (BYTEHOOK_MODE_AUTOMATIC == mode && 0 != bh_trampo_init()) { + status = BYTEHOOK_STATUS_CODE_INITERR_TRAMPO; + goto end; + } + if (0 != bytesig_init(SIGSEGV) || 0 != bytesig_init(SIGBUS)) { + status = BYTEHOOK_STATUS_CODE_INITERR_SIG; + goto end; + } + if (0 != bh_cfi_disable_slowpath()) { + status = BYTEHOOK_STATUS_CODE_INITERR_CFI; + goto end; + } + status = BYTEHOOK_STATUS_CODE_OK; // everything OK + + end: + __atomic_store_n(&bh_core.init_status, status, __ATOMIC_SEQ_CST); + } + pthread_mutex_unlock(&lock); + + BH_LOG_SHOW("%s: bytehook init(mode: %s, debug: %s), return: %d", bytehook_get_version(), + BYTEHOOK_MODE_AUTOMATIC == mode ? "AUTOMATIC" : "MANUAL", debug ? "true" : "false", + bh_core.init_status); + return bh_core.init_status; +} + +bytehook_stub_t bh_core_hook_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg, uintptr_t caller_addr) { + if (NULL == caller_path_name || NULL == sym_name || NULL == new_func) return NULL; + if (BYTEHOOK_STATUS_CODE_OK != bh_core.init_status) return NULL; + + bh_task_t *task = + bh_task_create_single(caller_path_name, callee_path_name, sym_name, new_func, hooked, hooked_arg); + if (NULL != task) { + bh_task_manager_add(bh_core.task_mgr, task); + bh_task_manager_hook(bh_core.task_mgr, task); + bh_recorder_add_hook(task->hook_status_code, caller_path_name, sym_name, (uintptr_t)new_func, + (uintptr_t)task, caller_addr); + } + return (bytehook_stub_t)task; +} + +bytehook_stub_t bh_core_hook_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg, uintptr_t caller_addr) { + if (NULL == caller_allow_filter || NULL == sym_name || NULL == new_func) return NULL; + if (BYTEHOOK_STATUS_CODE_OK != bh_core.init_status) return NULL; + + bh_task_t *task = bh_task_create_partial(caller_allow_filter, caller_allow_filter_arg, callee_path_name, + sym_name, new_func, hooked, hooked_arg); + if (NULL != task) { + bh_task_manager_add(bh_core.task_mgr, task); + bh_task_manager_hook(bh_core.task_mgr, task); + bh_recorder_add_hook(BYTEHOOK_STATUS_CODE_MAX, "PARTIAL", sym_name, (uintptr_t)new_func, (uintptr_t)task, + caller_addr); + } + return (bytehook_stub_t)task; +} + +bytehook_stub_t bh_core_hook_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg, uintptr_t caller_addr) { + if (NULL == sym_name || NULL == new_func) return NULL; + if (BYTEHOOK_STATUS_CODE_OK != bh_core.init_status) return NULL; + + bh_task_t *task = bh_task_create_all(callee_path_name, sym_name, new_func, hooked, hooked_arg); + if (NULL != task) { + bh_task_manager_add(bh_core.task_mgr, task); + bh_task_manager_hook(bh_core.task_mgr, task); + bh_recorder_add_hook(BYTEHOOK_STATUS_CODE_MAX, "ALL", sym_name, (uintptr_t)new_func, (uintptr_t)task, + caller_addr); + } + return (bytehook_stub_t)task; +} + +int bh_core_unhook(bytehook_stub_t stub, uintptr_t caller_addr) { + if (NULL == stub) return BYTEHOOK_STATUS_CODE_INVALID_ARG; + if (BYTEHOOK_STATUS_CODE_OK != bh_core.init_status) return bh_core.init_status; + + bh_task_t *task = (bh_task_t *)stub; + bh_task_manager_del(bh_core.task_mgr, task); + int status_code = bh_task_manager_unhook(bh_core.task_mgr, task); + bh_recorder_add_unhook(status_code, (uintptr_t)task, caller_addr); + bh_task_destroy(&task); + + return status_code; +} + +int bh_core_add_ignore(const char *caller_path_name) { + int r = bh_elf_manager_add_ignore(bh_core.elf_mgr, caller_path_name); + return 0 == r ? 0 : BYTEHOOK_STATUS_CODE_IGNORE; +} + +bool bh_core_get_debug(void) { + return bh_log_get_debug(); +} + +void bh_core_set_debug(bool debug) { + bh_log_set_debug(debug); +} + +bool bh_core_get_recordable(void) { + return bh_recorder_get_recordable(); +} + +void bh_core_set_recordable(bool recordable) { + bh_recorder_set_recordable(recordable); +} + +void *bh_core_get_prev_func(void *func) { + return bh_trampo_get_prev_func(func); +} + +void bh_core_pop_stack(void *return_address) { + bh_trampo_pop_stack(return_address); +} + +void *bh_core_get_return_address(void) { + return bh_trampo_get_return_address(); +} + +int bh_core_get_mode(void) { + return bh_core.mode; +} + +void bh_core_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + bh_dl_monitor_add_dlopen_callback(pre, post, data); +} + +void bh_core_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + bh_dl_monitor_del_dlopen_callback(pre, post, data); +} diff --git a/app/src/main/cpp/bytehook/bh_core.h b/app/src/main/cpp/bytehook/bh_core.h new file mode 100644 index 00000000..52b85815 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_core.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once +#include + +#include "bh_elf_manager.h" +#include "bh_hook_manager.h" +#include "bh_task_manager.h" +#include "bytehook.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + int init_status; + int mode; + bh_task_manager_t *task_mgr; + bh_hook_manager_t *hook_mgr; + bh_elf_manager_t *elf_mgr; +} bh_core_t; +#pragma clang diagnostic pop + +bh_core_t *bh_core_global(void); + +int bh_core_init(int mode, bool debug); + +bytehook_stub_t bh_core_hook_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg, uintptr_t caller_addr); + +bytehook_stub_t bh_core_hook_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg, uintptr_t caller_addr); + +bytehook_stub_t bh_core_hook_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg, uintptr_t caller_addr); + +int bh_core_unhook(bytehook_stub_t stub, uintptr_t caller_addr); + +int bh_core_add_ignore(const char *caller_path_name); + +bool bh_core_get_debug(void); +void bh_core_set_debug(bool debug); +bool bh_core_get_recordable(void); +void bh_core_set_recordable(bool recordable); + +void *bh_core_get_prev_func(void *func); + +void *bh_core_get_return_address(void); + +void bh_core_pop_stack(void *return_address); + +int bh_core_get_mode(void); + +void bh_core_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); + +void bh_core_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); diff --git a/app/src/main/cpp/bytehook/bh_dl.c b/app/src/main/cpp/bytehook/bh_dl.c new file mode 100644 index 00000000..7a4d52bf --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_dl.c @@ -0,0 +1,246 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_dl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_util.h" + +extern __attribute((weak)) unsigned long int getauxval(unsigned long int); + +#define BH_DL_SYMTAB_IS_EXPORT_SYM(shndx) \ + (SHN_UNDEF != (shndx) && !((shndx) >= SHN_LORESERVE && (shndx) <= SHN_HIRESERVE)) + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct { + uintptr_t load_bias; + uintptr_t base; + ElfW(Sym) *symtab; // .symtab + size_t symtab_cnt; + char *strtab; // .strtab + size_t strtab_sz; +} bh_dl_t; +#pragma clang diagnostic pop + +static void *bh_dl_read_to_memory(int file_fd, size_t file_sz, size_t data_offset, size_t data_len) { + if (0 == data_len) return NULL; + if (data_offset + data_len > file_sz) return NULL; + + if (data_offset != (size_t)lseek(file_fd, (off_t)data_offset, SEEK_SET)) return NULL; + + void *data = malloc(data_len); + if (NULL == data) return NULL; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-statement-expression" + if ((ssize_t)data_len != BH_UTIL_TEMP_FAILURE_RETRY(read(file_fd, data, data_len))) +#pragma clang diagnostic pop + { + free(data); + return NULL; + } + + return data; +} + +static void *bh_dl_read_section_to_memory(int file_fd, size_t file_sz, ElfW(Shdr) *shdr) { + return bh_dl_read_to_memory(file_fd, file_sz, (size_t)shdr->sh_offset, shdr->sh_size); +} + +static int bh_dl_load_symtab(bh_dl_t *self, const char *pathname) { + ElfW(Shdr) *shdrs = NULL; + char *shstrtab = NULL; + + // open file + int file_fd = open(pathname, O_RDONLY | O_CLOEXEC); + if (file_fd < 0) return -1; + + // get file size + struct stat st; + if (0 != fstat(file_fd, &st)) goto err; + size_t file_sz = (size_t)st.st_size; + + // get ELF header from memory + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)self->base; + if (0 == ehdr->e_shnum) goto err; + + // get section header table from file + shdrs = (ElfW(Shdr) *)bh_dl_read_to_memory(file_fd, file_sz, (size_t)ehdr->e_shoff, + ehdr->e_shentsize * ehdr->e_shnum); + if (NULL == shdrs) goto err; + + // get .shstrtab from file + ElfW(Shdr) *shdr_shstrtab = shdrs + ehdr->e_shstrndx; + shstrtab = (char *)bh_dl_read_section_to_memory(file_fd, file_sz, shdr_shstrtab); + if (NULL == shstrtab) goto err; + + // get .symtab and .strtab from file + for (size_t i = 0; i < ehdr->e_shnum; i++) { + ElfW(Shdr) *shdr = shdrs + i; + char *shdr_name = shstrtab + shdr->sh_name; + + if (SHT_SYMTAB == shdr->sh_type && 0 == strcmp(".symtab", shdr_name)) { + // check associated .strtab section info + if (shdr->sh_link >= ehdr->e_shnum) continue; + ElfW(Shdr) *shdr_strtab = shdrs + shdr->sh_link; + if (SHT_STRTAB != shdr_strtab->sh_type) continue; + + // get .symtab from file + self->symtab = (ElfW(Sym) *)bh_dl_read_section_to_memory(file_fd, file_sz, shdr); + if (NULL == self->symtab) goto err; + self->symtab_cnt = shdr->sh_size / shdr->sh_entsize; + + // get .strtab from file + self->strtab = (char *)bh_dl_read_section_to_memory(file_fd, file_sz, shdr_strtab); + if (NULL == self->strtab) goto err; + self->strtab_sz = shdr_strtab->sh_size; + + close(file_fd); + free(shdrs); + free(shstrtab); + return 0; // OK + } + } + +err: + if (file_fd >= 0) close(file_fd); + if (NULL != shdrs) free(shdrs); + if (NULL != shstrtab) free(shstrtab); + if (NULL != self->symtab) { + free(self->symtab); + self->symtab = NULL; + self->symtab_cnt = 0; + } + if (NULL != self->strtab) { + free(self->strtab); + self->strtab = NULL; + self->strtab_sz = 0; + } + return -1; // not found +} + +static uintptr_t bh_dl_find_linker_base_from_auxv(void) { + if (NULL == getauxval) return 0; + + uintptr_t base = (uintptr_t)getauxval(AT_BASE); + if (0 == base) return 0; + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0; + return base; +} + +#if __ANDROID_API__ < __ANDROID_API_J_MR2__ +static uintptr_t bh_dl_find_linker_base_from_maps(void) { + FILE *maps = fopen("/proc/self/maps", "r"); + if (NULL == maps) return 0; + + uintptr_t ret = 0; + char line[1024]; + while (fgets(line, sizeof(line), maps)) { + bh_util_trim_ending(line); + if (!bh_util_ends_with(line, " " BH_CONST_BASENAME_LINKER) && + !bh_util_ends_with(line, "/" BH_CONST_BASENAME_LINKER)) + continue; + + uintptr_t base, offset; + if (2 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r-xp %" SCNxPTR " ", &base, &offset)) break; + if (0 != offset) break; + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) break; + + ret = base; + break; + } + + fclose(maps); + return ret; +} +#endif + +void *bh_dl_open_linker(void) { + uintptr_t base = bh_dl_find_linker_base_from_auxv(); +#if __ANDROID_API__ < __ANDROID_API_J_MR2__ + if (0 == base) base = bh_dl_find_linker_base_from_maps(); +#endif + if (0 == base) return NULL; + + // ELF info + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + const ElfW(Phdr) *dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + ElfW(Half) dlpi_phnum = ehdr->e_phnum; + + // get bias + uintptr_t min_vaddr = UINTPTR_MAX; + for (size_t i = 0; i < dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; + } + } + if (UINTPTR_MAX == min_vaddr || base < min_vaddr) return NULL; + uintptr_t load_bias = base - min_vaddr; + + // create bh_dl_t object + bh_dl_t *self; + if (NULL == (self = calloc(1, sizeof(bh_dl_t)))) return NULL; + self->load_bias = load_bias; + self->base = base; + if (0 != bh_dl_load_symtab(self, BH_CONST_PATHNAME_LINKER)) { + free(self); + return NULL; + } + return (void *)self; +} + +void bh_dl_close(void *handle) { + bh_dl_t *self = (bh_dl_t *)handle; + + if (NULL != self->symtab) free(self->symtab); + if (NULL != self->strtab) free(self->strtab); + free(self); +} + +void *bh_dl_dsym(void *handle, const char *symbol) { + bh_dl_t *self = (bh_dl_t *)handle; + + for (size_t i = 0; i < self->symtab_cnt; i++) { + ElfW(Sym) *sym = self->symtab + i; + if (!BH_DL_SYMTAB_IS_EXPORT_SYM(sym->st_shndx)) continue; + if (0 != strncmp(self->strtab + sym->st_name, symbol, self->strtab_sz - sym->st_name)) continue; + return (void *)(self->load_bias + sym->st_value); + } + return NULL; +} diff --git a/app/src/main/cpp/bytehook/bh_dl.h b/app/src/main/cpp/bytehook/bh_dl.h new file mode 100644 index 00000000..5e2ef140 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_dl.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once + +void *bh_dl_open_linker(void); +void bh_dl_close(void *handle); +void *bh_dl_dsym(void *handle, const char *symbol); diff --git a/app/src/main/cpp/bytehook/bh_dl_iterate.c b/app/src/main/cpp/bytehook/bh_dl_iterate.c new file mode 100644 index 00000000..512b300a --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_dl_iterate.c @@ -0,0 +1,198 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_dl_iterate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_dl.h" +#include "bh_linker.h" +#include "bh_log.h" +#include "bh_util.h" + +/* + * ==================================================================== + * API-LEVEL ANDROID-VERSION SOLUTION + * ==================================================================== + * 16 4.1 /proc/self/maps + * 17 4.2 /proc/self/maps + * 18 4.3 /proc/self/maps + * 19 4.4 /proc/self/maps + * 20 4.4W /proc/self/maps + * -------------------------------------------------------------------- + * 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + * 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + * -------------------------------------------------------------------- + * >= 23 >= 6.0 dl_iterate_phdr() + * ==================================================================== + */ + +extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *); + +static int bh_dl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) { + // ignore invalid ELF + if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0; + + // callback + uintptr_t *pkg = (uintptr_t *)arg; + int (*callback)(struct dl_phdr_info *, size_t, void *) = + (int (*)(struct dl_phdr_info *, size_t, void *)) * pkg++; + void *data = (void *)*pkg; + return callback(info, size, data); +} + +static int bh_dl_iterate_by_linker(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) { + BH_LOG_INFO("DL iterate: iterate by dl_iterate_phdr"); + + if (NULL == dl_iterate_phdr) return -1; + + int api_level = bh_util_get_api_level(); + + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) bh_linker_lock(); + uintptr_t pkg[2] = {(uintptr_t)callback, (uintptr_t)data}; + dl_iterate_phdr(bh_dl_iterate_by_linker_cb, pkg); + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) bh_linker_unlock(); + + return 0; +} + +#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ + +static uintptr_t bh_dl_iterate_get_min_vaddr(struct dl_phdr_info *info) { + uintptr_t min_vaddr = UINTPTR_MAX; + for (size_t i = 0; i < info->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); + if (PT_LOAD == phdr->p_type) { + if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; + } + } + return min_vaddr; +} + +static int bh_dl_iterate_by_maps(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) { + BH_LOG_INFO("DL iterate: iterate by maps"); + + bh_linker_lock(); + + FILE *maps = fopen("/proc/self/maps", "r"); + if (NULL == maps) goto end; + + char buf1[1024], buf2[1024]; + char *line = buf1; + uintptr_t prev_base = 0; + bool try_next_line = false; + + while (fgets(line, sizeof(buf1), maps)) { + bh_util_trim_ending(line); + // Parsing maps directly has too much uncertainty, so it needs to be strict. + if (!bh_util_ends_with(line, BH_CONST_BASENAME_APP_PROCESS) && !bh_util_ends_with(line, ".so")) continue; + + uintptr_t base, offset; + char exec; + if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset)) + goto clean; + + if ('-' == exec && 0 == offset) { + // r--p + prev_base = base; + line = (line == buf1 ? buf2 : buf1); + try_next_line = true; + continue; + } else if (exec == 'x') { + // r-xp + char *pathname = NULL; + if (try_next_line && 0 != offset) { + char *prev = (line == buf1 ? buf2 : buf1); + char *prev_pathname = strchr(prev, '/'); + if (NULL == prev_pathname) goto clean; + + pathname = strchr(line, '/'); + if (NULL == pathname) goto clean; + + bh_util_trim_ending(prev_pathname); + bh_util_trim_ending(pathname); + if (0 != strcmp(prev_pathname, pathname)) goto clean; + + // we found the line with r-xp in the next line + base = prev_base; + offset = 0; + } + + if (0 != offset) goto clean; + + // get pathname + if (NULL == pathname) { + pathname = strchr(line, '/'); + if (NULL == pathname) goto clean; + bh_util_trim_ending(pathname); + } + + if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean; + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; + struct dl_phdr_info info; + info.dlpi_name = pathname; + info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); + info.dlpi_phnum = ehdr->e_phnum; + + // get load bias + uintptr_t min_vaddr = bh_dl_iterate_get_min_vaddr(&info); + if (UINTPTR_MAX == min_vaddr) goto clean; // ignore invalid ELF + info.dlpi_addr = (ElfW(Addr))(base - min_vaddr); + + // callback + if (0 != callback(&info, sizeof(struct dl_phdr_info), data)) break; + } + + clean: + try_next_line = false; + } + + fclose(maps); + +end: + bh_linker_unlock(); + return 0; +} + +#endif + +int bh_dl_iterate(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) { + // iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86) +#if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ + if (bh_util_get_api_level() < __ANDROID_API_L__) return bh_dl_iterate_by_maps(callback, data); +#endif + + // iterate by dl_iterate_phdr() + return bh_dl_iterate_by_linker(callback, data); +} diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h b/app/src/main/cpp/bytehook/bh_dl_iterate.h similarity index 76% rename from app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h rename to app/src/main/cpp/bytehook/bh_dl_iterate.h index b1eef1dc..55440092 100644 --- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h +++ b/app/src/main/cpp/bytehook/bh_dl_iterate.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 HexHacking Team +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,22 +19,10 @@ // SOFTWARE. // -// Created by caikelun on 2020-11-08. - -#ifndef IO_GITHUB_HEXHACKING_XDL_LZMA -#define IO_GITHUB_HEXHACKING_XDL_LZMA +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. +#pragma once +#include #include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size); - -#ifdef __cplusplus -} -#endif -#endif +int bh_dl_iterate(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data); diff --git a/app/src/main/cpp/bytehook/bh_dl_monitor.c b/app/src/main/cpp/bytehook/bh_dl_monitor.c new file mode 100644 index 00000000..bf4623e2 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_dl_monitor.c @@ -0,0 +1,620 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Tianzhou Shen (shentianzhou@bytedance.com) on 2020-06-02. + +#include "bh_dl_monitor.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_core.h" +#include "bh_linker.h" +#include "bh_log.h" +#include "bh_util.h" +#include "bytehook.h" +#include "queue.h" + +// clang-format off +/* + * This solution is derived from ByteDance Raphael (https://github.com/bytedance/memory-leak-detector), + * which designed and implemented by (alphabetical order): + * + * Tianzhou Shen (shentianzhou@bytedance.com) + * Yonggang Sun (sunyonggang@bytedance.com) + * + * =================================================================================================================================================================== + * API-LEVEL ANDROID-VERSION TARGET-LIB TARGET-FUNCTION SOLUTION + * =================================================================================================================================================================== + * 16 4.1 all-ELF dlopen HOOK -> CALL(original-function) + * 17 4.2 all-ELF dlopen HOOK -> CALL(original-function) + * 18 4.3 all-ELF dlopen HOOK -> CALL(original-function) + * 19 4.4 all-ELF dlopen HOOK -> CALL(original-function) + * 20 4.4W all-ELF dlopen HOOK -> CALL(original-function) + * ------------------------------------------------------------------------------------------------------------------------------------------------------------------- + * 21 5.0 all-ELF dlopen, android_dlopen_ext HOOK -> CALL(original-function) + * 22 5.1 all-ELF dlopen, android_dlopen_ext HOOK -> CALL(original-function) + * 23 6.0 all-ELF dlopen, android_dlopen_ext HOOK -> CALL(original-function) + * ------------------------------------------------------------------------------------------------------------------------------------------------------------------- + * 24 7.0 all-ELF dlopen, android_dlopen_ext HOOK -> CALL(dlopen_ext IN linker/linker64) with caller's address + * OR CALL(g_dl_mutex + do_dlopen IN linker/linker64) with caller's address + * 25 7.1 all-ELF dlopen, android_dlopen_ext HOOK -> CALL(dlopen_ext IN linker/linker64) with caller's address + * OR CALL(g_dl_mutex + do_dlopen IN linker/linker64) with caller's address + * ------------------------------------------------------------------------------------------------------------------------------------------------------------------- + * >= 26 >= 8.0 libdl.so __loader_dlopen, __loader_android_dlopen_ext HOOK -> CALL(original-function) with caller's address + * =================================================================================================================================================================== + */ +// clang-format on + +// hook function's type +typedef void *(*bh_dl_monitor_dlopen_t)(const char *, int); +typedef void *(*bh_dl_monitor_android_dlopen_ext_t)(const char *, int, const void *); +typedef void *(*bh_dl_monitor_loader_dlopen_t)(const char *, int, const void *); +typedef void *(*bh_dl_monitor_loader_android_dlopen_ext_t)(const char *, int, const void *, const void *); +typedef int (*bh_dl_monitor_dlclose_t)(void *); +typedef int (*bh_dl_monitor_loader_dlclose_t)(void *); + +// hook function's origin function address +// Keep these values after uninit to prevent the concurrent access from crashing. +static bh_dl_monitor_dlopen_t bh_dl_monitor_orig_dlopen = NULL; +static bh_dl_monitor_android_dlopen_ext_t bh_dl_monitor_orig_android_dlopen_ext = NULL; +static bh_dl_monitor_loader_dlopen_t bh_dl_monitor_orig_loader_dlopen = NULL; +static bh_dl_monitor_loader_android_dlopen_ext_t bh_dl_monitor_orig_loader_android_dlopen_ext = NULL; +static bh_dl_monitor_dlclose_t bh_dl_monitor_orig_dlclose = NULL; +static bh_dl_monitor_loader_dlclose_t bh_dl_monitor_orig_loader_dlclose = NULL; + +// hook task's stub for unhooking +// Reset to NULL after uninit. +static bytehook_stub_t bh_dl_monitor_stub_dlopen = NULL; +static bytehook_stub_t bh_dl_monitor_stub_android_dlopen_ext = NULL; +static bytehook_stub_t bh_dl_monitor_stub_loader_dlopen = NULL; +static bytehook_stub_t bh_dl_monitor_stub_loader_android_dlopen_ext = NULL; +static bytehook_stub_t bh_dl_monitor_stub_dlclose = NULL; +static bytehook_stub_t bh_dl_monitor_stub_loader_dlclose = NULL; + +// the callback which will be called after dlopen() or android_dlopen_ext() and dlclose() successful +// Keep these values after uninit to prevent the concurrent access from crashing. +static bh_dl_monitor_post_dlopen_t bh_dl_monitor_post_dlopen = NULL; +static void *bh_dl_monitor_post_dlopen_arg = NULL; +static bh_dl_monitor_post_dlclose_t bh_dl_monitor_post_dlclose = NULL; +static void *bh_dl_monitor_post_dlclose_arg = NULL; + +// the callbacks for every dlopen() or android_dlopen_ext() +typedef struct bh_dl_monitor_cb { + bytehook_pre_dlopen_t pre; + bytehook_post_dlopen_t post; + void *data; + TAILQ_ENTRY(bh_dl_monitor_cb, ) link; +} bh_dl_monitor_cb_t; +typedef TAILQ_HEAD(bh_dl_monitor_cb_queue, bh_dl_monitor_cb, ) bh_dl_monitor_cb_queue_t; +static bh_dl_monitor_cb_queue_t bh_dl_monitor_cbs = TAILQ_HEAD_INITIALIZER(bh_dl_monitor_cbs); +static pthread_rwlock_t bh_dl_monitor_cbs_lock = PTHREAD_RWLOCK_INITIALIZER; + +static void bh_dl_monitor_call_cb_pre(const char *filename) { + if (TAILQ_EMPTY(&bh_dl_monitor_cbs)) return; + + pthread_rwlock_rdlock(&bh_dl_monitor_cbs_lock); + bh_dl_monitor_cb_t *cb; + TAILQ_FOREACH(cb, &bh_dl_monitor_cbs, link) { + if (NULL != cb->pre) cb->pre(filename, cb->data); + } + pthread_rwlock_unlock(&bh_dl_monitor_cbs_lock); +} + +static void bh_dl_monitor_call_cb_post(const char *filename, int result) { + if (TAILQ_EMPTY(&bh_dl_monitor_cbs)) return; + + pthread_rwlock_rdlock(&bh_dl_monitor_cbs_lock); + bh_dl_monitor_cb_t *cb; + TAILQ_FOREACH(cb, &bh_dl_monitor_cbs, link) { + if (NULL != cb->post) cb->post(filename, result, cb->data); + } + pthread_rwlock_unlock(&bh_dl_monitor_cbs_lock); +} + +// callback for hooking dlopen() +static void bh_dl_monitor_proxy_dlopen_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, + void *new_func, void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && (void *)bh_dl_monitor_orig_dlopen != prev_func) + bh_dl_monitor_orig_dlopen = (bh_dl_monitor_dlopen_t)prev_func; +} + +// callback for hooking android_dlopen_ext() +static void bh_dl_monitor_proxy_android_dlopen_ext_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, + void *new_func, void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && + (void *)bh_dl_monitor_orig_android_dlopen_ext != prev_func) + bh_dl_monitor_orig_android_dlopen_ext = (bh_dl_monitor_android_dlopen_ext_t)prev_func; +} + +// callback for hooking __loader_dlopen() +static void bh_dl_monitor_proxy_loader_dlopen_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, + void *new_func, void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && (void *)bh_dl_monitor_orig_loader_dlopen != prev_func) + bh_dl_monitor_orig_loader_dlopen = (bh_dl_monitor_loader_dlopen_t)prev_func; +} + +// callback for hooking __loader_android_dlopen_ext() +static void bh_dl_monitor_proxy_loader_android_dlopen_ext_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, + const char *sym_name, void *new_func, + void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && + (void *)bh_dl_monitor_orig_loader_android_dlopen_ext != prev_func) + bh_dl_monitor_orig_loader_android_dlopen_ext = (bh_dl_monitor_loader_android_dlopen_ext_t)prev_func; +} + +// callback for hooking dlclose() +static void bh_dl_monitor_proxy_dlclose_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, + void *new_func, void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && (void *)bh_dl_monitor_orig_dlclose != prev_func) + bh_dl_monitor_orig_dlclose = (bh_dl_monitor_dlclose_t)prev_func; +} + +// callback for hooking __loader_dlclose() +static void bh_dl_monitor_proxy_loader_dlclose_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, + void *new_func, void *prev_func, void *arg) { + (void)task_stub, (void)caller_path_name, (void)sym_name, (void)new_func, (void)arg; + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR == status_code && (void *)bh_dl_monitor_orig_loader_dlclose != prev_func) + bh_dl_monitor_orig_loader_dlclose = (bh_dl_monitor_loader_dlclose_t)prev_func; +} + +// dlerror message TLS key +static pthread_key_t bh_dl_monitor_dlerror_msg_tls_key; + +// set dlerror message +static void bh_dl_monitor_set_dlerror_msg(void) { +#define LIBC_TLS_SLOT_DLERROR 6 +#define ERRMSG_SZ 256 + + char *msg = "dlopen failed"; + char *detail = (NULL != bh_linker_get_error_buffer ? bh_linker_get_error_buffer() : ""); + + if (NULL != bh_linker_bionic_format_dlerror) { + // call linker's __bionic_format_dlerror() + bh_linker_bionic_format_dlerror(msg, detail); + } else { + // get libc TLS array + void **libc_tls = NULL; +#if defined(__aarch64__) + __asm__("mrs %0, tpidr_el0" : "=r"(libc_tls)); +#elif defined(__arm__) + __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(libc_tls)); +#elif defined(__i386__) + __asm__("movl %%gs:0, %0" : "=r"(libc_tls)); +#elif defined(__x86_64__) + __asm__("mov %%fs:0, %0" : "=r"(libc_tls)); +#endif + + // build error message from linker's error buffer, save it in TLS + char *errmsg = msg; + if ('\0' != detail[0]) { + char *errmsg_tls = (char *)pthread_getspecific(bh_dl_monitor_dlerror_msg_tls_key); + if (NULL == errmsg_tls) { + if (NULL == (errmsg_tls = malloc(ERRMSG_SZ))) goto end; + pthread_setspecific(bh_dl_monitor_dlerror_msg_tls_key, (void *)errmsg_tls); + } + snprintf(errmsg_tls, ERRMSG_SZ, "%s: %s", msg, detail); + errmsg = errmsg_tls; + } + + end: + // set dlerror message + ((char **)libc_tls)[LIBC_TLS_SLOT_DLERROR] = errmsg; + } +} + +// dlerror message TLS DTOR +static void bh_dl_monitor_dlerror_msg_tls_dtor(void *buf) { + if (NULL != buf) free(buf); +} + +// lock between "dlclose"(wrlock) and "read elf cache"(rdlock) +static pthread_rwlock_t bh_dl_monitor_dlclose_lock = PTHREAD_RWLOCK_INITIALIZER; + +static int bh_dl_monitor_dlclose_wrlock(void) { + return pthread_rwlock_wrlock(&bh_dl_monitor_dlclose_lock); +} + +void bh_dl_monitor_dlclose_rdlock(void) { + pthread_rwlock_rdlock(&bh_dl_monitor_dlclose_lock); +} + +void bh_dl_monitor_dlclose_unlock(void) { + pthread_rwlock_unlock(&bh_dl_monitor_dlclose_lock); +} + +// proxy for dlopen() when API level [16, 25] +static void *bh_dl_monitor_proxy_dlopen(const char *filename, int flags) { + bh_dl_monitor_call_cb_pre(filename); + int api_level = bh_util_get_api_level(); + + // call dlopen() + bh_linker_add_lock_count(); + void *handle = NULL; + if (api_level >= __ANDROID_API_J__ && api_level <= __ANDROID_API_M__) { + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + handle = bh_dl_monitor_orig_dlopen(filename, flags); + else + handle = BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_dlopen, bh_dl_monitor_dlopen_t, filename, flags); + } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + void *caller_addr = BYTEHOOK_RETURN_ADDRESS(); + if (NULL != bh_linker_dlopen_ext) + handle = bh_linker_dlopen_ext(filename, flags, NULL, caller_addr); + else { + bh_linker_lock(); + handle = bh_linker_do_dlopen(filename, flags, NULL, caller_addr); + if (NULL == handle) bh_dl_monitor_set_dlerror_msg(); + bh_linker_unlock(); + } + } + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && NULL != handle && NULL != bh_dl_monitor_post_dlopen) { + BH_LOG_INFO("DL monitor: post dlopen(), filename: %s", filename); + bh_dl_monitor_post_dlopen(bh_dl_monitor_post_dlopen_arg); + } + + BYTEHOOK_POP_STACK(); + bh_dl_monitor_call_cb_post(filename, NULL != handle ? 0 : -1); + return handle; +} + +// proxy for android_dlopen_ext() when API level [21, 25] +static void *bh_dl_monitor_proxy_android_dlopen_ext(const char *filename, int flags, const void *extinfo) { + bh_dl_monitor_call_cb_pre(filename); + int api_level = bh_util_get_api_level(); + + // call android_dlopen_ext() + bh_linker_add_lock_count(); + void *handle = NULL; + if (api_level >= __ANDROID_API_L__ && api_level <= __ANDROID_API_M__) { + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + handle = bh_dl_monitor_orig_android_dlopen_ext(filename, flags, extinfo); + else + handle = BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_android_dlopen_ext, bh_dl_monitor_android_dlopen_ext_t, + filename, flags, extinfo); + } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + void *caller_addr = BYTEHOOK_RETURN_ADDRESS(); + if (NULL != bh_linker_dlopen_ext) + handle = bh_linker_dlopen_ext(filename, flags, extinfo, caller_addr); + else { + bh_linker_lock(); + handle = bh_linker_do_dlopen(filename, flags, extinfo, caller_addr); + if (NULL == handle) bh_dl_monitor_set_dlerror_msg(); + bh_linker_unlock(); + } + } + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && NULL != handle && NULL != bh_dl_monitor_post_dlopen) { + BH_LOG_INFO("DL monitor: post android_dlopen_ext(), filename: %s", filename); + bh_dl_monitor_post_dlopen(bh_dl_monitor_post_dlopen_arg); + } + + BYTEHOOK_POP_STACK(); + bh_dl_monitor_call_cb_post(filename, NULL != handle ? 0 : -1); + return handle; +} + +// proxy for __loader_dlopen() when API level >= 26 +static void *bh_dl_monitor_proxy_loader_dlopen(const char *filename, int flags, const void *caller_addr) { + bh_dl_monitor_call_cb_pre(filename); + + // call __loader_dlopen() + bh_linker_add_lock_count(); + void *handle = NULL; + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + handle = bh_dl_monitor_orig_loader_dlopen(filename, flags, caller_addr); + else + handle = BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_loader_dlopen, bh_dl_monitor_loader_dlopen_t, filename, + flags, caller_addr); + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && NULL != handle && NULL != bh_dl_monitor_post_dlopen) { + BH_LOG_INFO("DL monitor: post __loader_dlopen(), filename: %s", filename); + bh_dl_monitor_post_dlopen(bh_dl_monitor_post_dlopen_arg); + } + + BYTEHOOK_POP_STACK(); + bh_dl_monitor_call_cb_post(filename, NULL != handle ? 0 : -1); + return handle; +} + +// proxy for __loader_android_dlopen_ext() when API level >= 26 +static void *bh_dl_monitor_proxy_loader_android_dlopen_ext(const char *filename, int flags, + const void *extinfo, const void *caller_addr) { + bh_dl_monitor_call_cb_pre(filename); + + // call __loader_android_dlopen_ext() + bh_linker_add_lock_count(); + void *handle = NULL; + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + handle = bh_dl_monitor_orig_loader_android_dlopen_ext(filename, flags, extinfo, caller_addr); + else + handle = + BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_loader_android_dlopen_ext, + bh_dl_monitor_loader_android_dlopen_ext_t, filename, flags, extinfo, caller_addr); + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && NULL != handle && NULL != bh_dl_monitor_post_dlopen) { + BH_LOG_INFO("DL monitor: post __loader_android_dlopen_ext(), filename: %s", filename); + bh_dl_monitor_post_dlopen(bh_dl_monitor_post_dlopen_arg); + } + + BYTEHOOK_POP_STACK(); + bh_dl_monitor_call_cb_post(filename, NULL != handle ? 0 : -1); + return handle; +} + +// proxy for dlclose() +static int bh_dl_monitor_proxy_dlclose(void *handle) { + bool wrlocked = false; + if (!bh_linker_is_in_lock()) wrlocked = (0 == bh_dl_monitor_dlclose_wrlock()); + + // call dlclose() + bh_linker_add_lock_count(); + int ret; + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + ret = bh_dl_monitor_orig_dlclose(handle); + else + ret = BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_dlclose, bh_dl_monitor_dlclose_t, handle); + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && 0 == ret && NULL != bh_dl_monitor_post_dlclose) { + BH_LOG_INFO("DL monitor: post dlclose(), handle: %p", handle); + bh_dl_monitor_post_dlclose(wrlocked, bh_dl_monitor_post_dlclose_arg); + } + + if (wrlocked) bh_dl_monitor_dlclose_unlock(); + BYTEHOOK_POP_STACK(); + return ret; +} + +// proxy for __loader_dlclose() +static int bh_dl_monitor_proxy_loader_dlclose(void *handle) { + bool wrlocked = false; + if (!bh_linker_is_in_lock()) wrlocked = (0 == bh_dl_monitor_dlclose_wrlock()); + + // call __loader_dlclose() + bh_linker_add_lock_count(); + int ret; + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + ret = bh_dl_monitor_orig_loader_dlclose(handle); + else + ret = BYTEHOOK_CALL_PREV(bh_dl_monitor_proxy_loader_dlclose, bh_dl_monitor_loader_dlclose_t, handle); + bh_linker_sub_lock_count(); + + // call dl_iterate_phdr() to update ELF-info-cache + if (!bh_linker_is_in_lock() && 0 == ret && NULL != bh_dl_monitor_post_dlclose) { + BH_LOG_INFO("DL monitor: post __loader_dlclose(), handle: %p", handle); + bh_dl_monitor_post_dlclose(wrlocked, bh_dl_monitor_post_dlclose_arg); + } + + if (wrlocked) bh_dl_monitor_dlclose_unlock(); + BYTEHOOK_POP_STACK(); + return ret; +} + +static int bh_dl_monitor_hook(void) { + int api_level = bh_util_get_api_level(); + + if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + if (NULL != bh_linker_do_dlopen && NULL == bh_linker_bionic_format_dlerror && + NULL != bh_linker_get_error_buffer) { + if (0 != pthread_key_create(&bh_dl_monitor_dlerror_msg_tls_key, bh_dl_monitor_dlerror_msg_tls_dtor)) + goto err; + } + } + + if (api_level >= __ANDROID_API_J__ && api_level <= __ANDROID_API_N_MR1__) { + if (NULL == (bh_dl_monitor_stub_dlopen = bh_core_hook_all( + NULL, BH_CONST_SYM_DLOPEN, (void *)bh_dl_monitor_proxy_dlopen, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) ? bh_dl_monitor_proxy_dlopen_hooked : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + } + + if (api_level >= __ANDROID_API_L__ && api_level <= __ANDROID_API_N_MR1__) { + if (NULL == + (bh_dl_monitor_stub_android_dlopen_ext = bh_core_hook_all( + NULL, BH_CONST_SYM_ANDROID_DLOPEN_EXT, (void *)bh_dl_monitor_proxy_android_dlopen_ext, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) ? bh_dl_monitor_proxy_android_dlopen_ext_hooked + : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + } + + if (api_level >= __ANDROID_API_O__) { + if (NULL == + (bh_dl_monitor_stub_loader_dlopen = bh_core_hook_single( + BH_CONST_BASENAME_DL, NULL, + BH_CONST_SYM_LOADER_DLOPEN, // STT_FUNC or STT_NOTYPE + (void *)bh_dl_monitor_proxy_loader_dlopen, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) ? bh_dl_monitor_proxy_loader_dlopen_hooked : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + + if (NULL == (bh_dl_monitor_stub_loader_android_dlopen_ext = + bh_core_hook_single(BH_CONST_BASENAME_DL, NULL, + BH_CONST_SYM_LOADER_ANDROID_DLOPEN_EXT, // STT_FUNC or STT_NOTYPE + (void *)bh_dl_monitor_proxy_loader_android_dlopen_ext, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) + ? bh_dl_monitor_proxy_loader_android_dlopen_ext_hooked + : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + } + + if (api_level < __ANDROID_API_O__) { + if (NULL == (bh_dl_monitor_stub_dlclose = bh_core_hook_all( + NULL, BH_CONST_SYM_DLCLOSE, (void *)bh_dl_monitor_proxy_dlclose, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) ? bh_dl_monitor_proxy_dlclose_hooked : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + } else { + if (NULL == + (bh_dl_monitor_stub_loader_dlclose = bh_core_hook_single( + BH_CONST_BASENAME_DL, NULL, + BH_CONST_SYM_LOADER_DLCLOSE, // STT_FUNC or STT_NOTYPE + (void *)bh_dl_monitor_proxy_loader_dlclose, + (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) ? bh_dl_monitor_proxy_loader_dlclose_hooked : NULL, + NULL, (uintptr_t)(__builtin_return_address(0))))) + goto err; + } + + return 0; + +err: + bh_dl_monitor_uninit(); + return -1; +} + +static bool bh_dl_monitor_initing = false; +bool bh_dl_monitor_is_initing(void) { + return bh_dl_monitor_initing; +} + +int bh_dl_monitor_init(void) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool inited = false; + static bool inited_ok = false; + + if (inited) return inited_ok ? 0 : -1; // Do not repeat the initialization. + + int r; + pthread_mutex_lock(&lock); + bh_dl_monitor_initing = true; + if (!inited) { + __atomic_store_n(&inited, true, __ATOMIC_SEQ_CST); + BH_LOG_INFO("DL monitor: pre init"); + if (0 == (r = bh_dl_monitor_hook())) { + __atomic_store_n(&inited_ok, true, __ATOMIC_SEQ_CST); + BH_LOG_INFO("DL monitor: post init, OK"); + } else { + BH_LOG_ERROR("DL monitor: post init, FAILED"); + } + } else { + r = inited_ok ? 0 : -1; + } + bh_dl_monitor_initing = false; + pthread_mutex_unlock(&lock); + return r; +} + +void bh_dl_monitor_uninit(void) { + if (NULL != bh_dl_monitor_stub_dlopen) { + bh_core_unhook(bh_dl_monitor_stub_dlopen, 0); + bh_dl_monitor_stub_dlopen = NULL; + } + if (NULL != bh_dl_monitor_stub_android_dlopen_ext) { + bh_core_unhook(bh_dl_monitor_stub_android_dlopen_ext, 0); + bh_dl_monitor_stub_android_dlopen_ext = NULL; + } + if (NULL != bh_dl_monitor_stub_loader_dlopen) { + bh_core_unhook(bh_dl_monitor_stub_loader_dlopen, 0); + bh_dl_monitor_stub_loader_dlopen = NULL; + } + if (NULL != bh_dl_monitor_stub_loader_android_dlopen_ext) { + bh_core_unhook(bh_dl_monitor_stub_loader_android_dlopen_ext, 0); + bh_dl_monitor_stub_loader_android_dlopen_ext = NULL; + } + if (NULL != bh_dl_monitor_stub_dlclose) { + bh_core_unhook(bh_dl_monitor_stub_dlclose, 0); + bh_dl_monitor_stub_dlclose = NULL; + } + if (NULL != bh_dl_monitor_stub_loader_dlclose) { + bh_core_unhook(bh_dl_monitor_stub_loader_dlclose, 0); + bh_dl_monitor_stub_loader_dlclose = NULL; + } +} + +void bh_dl_monitor_set_post_dlopen(bh_dl_monitor_post_dlopen_t cb, void *cb_arg) { + bh_dl_monitor_post_dlopen_arg = cb_arg; + __atomic_store_n(&bh_dl_monitor_post_dlopen, cb, __ATOMIC_SEQ_CST); +} + +void bh_dl_monitor_set_post_dlclose(bh_dl_monitor_post_dlclose_t cb, void *cb_arg) { + bh_dl_monitor_post_dlclose_arg = cb_arg; + __atomic_store_n(&bh_dl_monitor_post_dlclose, cb, __ATOMIC_SEQ_CST); +} + +void bh_dl_monitor_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + if (NULL == pre && NULL == post) return; + + bh_dl_monitor_cb_t *cb_new = malloc(sizeof(bh_dl_monitor_cb_t)); + if (NULL == cb_new) return; + cb_new->pre = pre; + cb_new->post = post; + cb_new->data = data; + + bh_dl_monitor_init(); + + bh_dl_monitor_cb_t *cb = NULL; + pthread_rwlock_wrlock(&bh_dl_monitor_cbs_lock); + TAILQ_FOREACH(cb, &bh_dl_monitor_cbs, link) { + if (cb->pre == pre && cb->post == post && cb->data == data) break; + } + if (NULL == cb) { + TAILQ_INSERT_TAIL(&bh_dl_monitor_cbs, cb_new, link); + cb_new = NULL; + } + pthread_rwlock_unlock(&bh_dl_monitor_cbs_lock); + + if (NULL != cb_new) free(cb_new); +} + +void bh_dl_monitor_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + if (NULL == pre && NULL == post) return; + + bh_dl_monitor_cb_t *cb = NULL, *cb_tmp; + pthread_rwlock_wrlock(&bh_dl_monitor_cbs_lock); + TAILQ_FOREACH_SAFE(cb, &bh_dl_monitor_cbs, link, cb_tmp) { + if (cb->pre == pre && cb->post == post && cb->data == data) { + TAILQ_REMOVE(&bh_dl_monitor_cbs, cb, link); + break; + } + } + pthread_rwlock_unlock(&bh_dl_monitor_cbs_lock); + + if (NULL != cb) free(cb); +} diff --git a/app/src/main/cpp/shadowhook/sh_hub.h b/app/src/main/cpp/bytehook/bh_dl_monitor.h similarity index 57% rename from app/src/main/cpp/shadowhook/sh_hub.h rename to app/src/main/cpp/bytehook/bh_dl_monitor.h index 2e5f91f2..1184d242 100644 --- a/app/src/main/cpp/shadowhook/sh_hub.h +++ b/app/src/main/cpp/bytehook/bh_dl_monitor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,27 +19,26 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Tianzhou Shen (shentianzhou@bytedance.com) on 2020-06-02. #pragma once + #include -#include -int sh_hub_init(void); +#include "bytehook.h" -typedef struct sh_hub sh_hub_t; +typedef void (*bh_dl_monitor_post_dlopen_t)(void *arg); +void bh_dl_monitor_set_post_dlopen(bh_dl_monitor_post_dlopen_t cb, void *cb_arg); -sh_hub_t *sh_hub_create(uintptr_t target_addr, uintptr_t *trampo); -void sh_hub_destroy(sh_hub_t *self, bool with_delay); +typedef void (*bh_dl_monitor_post_dlclose_t)(bool sync_refresh, void *arg); +void bh_dl_monitor_set_post_dlclose(bh_dl_monitor_post_dlclose_t cb, void *cb_arg); -uintptr_t sh_hub_get_orig_addr(sh_hub_t *self); -uintptr_t *sh_hub_get_orig_addr_addr(sh_hub_t *self); +void bh_dl_monitor_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); +void bh_dl_monitor_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); -int sh_hub_add_proxy(sh_hub_t *self, uintptr_t func); -int sh_hub_del_proxy(sh_hub_t *self, uintptr_t func, bool *have_enabled_proxy); +bool bh_dl_monitor_is_initing(void); +int bh_dl_monitor_init(void); +void bh_dl_monitor_uninit(void); -void *sh_hub_get_prev_func(void *func); -void sh_hub_pop_stack(void *return_address); -void sh_hub_allow_reentrant(void *return_address); -void sh_hub_disallow_reentrant(void *return_address); -void *sh_hub_get_return_address(void); +void bh_dl_monitor_dlclose_rdlock(void); +void bh_dl_monitor_dlclose_unlock(void); diff --git a/app/src/main/cpp/bytehook/bh_elf.c b/app/src/main/cpp/bytehook/bh_elf.c new file mode 100644 index 00000000..a2ea182b --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_elf.c @@ -0,0 +1,696 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_elf.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_log.h" +#include "bh_sleb128.h" +#include "bh_util.h" +#include "bytesig.h" + +#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) +#define PFLAGS_TO_PROT(x) \ + (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ + MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) + +#define BH_ELF_IS_IMPORT_SYM(shndx) (SHN_UNDEF == (shndx)) +#define BH_ELF_IS_EXPORT_SYM(shndx) (SHN_UNDEF != (shndx)) // this is enough for .dynsym + +#if defined(__arm__) +#define BH_ELF_R_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt +#define BH_ELF_R_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn +#define BH_ELF_R_ABS R_ARM_ABS32 //.rel.dyn +#elif defined(__aarch64__) +#define BH_ELF_R_JUMP_SLOT R_AARCH64_JUMP_SLOT +#define BH_ELF_R_GLOB_DAT R_AARCH64_GLOB_DAT +#define BH_ELF_R_ABS R_AARCH64_ABS64 +#elif defined(__i386__) +#define BH_ELF_R_JUMP_SLOT R_386_JMP_SLOT +#define BH_ELF_R_GLOB_DAT R_386_GLOB_DAT +#define BH_ELF_R_ABS R_386_32 +#elif defined(__x86_64__) +#define BH_ELF_R_JUMP_SLOT R_X86_64_JUMP_SLOT +#define BH_ELF_R_GLOB_DAT R_X86_64_GLOB_DAT +#define BH_ELF_R_ABS R_X86_64_64 +#endif + +#if defined(__LP64__) +#define BH_ELF_R_SYM(info) ELF64_R_SYM(info) +#define BH_ELF_R_TYPE(info) ELF64_R_TYPE(info) +#else +#define BH_ELF_R_SYM(info) ELF32_R_SYM(info) +#define BH_ELF_R_TYPE(info) ELF32_R_TYPE(info) +#endif + +#define RELOCATION_GROUPED_BY_INFO_FLAG ((size_t)1) +#define RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG ((size_t)2) +#if defined(__LP64__) +#define RELOCATION_GROUPED_BY_ADDEND_FLAG ((size_t)4) +#endif +#define RELOCATION_GROUP_HAS_ADDEND_FLAG ((size_t)8) + +static void bh_elf_iterate_aps2(bh_sleb128_decoder_t *decoder, bool (*callback)(Elf_Reloc *, void *), + void *arg) { + size_t num_relocs; + if (0 != bh_sleb128_decoder_next(decoder, &num_relocs)) return; + + Elf_Reloc reloc; + if (0 != bh_sleb128_decoder_next(decoder, (size_t *)&reloc.r_offset)) return; + + for (size_t idx = 0; idx < num_relocs;) { + size_t group_size; + if (0 != bh_sleb128_decoder_next(decoder, &group_size)) return; + size_t group_flags; + if (0 != bh_sleb128_decoder_next(decoder, &group_flags)) return; + size_t group_r_offset_delta = 0; + + if (group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) { + if (0 != bh_sleb128_decoder_next(decoder, &group_r_offset_delta)) return; + } + if (group_flags & RELOCATION_GROUPED_BY_INFO_FLAG) { + if (0 != bh_sleb128_decoder_next(decoder, (size_t *)&reloc.r_info)) return; + } + +#if defined(__LP64__) + const size_t group_flags_reloc = + group_flags & (RELOCATION_GROUP_HAS_ADDEND_FLAG | RELOCATION_GROUPED_BY_ADDEND_FLAG); + if (group_flags_reloc == (RELOCATION_GROUP_HAS_ADDEND_FLAG | RELOCATION_GROUPED_BY_ADDEND_FLAG)) { + size_t val; + if (0 != bh_sleb128_decoder_next(decoder, &val)) return; + reloc.r_addend += val; + } else { + reloc.r_addend = 0; + } +#else + if (__predict_false(group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG)) return; +#endif + + for (size_t i = 0; i < group_size; i++) { + if (group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) { + reloc.r_offset += group_r_offset_delta; + } else { + size_t val; + if (0 != bh_sleb128_decoder_next(decoder, &val)) return; + reloc.r_offset += val; + } + if ((group_flags & RELOCATION_GROUPED_BY_INFO_FLAG) == 0) { + if (0 != bh_sleb128_decoder_next(decoder, (size_t *)&reloc.r_info)) return; + } +#if defined(__LP64__) + if (group_flags_reloc == RELOCATION_GROUP_HAS_ADDEND_FLAG) { + size_t val; + if (0 != bh_sleb128_decoder_next(decoder, &val)) return; + reloc.r_addend += val; + } +#endif + if (!callback(&reloc, arg)) return; + } + + idx += group_size; + } +} + +static void bh_elf_parse_dynamic_unsafe(bh_elf_t *self, ElfW(Dyn) *dynamic) { + // iterate the dynamic segment + for (ElfW(Dyn) *entry = dynamic; entry && entry->d_tag != DT_NULL; entry++) { + switch (entry->d_tag) { + //.rel.plt / .rela.plt + case DT_JMPREL: + self->rel_plt = (const Elf_Reloc *)(self->load_bias + entry->d_un.d_ptr); + break; + case DT_PLTRELSZ: + self->rel_plt_cnt = (size_t)entry->d_un.d_val / sizeof(Elf_Reloc); + break; + + //.rel.dyn / .rela.dyn + case DT_REL: + case DT_RELA: + self->rel_dyn = (const Elf_Reloc *)(self->load_bias + entry->d_un.d_ptr); + break; + case DT_RELSZ: + case DT_RELASZ: + self->rel_dyn_cnt = (size_t)entry->d_un.d_val / sizeof(Elf_Reloc); + break; + + //.rel.dyn / .rela.dyn (APS2 format) + case DT_ANDROID_REL: + case DT_ANDROID_RELA: + self->rel_dyn_aps2 = (uint8_t *)(self->load_bias + entry->d_un.d_ptr); + break; + case DT_ANDROID_RELSZ: + case DT_ANDROID_RELASZ: + self->rel_dyn_aps2_sz = (size_t)entry->d_un.d_val; + break; + + //.dynsym + case DT_SYMTAB: + self->dynsym = (ElfW(Sym) *)(self->load_bias + entry->d_un.d_ptr); + break; + + //.dynstr + case DT_STRTAB: + self->dynstr = (const char *)(self->load_bias + entry->d_un.d_ptr); + break; + + //.hash + case DT_HASH: + self->sysv_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; + self->sysv_hash.chains_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; + self->sysv_hash.buckets = &(((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]); + self->sysv_hash.chains = &(self->sysv_hash.buckets[self->sysv_hash.buckets_cnt]); + break; + + //.gnu.hash + case DT_GNU_HASH: + self->gnu_hash.buckets_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[0]; + self->gnu_hash.symoffset = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[1]; + self->gnu_hash.bloom_cnt = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[2]; + self->gnu_hash.bloom_shift = ((const uint32_t *)(self->load_bias + entry->d_un.d_ptr))[3]; + self->gnu_hash.bloom = (const ElfW(Addr) *)(self->load_bias + entry->d_un.d_ptr + 16); + self->gnu_hash.buckets = (const uint32_t *)(&(self->gnu_hash.bloom[self->gnu_hash.bloom_cnt])); + self->gnu_hash.chains = (const uint32_t *)(&(self->gnu_hash.buckets[self->gnu_hash.buckets_cnt])); + break; + + default: + break; + } + } + + // check and fix APS2 + if (NULL != self->rel_dyn_aps2) { + char *rel = (char *)self->rel_dyn_aps2; + if (self->rel_dyn_aps2_sz < 4 || rel[0] != 'A' || rel[1] != 'P' || rel[2] != 'S' || rel[3] != '2') { + self->rel_dyn_aps2 = 0; + self->rel_dyn_aps2_sz = 0; + } else { + self->rel_dyn_aps2 += 4; + self->rel_dyn_aps2_sz -= 4; + } + } +} + +static int bh_elf_parse_dynamic(bh_elf_t *self) { + if (self->error) return -1; + if (self->dyn_parsed) return 0; + + pthread_mutex_lock(&self->dyn_parse_lock); + if (!self->dyn_parsed) { + self->dyn_parsed = true; + BYTESIG_TRY(SIGSEGV, SIGBUS) { + ElfW(Dyn) *dynamic = NULL; + for (size_t i = 0; i < self->dlpi_phnum; i++) { + if (self->dlpi_phdr[i].p_type == PT_DYNAMIC) { + dynamic = (ElfW(Dyn) *)(self->load_bias + self->dlpi_phdr[i].p_vaddr); + break; + } + } + if (NULL == dynamic) + self->error = true; + else + bh_elf_parse_dynamic_unsafe(self, dynamic); + } + BYTESIG_CATCH() + self->error = true; + BYTESIG_EXIT + } + pthread_mutex_unlock(&self->dyn_parse_lock); + + return self->error ? -1 : 0; +} + +bh_elf_t *bh_elf_create(struct dl_phdr_info *info) { + if (0 == info->dlpi_phdr || NULL == info->dlpi_name || NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) + return NULL; + + bh_elf_t *self; + if (NULL == (self = calloc(1, sizeof(bh_elf_t)))) return NULL; + if (NULL == (self->pathname = strdup(info->dlpi_name))) { + free(self); + return NULL; + } + self->exist = false; + pthread_mutex_init(&self->hook_lock, NULL); + self->error = false; +#ifdef __LP64__ + self->cfi_hooked = false; + self->cfi_hooked_ok = false; + pthread_mutex_init(&self->cfi_hook_lock, NULL); +#endif + self->load_bias = info->dlpi_addr; + self->dlpi_phdr = info->dlpi_phdr; + self->dlpi_phnum = info->dlpi_phnum; + self->dyn_parsed = false; + pthread_mutex_init(&self->dyn_parse_lock, NULL); + + return self; +} + +void bh_elf_destroy(bh_elf_t **self) { + if (NULL == self || NULL == *self) return; + + pthread_mutex_destroy(&(*self)->hook_lock); + pthread_mutex_destroy(&(*self)->dyn_parse_lock); + if (NULL != (*self)->pathname) free((void *)(uintptr_t)(*self)->pathname); + free(*self); + *self = NULL; +} + +bool bh_elf_is_match(bh_elf_t *self, const char *name) { + if ('/' == self->pathname[0] && '/' != name[0]) + return bh_util_ends_with(self->pathname, name); + else if ('/' != self->pathname[0] && '/' == name[0]) + return bh_util_ends_with(name, self->pathname); + else + return 0 == strcmp(self->pathname, name); +} + +bool bh_elf_get_error(bh_elf_t *self) { + return self->error; +} + +void bh_elf_set_error(bh_elf_t *self, bool error) { + self->error = error; +} + +#ifdef __LP64__ +void bh_elf_cfi_hook_lock(bh_elf_t *self) { + pthread_mutex_lock(&self->cfi_hook_lock); +} + +void bh_elf_cfi_hook_unlock(bh_elf_t *self) { + pthread_mutex_unlock(&self->cfi_hook_lock); +} +#endif + +void bh_elf_hook_lock(bh_elf_t *self) { + pthread_mutex_lock(&self->hook_lock); +} + +void bh_elf_hook_unlock(bh_elf_t *self) { + pthread_mutex_unlock(&self->hook_lock); +} + +void bh_elf_set_exist(bh_elf_t *self) { + self->exist = true; +} + +void bh_elf_unset_exist(bh_elf_t *self) { + self->exist = false; +} + +bool bh_elf_is_exist(bh_elf_t *self) { + return self->exist; +} + +static int bh_elf_get_protect_by_addr_unsafe(bh_elf_t *self, void *addr) { + for (size_t i = 0; i < self->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); + if (self->dlpi_phdr[i].p_type == PT_GNU_RELRO) + if ((uintptr_t)addr >= (self->load_bias + phdr->p_vaddr) && + (uintptr_t)addr < (self->load_bias + phdr->p_vaddr + phdr->p_memsz)) + return PROT_READ; + } + + for (size_t i = 0; i < self->dlpi_phnum; i++) { + const ElfW(Phdr) *phdr = &(self->dlpi_phdr[i]); + if (self->dlpi_phdr[i].p_type == PT_LOAD) + if ((uintptr_t)addr >= (self->load_bias + phdr->p_vaddr) && + (uintptr_t)addr < (self->load_bias + phdr->p_vaddr + phdr->p_memsz)) + return PFLAGS_TO_PROT(phdr->p_flags); + } + + return 0; +} + +int bh_elf_get_protect_by_addr(bh_elf_t *self, void *addr) { + int prot = 0; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + prot = bh_elf_get_protect_by_addr_unsafe(self, addr); + } + BYTESIG_CATCH() { + self->error = true; + } + BYTESIG_EXIT + + return prot; +} + +static uint32_t bh_elf_sysv_hash(const uint8_t *name) { + uint32_t h = 0, g; + + while (*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + return h; +} + +static uint32_t bh_elf_gnu_hash(const uint8_t *name) { + uint32_t h = 5381; + + while (*name) { + h += (h << 5) + *name++; + } + return h; +} + +static ElfW(Sym) *bh_elf_find_symbol_by_name_use_sysv_hash(bh_elf_t *self, const char *sym_name) { + uint32_t hash = bh_elf_sysv_hash((const uint8_t *)sym_name); + + for (uint32_t i = self->sysv_hash.buckets[hash % self->sysv_hash.buckets_cnt]; 0 != i; + i = self->sysv_hash.chains[i]) { + ElfW(Sym) *sym = self->dynsym + i; + unsigned char type = ELF_ST_TYPE(sym->st_info); + if (STT_FUNC != type && STT_GNU_IFUNC != type && STT_NOTYPE != type) + continue; // find function only, allow no-type + if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue; + return sym; + } + + return NULL; +} + +static ElfW(Sym) *bh_elf_find_symbol_by_name_use_gnu_hash(bh_elf_t *self, const char *sym_name) { + uint32_t hash = bh_elf_gnu_hash((const uint8_t *)sym_name); + + static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; + size_t word = self->gnu_hash.bloom[(hash / elfclass_bits) % self->gnu_hash.bloom_cnt]; + size_t mask = 0 | (size_t)1 << (hash % elfclass_bits) | + (size_t)1 << ((hash >> self->gnu_hash.bloom_shift) % elfclass_bits); + + // if at least one bit is not set, this symbol is surely missing + if ((word & mask) != mask) return NULL; + + // ignore STN_UNDEF + uint32_t i = self->gnu_hash.buckets[hash % self->gnu_hash.buckets_cnt]; + if (i < self->gnu_hash.symoffset) return NULL; + + // loop through the chain + while (1) { + ElfW(Sym) *sym = self->dynsym + i; + unsigned char type = ELF_ST_TYPE(sym->st_info); + uint32_t sym_hash = self->gnu_hash.chains[i - self->gnu_hash.symoffset]; + + if ((hash | (uint32_t)1) == (sym_hash | (uint32_t)1) && + (STT_FUNC == type || STT_GNU_IFUNC == type || + STT_NOTYPE == type) && // find function only, allow no-type + 0 == strcmp(self->dynstr + sym->st_name, sym_name)) { + return sym; + } + + // chain ends with an element with the lowest bit set to 1 + if (sym_hash & (uint32_t)1) break; + + i++; + } + + return NULL; +} + +static ElfW(Sym) *bh_elf_find_symbol_by_name_use_import_table(bh_elf_t *self, const char *sym_name) { + for (size_t i = 0; i < self->gnu_hash.symoffset; i++) { + ElfW(Sym) *sym = self->dynsym + i; + unsigned char type = ELF_ST_TYPE(sym->st_info); + if (STT_FUNC != type && STT_NOTYPE != type) continue; // find function only, allow no-type + if (0 != strcmp(self->dynstr + sym->st_name, sym_name)) continue; + return sym; + } + + return NULL; +} + +static ElfW(Sym) *bh_elf_find_import_func_symbol_by_symbol_name(bh_elf_t *self, const char *sym_name) { + ElfW(Sym) *sym; + + // from SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + if (self->sysv_hash.buckets_cnt > 0) { + sym = bh_elf_find_symbol_by_name_use_sysv_hash(self, sym_name); + if (NULL != sym && BH_ELF_IS_IMPORT_SYM(sym->st_shndx)) return sym; + } + + // from import table's linear search (.dynsym -> .dynstr), O(n) + O(1) + if (self->gnu_hash.symoffset > 0) { + sym = bh_elf_find_symbol_by_name_use_import_table(self, sym_name); + if (NULL != sym && BH_ELF_IS_IMPORT_SYM(sym->st_shndx)) return sym; + } + + // .gnu.hash only contains the exported functions of ELF. If there is only .gnu.hash in ELF, + // and the caller and callee of the hook function are the same ELF, + // and this function also exists in the export table of ELF, you can find it through .gnu.hash. + // + // from GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + if (self->gnu_hash.buckets_cnt > 0) { + sym = bh_elf_find_symbol_by_name_use_gnu_hash(self, sym_name); + if (NULL != sym && BH_ELF_IS_EXPORT_SYM(sym->st_shndx)) return sym; + } + + // PLT that is only used internally may not be included in the .hash or .gnu.hash. + // + // from .rel.plt and .rel.dyn, O(n) + for (size_t i = 0; i < self->rel_plt_cnt; i++) { + size_t sym_idx = BH_ELF_R_SYM(self->rel_plt[i].r_info); + if (0 == strcmp(self->dynstr + self->dynsym[sym_idx].st_name, sym_name)) return &self->dynsym[sym_idx]; + } + for (size_t i = 0; i < self->rel_dyn_cnt; i++) { + size_t sym_idx = BH_ELF_R_SYM(self->rel_dyn[i].r_info); + if (0 == strcmp(self->dynstr + self->dynsym[sym_idx].st_name, sym_name)) return &self->dynsym[sym_idx]; + } + return NULL; +} + +static ElfW(Sym) *bh_elf_find_export_func_symbol_by_symbol_name_unsafe(bh_elf_t *self, const char *sym_name) { + ElfW(Sym) *sym = NULL; + + // from GNU hash (.gnu.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + if (self->gnu_hash.buckets_cnt > 0) { + sym = bh_elf_find_symbol_by_name_use_gnu_hash(self, sym_name); + if (NULL != sym && BH_ELF_IS_EXPORT_SYM(sym->st_shndx)) return sym; + } + + // from SYSV hash (.hash -> .dynsym -> .dynstr), O(x) + O(1) + O(1) + if (self->sysv_hash.buckets_cnt > 0) { + sym = bh_elf_find_symbol_by_name_use_sysv_hash(self, sym_name); + if (NULL != sym && BH_ELF_IS_EXPORT_SYM(sym->st_shndx)) return sym; + } + + return NULL; +} + +ElfW(Sym) *bh_elf_find_export_func_symbol_by_symbol_name(bh_elf_t *self, const char *sym_name) { + if (self->error) return NULL; + if (0 != bh_elf_parse_dynamic(self)) return NULL; + + ElfW(Sym) *sym = NULL; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + sym = bh_elf_find_export_func_symbol_by_symbol_name_unsafe(self, sym_name); + } + BYTESIG_CATCH() { + self->error = true; + sym = NULL; + } + BYTESIG_EXIT + + return sym; +} + +static bool bh_elf_find_import_func_addr_by_symbol_name_unsafe_aps2_cb(Elf_Reloc *rel, void *arg) { + void **pkg = (void **)arg; + bh_elf_t *self = (bh_elf_t *)*pkg++; + const ElfW(Sym) *sym = (const ElfW(Sym) *)*pkg++; + void **addr_array = (void **)*pkg++; + size_t addr_array_cap = (size_t)*pkg++; + size_t *addr_array_sz = (size_t *)*pkg; + + if (&(self->dynsym[BH_ELF_R_SYM(rel->r_info)]) != sym) return true; + if (BH_ELF_R_GLOB_DAT != BH_ELF_R_TYPE(rel->r_info) && BH_ELF_R_ABS != BH_ELF_R_TYPE(rel->r_info)) + return true; + + addr_array[(*addr_array_sz)++] = (void *)(self->load_bias + rel->r_offset); + return *addr_array_sz < addr_array_cap; +} + +static size_t bh_elf_find_import_func_addr_by_symbol_name_unsafe(bh_elf_t *self, const char *sym_name, + void **addr_array, size_t addr_array_cap) { + size_t addr_array_sz = 0; + + ElfW(Sym) *sym = bh_elf_find_import_func_symbol_by_symbol_name(self, sym_name); + if (NULL == sym) return 0; + + for (size_t i = 0; i < self->rel_plt_cnt; i++) { + const Elf_Reloc *rel = &(self->rel_plt[i]); + if (&(self->dynsym[BH_ELF_R_SYM(rel->r_info)]) != sym) continue; + if (BH_ELF_R_JUMP_SLOT != BH_ELF_R_TYPE(rel->r_info)) continue; + + addr_array[addr_array_sz++] = (void *)(self->load_bias + rel->r_offset); + if (addr_array_sz >= addr_array_cap) return addr_array_sz; + } + + for (size_t i = 0; i < self->rel_dyn_cnt; i++) { + const Elf_Reloc *rel = &(self->rel_dyn[i]); + if (&(self->dynsym[BH_ELF_R_SYM(rel->r_info)]) != sym) continue; + if (BH_ELF_R_GLOB_DAT != BH_ELF_R_TYPE(rel->r_info) && BH_ELF_R_ABS != BH_ELF_R_TYPE(rel->r_info)) + continue; + + addr_array[addr_array_sz++] = (void *)(self->load_bias + rel->r_offset); + if (addr_array_sz >= addr_array_cap) return addr_array_sz; + } + + if (NULL != self->rel_dyn_aps2) { + bh_sleb128_decoder_t decoder; + bh_sleb128_decoder_init(&decoder, self->rel_dyn_aps2, self->rel_dyn_aps2_sz); + void *pkg[5] = {self, sym, (void *)addr_array, (void *)addr_array_cap, &addr_array_sz}; + bh_elf_iterate_aps2(&decoder, bh_elf_find_import_func_addr_by_symbol_name_unsafe_aps2_cb, pkg); + } + + return addr_array_sz; +} + +size_t bh_elf_find_import_func_addr_by_symbol_name(bh_elf_t *self, const char *sym_name, void **addr_array, + size_t addr_array_cap) { + if (self->error) return 0; + if (0 != bh_elf_parse_dynamic(self)) return 0; + + size_t addr_array_sz = 0; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + addr_array_sz = + bh_elf_find_import_func_addr_by_symbol_name_unsafe(self, sym_name, addr_array, addr_array_cap); + } + BYTESIG_CATCH() { + self->error = true; + addr_array_sz = 0; + } + BYTESIG_EXIT + + return addr_array_sz; +} + +static bool bh_elf_find_import_func_addr_by_callee_addr_unsafe_aps2_cb(Elf_Reloc *rel, void *arg) { + void **pkg = (void **)arg; + bh_elf_t *self = (bh_elf_t *)*pkg++; + void *target_addr = *pkg++; + void **addr_array = (void **)*pkg++; + size_t addr_array_cap = (size_t)*pkg++; + size_t *addr_array_sz = (size_t *)*pkg; + + if (BH_ELF_R_GLOB_DAT != BH_ELF_R_TYPE(rel->r_info) && BH_ELF_R_ABS != BH_ELF_R_TYPE(rel->r_info)) + return true; + if (*((void **)(self->load_bias + rel->r_offset)) != target_addr) return true; + + addr_array[(*addr_array_sz)++] = (void *)(self->load_bias + rel->r_offset); + return *addr_array_sz < addr_array_cap; +} + +static size_t bh_elf_find_import_func_addr_by_callee_addr_unsafe(bh_elf_t *self, void *target_addr, + void **addr_array, size_t addr_array_cap) { + size_t addr_array_sz = 0; + + for (size_t i = 0; i < self->rel_plt_cnt; i++) { + const Elf_Reloc *rel = &(self->rel_plt[i]); + if (BH_ELF_R_JUMP_SLOT != BH_ELF_R_TYPE(rel->r_info)) continue; + if (*((void **)(self->load_bias + rel->r_offset)) != target_addr) continue; + + addr_array[addr_array_sz++] = (void *)(self->load_bias + rel->r_offset); + if (addr_array_sz >= addr_array_cap) return addr_array_sz; + } + + for (size_t i = 0; i < self->rel_dyn_cnt; i++) { + const Elf_Reloc *rel = &(self->rel_dyn[i]); + if (BH_ELF_R_GLOB_DAT != BH_ELF_R_TYPE(rel->r_info) && BH_ELF_R_ABS != BH_ELF_R_TYPE(rel->r_info)) + continue; + if (*((void **)(self->load_bias + rel->r_offset)) != target_addr) continue; + + addr_array[addr_array_sz++] = (void *)(self->load_bias + rel->r_offset); + if (addr_array_sz >= addr_array_cap) return addr_array_sz; + } + + if (NULL != self->rel_dyn_aps2) { + bh_sleb128_decoder_t decoder; + bh_sleb128_decoder_init(&decoder, self->rel_dyn_aps2, self->rel_dyn_aps2_sz); + void *pkg[5] = {self, target_addr, (void *)addr_array, (void *)addr_array_cap, &addr_array_sz}; + bh_elf_iterate_aps2(&decoder, bh_elf_find_import_func_addr_by_callee_addr_unsafe_aps2_cb, pkg); + } + + return addr_array_sz; +} + +size_t bh_elf_find_import_func_addr_by_callee_addr(bh_elf_t *self, void *target_addr, void **addr_array, + size_t addr_array_cap) { + if (self->error) return 0; + if (0 != bh_elf_parse_dynamic(self)) return 0; + + size_t addr_array_sz; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + addr_array_sz = + bh_elf_find_import_func_addr_by_callee_addr_unsafe(self, target_addr, addr_array, addr_array_cap); + } + BYTESIG_CATCH() { + self->error = true; + addr_array_sz = 0; + } + BYTESIG_EXIT + + return addr_array_sz; +} + +static void *bh_elf_find_export_func_addr_by_symbol_name_unsafe(bh_elf_t *self, const char *sym_name) { + ElfW(Sym) *sym = bh_elf_find_export_func_symbol_by_symbol_name_unsafe(self, sym_name); + if (NULL == sym) return NULL; + + return (void *)(self->load_bias + sym->st_value); +} + +void *bh_elf_find_export_func_addr_by_symbol_name(bh_elf_t *self, const char *sym_name) { + if (self->error) return NULL; + if (0 != bh_elf_parse_dynamic(self)) return NULL; + + void *addr = NULL; + + BYTESIG_TRY(SIGSEGV, SIGBUS) { + addr = bh_elf_find_export_func_addr_by_symbol_name_unsafe(self, sym_name); + } + BYTESIG_CATCH() { + self->error = true; + addr = NULL; + } + BYTESIG_EXIT + + return addr; +} diff --git a/app/src/main/cpp/bytehook/bh_elf.h b/app/src/main/cpp/bytehook/bh_elf.h new file mode 100644 index 00000000..2b4c1f02 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_elf.h @@ -0,0 +1,151 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "tree.h" + +// API level >= 21: +// https://android.googlesource.com/platform/bionic/+/refs/heads/master/linker/linker_common_types.h Android +// uses RELA for LP64. +#if defined(__LP64__) +typedef ElfW(Rela) Elf_Reloc; +#else +typedef ElfW(Rel) Elf_Reloc; +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" + +// program header list +typedef struct bh_elf_ph { + uintptr_t start; + uintptr_t end; + int protect; + SLIST_ENTRY(bh_elf_ph, ) link; +} bh_elf_ph_t; +typedef SLIST_HEAD(bh_elf_ph_list, bh_elf_ph, ) bh_elf_ph_list_t; + +// ELF info +typedef struct bh_elf { + bool exist; + pthread_mutex_t hook_lock; + bool error; +#ifdef __LP64__ + bool cfi_hooked; + bool cfi_hooked_ok; + pthread_mutex_t cfi_hook_lock; +#endif + + const char *pathname; + uintptr_t load_bias; + const ElfW(Phdr) *dlpi_phdr; + size_t dlpi_phnum; + bool dyn_parsed; + pthread_mutex_t dyn_parse_lock; + + const Elf_Reloc *rel_plt; //.rel.plt / .rela.plt (relocation table for function) + size_t rel_plt_cnt; + + const Elf_Reloc *rel_dyn; //.rel.dyn / .rela.dyn (relocation table for data) + size_t rel_dyn_cnt; + + uint8_t *rel_dyn_aps2; //.rel.dyn / .rela.dyn (relocation table for data. APS2 format) + size_t rel_dyn_aps2_sz; + + ElfW(Sym) *dynsym; //.dynsym (dynamic symbol-table, symbol-index -> string-table's offset) + const char *dynstr; //.dynstr (dynamic string-table) + + //.hash (SYSV hash for string-table) + struct { + const uint32_t *buckets; + uint32_t buckets_cnt; + const uint32_t *chains; + uint32_t chains_cnt; + } sysv_hash; + + //.gnu.hash (GNU hash for string-table) + struct { + const uint32_t *buckets; + uint32_t buckets_cnt; + const uint32_t *chains; + uint32_t symoffset; + const ElfW(Addr) *bloom; + uint32_t bloom_cnt; + uint32_t bloom_shift; + } gnu_hash; + + RB_ENTRY(bh_elf) link; + TAILQ_ENTRY(bh_elf, ) link_list; +} bh_elf_t; +typedef TAILQ_HEAD(bh_elf_list, bh_elf, ) bh_elf_list_t; + +#pragma clang diagnostic pop + +bh_elf_t *bh_elf_create(struct dl_phdr_info *info); +void bh_elf_destroy(bh_elf_t **self); + +bool bh_elf_is_match(bh_elf_t *self, const char *name); + +bool bh_elf_get_error(bh_elf_t *self); +void bh_elf_set_error(bh_elf_t *self, bool error); + +#ifdef __LP64__ +void bh_elf_cfi_hook_lock(bh_elf_t *self); +void bh_elf_cfi_hook_unlock(bh_elf_t *self); +#endif + +void bh_elf_hook_lock(bh_elf_t *self); +void bh_elf_hook_unlock(bh_elf_t *self); + +void bh_elf_set_exist(bh_elf_t *self); +void bh_elf_unset_exist(bh_elf_t *self); +bool bh_elf_is_exist(bh_elf_t *self); + +// get protect info by address +int bh_elf_get_protect_by_addr(bh_elf_t *self, void *addr); + +// find export function symbol info by symbol name +// signal-safe +ElfW(Sym) *bh_elf_find_export_func_symbol_by_symbol_name(bh_elf_t *self, const char *sym_name); + +// find import function address'address list by symbol name form .rel.plt and .rel.dyn +// signal-safe +size_t bh_elf_find_import_func_addr_by_symbol_name(bh_elf_t *self, const char *sym_name, void **addr_array, + size_t addr_array_cap); + +// find import function address'address list by callee address form .rel.plt and .rel.dyn +// signal-safe +size_t bh_elf_find_import_func_addr_by_callee_addr(bh_elf_t *self, void *target_addr, void **addr_array, + size_t addr_array_cap); + +// find export function address by symbol name (equal to dlsym()) +// signal-safe +void *bh_elf_find_export_func_addr_by_symbol_name(bh_elf_t *self, const char *sym_name); diff --git a/app/src/main/cpp/bytehook/bh_elf_manager.c b/app/src/main/cpp/bytehook/bh_elf_manager.c new file mode 100644 index 00000000..9053834e --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_elf_manager.c @@ -0,0 +1,278 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_elf_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_dl_iterate.h" +#include "bh_dl_monitor.h" +#include "bh_elf.h" +#include "bh_linker.h" +#include "bh_log.h" +#include "bh_util.h" +#include "tree.h" + +// block list +typedef struct bh_elf_manager_block { + char *caller_path_name; + TAILQ_ENTRY(bh_elf_manager_block, ) link; +} bh_elf_manager_block_t; +typedef TAILQ_HEAD(bh_elf_manager_block_list, bh_elf_manager_block, ) bh_elf_manager_block_list_t; + +// RB-tree for ELF info (bh_elf_t) +static __inline__ int bh_elf_cmp(bh_elf_t *a, bh_elf_t *b) { + return strcmp(a->pathname, b->pathname); +} +typedef RB_HEAD(bh_elf_tree, bh_elf) bh_elf_tree_t; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +RB_GENERATE_STATIC(bh_elf_tree, bh_elf, link, bh_elf_cmp) +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +struct bh_elf_manager { + bool contain_pathname; + bool contain_basename; + bh_elf_tree_t elfs; + size_t elfs_cnt; + bh_elf_list_t abandoned_elfs; + pthread_rwlock_t elfs_lock; + bh_elf_manager_block_list_t blocklist; + pthread_mutex_t blocklist_lock; +}; +#pragma clang diagnostic pop + +bh_elf_manager_t *bh_elf_manager_create(void) { + bh_elf_manager_t *self; + if (NULL == (self = malloc(sizeof(bh_elf_manager_t)))) return NULL; + self->contain_pathname = false; + self->contain_basename = false; + RB_INIT(&self->elfs); + self->elfs_cnt = 0; + TAILQ_INIT(&self->abandoned_elfs); + pthread_rwlock_init(&self->elfs_lock, NULL); + TAILQ_INIT(&self->blocklist); + pthread_mutex_init(&self->blocklist_lock, NULL); + + return self; +} + +int bh_elf_manager_add_ignore(bh_elf_manager_t *self, const char *caller_path_name) { + bh_elf_manager_block_t *block; + if (NULL == (block = calloc(1, sizeof(bh_elf_t)))) return -1; + if (NULL == (block->caller_path_name = strdup(caller_path_name))) { + free(block); + return -1; + } + + bh_elf_manager_block_t *tmp; + pthread_mutex_lock(&self->blocklist_lock); + TAILQ_FOREACH(tmp, &self->blocklist, link) { + if (0 == strcmp(tmp->caller_path_name, caller_path_name)) break; + } + if (NULL == tmp) { + TAILQ_INSERT_TAIL(&self->blocklist, block, link); + block = NULL; + } + pthread_mutex_unlock(&self->blocklist_lock); + + if (NULL != block) { + free(block->caller_path_name); + free(block); + } + return 0; +} + +static bool bh_elf_manager_check_ignore(bh_elf_manager_t *self, const char *caller_path_name) { + bool result = true; + pthread_mutex_lock(&self->blocklist_lock); + + bh_elf_manager_block_t *tmp; + TAILQ_FOREACH(tmp, &self->blocklist, link) { + if ('/' == caller_path_name[0] && '/' != tmp->caller_path_name[0]) { + if (bh_util_ends_with(caller_path_name, tmp->caller_path_name)) goto ignore; + } else if ('/' != caller_path_name[0] && '/' == tmp->caller_path_name[0]) { + if (bh_util_ends_with(tmp->caller_path_name, caller_path_name)) goto ignore; + } else { + if (0 == strcmp(caller_path_name, tmp->caller_path_name)) goto ignore; + } + } + result = false; + +ignore: + pthread_mutex_unlock(&self->blocklist_lock); + return result; +} + +static int bh_elf_manager_iterate_cb(struct dl_phdr_info *info, size_t size, void *arg) { + (void)size; + + uintptr_t *pkg = (uintptr_t *)arg; + bh_elf_manager_t *self = (bh_elf_manager_t *)*pkg++; + bh_elf_list_t *new_elfs = (bh_elf_list_t *)*pkg; + + // ignore invalid or unwanted ELF + if (bh_util_ends_with(info->dlpi_name, BH_CONST_BASENAME_BYTEHOOK)) return 0; + // if (!bh_util_ends_with(info->dlpi_name, BH_CONST_BASENAME_APP_PROCESS) && + // !bh_util_ends_with(info->dlpi_name, ".so")) + // return 0; + if (bh_elf_manager_check_ignore(self, info->dlpi_name)) return 0; + + bh_elf_t elf_key = {.pathname = info->dlpi_name}; + bh_elf_t *elf = RB_FIND(bh_elf_tree, &self->elfs, &elf_key); + if (NULL == elf) { + // create new ELF object + if (NULL != (elf = bh_elf_create(info))) { + RB_INSERT(bh_elf_tree, &self->elfs, elf); + self->elfs_cnt++; + + if (NULL != new_elfs) TAILQ_INSERT_TAIL(new_elfs, elf, link_list); + + if ((!self->contain_pathname) && ('/' == info->dlpi_name[0])) self->contain_pathname = true; + if ((!self->contain_basename) && ('/' != info->dlpi_name[0])) self->contain_basename = true; + + BH_LOG_INFO("ELF manager: add %" BH_UTIL_PRIxADDR " %s", elf->load_bias, elf->pathname); + } + } + + // ELF object set exist + if (NULL != elf) bh_elf_set_exist(elf); + + return 0; +} + +void bh_elf_manager_refresh(bh_elf_manager_t *self, bool sync_clean, bh_elf_manager_post_add_cb_t cb, + void *cb_arg) { + bh_elf_list_t new_elfs = TAILQ_HEAD_INITIALIZER(new_elfs); + + uintptr_t pkg[2]; + pkg[0] = (uintptr_t)self; + pkg[1] = (NULL == cb ? (uintptr_t)NULL : (uintptr_t)(&new_elfs)); + + // write-lock ELFs-tree + if (0 != pthread_rwlock_wrlock(&self->elfs_lock)) return; + + // iterate ELFs + bh_dl_iterate(bh_elf_manager_iterate_cb, (void *)pkg); + + // clean all ELF which not exist in linker's solist + bh_elf_t *elf, *elf_tmp; + RB_FOREACH_SAFE(elf, bh_elf_tree, &self->elfs, elf_tmp) { + if (!bh_elf_is_exist(elf)) { + RB_REMOVE(bh_elf_tree, &self->elfs, elf); + self->elfs_cnt--; + TAILQ_INSERT_TAIL(&self->abandoned_elfs, elf, link_list); + } else { + // waiting for the next round of checking existence + bh_elf_unset_exist(elf); + } + } + + // if we are in sync-clean status, no other iterate or hooks can be performed at the same time + if (sync_clean) { + // remove and destroy all unreferenced ELF object in the abandoned list + TAILQ_FOREACH_SAFE(elf, &self->abandoned_elfs, link_list, elf_tmp) { + TAILQ_REMOVE(&self->abandoned_elfs, elf, link_list); + bh_elf_destroy(&elf); + } + } + + // unlock ELFs-tree + pthread_rwlock_unlock(&self->elfs_lock); + + // do callback for newborn ELFs (no need to lock) + if (NULL != cb) { + TAILQ_FOREACH_SAFE(elf, &new_elfs, link_list, elf_tmp) { + TAILQ_REMOVE(&new_elfs, elf, link_list); + cb(elf, cb_arg); + } + } +} + +void bh_elf_manager_iterate(bh_elf_manager_t *self, bh_elf_manager_iterate_cb_t cb, void *cb_arg) { + if (0 == self->elfs_cnt) return; + + // get a copy of ELFs (only the pointers) + bh_elf_t **copy_elfs = NULL; + size_t copy_elfs_cnt = 0; + pthread_rwlock_rdlock(&self->elfs_lock); + if (self->elfs_cnt > 0) { + if (NULL != (copy_elfs = malloc(sizeof(bh_elf_t *) * self->elfs_cnt))) { + copy_elfs_cnt = self->elfs_cnt; + size_t i = 0; + bh_elf_t *elf; + RB_FOREACH(elf, bh_elf_tree, &self->elfs) { + copy_elfs[i++] = elf; + } + } + } + pthread_rwlock_unlock(&self->elfs_lock); + + // do callback copy ELFs (no need to lock) + if (NULL != copy_elfs) { + bool cb_next = true; + for (size_t i = 0; i < copy_elfs_cnt; i++) { + if (cb_next) cb_next = cb(copy_elfs[i], cb_arg); + } + free(copy_elfs); + } +} + +bh_elf_t *bh_elf_manager_find_elf(bh_elf_manager_t *self, const char *pathname) { + bh_elf_t *elf = NULL; + + pthread_rwlock_rdlock(&self->elfs_lock); + + if (('/' == pathname[0] && self->contain_pathname && !self->contain_basename) || + ('/' != pathname[0] && self->contain_basename && !self->contain_pathname)) { + bh_elf_t elf_key = {.pathname = pathname}; + elf = RB_FIND(bh_elf_tree, &self->elfs, &elf_key); + } else { + RB_FOREACH(elf, bh_elf_tree, &self->elfs) { + if (bh_elf_is_match(elf, pathname)) break; + } + } + + pthread_rwlock_unlock(&self->elfs_lock); + + return elf; +} + +void *bh_elf_manager_find_export_addr(bh_elf_manager_t *self, const char *pathname, const char *sym_name) { + bh_elf_t *elf = bh_elf_manager_find_elf(self, pathname); + if (NULL == elf) return NULL; + + return bh_elf_find_export_func_addr_by_symbol_name(elf, sym_name); +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h b/app/src/main/cpp/bytehook/bh_elf_manager.h similarity index 54% rename from app/src/main/cpp/shadowhook/arch/arm/sh_t16.h rename to app/src/main/cpp/bytehook/bh_elf_manager.h index 57410a6d..6769ad11 100644 --- a/app/src/main/cpp/shadowhook/arch/arm/sh_t16.h +++ b/app/src/main/cpp/bytehook/bh_elf_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,28 +19,27 @@ // SOFTWARE. // -// Created by Pengying Xu (xupengying@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once #include -#include -#include - -#include "sh_txx.h" - -typedef struct { - uint16_t insts[8]; - size_t insts_len; // 2 - 16 (bytes) - size_t insts_cnt; // 1 - 4 - size_t insts_else_cnt; // 0 - 3 - uintptr_t pcs[4]; - uint8_t firstcond; - uint8_t padding[3]; -} sh_t16_it_t; - -bool sh_t16_parse_it(sh_t16_it_t *it, uint16_t inst, uintptr_t pc); -void sh_t16_rewrite_it_else(uint16_t *buf, uint16_t imm9, sh_t16_it_t *it); -void sh_t16_rewrite_it_then(uint16_t *buf, uint16_t imm12); - -size_t sh_t16_get_rewrite_inst_len(uint16_t inst); -size_t sh_t16_rewrite(uint16_t *buf, uint16_t inst, uintptr_t pc, sh_txx_rewrite_info_t *rinfo); + +#include "bh_elf.h" + +typedef struct bh_elf_manager bh_elf_manager_t; + +bh_elf_manager_t *bh_elf_manager_create(void); + +int bh_elf_manager_add_ignore(bh_elf_manager_t *self, const char *caller_path_name); + +typedef void (*bh_elf_manager_post_add_cb_t)(bh_elf_t *elf, void *arg); +void bh_elf_manager_refresh(bh_elf_manager_t *self, bool sync_clean, bh_elf_manager_post_add_cb_t cb, + void *cb_arg); + +typedef bool (*bh_elf_manager_iterate_cb_t)(bh_elf_t *elf, void *arg); +void bh_elf_manager_iterate(bh_elf_manager_t *self, bh_elf_manager_iterate_cb_t cb, void *cb_arg); + +bh_elf_t *bh_elf_manager_find_elf(bh_elf_manager_t *self, const char *pathname); + +// similar to dlsym(), but search .dynsym +void *bh_elf_manager_find_export_addr(bh_elf_manager_t *self, const char *pathname, const char *sym_name); diff --git a/app/src/main/cpp/bytehook/bh_hook.c b/app/src/main/cpp/bytehook/bh_hook.c new file mode 100644 index 00000000..ecece923 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_hook.c @@ -0,0 +1,125 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_hook.h" + +#include +#include +#include +#include +#include + +#include "bh_log.h" +#include "bh_task.h" + +bh_hook_t *bh_hook_create(void *got_addr, void *orig_func) { + bh_hook_t *self; + + if (NULL == (self = malloc(sizeof(bh_hook_t)))) return NULL; + self->got_addr = got_addr; + self->orig_func = orig_func; + SLIST_INIT(&self->running_list); + pthread_mutex_init(&self->running_list_lock, NULL); + + return self; +} + +void bh_hook_destroy(bh_hook_t **self) { + if (NULL == self || NULL == *self) return; + + pthread_mutex_destroy(&(*self)->running_list_lock); + free(*self); + *self = NULL; +} + +int bh_hook_add_func(bh_hook_t *self, void *func, uint32_t task_id) { + bh_hook_call_t *running; + int r = BYTEHOOK_STATUS_CODE_OK; + + pthread_mutex_lock(&self->running_list_lock); + + // check repeated funcion + SLIST_FOREACH(running, &self->running_list, link) { + if (running->enabled && running->func == func) { + r = BYTEHOOK_STATUS_CODE_REPEATED_FUNC; + goto end; + } + } + + // try to re-enable an exists item + SLIST_FOREACH(running, &self->running_list, link) { + if (running->func == func && running->task_id == task_id) { + if (!running->enabled) __atomic_store_n((bool *)&running->enabled, true, __ATOMIC_SEQ_CST); + + BH_LOG_INFO("hook chain: add(re-enable) func, GOT %" PRIxPTR ", func %" PRIxPTR, + (uintptr_t)self->got_addr, (uintptr_t)func); + goto end; + } + } + + // create new item + if (NULL == (running = malloc(sizeof(bh_hook_call_t)))) { + r = BYTEHOOK_STATUS_CODE_APPEND_TRAMPO; + goto end; + } + running->func = func; + running->enabled = true; + running->task_id = task_id; + + // insert to the head of the running_list + // + // equivalent to: SLIST_INSERT_HEAD(&self->running_list, running, link); + // but: __ATOMIC_RELEASE ensures readers see only fully-constructed item + SLIST_NEXT(running, link) = SLIST_FIRST(&self->running_list); + __atomic_store_n((uintptr_t *)(&SLIST_FIRST(&self->running_list)), (uintptr_t)running, __ATOMIC_RELEASE); + + BH_LOG_INFO("hook chain: add(new) func, GOT %" PRIxPTR ", func %" PRIxPTR, (uintptr_t)self->got_addr, + (uintptr_t)func); + +end: + pthread_mutex_unlock(&self->running_list_lock); + return r; +} + +bool bh_hook_del_func(bh_hook_t *self, void *func) { + bool useful = false; + + pthread_mutex_lock(&self->running_list_lock); + + bh_hook_call_t *running; + SLIST_FOREACH(running, &self->running_list, link) { + if (running->func == func) { + if (running->enabled) __atomic_store_n((bool *)&running->enabled, false, __ATOMIC_SEQ_CST); + + BH_LOG_INFO("hook chain: del func, GOT %" PRIxPTR ", func %" PRIxPTR, (uintptr_t)self->got_addr, + (uintptr_t)func); + } + + if (running->enabled && !useful) + useful = true; // still useful, so do not remove the hook chain and the trampoline + } + + pthread_mutex_unlock(&self->running_list_lock); + + return useful; +} diff --git a/app/src/main/cpp/shadowhook/sh_linker.h b/app/src/main/cpp/bytehook/bh_hook.h similarity index 56% rename from app/src/main/cpp/shadowhook/sh_linker.h rename to app/src/main/cpp/bytehook/bh_hook.h index ab069aed..83eb98ec 100644 --- a/app/src/main/cpp/shadowhook/sh_linker.h +++ b/app/src/main/cpp/bytehook/bh_hook.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,23 +19,38 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once +#include #include -#include -#include "xdl.h" +#include "queue.h" +#include "tree.h" -int sh_linker_init(void); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" -const char *sh_linker_match_dlfcn(uintptr_t target_addr); -bool sh_linker_need_to_hook_dlopen(uintptr_t target_addr); +typedef struct bh_hook_call { + void *func; + bool enabled; + uint32_t task_id; + SLIST_ENTRY(bh_hook_call, ) link; +} bh_hook_call_t; +typedef SLIST_HEAD(bh_hook_call_list, bh_hook_call, ) bh_hook_call_list_t; -typedef void (*sh_linker_post_dlopen_t)(void *arg); -int sh_linker_hook_dlopen(sh_linker_post_dlopen_t post_dlopen, void *post_dlopen_arg); +typedef struct bh_hook { + void *got_addr; + void *orig_func; + bh_hook_call_list_t running_list; + pthread_mutex_t running_list_lock; + RB_ENTRY(bh_hook) link; +} bh_hook_t; -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); +#pragma clang diagnostic pop + +bh_hook_t *bh_hook_create(void *got_addr, void *orig_func); +void bh_hook_destroy(bh_hook_t **self); + +int bh_hook_add_func(bh_hook_t *self, void *func, uint32_t task_id); +bool bh_hook_del_func(bh_hook_t *self, void *func); diff --git a/app/src/main/cpp/bytehook/bh_hook_manager.c b/app/src/main/cpp/bytehook/bh_hook_manager.c new file mode 100644 index 00000000..1f52fff0 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_hook_manager.c @@ -0,0 +1,540 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_hook_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_core.h" +#include "bh_elf.h" +#include "bh_hook.h" +#include "bh_log.h" +#include "bh_task.h" +#include "bh_trampo.h" +#include "bh_util.h" +#include "bytesig.h" +#include "tree.h" + +#define BH_HOOK_MANAGER_GOT_MAX_CAP 32 + +// RB-tree for ELF info (bh_elf_t) +static __inline__ int bh_hook_cmp(bh_hook_t *a, bh_hook_t *b) { + if (a->got_addr == b->got_addr) + return 0; + else + return a->got_addr > b->got_addr ? 1 : -1; +} +typedef RB_HEAD(bh_hook_tree, bh_hook) bh_hook_tree_t; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +RB_GENERATE_STATIC(bh_hook_tree, bh_hook, link, bh_hook_cmp) +#pragma clang diagnostic pop + +struct bh_hook_manager { + bh_hook_tree_t hooks; + bh_hook_tree_t abandoned_hooks; + pthread_mutex_t hooks_lock; +}; + +bh_hook_manager_t *bh_hook_manager_create(void) { + bh_hook_manager_t *self; + if (NULL == (self = malloc(sizeof(bh_hook_manager_t)))) return NULL; + RB_INIT(&self->hooks); + RB_INIT(&self->abandoned_hooks); + pthread_mutex_init(&self->hooks_lock, NULL); + return self; +} + +static bh_hook_t *bh_hook_manager_find_hook(bh_hook_manager_t *self, void *got_addr) { + bh_hook_t hook_key = {.got_addr = got_addr}; + return RB_FIND(bh_hook_tree, &self->hooks, &hook_key); +} + +static bh_hook_t *bh_hook_manager_create_hook(bh_hook_manager_t *self, void *got_addr, void *orig_func, + void **trampo) { + // create hook chain + bh_hook_t *hook = bh_hook_create(got_addr, orig_func); + if (NULL == hook) return NULL; + + // create trampoline for the hook chain + *trampo = bh_trampo_create(hook); + if (NULL == *trampo) { + bh_hook_destroy(&hook); + return NULL; + } + + // save the hook chain + RB_INSERT(bh_hook_tree, &self->hooks, hook); + + BH_LOG_INFO("hook chain: created for GOT %" PRIxPTR ", orig func %" PRIxPTR, (uintptr_t)got_addr, + (uintptr_t)orig_func); + return hook; +} + +static int bh_hook_manager_add_func(bh_hook_manager_t *self, bh_elf_t *caller_elf, void *got_addr, + void *orig_func, bh_task_t *task, void **trampo, void **orig_func_ret) { + *trampo = NULL; + int r; + + pthread_mutex_lock(&self->hooks_lock); + + // find or create hook chain + bh_hook_t *hook = bh_hook_manager_find_hook(self, got_addr); + *orig_func_ret = (NULL == hook ? orig_func : hook->orig_func); + if (NULL == hook) hook = bh_hook_manager_create_hook(self, got_addr, orig_func, trampo); + if (NULL == hook) { + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_NEW_TRAMPO, caller_elf->pathname, orig_func); + r = BYTEHOOK_STATUS_CODE_NEW_TRAMPO; + goto end; + } + + // add new-func to hook chain + if (0 != (r = bh_hook_add_func(hook, task->new_func, task->id))) { + bh_task_hooked(task, r, caller_elf->pathname, orig_func); + goto end; + } + + r = 0; // OK + +end: + pthread_mutex_unlock(&self->hooks_lock); + return r; +} + +static int bh_hook_manager_del_func(bh_hook_manager_t *self, void *got_addr, bh_task_t *task, + void **restore_func) { + int r = -1; + + if (NULL != restore_func) *restore_func = NULL; + + pthread_mutex_lock(&self->hooks_lock); + + bh_hook_t *hook = bh_hook_manager_find_hook(self, got_addr); + if (NULL == hook) goto end; + + bool useful = bh_hook_del_func(hook, task->new_func); + if (!useful) { + // move hook chain to abandoned-hooks set + // we can't delete it, because other threads may be running on its trampoline + RB_REMOVE(bh_hook_tree, &self->hooks, hook); + RB_INSERT(bh_hook_tree, &self->abandoned_hooks, hook); + + if (NULL != restore_func) *restore_func = hook->orig_func; + } + + r = 0; // OK + +end: + pthread_mutex_unlock(&self->hooks_lock); + return r; +} + +static int bh_hook_manager_verify_got_value(bh_elf_t *caller_elf, bh_task_t *task, void *got_addr) { + // check for GOT's address itself + Dl_info info; + if (0 == dladdr(got_addr, &info)) return -1; + + // check for GOT-value's address + if (0 == dladdr(*((void **)got_addr), &info)) { + // bypass for libdl.so + if (bh_elf_is_match(caller_elf, BH_CONST_BASENAME_DL)) { + BH_LOG_INFO("hook chain: verify bypass libdl.so: %s", task->sym_name); + return 0; + } + + // bypass for dl-functions + if (0 == strcmp(task->sym_name, "dlopen") || 0 == strcmp(task->sym_name, "dlclose") || + 0 == strcmp(task->sym_name, "dlsym") || 0 == strcmp(task->sym_name, "dlvsym") || + 0 == strcmp(task->sym_name, "dladdr") || 0 == strcmp(task->sym_name, "dlerror") || + 0 == strcmp(task->sym_name, "dl_iterate_phdr") || + 0 == strcmp(task->sym_name, "dl_unwind_find_exidx") || + 0 == strcmp(task->sym_name, "android_dlopen_ext") || + 0 == strcmp(task->sym_name, "android_dlwarning") || + 0 == strcmp(task->sym_name, "android_get_LD_LIBRARY_PATH") || + 0 == strcmp(task->sym_name, "android_update_LD_LIBRARY_PATH") || + 0 == strcmp(task->sym_name, "android_set_application_target_sdk_version") || + 0 == strcmp(task->sym_name, "android_get_application_target_sdk_version") || + 0 == strcmp(task->sym_name, "android_init_namespaces") || + 0 == strcmp(task->sym_name, "android_create_namespace")) { + BH_LOG_INFO("hook chain: verify bypass dl-functions: %s", task->sym_name); + return 0; + } + + return -1; + } + + // check for normal export func + if (NULL != info.dli_sname && 0 == strcmp(info.dli_sname, task->sym_name)) { + BH_LOG_INFO("hook chain: verify OK: %s in %s", task->sym_name, info.dli_fname); + return 0; + } + + // get ELF + if (NULL == info.dli_fname || '\0' == info.dli_fname[0]) return -1; + bh_elf_t *callee_elf = bh_elf_manager_find_elf(bh_core_global()->elf_mgr, info.dli_fname); + if (NULL == callee_elf) return -1; + + int r = -1; + + // bypass for ifunc + if (NULL == info.dli_sname) { + ElfW(Sym) *sym = bh_elf_find_export_func_symbol_by_symbol_name(callee_elf, task->sym_name); + if (NULL != sym && STT_GNU_IFUNC == ELF_ST_TYPE(sym->st_info)) { + BH_LOG_INFO("hook chain: verify bypass ifunc: %s in %s", task->sym_name, info.dli_fname); + r = 0; + } + } + // bypass for alias-func + else { + void *addr = bh_elf_find_export_func_addr_by_symbol_name(callee_elf, info.dli_sname); + if (NULL != addr && addr == *((void **)got_addr)) { + BH_LOG_INFO("hook chain: verify bypass alias-func: %s in %s", task->sym_name, info.dli_fname); + r = 0; + } + } + + return r; +} + +static int bh_hook_manager_replace_got_value(bh_elf_t *caller_elf, bh_task_t *task, void *got_addr, + void *orig_func, void *new_func) { + // verify the GOT value + if (BH_TASK_STATUS_UNHOOKING != task->status) { + if (0 != bh_hook_manager_verify_got_value(caller_elf, task, got_addr)) { + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_GOT_VERIFY, caller_elf->pathname, orig_func); + return BYTEHOOK_STATUS_CODE_GOT_VERIFY; + } + } + + // get permission by address + int prot = bh_elf_get_protect_by_addr(caller_elf, got_addr); + if (0 == prot) { + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_GET_PROT, caller_elf->pathname, orig_func); + return BYTEHOOK_STATUS_CODE_GET_PROT; + } + + // add write permission + if (0 == (prot & PROT_WRITE)) { + if (0 != bh_util_set_addr_protect(got_addr, prot | PROT_WRITE)) { + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_SET_PROT, caller_elf->pathname, orig_func); + return BYTEHOOK_STATUS_CODE_SET_PROT; + } + } + + // replace the target function address by "new_func" + int r; + BYTESIG_TRY(SIGSEGV, SIGBUS) { + __atomic_store_n((uintptr_t *)got_addr, (uintptr_t)new_func, __ATOMIC_SEQ_CST); + r = 0; + } + BYTESIG_CATCH() { + bh_elf_set_error(caller_elf, true); + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_SET_GOT, caller_elf->pathname, orig_func); + r = BYTEHOOK_STATUS_CODE_SET_GOT; + } + BYTESIG_EXIT + + // delete write permission + if (0 == (prot & PROT_WRITE)) bh_util_set_addr_protect(got_addr, prot); + + return r; +} + +static size_t bh_hook_manager_find_all_got(bh_elf_t *caller_elf, bh_task_t *task, void **addr_array, + size_t addr_array_cap) { + if (NULL == task->callee_addr) { + // by import symbol name + return bh_elf_find_import_func_addr_by_symbol_name(caller_elf, task->sym_name, addr_array, + addr_array_cap); + } else { + // by callee address + return bh_elf_find_import_func_addr_by_callee_addr(caller_elf, task->callee_addr, addr_array, + addr_array_cap); + } +} + +static int bh_hook_manager_hook_single_got(bh_hook_manager_t *self, bh_elf_t *caller_elf, bh_task_t *task, + void *got_addr, void **orig_func_ret) { + int r; + + void *orig_func = NULL; + BYTESIG_TRY(SIGSEGV, SIGBUS) { + orig_func = *((void **)got_addr); + } + BYTESIG_CATCH() { + bh_elf_set_error(caller_elf, true); + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_READ_ELF, caller_elf->pathname, orig_func); + return BYTEHOOK_STATUS_CODE_SET_GOT; + } + BYTESIG_EXIT + + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) { + // manual mode: + + // 1. always patch with the externally specified address + r = bh_hook_manager_replace_got_value(caller_elf, task, got_addr, orig_func, task->new_func); + + // 2. save the original address in task object for unhook + if (0 == r) { + bh_task_set_manual_orig_func(task, orig_func); + BH_LOG_INFO("hook chain: manual REPLACE. GOT %" PRIxPTR ": %" PRIxPTR " -> %" PRIxPTR ", %s, %s", + (uintptr_t)got_addr, (uintptr_t)orig_func, (uintptr_t)task->new_func, task->sym_name, + caller_elf->pathname); + } + + // 3. return the original address + if (0 == r) *orig_func_ret = orig_func; + } else { + // automatic mode: + + // 1. add new-func to the hook chain + void *trampo = NULL; + void *orig_func_real = NULL; + r = bh_hook_manager_add_func(self, caller_elf, got_addr, orig_func, task, &trampo, &orig_func_real); + + // 2. replace with the trampoline address if we haven't done it yet + if (0 == r && NULL != trampo) { + r = bh_hook_manager_replace_got_value(caller_elf, task, got_addr, orig_func, trampo); + if (0 == r) { + BH_LOG_INFO("hook chain: auto REPLACE. GOT %" PRIxPTR ": %" PRIxPTR " -> %" PRIxPTR ", %s, %s", + (uintptr_t)got_addr, (uintptr_t)orig_func, (uintptr_t)trampo, task->sym_name, + caller_elf->pathname); + } else { + bh_hook_manager_del_func(self, got_addr, task, NULL); + } + } + + // 3. return the original address + if (0 == r) *orig_func_ret = orig_func_real; + } + + // done + if (0 == r) { + BH_LOG_INFO("hook chain: hook OK. GOT %" PRIxPTR ": + %" PRIxPTR ", %s, %s", (uintptr_t)got_addr, + (uintptr_t)task->new_func, task->sym_name, caller_elf->pathname); + } + + return r; +} + +static int bh_hook_manager_unhook_single_got(bh_hook_manager_t *self, bh_elf_t *caller_elf, bh_task_t *task, + void *got_addr) { + int r = 0; + void *restore_func; + + void *orig_func = NULL; + BYTESIG_TRY(SIGSEGV, SIGBUS) { + orig_func = *((void **)got_addr); + } + BYTESIG_CATCH() { + bh_elf_set_error(caller_elf, true); + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_READ_ELF, caller_elf->pathname, orig_func); + return BYTEHOOK_STATUS_CODE_SET_GOT; + } + BYTESIG_EXIT + + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) { + // manual mode: + restore_func = bh_task_get_manual_orig_func(task); + if (NULL != restore_func) { + r = bh_hook_manager_replace_got_value(caller_elf, task, got_addr, NULL, restore_func); + if (0 == r) { + BH_LOG_INFO("hook chain: manual RESTORE. GOT %" PRIxPTR ": %" PRIxPTR " -> %" PRIxPTR ", %s, %s", + (uintptr_t)got_addr, (uintptr_t)orig_func, (uintptr_t)restore_func, task->sym_name, + caller_elf->pathname); + } + } + } else { + // automatic mode: + + // 1. delete new-func in the hook chain + r = bh_hook_manager_del_func(self, got_addr, task, &restore_func); + + // 2. restore GOT to original function if there is no enabled hook func + if (0 == r && NULL != restore_func) { + r = bh_hook_manager_replace_got_value(caller_elf, task, got_addr, NULL, restore_func); + if (0 == r) { + BH_LOG_INFO("hook chain: auto RESTORE. GOT %" PRIxPTR ": %" PRIxPTR " -> %" PRIxPTR ", %s, %s", + (uintptr_t)got_addr, (uintptr_t)orig_func, (uintptr_t)restore_func, task->sym_name, + caller_elf->pathname); + } + } + } + + if (0 == r) { + BH_LOG_INFO("hook chain: unhook OK. GOT %" PRIxPTR ": - %" PRIxPTR ", %s, %s", (uintptr_t)got_addr, + (uintptr_t)task->new_func, task->sym_name, caller_elf->pathname); + } + + return r; +} + +static void bh_hook_manager_hook_impl(bh_hook_manager_t *self, bh_task_t *task, bh_elf_t *caller_elf) { + // get all GOT entries + void *addr_array[BH_HOOK_MANAGER_GOT_MAX_CAP]; + size_t addr_array_sz = + bh_hook_manager_find_all_got(caller_elf, task, addr_array, BH_HOOK_MANAGER_GOT_MAX_CAP); + if (0 == addr_array_sz) { + if (BH_TASK_TYPE_SINGLE == task->type) + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_NOSYM, caller_elf->pathname, NULL); + return; + } + + // do callback with BYTEHOOK_STATUS_CODE_ORIG_ADDR for manual-mode + // + // In manual mode, the caller needs to save the original function address + // in the hooked callback, and then may call the original function through + // this address in the proxy function. So we need to execute the hooked callback + // first, and then execute the address replacement in the GOT, otherwise it + // will cause a crash due to timing issues. + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) { + void *orig_func_real = *((void **)(addr_array[0])); + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_ORIG_ADDR, caller_elf->pathname, orig_func_real); + } + + // hook + bool everything_ok = true; + void *orig_func = NULL; + bh_elf_hook_lock(caller_elf); + for (size_t i = 0; i < addr_array_sz; i++) + if (0 != bh_hook_manager_hook_single_got(self, caller_elf, task, addr_array[i], &orig_func)) + everything_ok = false; + bh_elf_hook_unlock(caller_elf); + + // do callback with STATUS_CODE_OK only once + if (everything_ok) bh_task_hooked(task, BYTEHOOK_STATUS_CODE_OK, caller_elf->pathname, orig_func); +} + +#ifdef __LP64__ +static void bh_hook_manager_cfi_slowpath(uint64_t CallSiteTypeId, void *Ptr) { + (void)CallSiteTypeId, (void)Ptr; + + BYTEHOOK_POP_STACK(); +} + +static void bh_hook_manager_cfi_slowpath_diag(uint64_t CallSiteTypeId, void *Ptr, void *DiagData) { + (void)CallSiteTypeId, (void)Ptr, (void)DiagData; + + BYTEHOOK_POP_STACK(); +} + +static void bh_hook_manager_cfi_hooked(bytehook_stub_t task_stub, int status_code, + const char *caller_path_name, const char *sym_name, void *new_func, + void *prev_func, void *arg) { + (void)task_stub, (void)new_func, (void)prev_func; + + bool *ok = (bool *)arg; + if (BYTEHOOK_STATUS_CODE_OK == status_code) { + BH_LOG_INFO("hook cfi OK: %s, %s", caller_path_name, sym_name); + *ok = true; + } + if (BYTEHOOK_STATUS_CODE_NOSYM == status_code) { + BH_LOG_INFO("hook cfi NOSYM: %s, %s", caller_path_name, sym_name); + *ok = true; + } +} + +static bool bh_hook_manager_hook_cfi(bh_hook_manager_t *self, bh_elf_t *caller_elf) { + bool ok; + + ok = false; + bh_task_t *task = + bh_task_create_single(caller_elf->pathname, NULL, BH_CONST_SYM_CFI_SLOWPATH, + (void *)bh_hook_manager_cfi_slowpath, bh_hook_manager_cfi_hooked, (void *)&ok); + if (NULL == task) return false; + bh_hook_manager_hook_impl(self, task, caller_elf); + bh_task_destroy(&task); + if (!ok) return false; + + ok = false; + task = bh_task_create_single(caller_elf->pathname, NULL, BH_CONST_SYM_CFI_SLOWPATH_DIAG, + (void *)bh_hook_manager_cfi_slowpath_diag, bh_hook_manager_cfi_hooked, + (void *)&ok); + if (NULL == task) return false; + bh_hook_manager_hook_impl(self, task, caller_elf); + bh_task_destroy(&task); + if (!ok) return false; + + return true; +} +#endif + +void bh_hook_manager_hook(bh_hook_manager_t *self, bh_task_t *task, bh_elf_t *caller_elf) { + // check ELF + if (bh_elf_get_error(caller_elf)) { + if (BH_TASK_TYPE_SINGLE == task->type) + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_READ_ELF, caller_elf->pathname, NULL); + return; + } + +#ifdef __LP64__ + if (bh_util_get_api_level() >= __ANDROID_API_O__) { + // hook __cfi_slowpath and __cfi_slowpath_diag (only once) + if (!caller_elf->cfi_hooked) { + bh_elf_cfi_hook_lock(caller_elf); + if (!caller_elf->cfi_hooked) { + caller_elf->cfi_hooked_ok = bh_hook_manager_hook_cfi(self, caller_elf); + caller_elf->cfi_hooked = true; + } + bh_elf_cfi_hook_unlock(caller_elf); + } + + // check CIF hook + if (!caller_elf->cfi_hooked_ok) { + if (BH_TASK_TYPE_SINGLE == task->type) + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_CFI_HOOK_FAILED, caller_elf->pathname, NULL); + return; + } + } +#endif + + bh_hook_manager_hook_impl(self, task, caller_elf); +} + +void bh_hook_manager_unhook(bh_hook_manager_t *self, bh_task_t *task, bh_elf_t *caller_elf) { + // get all GOT entries + void *addr_array[BH_HOOK_MANAGER_GOT_MAX_CAP]; + size_t addr_array_sz = bh_elf_find_import_func_addr_by_symbol_name(caller_elf, task->sym_name, addr_array, + BH_HOOK_MANAGER_GOT_MAX_CAP); + if (0 == addr_array_sz) return; + + // unhook + bool everything_ok = true; + bh_elf_hook_lock(caller_elf); + for (size_t i = 0; i < addr_array_sz; i++) + if (0 != bh_hook_manager_unhook_single_got(self, caller_elf, task, addr_array[i])) everything_ok = false; + bh_elf_hook_unlock(caller_elf); + + // do callback with STATUS_CODE_OK only once + if (everything_ok) bh_task_hooked(task, BYTEHOOK_STATUS_CODE_OK, caller_elf->pathname, NULL); +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h b/app/src/main/cpp/bytehook/bh_hook_manager.h similarity index 69% rename from app/src/main/cpp/shadowhook/arch/arm/sh_txx.h rename to app/src/main/cpp/bytehook/bh_hook_manager.h index b805f0d9..e29b0fb2 100644 --- a/app/src/main/cpp/shadowhook/arch/arm/sh_txx.h +++ b/app/src/main/cpp/bytehook/bh_hook_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,21 +19,19 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once +#include #include -#include -#include -typedef struct { - uintptr_t start_addr; - uintptr_t end_addr; - uint16_t *buf; - size_t buf_offset; - size_t inst_lens[13]; // 26 / 2 = 13 - size_t inst_lens_cnt; -} sh_txx_rewrite_info_t; +#include "bh_elf.h" +#include "bh_task.h" +#include "queue.h" -bool sh_txx_is_addr_need_fix(uintptr_t addr, sh_txx_rewrite_info_t *rinfo); -uintptr_t sh_txx_fix_addr(uintptr_t addr, sh_txx_rewrite_info_t *rinfo); +typedef struct bh_hook_manager bh_hook_manager_t; + +bh_hook_manager_t *bh_hook_manager_create(void); + +void bh_hook_manager_hook(bh_hook_manager_t *self, bh_task_t *task, bh_elf_t *caller_elf); +void bh_hook_manager_unhook(bh_hook_manager_t *self, bh_task_t *task, bh_elf_t *caller_elf); diff --git a/app/src/main/cpp/bytehook/bh_jni.c b/app/src/main/cpp/bytehook/bh_jni.c new file mode 100644 index 00000000..571182b2 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_jni.c @@ -0,0 +1,146 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include +#include + +#include "bytehook.h" + +#define BH_JNI_VERSION JNI_VERSION_1_6 +#define BH_JNI_CLASS_NAME "com/bytedance/android/bytehook/ByteHook" + +static jstring bh_jni_get_version(JNIEnv *env, jobject thiz) { + (void)thiz; + return (*env)->NewStringUTF(env, bytehook_get_version()); +} + +static jint bh_jni_init(JNIEnv *env, jobject thiz, jint mode, jboolean debug) { + (void)env; + (void)thiz; + + return bytehook_init((int)mode, (bool)debug); +} + +static jint bh_jni_add_ignore(JNIEnv *env, jobject thiz, jstring caller_path_name) { + (void)env; + (void)thiz; + + int r = BYTEHOOK_STATUS_CODE_IGNORE; + if (!caller_path_name) return r; + + const char *c_caller_path_name; + if (NULL == (c_caller_path_name = (*env)->GetStringUTFChars(env, caller_path_name, 0))) goto clean; + r = bytehook_add_ignore(c_caller_path_name); + +clean: + if (caller_path_name && c_caller_path_name) + (*env)->ReleaseStringUTFChars(env, caller_path_name, c_caller_path_name); + return r; +} + +static jint bh_jni_get_mode(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return BYTEHOOK_MODE_AUTOMATIC == bytehook_get_mode() ? 0 : 1; +} + +static jboolean bh_jni_get_debug(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return bytehook_get_debug(); +} + +static void bh_jni_set_debug(JNIEnv *env, jobject thiz, jboolean debug) { + (void)env; + (void)thiz; + + bytehook_set_debug((bool)debug); +} + +static jboolean bh_jni_get_recordable(JNIEnv *env, jobject thiz) { + (void)env, (void)thiz; + + return bytehook_get_recordable(); +} + +static void bh_jni_set_recordable(JNIEnv *env, jobject thiz, jboolean recordable) { + (void)env, (void)thiz; + + bytehook_set_recordable((bool)recordable); +} + +static jstring bh_jni_get_records(JNIEnv *env, jobject thiz, jint item_flags) { + (void)thiz; + + char *str = bytehook_get_records((uint32_t)item_flags); + if (NULL == str) return NULL; + + jstring jstr = (*env)->NewStringUTF(env, str); + free(str); + return jstr; +} + +static jstring bh_jni_get_arch(JNIEnv *env, jobject thiz) { + (void)thiz; + +#if defined(__arm__) + char *arch = "arm"; +#elif defined(__aarch64__) + char *arch = "arm64"; +#elif defined(__i386__) + char *arch = "x86"; +#elif defined(__x86_64__) + char *arch = "x86_64"; +#else + char *arch = "unsupported"; +#endif + + return (*env)->NewStringUTF(env, arch); +} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + (void)reserved; + + if (__predict_false(NULL == vm)) return JNI_ERR; + + JNIEnv *env; + if (__predict_false(JNI_OK != (*vm)->GetEnv(vm, (void **)&env, BH_JNI_VERSION))) return JNI_ERR; + if (__predict_false(NULL == env || NULL == *env)) return JNI_ERR; + + jclass cls; + if (__predict_false(NULL == (cls = (*env)->FindClass(env, BH_JNI_CLASS_NAME)))) return JNI_ERR; + + JNINativeMethod m[] = {{"nativeGetVersion", "()Ljava/lang/String;", (void *)bh_jni_get_version}, + {"nativeInit", "(IZ)I", (void *)bh_jni_init}, + {"nativeAddIgnore", "(Ljava/lang/String;)I", (void *)bh_jni_add_ignore}, + {"nativeGetMode", "()I", (void *)bh_jni_get_mode}, + {"nativeGetDebug", "()Z", (void *)bh_jni_get_debug}, + {"nativeSetDebug", "(Z)V", (void *)bh_jni_set_debug}, + {"nativeGetRecordable", "()Z", (void *)bh_jni_get_recordable}, + {"nativeSetRecordable", "(Z)V", (void *)bh_jni_set_recordable}, + {"nativeGetRecords", "(I)Ljava/lang/String;", (void *)bh_jni_get_records}, + {"nativeGetArch", "()Ljava/lang/String;", (void *)bh_jni_get_arch}}; + if (__predict_false(0 != (*env)->RegisterNatives(env, cls, m, sizeof(m) / sizeof(m[0])))) return JNI_ERR; + + return BH_JNI_VERSION; +} diff --git a/app/src/main/cpp/bytehook/bh_linker.c b/app/src/main/cpp/bytehook/bh_linker.c new file mode 100644 index 00000000..4458843b --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_linker.c @@ -0,0 +1,196 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_linker.h" + +#include +#include +#include +#include +#include + +#include "bh_const.h" +#include "bh_dl.h" +#include "bh_linker_mutex.h" +#include "bh_log.h" +#include "bh_util.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#ifndef __ANDROID_API_U__ +#define __ANDROID_API_U__ 34 +#endif +#pragma clang diagnostic pop + +typedef struct { + // bits: name: description: + // 15-14 type mutex type, can be 0 (normal), 1 (recursive), 2 (errorcheck) + // 13 shared process-shared flag + // 12-2 counter - 1 + // 1-0 state lock state, can be 0 (unlocked), 1 (locked contended), 2 (locked uncontended) + uint16_t state; +#if defined(__LP64__) + uint16_t pad; + int owner_tid; + char reserved[28]; +#else + uint16_t owner_tid; +#endif +} bh_linker_mutex_internal_t __attribute__((aligned(4))); + +#define BH_LINKER_MUTEX_IS_UNLOCKED(v) (((v)&0x3) == 0) +#define BH_LINKER_MUTEX_COUNTER(v) (((v)&0x1FFC) >> 2) + +bh_linker_dlopen_ext_t bh_linker_dlopen_ext = NULL; +bh_linker_do_dlopen_t bh_linker_do_dlopen = NULL; +bh_linker_get_error_buffer_t bh_linker_get_error_buffer = NULL; +bh_linker_bionic_format_dlerror_t bh_linker_bionic_format_dlerror = NULL; + +static pthread_mutex_t *bh_linker_g_dl_mutex = NULL; +static bool bh_linker_g_dl_mutex_compatible = false; +static pthread_key_t bh_linker_g_dl_mutex_key; + +static bool bh_linker_check_lock_compatible_helper(bh_linker_mutex_internal_t *m, bool unlocked, + uint16_t counter, int owner_tid) { + return BH_LINKER_MUTEX_IS_UNLOCKED(m->state) == unlocked && BH_LINKER_MUTEX_COUNTER(m->state) == counter && + m->owner_tid == owner_tid; +} + +static bool bh_linker_check_lock_compatible(void) { + bool result = true; + int tid = gettid(); + pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + bh_linker_mutex_internal_t *m = (bh_linker_mutex_internal_t *)&mutex; + + if (!bh_linker_check_lock_compatible_helper(m, true, 0, 0)) result = false; + pthread_mutex_lock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, false, 0, tid)) result = false; + pthread_mutex_lock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, false, 1, tid)) result = false; + pthread_mutex_lock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, false, 2, tid)) result = false; + pthread_mutex_unlock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, false, 1, tid)) result = false; + pthread_mutex_unlock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, false, 0, tid)) result = false; + pthread_mutex_unlock(&mutex); + if (!bh_linker_check_lock_compatible_helper(m, true, 0, 0)) result = false; + + return result; +} + +#if __ANDROID_API__ < __ANDROID_API_L__ +static int bh_linker_init_android_4x(void) { +#if defined(__arm__) + return NULL == (bh_linker_g_dl_mutex = bh_linker_mutex_get_by_stack()) ? -1 : 0; +#else + return 0 != pthread_key_create(&bh_linker_g_dl_mutex_key, NULL) ? -1 : 0; +#endif +} +#endif + +int bh_linker_init(void) { + bh_linker_g_dl_mutex_compatible = bh_linker_check_lock_compatible(); + int api_level = bh_util_get_api_level(); + + // for Android 4.x +#if __ANDROID_API__ < __ANDROID_API_L__ + if (api_level < __ANDROID_API_L__) return bh_linker_init_android_4x(); +#endif + + if (!bh_linker_g_dl_mutex_compatible) { + // If the mutex ABI is not compatible, then we need to use an alternative. + if (0 != pthread_key_create(&bh_linker_g_dl_mutex_key, NULL)) return -1; + } + + void *linker = bh_dl_open_linker(); + if (NULL == linker) goto err; + + // for Android 5.0, 5.1, 7.0, 7.1 and all mutex ABI compatible cases + if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level || + __ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level || + bh_linker_g_dl_mutex_compatible) { + bh_linker_g_dl_mutex = (pthread_mutex_t *)(bh_dl_dsym(linker, BH_CONST_SYM_G_DL_MUTEX)); + if (NULL == bh_linker_g_dl_mutex && api_level >= __ANDROID_API_U__) + bh_linker_g_dl_mutex = (pthread_mutex_t *)(bh_dl_dsym(linker, BH_CONST_SYM_G_DL_MUTEX_U_QPR2)); + if (NULL == bh_linker_g_dl_mutex) goto err; + } + + // for Android 7.0, 7.1 + if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { + bh_linker_dlopen_ext = (bh_linker_dlopen_ext_t)(bh_dl_dsym(linker, BH_CONST_SYM_DLOPEN_EXT)); + if (NULL == bh_linker_dlopen_ext) { + if (NULL == (bh_linker_do_dlopen = (bh_linker_do_dlopen_t)(bh_dl_dsym(linker, BH_CONST_SYM_DO_DLOPEN)))) + goto err; + bh_linker_get_error_buffer = + (bh_linker_get_error_buffer_t)(bh_dl_dsym(linker, BH_CONST_SYM_LINKER_GET_ERROR_BUFFER)); + bh_linker_bionic_format_dlerror = + (bh_linker_bionic_format_dlerror_t)(bh_dl_dsym(linker, BH_CONST_SYM_BIONIC_FORMAT_DLERROR)); + } + } + + bh_dl_close(linker); + return 0; + +err: + if (NULL != linker) bh_dl_close(linker); + bh_linker_do_dlopen = NULL; + bh_linker_dlopen_ext = NULL; + bh_linker_g_dl_mutex = NULL; + bh_linker_get_error_buffer = NULL; + bh_linker_bionic_format_dlerror = NULL; + return -1; +} + +void bh_linker_lock(void) { + if (__predict_true(NULL != bh_linker_g_dl_mutex)) pthread_mutex_lock(bh_linker_g_dl_mutex); +} + +void bh_linker_unlock(void) { + if (__predict_true(NULL != bh_linker_g_dl_mutex)) pthread_mutex_unlock(bh_linker_g_dl_mutex); +} + +bool bh_linker_is_in_lock(void) { + if (__predict_false(!bh_linker_g_dl_mutex_compatible || NULL == bh_linker_g_dl_mutex)) { + return (intptr_t)(pthread_getspecific(bh_linker_g_dl_mutex_key)) > 0; + } else { + bh_linker_mutex_internal_t *m = (bh_linker_mutex_internal_t *)bh_linker_g_dl_mutex; + uint16_t state = __atomic_load_n(&(m->state), __ATOMIC_RELAXED); + int owner_tid = (int)__atomic_load_n(&(m->owner_tid), __ATOMIC_RELAXED); + return !BH_LINKER_MUTEX_IS_UNLOCKED(state) && owner_tid == gettid(); + } +} + +void bh_linker_add_lock_count(void) { + if (__predict_false(!bh_linker_g_dl_mutex_compatible || NULL == bh_linker_g_dl_mutex)) { + intptr_t count = (intptr_t)(pthread_getspecific(bh_linker_g_dl_mutex_key)); + pthread_setspecific(bh_linker_g_dl_mutex_key, (void *)(count + 1)); + } +} + +void bh_linker_sub_lock_count(void) { + if (__predict_false(!bh_linker_g_dl_mutex_compatible || NULL == bh_linker_g_dl_mutex)) { + intptr_t count = (intptr_t)(pthread_getspecific(bh_linker_g_dl_mutex_key)); + pthread_setspecific(bh_linker_g_dl_mutex_key, (void *)(count - 1)); + } +} diff --git a/app/src/main/cpp/bytehook/bh_linker.h b/app/src/main/cpp/bytehook/bh_linker.h new file mode 100644 index 00000000..46890973 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_linker.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once + +#include +#include + +// linker's __dl__ZL10dlopen_extPKciPK17android_dlextinfoPv for Android 7.0, 7.1 +typedef void *(*bh_linker_dlopen_ext_t)(const char *, int, const void *, void *); +extern bh_linker_dlopen_ext_t bh_linker_dlopen_ext; + +// linker's __dl__Z9do_dlopenPKciPK17android_dlextinfoPv for Android 7.0, 7.1 +typedef void *(*bh_linker_do_dlopen_t)(const char *, int, const void *, void *); +extern bh_linker_do_dlopen_t bh_linker_do_dlopen; + +// linker's __dl__Z23linker_get_error_bufferv for Android 7.0, 7.1 +typedef char *(*bh_linker_get_error_buffer_t)(void); +extern bh_linker_get_error_buffer_t bh_linker_get_error_buffer; + +// linker's __dl__ZL23__bionic_format_dlerrorPKcS0_ for Android 7.0, 7.1 +typedef void (*bh_linker_bionic_format_dlerror_t)(const char *, const char *); +extern bh_linker_bionic_format_dlerror_t bh_linker_bionic_format_dlerror; + +int bh_linker_init(void); + +void bh_linker_lock(void); +void bh_linker_unlock(void); +bool bh_linker_is_in_lock(void); +void bh_linker_add_lock_count(void); +void bh_linker_sub_lock_count(void); diff --git a/app/src/main/cpp/bytehook/bh_linker_mutex.c b/app/src/main/cpp/bytehook/bh_linker_mutex.c new file mode 100644 index 00000000..ded8799f --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_linker_mutex.c @@ -0,0 +1,187 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Yingmin Piao (piaoyingmin@bytedance.com) on 2022-09-26. + +#include "bh_linker_mutex.h" + +#include + +#if defined(__arm__) && __ANDROID_API__ < __ANDROID_API_L__ + +#include +#include +#include +#include +#include + +#include "bh_log.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" +#define FIELD_MASK(shift, bits) (((1 << (bits)) - 1) << (shift)) +#define FIELD_TO_BITS(val, shift, bits) (((val) & ((1 << (bits)) - 1)) << (shift)) +#define FIELD_FROM_BITS(val, shift, bits) (((val) >> (shift)) & ((1 << (bits)) - 1)) + +#define MUTEX_SHARED_SHIFT 13 +#define MUTEX_SHARED_MASK FIELD_MASK(MUTEX_SHARED_SHIFT, 1) + +#define MUTEX_TYPE_SHIFT 14 +#define MUTEX_TYPE_LEN 2 +#define MUTEX_TYPE_MASK FIELD_MASK(MUTEX_TYPE_SHIFT, MUTEX_TYPE_LEN) +#define MUTEX_TYPE_RECURSIVE 1 +#define MUTEX_TYPE_TO_BITS(t) FIELD_TO_BITS(t, MUTEX_TYPE_SHIFT, MUTEX_TYPE_LEN) +#define MUTEX_TYPE_BITS_RECURSIVE MUTEX_TYPE_TO_BITS(MUTEX_TYPE_RECURSIVE) + +#define MUTEX_OWNER_SHIFT 16 +#define MUTEX_OWNER_LEN 16 + +#define MUTEX_OWNER_FROM_BITS(v) FIELD_FROM_BITS(v, MUTEX_OWNER_SHIFT, MUTEX_OWNER_LEN) +#pragma clang diagnostic pop + +extern __attribute((weak)) unsigned long int getauxval(unsigned long int type); + +static int get_dl_data_segment(size_t *begin, size_t *end) { + if (getauxval == NULL) return -1; + + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)(size_t)getauxval(AT_BASE); + if (ehdr == NULL) return -1; + + if (0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return -1; + + ElfW(Phdr) *phdr = (ElfW(Phdr) *)((size_t)ehdr + ehdr->e_phoff); + + for (int i = 0; i < ehdr->e_phnum; i++) { + if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_W)) { + *begin = (size_t)ehdr + (size_t)phdr->p_vaddr; + *end = *begin + (size_t)phdr->p_memsz; + return 0; + } + phdr++; + } + + return -1; +} + +static int is_mutex_value(int *lock) { + int value = *lock; + if (value == MUTEX_TYPE_BITS_RECURSIVE) { + return 1; + } + + if ((value & MUTEX_TYPE_MASK) != MUTEX_TYPE_BITS_RECURSIVE || (value & MUTEX_SHARED_MASK)) { + return 0; + } + + if (gettid() == MUTEX_OWNER_FROM_BITS(value)) { + return 1; + } + + for (int i = 0; i < 1000; i++) { + volatile int newval = *lock; + if (newval == MUTEX_TYPE_BITS_RECURSIVE) { + return 1; + } + usleep(1 * 1000); + } + return 0; +} + +#define MIN_CODE_SIZE 128 +#define MAX_COUNT_OF_STACK_CHECK 32 +#define MAX_COUNT_IF_CALLEE_SAVE_REG 13 // r4..r15 + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wlanguage-extension-token" +static pthread_mutex_t *find_dl_lock_by_stack(void) { + size_t begin = 0, end = 0; + if (0 != get_dl_data_segment(&begin, &end) || begin >= end) { + BH_LOG_ERROR("not found data segment!"); + return NULL; + } + + void *somain = dlopen(NULL, RTLD_LOCAL); + if (somain == NULL) { + BH_LOG_ERROR("not found somain!"); + return NULL; + } + + size_t stack[MAX_COUNT_OF_STACK_CHECK]; + size_t *cursp = NULL; + + /* don't modify this codes, even debug >>> */ + __asm__ volatile("mov %[_cur_sp], sp" : [_cur_sp] "=r"(cursp) : :); + + dlclose(somain); + + if (cursp == NULL) return NULL; + + cursp -= 1; + + for (size_t i = 0; i < MAX_COUNT_OF_STACK_CHECK; i++) { + stack[i] = *(cursp - i); + } + /* <<< don't modify this codes, even debug */ + +#if 0 + BH_LOG_INFO("cursp=%p, dlclose=%p, begin=%p, end=%p", + (void *)cursp, (void *)&dlclose, (void *)begin, (void *)end); + + for(size_t i = 0; i < MAX_COUNT_OF_STACK_CHECK; i++) + { + BH_LOG_INFO("[%p]=%p", (void *)(cursp - i), (void *)stack[i]); + } +#endif + + for (size_t i = 0; i < MAX_COUNT_OF_STACK_CHECK; i++) { + if (stack[i] > (size_t)&dlclose && stack[i] < (size_t)&dlclose + MIN_CODE_SIZE) // found + { + size_t last = MAX_COUNT_OF_STACK_CHECK; + + if (i < MAX_COUNT_OF_STACK_CHECK - MAX_COUNT_IF_CALLEE_SAVE_REG) { + last = i + MAX_COUNT_IF_CALLEE_SAVE_REG; + } + + for (size_t j = 0; j < last; j++) { + if ((stack[j] & 0x3) == 0 && stack[j] > begin && stack[j] < end) { + if (is_mutex_value((int *)stack[j])) { + return (pthread_mutex_t *)stack[j]; + } + } + } + break; + } + } + return NULL; +} +#pragma clang diagnostic pop + +pthread_mutex_t *bh_linker_mutex_get_by_stack(void) { + return find_dl_lock_by_stack(); +} + +#else + +pthread_mutex_t *bh_linker_mutex_get_by_stack(void) { + return NULL; +} + +#endif diff --git a/app/src/main/cpp/shadowhook/sh_enter.h b/app/src/main/cpp/bytehook/bh_linker_mutex.h similarity index 83% rename from app/src/main/cpp/shadowhook/sh_enter.h rename to app/src/main/cpp/bytehook/bh_linker_mutex.h index 62bc004d..3fa80355 100644 --- a/app/src/main/cpp/shadowhook/sh_enter.h +++ b/app/src/main/cpp/bytehook/bh_linker_mutex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,12 +19,10 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Yingmin Piao (piaoyingmin@bytedance.com) on 2022-09-26. #pragma once -#include -int sh_enter_init(void); +#include -uintptr_t sh_enter_alloc(void); -void sh_enter_free(uintptr_t enter); +pthread_mutex_t *bh_linker_mutex_get_by_stack(void); diff --git a/app/src/main/cpp/bytehook/bh_log.c b/app/src/main/cpp/bytehook/bh_log.c new file mode 100644 index 00000000..d5c5ce75 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_log.c @@ -0,0 +1,40 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_log.h" + +#include +#include + +android_LogPriority bh_log_priority = ANDROID_LOG_SILENT; + +bool bh_log_get_debug(void) { + return bh_log_priority <= ANDROID_LOG_INFO; +} + +void bh_log_set_debug(bool debug) { + if (__predict_false(debug)) + bh_log_priority = ANDROID_LOG_INFO; + else + bh_log_priority = ANDROID_LOG_SILENT; +} diff --git a/app/src/main/cpp/shadowhook/common/sh_log.h b/app/src/main/cpp/bytehook/bh_log.h similarity index 55% rename from app/src/main/cpp/shadowhook/common/sh_log.h rename to app/src/main/cpp/bytehook/bh_log.h index 9d44d822..144d5edb 100644 --- a/app/src/main/cpp/shadowhook/common/sh_log.h +++ b/app/src/main/cpp/bytehook/bh_log.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,52 +19,42 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once #include -#include #include #include -#include -extern android_LogPriority sh_log_priority; +extern android_LogPriority bh_log_priority; -#define SH_LOG_TAG "shadowhook_tag" +#define BH_LOG_TAG "bytehook_tag" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -// Notice: -// We don't use ANDROID_LOG_DEBUG, because some Android devices will filter out ANDROID_LOG_DEBUG. -#ifdef SH_CONFIG_DEBUG -#define SH_LOG_DEBUG(fmt, ...) \ +#define BH_LOG_INFO(fmt, ...) \ do { \ - if (sh_log_priority <= ANDROID_LOG_INFO) \ - __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + if (__predict_false(bh_log_priority <= ANDROID_LOG_INFO)) \ + __android_log_print(ANDROID_LOG_INFO, BH_LOG_TAG, fmt, ##__VA_ARGS__); \ } while (0) -#else -#define SH_LOG_DEBUG(fmt, ...) -#endif - -#define SH_LOG_INFO(fmt, ...) \ - do { \ - if (__predict_false(sh_log_priority <= ANDROID_LOG_INFO)) \ - __android_log_print(ANDROID_LOG_INFO, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ - } while (0) -#define SH_LOG_WARN(fmt, ...) \ +#define BH_LOG_WARN(fmt, ...) \ do { \ - if (__predict_false(sh_log_priority <= ANDROID_LOG_WARN)) \ - __android_log_print(ANDROID_LOG_WARN, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + if (__predict_false(bh_log_priority <= ANDROID_LOG_WARN)) \ + __android_log_print(ANDROID_LOG_WARN, BH_LOG_TAG, fmt, ##__VA_ARGS__); \ } while (0) -#define SH_LOG_ERROR(fmt, ...) \ +#define BH_LOG_ERROR(fmt, ...) \ do { \ - if (__predict_false(sh_log_priority <= ANDROID_LOG_ERROR)) \ - __android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__); \ + if (__predict_false(bh_log_priority <= ANDROID_LOG_ERROR)) \ + __android_log_print(ANDROID_LOG_ERROR, BH_LOG_TAG, fmt, ##__VA_ARGS__); \ + } while (0) + +#define BH_LOG_SHOW(fmt, ...) \ + do { \ + __android_log_print(ANDROID_LOG_WARN, BH_LOG_TAG, fmt, ##__VA_ARGS__); \ } while (0) -#define SH_LOG_ALWAYS_SHOW(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, SH_LOG_TAG, fmt, ##__VA_ARGS__) #pragma clang diagnostic pop -bool sh_log_get_debuggable(void); -void sh_log_set_debuggable(bool debuggable); +bool bh_log_get_debug(void); +void bh_log_set_debug(bool debug); diff --git a/app/src/main/cpp/bytehook/bh_recorder.c b/app/src/main/cpp/bytehook/bh_recorder.c new file mode 100644 index 00000000..62713248 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_recorder.c @@ -0,0 +1,428 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-10-18. + +#include "bh_recorder.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_util.h" +#include "bytehook.h" + +#define BH_RECORDER_OP_HOOK 0 +#define BH_RECORDER_OP_UNHOOK 1 + +#define BH_RECORDER_LIB_NAME_MAX 512 +#define BH_RECORDER_SYM_NAME_MAX 1024 + +#define BH_RECORDER_STRINGS_BUF_EXPAND_STEP (1024 * 16) +#define BH_RECORDER_STRINGS_BUF_MAX (1024 * 128) +#define BH_RECORDER_RECORDS_BUF_EXPAND_STEP (1024 * 32) +#define BH_RECORDER_RECORDS_BUF_MAX (1024 * 384) +#define BH_RECORDER_OUTPUT_BUF_EXPAND_STEP (1024 * 128) +#define BH_RECORDER_OUTPUT_BUF_MAX (1024 * 1024) + +static bool bh_recorder_recordable = false; + +bool bh_recorder_get_recordable(void) { + return bh_recorder_recordable; +} + +void bh_recorder_set_recordable(bool recordable) { + bh_recorder_recordable = recordable; +} + +typedef struct { + void *ptr; + size_t cap; + size_t sz; + pthread_mutex_t lock; +} bh_recorder_buf_t; + +static int bh_recorder_buf_append(bh_recorder_buf_t *buf, size_t step, size_t max, const void *header, + size_t header_sz, const void *body, size_t body_sz) { + size_t needs = (header_sz + (NULL != body ? body_sz : 0)); + if (needs > step) return -1; + + if (buf->cap - buf->sz < needs) { + size_t new_cap = buf->cap + step; + if (new_cap > max) return -1; + void *new_ptr = realloc(buf->ptr, new_cap); + if (NULL == new_ptr) return -1; + buf->ptr = new_ptr; + buf->cap = new_cap; + } + + memcpy((void *)((uintptr_t)buf->ptr + buf->sz), header, header_sz); + if (NULL != body) memcpy((void *)((uintptr_t)buf->ptr + buf->sz + header_sz), body, body_sz); + buf->sz += needs; + return 0; +} + +static void bh_recorder_buf_free(bh_recorder_buf_t *buf) { + if (NULL != buf->ptr) { + free(buf->ptr); + buf->ptr = NULL; + } +} + +static bh_recorder_buf_t bh_recorder_strings = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; +static bh_recorder_buf_t bh_recorder_records = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; +static bool bh_recorder_error = false; + +typedef struct { + uint16_t str_len; // body length, in order to speed up the search +} __attribute__((packed)) bh_recorder_str_header_t; +// +body: string, including the terminating null byte ('\0') + +typedef struct { + uint64_t op : 8; + uint64_t error_number : 8; + uint64_t ts_ms : 48; + uintptr_t stub; + uint16_t caller_lib_name_idx; + uint16_t lib_name_idx; + uint16_t sym_name_idx; + uintptr_t new_addr; +} __attribute__((packed)) bh_recorder_record_hook_header_t; +// no body + +typedef struct { + uint64_t op : 8; + uint64_t error_number : 8; + uint64_t ts_ms : 48; + uintptr_t stub; + uint16_t caller_lib_name_idx; +} __attribute__((packed)) bh_recorder_record_unhook_header_t; +// no body + +static int bh_recorder_add_str(const char *str, size_t str_len, uint16_t *str_idx) { + uint16_t idx = 0; + bool ok = false; + + pthread_mutex_lock(&bh_recorder_strings.lock); + + // find in existing strings + size_t i = 0; + while (i < bh_recorder_strings.sz) { + bh_recorder_str_header_t *header = (bh_recorder_str_header_t *)((uintptr_t)bh_recorder_strings.ptr + i); + if (header->str_len == str_len) { + void *tmp = (void *)((uintptr_t)bh_recorder_strings.ptr + i + sizeof(header->str_len)); + if (0 == memcmp(tmp, str, str_len)) { + *str_idx = idx; + ok = true; + break; // OK + } + } + i += (sizeof(bh_recorder_str_header_t) + header->str_len + 1); + idx++; + if (idx == UINT16_MAX) break; // failed + } + + // insert a new string + if (!ok && idx < UINT16_MAX) { + // append new string + bh_recorder_str_header_t header = {(uint16_t)str_len}; + if (0 == bh_recorder_buf_append(&bh_recorder_strings, BH_RECORDER_STRINGS_BUF_EXPAND_STEP, + BH_RECORDER_STRINGS_BUF_MAX, &header, sizeof(header), str, str_len + 1)) { + *str_idx = idx; + ok = true; // OK + } + } + + pthread_mutex_unlock(&bh_recorder_strings.lock); + + return ok ? 0 : -1; +} + +static char *bh_recorder_find_str(uint16_t idx) { + uint16_t cur_idx = 0; + + size_t i = 0; + while (i < bh_recorder_strings.sz && cur_idx < idx) { + bh_recorder_str_header_t *header = (bh_recorder_str_header_t *)((uintptr_t)bh_recorder_strings.ptr + i); + i += (sizeof(bh_recorder_str_header_t) + header->str_len + 1); + cur_idx++; + } + if (cur_idx != idx) return "error"; + + bh_recorder_str_header_t *header = (bh_recorder_str_header_t *)((uintptr_t)bh_recorder_strings.ptr + i); + return (char *)((uintptr_t)header + sizeof(bh_recorder_str_header_t)); +} + +static long bh_recorder_tz = LONG_MAX; + +static uint64_t bh_recorder_get_timestamp_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + + if (LONG_MAX == bh_recorder_tz) { + // struct tm tm; + // if (NULL != localtime_r((time_t *)(&(tv.tv_sec)), &tm)) bh_recorder_tz = tm.tm_gmtoff; + bh_recorder_tz = 0; + } + + return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000; +} + +static size_t bh_recorder_format_timestamp_ms(uint64_t ts_ms, char *buf, size_t buf_len) { + time_t sec = (time_t)(ts_ms / 1000); + suseconds_t msec = (time_t)(ts_ms % 1000); + + struct tm tm; + bh_util_localtime_r(&sec, bh_recorder_tz, &tm); + + return bh_util_snprintf(buf, buf_len, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld%c%02ld:%02ld,", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + msec, bh_recorder_tz < 0 ? '-' : '+', labs(bh_recorder_tz / 3600), + labs(bh_recorder_tz % 3600)); +} + +static const char *bh_recorder_get_basename(const char *lib_name) { + const char *p = strrchr(lib_name, '/'); + if (NULL != p && '\0' != *(p + 1)) + return p + 1; + else + return lib_name; +} + +static void bh_recorder_get_basename_by_addr(uintptr_t addr, char *lib_name, size_t lib_name_sz) { + Dl_info info; + if (0 == dladdr((void *)addr, &info) || NULL == info.dli_fname || '\0' == info.dli_fname[0]) + strlcpy(lib_name, "unknown", lib_name_sz); + else { + const char *p = strrchr(info.dli_fname, '/'); + if (NULL == p || '\0' == *(p + 1)) + p = info.dli_fname; + else + p++; + strlcpy(lib_name, p, lib_name_sz); + } +} + +int bh_recorder_add_hook(int error_number, const char *lib_name, const char *sym_name, uintptr_t new_addr, + uintptr_t stub, uintptr_t caller_addr) { + if (!bh_recorder_recordable) return 0; + if (bh_recorder_error) return -1; + + // lib_name + if (NULL == lib_name) + lib_name = "unknown"; + else + lib_name = bh_recorder_get_basename(lib_name); + size_t lib_name_len = strlen(lib_name); + if (0 == lib_name_len || lib_name_len > BH_RECORDER_LIB_NAME_MAX) return -1; + + // sym_name + if (NULL == sym_name) return -1; + size_t sym_name_len = strlen(sym_name); + if (0 == sym_name_len || sym_name_len > BH_RECORDER_SYM_NAME_MAX) return -1; + + // caller_lib_name + char caller_lib_name[BH_RECORDER_LIB_NAME_MAX]; + bh_recorder_get_basename_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name)); + size_t caller_lib_name_len = strlen(caller_lib_name); + + // add strings to strings-pool + uint16_t lib_name_idx, sym_name_idx, caller_lib_name_idx; + if (0 != bh_recorder_add_str(lib_name, lib_name_len, &lib_name_idx)) goto err; + if (0 != bh_recorder_add_str(sym_name, sym_name_len, &sym_name_idx)) goto err; + if (0 != bh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err; + + // append new hook record + bh_recorder_record_hook_header_t header = {BH_RECORDER_OP_HOOK, + (uint8_t)error_number, + bh_recorder_get_timestamp_ms(), + stub, + caller_lib_name_idx, + lib_name_idx, + sym_name_idx, + new_addr}; + pthread_mutex_lock(&bh_recorder_records.lock); + int r = bh_recorder_buf_append(&bh_recorder_records, BH_RECORDER_RECORDS_BUF_EXPAND_STEP, + BH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0); + pthread_mutex_unlock(&bh_recorder_records.lock); + if (0 != r) goto err; + + return 0; + +err: + bh_recorder_error = true; + return -1; +} + +int bh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr) { + if (!bh_recorder_recordable) return 0; + if (bh_recorder_error) return -1; + + char caller_lib_name[BH_RECORDER_LIB_NAME_MAX]; + bh_recorder_get_basename_by_addr(caller_addr, caller_lib_name, sizeof(caller_lib_name)); + size_t caller_lib_name_len = strlen(caller_lib_name); + + uint16_t caller_lib_name_idx; + if (0 != bh_recorder_add_str(caller_lib_name, caller_lib_name_len, &caller_lib_name_idx)) goto err; + + bh_recorder_record_unhook_header_t header = {BH_RECORDER_OP_UNHOOK, (uint8_t)error_number, + bh_recorder_get_timestamp_ms(), stub, caller_lib_name_idx}; + pthread_mutex_lock(&bh_recorder_records.lock); + int r = bh_recorder_buf_append(&bh_recorder_records, BH_RECORDER_RECORDS_BUF_EXPAND_STEP, + BH_RECORDER_RECORDS_BUF_MAX, &header, sizeof(header), NULL, 0); + pthread_mutex_unlock(&bh_recorder_records.lock); + if (0 != r) goto err; + + return 0; + +err: + bh_recorder_error = true; + return -1; +} + +static const char *bh_recorder_get_op_name(uint8_t op) { + switch (op) { + case BH_RECORDER_OP_HOOK: + return "hook"; + case BH_RECORDER_OP_UNHOOK: + return "unhook"; + default: + return "error"; + } +} + +static void bh_recorder_output(char **str, int fd, uint32_t item_flags) { + if (NULL == bh_recorder_records.ptr || 0 == bh_recorder_records.sz) return; + + bh_recorder_buf_t output = {NULL, 0, 0, PTHREAD_MUTEX_INITIALIZER}; + + pthread_mutex_lock(&bh_recorder_records.lock); + pthread_mutex_lock(&bh_recorder_strings.lock); + + char line[BH_RECORDER_LIB_NAME_MAX * 2 + BH_RECORDER_SYM_NAME_MAX + 256]; + size_t line_sz; + size_t i = 0; + while (i < bh_recorder_records.sz) { + line_sz = 0; + bh_recorder_record_hook_header_t *header = + (bh_recorder_record_hook_header_t *)((uintptr_t)bh_recorder_records.ptr + i); + + if (item_flags & BYTEHOOK_RECORD_ITEM_TIMESTAMP) + line_sz += bh_recorder_format_timestamp_ms(header->ts_ms, line + line_sz, sizeof(line) - line_sz); + if (item_flags & BYTEHOOK_RECORD_ITEM_CALLER_LIB_NAME) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + bh_recorder_find_str(header->caller_lib_name_idx)); + if (item_flags & BYTEHOOK_RECORD_ITEM_OP) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + bh_recorder_get_op_name(header->op)); + if ((item_flags & BYTEHOOK_RECORD_ITEM_LIB_NAME) && header->op != BH_RECORDER_OP_UNHOOK) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + bh_recorder_find_str(header->lib_name_idx)); + if ((item_flags & BYTEHOOK_RECORD_ITEM_SYM_NAME) && header->op != BH_RECORDER_OP_UNHOOK) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%s,", + bh_recorder_find_str(header->sym_name_idx)); + if ((item_flags & BYTEHOOK_RECORD_ITEM_NEW_ADDR) && header->op != BH_RECORDER_OP_UNHOOK) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->new_addr); + if (item_flags & BYTEHOOK_RECORD_ITEM_ERRNO) + line_sz += + bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIu8 ",", header->error_number); + if (item_flags & BYTEHOOK_RECORD_ITEM_STUB) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "%" PRIxPTR ",", header->stub); + line[line_sz - 1] = '\n'; + + if (NULL != str) { + // append to string + if (0 != bh_recorder_buf_append(&output, BH_RECORDER_OUTPUT_BUF_EXPAND_STEP, BH_RECORDER_OUTPUT_BUF_MAX, + line, line_sz, NULL, 0)) { + bh_recorder_buf_free(&output); + break; // failed + } + } else { + // write to FD + if (0 != bh_util_write(fd, line, line_sz)) break; // failed + } + + i += (BH_RECORDER_OP_UNHOOK == header->op ? sizeof(bh_recorder_record_unhook_header_t) + : sizeof(bh_recorder_record_hook_header_t)); + } + + pthread_mutex_unlock(&bh_recorder_strings.lock); + pthread_mutex_unlock(&bh_recorder_records.lock); + + // error message + if (bh_recorder_error) { + line_sz = 0; + + if (item_flags & BYTEHOOK_RECORD_ITEM_TIMESTAMP) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "9999-99-99T00:00:00.000+00:00,"); + if (item_flags & BYTEHOOK_RECORD_ITEM_CALLER_LIB_NAME) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + if (item_flags & BYTEHOOK_RECORD_ITEM_OP) + line_sz += bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + + if (0 == line_sz) line_sz = bh_util_snprintf(line + line_sz, sizeof(line) - line_sz, "error,"); + + line[line_sz - 1] = '\n'; + + if (NULL != str) { + // append to string + if (0 != bh_recorder_buf_append(&output, BH_RECORDER_OUTPUT_BUF_EXPAND_STEP, BH_RECORDER_OUTPUT_BUF_MAX, + line, line_sz, NULL, 0)) { + bh_recorder_buf_free(&output); + return; // failed + } + } else { + // write to FD + if (0 != bh_util_write(fd, line, line_sz)) return; // failed + } + } + + // return string + if (NULL != str) { + if (0 != bh_recorder_buf_append(&output, BH_RECORDER_OUTPUT_BUF_EXPAND_STEP, BH_RECORDER_OUTPUT_BUF_MAX, + "", 1, NULL, 0)) { + bh_recorder_buf_free(&output); + return; // failed + } + *str = output.ptr; + } +} + +char *bh_recorder_get(uint32_t item_flags) { + if (!bh_recorder_recordable) return NULL; + if (0 == (item_flags & BYTEHOOK_RECORD_ITEM_ALL)) return NULL; + + char *str = NULL; + bh_recorder_output(&str, -1, item_flags); + return str; +} + +void bh_recorder_dump(int fd, uint32_t item_flags) { + if (!bh_recorder_recordable) return; + if (0 == (item_flags & BYTEHOOK_RECORD_ITEM_ALL)) return; + if (fd < 0) return; + bh_recorder_output(NULL, fd, item_flags); +} diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h b/app/src/main/cpp/bytehook/bh_recorder.h similarity index 67% rename from app/src/main/cpp/shadowhook/arch/arm/sh_t32.h rename to app/src/main/cpp/bytehook/bh_recorder.h index 6d06c9bd..7208c3ce 100644 --- a/app/src/main/cpp/shadowhook/arch/arm/sh_t32.h +++ b/app/src/main/cpp/bytehook/bh_recorder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,18 +19,18 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-10-18. #pragma once #include -#include #include -#include "sh_txx.h" +bool bh_recorder_get_recordable(void); +void bh_recorder_set_recordable(bool recordable); -size_t sh_t32_get_rewrite_inst_len(uint16_t high_inst, uint16_t low_inst); -size_t sh_t32_rewrite(uint16_t *buf, uint16_t high_inst, uint16_t low_inst, uintptr_t pc, - sh_txx_rewrite_info_t *rinfo); +int bh_recorder_add_hook(int error_number, const char *lib_name, const char *sym_name, uintptr_t new_addr, + uintptr_t stub, uintptr_t caller_addr); +int bh_recorder_add_unhook(int error_number, uintptr_t stub, uintptr_t caller_addr); -size_t sh_t32_absolute_jump(uint16_t *buf, bool is_align4, uintptr_t addr); -size_t sh_t32_relative_jump(uint16_t *buf, uintptr_t addr, uintptr_t pc); +char *bh_recorder_get(uint32_t item_flags); +void bh_recorder_dump(int fd, uint32_t item_flags); diff --git a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h b/app/src/main/cpp/bytehook/bh_sleb128.c similarity index 60% rename from app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h rename to app/src/main/cpp/bytehook/bh_sleb128.c index 4e41b94e..26c210cb 100644 --- a/app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h +++ b/app/src/main/cpp/bytehook/bh_sleb128.c @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,26 +19,36 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_sleb128.h" -#pragma once #include #include -#include "sh_inst.h" +void bh_sleb128_decoder_init(bh_sleb128_decoder_t *self, uint8_t *data, size_t data_sz) { + self->cur = data; + self->end = data + data_sz; +} + +int bh_sleb128_decoder_next(bh_sleb128_decoder_t *self, size_t *ret) { + size_t value = 0; + static const size_t size = 8 * sizeof(value); + size_t shift = 0; + uint8_t byte; + + do { + if (self->cur >= self->end) return -1; -typedef struct { - uintptr_t start_addr; - uintptr_t end_addr; - uint32_t *buf; - size_t buf_offset; - size_t inst_lens[4]; - size_t inst_lens_cnt; -} sh_a64_rewrite_info_t; + byte = *(self->cur)++; + value |= ((size_t)(byte & 127) << shift); + shift += 7; + } while (byte & 128); -size_t sh_a64_get_rewrite_inst_len(uint32_t inst); -size_t sh_a64_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a64_rewrite_info_t *rinfo); + if (shift < size && (byte & 64)) { + value |= -((size_t)(1) << shift); + } -size_t sh_a64_absolute_jump_with_br(uint32_t *buf, uintptr_t addr); -size_t sh_a64_absolute_jump_with_ret(uint32_t *buf, uintptr_t addr); -size_t sh_a64_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc); + *ret = value; + return 0; +} diff --git a/app/src/main/cpp/shadowhook/sh_exit.h b/app/src/main/cpp/bytehook/bh_sleb128.h similarity index 72% rename from app/src/main/cpp/shadowhook/sh_exit.h rename to app/src/main/cpp/bytehook/bh_sleb128.h index fbc99da6..8327bad7 100644 --- a/app/src/main/cpp/shadowhook/sh_exit.h +++ b/app/src/main/cpp/bytehook/bh_sleb128.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,16 +19,16 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once #include #include -#include "xdl.h" +typedef struct { + uint8_t *cur; + uint8_t *end; +} bh_sleb128_decoder_t; -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); +void bh_sleb128_decoder_init(bh_sleb128_decoder_t *self, uint8_t *data, size_t data_sz); +int bh_sleb128_decoder_next(bh_sleb128_decoder_t *self, size_t *ret); diff --git a/app/src/main/cpp/bytehook/bh_task.c b/app/src/main/cpp/bytehook/bh_task.c new file mode 100644 index 00000000..f09ce686 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_task.c @@ -0,0 +1,223 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_task.h" + +#include +#include +#include +#include +#include + +#include "bh_core.h" +#include "bh_elf_manager.h" +#include "bh_hook_manager.h" +#include "bh_log.h" +#include "bh_recorder.h" +#include "bh_util.h" +#include "queue.h" + +#define BH_TASK_ORIG_FUNC_UNSET ((void *)0x0) +#define BH_TASK_ORIG_FUNC_INVALID ((void *)0x1) + +static uint32_t bh_task_id_seed = 0; + +static bh_task_t *bh_task_create(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg) { + bh_task_t *self; + if (NULL == (self = malloc(sizeof(bh_task_t)))) return NULL; + self->id = __atomic_fetch_add(&bh_task_id_seed, 1, __ATOMIC_RELAXED); + self->callee_path_name = (NULL != callee_path_name ? strdup(callee_path_name) : NULL); + self->callee_addr = NULL; + self->sym_name = strdup(sym_name); + self->new_func = new_func; + self->hooked = hooked; + self->hooked_arg = hooked_arg; + self->hook_status_code = BYTEHOOK_STATUS_CODE_MAX; + self->manual_orig_func = BH_TASK_ORIG_FUNC_UNSET; + + return self; +} + +bh_task_t *bh_task_create_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg) { + bh_task_t *self; + if (NULL == (self = bh_task_create(callee_path_name, sym_name, new_func, hooked, hooked_arg))) return NULL; + self->type = BH_TASK_TYPE_SINGLE; + self->status = BH_TASK_STATUS_UNFINISHED; + self->caller_path_name = (NULL != caller_path_name ? strdup(caller_path_name) : NULL); + return self; +} + +bh_task_t *bh_task_create_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg) { + bh_task_t *self; + if (NULL == (self = bh_task_create(callee_path_name, sym_name, new_func, hooked, hooked_arg))) return NULL; + self->type = BH_TASK_TYPE_PARTIAL; + self->status = BH_TASK_STATUS_LONGTERM; + self->caller_path_name = NULL; + self->caller_allow_filter = caller_allow_filter; + self->caller_allow_filter_arg = caller_allow_filter_arg; + return self; +} + +bh_task_t *bh_task_create_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg) { + bh_task_t *self; + if (NULL == (self = bh_task_create(callee_path_name, sym_name, new_func, hooked, hooked_arg))) return NULL; + self->type = BH_TASK_TYPE_ALL; + self->status = BH_TASK_STATUS_LONGTERM; + self->caller_path_name = NULL; + return self; +} + +void bh_task_destroy(bh_task_t **self) { + if (NULL == self || NULL == *self) return; + + if (NULL != (*self)->caller_path_name) free((*self)->caller_path_name); + if (NULL != (*self)->callee_path_name) free((*self)->callee_path_name); + if (NULL != (*self)->sym_name) free((*self)->sym_name); + free(*self); + *self = NULL; +} + +static bool bh_task_hook_or_unhook(bh_task_t *self, bh_elf_t *elf) { + void (*hook_or_unhook)(bh_hook_manager_t *, bh_task_t *, bh_elf_t *) = + (BH_TASK_STATUS_UNHOOKING == self->status ? bh_hook_manager_unhook : bh_hook_manager_hook); + + switch (self->type) { + case BH_TASK_TYPE_SINGLE: + if (bh_elf_is_match(elf, self->caller_path_name)) { + hook_or_unhook(bh_core_global()->hook_mgr, self, elf); + if (BH_TASK_STATUS_UNHOOKING != self->status) self->status = BH_TASK_STATUS_FINISHED; + return false; // already found the ELF for single task, no need to continue + } + return true; // continue + case BH_TASK_TYPE_PARTIAL: + if (self->caller_allow_filter(elf->pathname, self->caller_allow_filter_arg)) + hook_or_unhook(bh_core_global()->hook_mgr, self, elf); + return true; // continue + case BH_TASK_TYPE_ALL: + hook_or_unhook(bh_core_global()->hook_mgr, self, elf); + return true; // continue + } +} + +static bool bh_task_elf_iterate_cb(bh_elf_t *elf, void *arg) { + return bh_task_hook_or_unhook((bh_task_t *)arg, elf); +} + +static void bh_task_handle(bh_task_t *self) { + switch (self->type) { + case BH_TASK_TYPE_SINGLE: { + bh_elf_t *caller_elf = bh_elf_manager_find_elf(bh_core_global()->elf_mgr, self->caller_path_name); + if (NULL != caller_elf) bh_task_hook_or_unhook(self, caller_elf); + break; + } + case BH_TASK_TYPE_ALL: + case BH_TASK_TYPE_PARTIAL: + bh_elf_manager_iterate(bh_core_global()->elf_mgr, bh_task_elf_iterate_cb, (void *)self); + break; + } +} + +static int bh_task_check_pre_hook(bh_task_t *self) { + // already finished, don't continue + if (BH_TASK_STATUS_FINISHED == self->status) return -1; + + if (NULL != self->callee_path_name && NULL == self->callee_addr) { + self->callee_addr = + bh_elf_manager_find_export_addr(bh_core_global()->elf_mgr, self->callee_path_name, self->sym_name); + + // could not found callee by callee's pathname, don't continue + if (NULL == self->callee_addr) return -1; + } + + return 0; +} + +void bh_task_hook(bh_task_t *self) { + if (0 != bh_task_check_pre_hook(self)) return; + + bh_task_handle(self); +} + +void bh_task_hook_elf(bh_task_t *self, bh_elf_t *elf) { + if (0 != bh_task_check_pre_hook(self)) return; + + bh_task_hook_or_unhook(self, elf); +} + +int bh_task_unhook(bh_task_t *self) { + self->status = BH_TASK_STATUS_UNHOOKING; + + if (BYTEHOOK_MODE_MANUAL == bh_core_get_mode()) { + if (BH_TASK_ORIG_FUNC_UNSET == self->manual_orig_func) { + return BYTEHOOK_STATUS_CODE_OK; // not hooked yet? not an error + } else if (BH_TASK_ORIG_FUNC_INVALID == self->manual_orig_func) { + BH_LOG_WARN("task: try to unhook with unmatch original function in manual mode"); + return BYTEHOOK_STATUS_CODE_UNMATCH_ORIG_FUNC; + } + } + + bh_task_handle(self); + return BYTEHOOK_STATUS_CODE_OK; +} + +void bh_task_set_manual_orig_func(bh_task_t *self, void *orig_func) { + if (NULL == orig_func || BH_TASK_ORIG_FUNC_INVALID == orig_func) return; + + if (BH_TASK_ORIG_FUNC_UNSET == self->manual_orig_func) { + self->manual_orig_func = orig_func; + } else if (BH_TASK_ORIG_FUNC_INVALID == self->manual_orig_func) { + return; + } else { + if (self->manual_orig_func != orig_func) self->manual_orig_func = BH_TASK_ORIG_FUNC_INVALID; + } +} + +void *bh_task_get_manual_orig_func(bh_task_t *self) { + if (BH_TASK_ORIG_FUNC_UNSET == self->manual_orig_func) return NULL; + if (BH_TASK_ORIG_FUNC_INVALID == self->manual_orig_func) return NULL; + + return self->manual_orig_func; +} + +void bh_task_hooked(bh_task_t *self, int status_code, const char *caller_path_name, void *orig_func) { + // single type task always with a caller_path_name + if (BH_TASK_TYPE_SINGLE == self->type && NULL == caller_path_name) + caller_path_name = self->caller_path_name; + + // save hook-status-code for single-task + if (BYTEHOOK_STATUS_CODE_ORIG_ADDR != status_code && BH_TASK_TYPE_SINGLE == self->type && + BH_TASK_STATUS_UNHOOKING != self->status) + self->hook_status_code = status_code; + + // do callback + if (NULL != self->hooked && BH_TASK_STATUS_UNHOOKING != self->status) + self->hooked(self, status_code, caller_path_name, self->sym_name, self->new_func, orig_func, + self->hooked_arg); +} diff --git a/app/src/main/cpp/bytehook/bh_task.h b/app/src/main/cpp/bytehook/bh_task.h new file mode 100644 index 00000000..62f9261a --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_task.h @@ -0,0 +1,96 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#pragma once +#include + +#include "bh_elf.h" +#include "bh_hook.h" +#include "bytehook.h" +#include "queue.h" + +typedef enum { BH_TASK_TYPE_SINGLE = 0, BH_TASK_TYPE_ALL, BH_TASK_TYPE_PARTIAL } bh_task_type_t; + +typedef enum { + BH_TASK_STATUS_UNFINISHED = 0, + BH_TASK_STATUS_FINISHED, + BH_TASK_STATUS_LONGTERM, + BH_TASK_STATUS_UNHOOKING +} bh_task_status_t; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +typedef struct bh_task { + uint32_t id; // unique id + bh_task_type_t type; + bh_task_status_t status; + + // caller + char *caller_path_name; // for single + bytehook_caller_allow_filter_t caller_allow_filter; // for partial + void *caller_allow_filter_arg; // for partial + + // callee + char *callee_path_name; + void *callee_addr; + + // symbol + char *sym_name; + + // new function address + void *new_func; + + // callback + bytehook_hooked_t hooked; + void *hooked_arg; + + int hook_status_code; // for single type + + void *manual_orig_func; // for manual mode + + TAILQ_ENTRY(bh_task, ) link; +} bh_task_t; +#pragma clang diagnostic pop + +bh_task_t *bh_task_create_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg); + +bh_task_t *bh_task_create_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg); + +bh_task_t *bh_task_create_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg); + +void bh_task_destroy(bh_task_t **self); + +void bh_task_hook(bh_task_t *self); +void bh_task_hook_elf(bh_task_t *self, bh_elf_t *elf); +int bh_task_unhook(bh_task_t *self); + +void bh_task_set_manual_orig_func(bh_task_t *self, void *orig_func); +void *bh_task_get_manual_orig_func(bh_task_t *self); + +void bh_task_hooked(bh_task_t *self, int status_code, const char *caller_path_name, void *orig_func); diff --git a/app/src/main/cpp/bytehook/bh_task_manager.c b/app/src/main/cpp/bytehook/bh_task_manager.c new file mode 100644 index 00000000..4afbc347 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_task_manager.c @@ -0,0 +1,165 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bh_task_manager.h" + +#include +#include +#include +#include +#include +#include + +#include "bh_core.h" +#include "bh_dl_monitor.h" +#include "bh_elf_manager.h" +#include "bh_log.h" +#include "bh_task.h" +#include "queue.h" + +typedef TAILQ_HEAD(bh_task_queue, bh_task, ) bh_task_queue_t; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +struct bh_task_manager { + bh_task_queue_t tasks; + pthread_rwlock_t lock; +}; +#pragma clang diagnostic pop + +bh_task_manager_t *bh_task_manager_create(void) { + bh_task_manager_t *self = malloc(sizeof(bh_task_manager_t)); + if (NULL == self) return NULL; + TAILQ_INIT(&self->tasks); + pthread_rwlock_init(&self->lock, NULL); + return self; +} + +void bh_task_manager_add(bh_task_manager_t *self, bh_task_t *task) { + pthread_rwlock_wrlock(&self->lock); + TAILQ_INSERT_TAIL(&self->tasks, task, link); + pthread_rwlock_unlock(&self->lock); +} + +void bh_task_manager_del(bh_task_manager_t *self, bh_task_t *task) { + pthread_rwlock_wrlock(&self->lock); + TAILQ_REMOVE(&self->tasks, task, link); + pthread_rwlock_unlock(&self->lock); +} + +static void bh_task_manager_post_new_elf(bh_elf_t *elf, void *arg) { + BH_LOG_INFO("task manager: try hook in new ELF: %s", elf->pathname); + bh_task_manager_t *self = (bh_task_manager_t *)arg; + + pthread_rwlock_rdlock(&self->lock); + bh_task_t *task; + TAILQ_FOREACH(task, &self->tasks, link) { + bh_task_hook_elf(task, elf); + } + pthread_rwlock_unlock(&self->lock); +} + +static void bh_task_manager_post_dlopen(void *arg) { + BH_LOG_INFO("task manager: post dlopen() OK"); + + bh_dl_monitor_dlclose_rdlock(); + bh_elf_manager_refresh(bh_core_global()->elf_mgr, false, bh_task_manager_post_new_elf, arg); + bh_dl_monitor_dlclose_unlock(); +} + +static void bh_task_manager_post_dlclose(bool sync_refresh, void *arg) { + (void)arg; + BH_LOG_INFO("task manager: post dlclose() OK, sync_refresh: %d", sync_refresh); + + if (sync_refresh) { + // in the range of dl_monitor's write-lock + bh_elf_manager_refresh(bh_core_global()->elf_mgr, true, NULL, NULL); + } else { + bh_dl_monitor_dlclose_rdlock(); + bh_elf_manager_refresh(bh_core_global()->elf_mgr, false, NULL, NULL); + bh_dl_monitor_dlclose_unlock(); + } +} + +static int bh_task_manager_init_dl_monitor(bh_task_manager_t *self) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool inited = false; + static bool inited_ok = false; + + if (inited) return inited_ok ? 0 : -1; // Do not repeat the initialization. + + int r; + pthread_mutex_lock(&lock); + if (!inited) { + bh_dl_monitor_set_post_dlopen(bh_task_manager_post_dlopen, self); + bh_dl_monitor_set_post_dlclose(bh_task_manager_post_dlclose, NULL); + if (0 == (r = bh_dl_monitor_init())) inited_ok = true; + inited = true; + } else { + r = inited_ok ? 0 : -1; + } + pthread_mutex_unlock(&lock); + return r; +} + +void bh_task_manager_hook(bh_task_manager_t *self, bh_task_t *task) { + if (bh_dl_monitor_is_initing()) { + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static bool oneshot_refreshed = false; + if (!oneshot_refreshed) { + bool hooked = false; + pthread_mutex_lock(&lock); + if (!oneshot_refreshed) { + bh_dl_monitor_dlclose_rdlock(); + bh_elf_manager_refresh(bh_core_global()->elf_mgr, false, NULL, NULL); + bh_task_hook(task); + bh_dl_monitor_dlclose_unlock(); + oneshot_refreshed = true; + hooked = true; + } + pthread_mutex_unlock(&lock); + if (hooked) return; + } + } else { + // start & check dl-monitor + if (0 != bh_task_manager_init_dl_monitor(self)) { + // For internal tasks in the DL monitor, this is not an error. + // But these internal tasks do not set callbacks, so there will be no side effects. + bh_task_hooked(task, BYTEHOOK_STATUS_CODE_INITERR_DLMTR, NULL, NULL); + return; + } + } + + bh_dl_monitor_dlclose_rdlock(); + bh_task_hook(task); + bh_dl_monitor_dlclose_unlock(); +} + +int bh_task_manager_unhook(bh_task_manager_t *self, bh_task_t *task) { + (void)self; + + bh_dl_monitor_dlclose_rdlock(); + int r = bh_task_unhook(task); + bh_dl_monitor_dlclose_unlock(); + return r; +} diff --git a/app/src/main/cpp/shadowhook/sh_switch.h b/app/src/main/cpp/bytehook/bh_task_manager.h similarity index 68% rename from app/src/main/cpp/shadowhook/sh_switch.h rename to app/src/main/cpp/bytehook/bh_task_manager.h index fa64cc75..96191d7d 100644 --- a/app/src/main/cpp/shadowhook/sh_switch.h +++ b/app/src/main/cpp/bytehook/bh_task_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,16 +19,17 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. #pragma once -#include +#include "bh_task.h" -#include "xdl.h" +typedef struct bh_task_manager bh_task_manager_t; -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 sh_switch_unhook(uintptr_t target_addr, uintptr_t new_addr); +bh_task_manager_t *bh_task_manager_create(void); -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); +void bh_task_manager_add(bh_task_manager_t *self, bh_task_t *task); +void bh_task_manager_del(bh_task_manager_t *self, bh_task_t *task); + +void bh_task_manager_hook(bh_task_manager_t *self, bh_task_t *task); +int bh_task_manager_unhook(bh_task_manager_t *self, bh_task_t *task); diff --git a/app/src/main/cpp/bytehook/bh_trampo.c b/app/src/main/cpp/bytehook/bh_trampo.c new file mode 100644 index 00000000..8b7ba473 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_trampo.c @@ -0,0 +1,285 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Li Zhang (zhangli.foxleezh@bytedance.com) on 2020-06-21. + +#include "bh_trampo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bh_hook.h" +#include "bh_log.h" +#include "bh_util.h" +#include "bytehook.h" +#include "bytesig.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#include "linux_syscall_support.h" +#pragma clang diagnostic pop + +#define BH_TRAMPO_BLOCK_NAME "bytehook-plt-trampolines" +#define BH_TRAMPO_BLOCK_SIZE 4096 +#define BH_TRAMPO_ALIGN 4 +#define BH_TRAMPO_STACK_NAME "bytehook-stack" +#define BH_TRAMPO_STACK_SIZE 4096 +#define BH_TRAMPO_STACK_FRAME_MAX 16 +#define BH_TRAMPO_THREAD_MAX 1024 + +typedef struct { + bh_hook_call_list_t proxies; + void *orig_func; + void *return_address; +} bh_trampo_frame_t; + +typedef struct { + size_t frames_cnt; + bh_trampo_frame_t frames[BH_TRAMPO_STACK_FRAME_MAX]; +} bh_trampo_stack_t; + +static pthread_key_t bh_trampo_tls_key; +static bh_trampo_stack_t bh_hub_stack_cache[BH_TRAMPO_THREAD_MAX]; +static uint8_t bh_hub_stack_cache_used[BH_TRAMPO_THREAD_MAX]; + +static bh_trampo_stack_t *bh_trampo_stack_create(void) { + // get stack from global cache + for (size_t i = 0; i < BH_TRAMPO_THREAD_MAX; i++) { + uint8_t *used = &(bh_hub_stack_cache_used[i]); + if (0 == *used) { + uint8_t expected = 0; + if (__atomic_compare_exchange_n(used, &expected, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + bh_trampo_stack_t *stack = &(bh_hub_stack_cache[i]); + stack->frames_cnt = 0; + return stack; // OK + } + } + } + + // create new stack by mmap + void *buf = + sys_mmap(NULL, BH_TRAMPO_STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (MAP_FAILED == buf) return NULL; // failed + sys_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)buf, BH_TRAMPO_STACK_SIZE, + (unsigned long)BH_TRAMPO_STACK_NAME); + bh_trampo_stack_t *stack = (bh_trampo_stack_t *)buf; + stack->frames_cnt = 0; + return stack; // OK +} + +static void bh_trampo_stack_destroy(void *buf) { + if (NULL == buf) return; + + if ((uintptr_t)bh_hub_stack_cache <= (uintptr_t)buf && + (uintptr_t)buf < ((uintptr_t)bh_hub_stack_cache + sizeof(bh_hub_stack_cache))) { + // return stack to global cache + size_t i = ((uintptr_t)buf - (uintptr_t)bh_hub_stack_cache) / sizeof(bh_trampo_stack_t); + uint8_t *used = &(bh_hub_stack_cache_used[i]); + if (1 != *used) abort(); + __atomic_store_n(used, 0, __ATOMIC_RELEASE); + } else { + // munmap stack + munmap(buf, BH_TRAMPO_STACK_SIZE); + } +} + +int bh_trampo_init(void) { + if (0 != pthread_key_create(&bh_trampo_tls_key, bh_trampo_stack_destroy)) return -1; + memset(&bh_hub_stack_cache, 0, sizeof(bh_hub_stack_cache)); + memset(&bh_hub_stack_cache_used, 0, sizeof(bh_hub_stack_cache_used)); + return 0; +} + +static void *bh_trampo_push_stack(bh_hook_t *hook, void *return_address) { + bh_trampo_stack_t *stack = (bh_trampo_stack_t *)pthread_getspecific(bh_trampo_tls_key); + + // create TLS data, only once + if (__predict_false(NULL == stack)) { + if (__predict_false(NULL == (stack = bh_trampo_stack_create()))) goto end; + pthread_setspecific(bh_trampo_tls_key, (void *)stack); + } + + // check whether a recursive call occurred + bool recursive = false; + for (size_t i = stack->frames_cnt; i > 0; i--) { + bh_trampo_frame_t *frame = &stack->frames[i - 1]; + + if (frame->orig_func == hook->orig_func) { + // recursive call found + recursive = true; + break; + } + } + + // find and return the first enabled hook-function in the hook-chain + // (does not include the original function) + if (!recursive) { + bh_hook_call_t *running; + SLIST_FOREACH(running, &hook->running_list, link) { + if (running->enabled) { + // push a new frame for the current proxy + if (stack->frames_cnt >= BH_TRAMPO_STACK_FRAME_MAX) goto end; + stack->frames_cnt++; + bh_trampo_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + frame->proxies = hook->running_list; + frame->orig_func = hook->orig_func; + frame->return_address = return_address; + + return running->func; + } + } + } + + // if not found enabled hook-function in the hook-chain, or recursive call found, + // just return the original-function +end: + return hook->orig_func; +} + +static void *bh_trampo_allocate(size_t sz) { + // current trampo block and info + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + static void *block = NULL; + static size_t remaining = 0; + + // return value + void *ret; + + // round sz up to nearest 4-bytes boundary + sz = (sz + ((size_t)BH_TRAMPO_ALIGN - 1)) & ~((size_t)BH_TRAMPO_ALIGN - 1); + + pthread_mutex_lock(&lock); + + // get/create an usable block + if (remaining < sz) { + // create new memory map + block = sys_mmap(NULL, BH_TRAMPO_BLOCK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (MAP_FAILED == block) { + ret = NULL; + goto end; + } + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, block, BH_TRAMPO_BLOCK_SIZE, BH_TRAMPO_BLOCK_NAME); + + // update the remaining size info + remaining = BH_TRAMPO_BLOCK_SIZE; + + BH_LOG_INFO("trampo block: created at %" PRIxPTR ", size %d", (uintptr_t)block, BH_TRAMPO_BLOCK_SIZE); + } + + // got it + ret = (void *)((size_t)block + BH_TRAMPO_BLOCK_SIZE - remaining); + remaining -= sz; + +end: + pthread_mutex_unlock(&lock); + return ret; +} + +static void *bh_trampo_template_pointer(void) { +#if defined(__arm__) && defined(__thumb__) + return (void *)((uintptr_t)&bh_trampo_template - 1); +#else + return (void *)&bh_trampo_template; +#endif +} + +void *bh_trampo_create(bh_hook_t *hook) { + size_t code_size = (uintptr_t)(&bh_trampo_data) - (uintptr_t)(bh_trampo_template_pointer()); + size_t data_size = sizeof(void *) + sizeof(void *); + + // create trampoline + void *trampo = bh_trampo_allocate(code_size + data_size); + if (NULL == trampo) return NULL; + + // fill in code + BYTESIG_TRY(SIGSEGV, SIGBUS) { + memcpy(trampo, bh_trampo_template_pointer(), code_size); + } + BYTESIG_CATCH() { + return NULL; + } + BYTESIG_EXIT + + // file in data + void **data = (void **)((uintptr_t)trampo + code_size); + *data++ = (void *)bh_trampo_push_stack; + *data = (void *)hook; + + // clear CPU cache + __builtin___clear_cache((char *)trampo, (char *)trampo + code_size + data_size); + + BH_LOG_INFO("trampo: created for GOT %" PRIxPTR " at %" PRIxPTR ", size %zu + %zu = %zu", + (uintptr_t)hook->got_addr, (uintptr_t)trampo, code_size, data_size, code_size + data_size); + +#if defined(__arm__) && defined(__thumb__) + trampo = (void *)((uintptr_t)trampo + 1); +#endif + return trampo; +} + +void *bh_trampo_get_prev_func(void *func) { + bh_trampo_stack_t *stack = (bh_trampo_stack_t *)pthread_getspecific(bh_trampo_tls_key); + if (0 == stack->frames_cnt) abort(); // called in a non-hook status? + bh_trampo_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + // find and return the next enabled hook-function in the hook-chain + bool found = false; + bh_hook_call_t *running; + SLIST_FOREACH(running, &(frame->proxies), link) { + if (!found) { + if (running->func == func) found = true; + } else { + if (running->enabled) break; + } + } + if (NULL != running) return running->func; + + // did not find, return the original-function + return frame->orig_func; +} + +void bh_trampo_pop_stack(void *return_address) { + bh_trampo_stack_t *stack = (bh_trampo_stack_t *)pthread_getspecific(bh_trampo_tls_key); + if (0 == stack->frames_cnt) return; + bh_trampo_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + if (return_address == frame->return_address) stack->frames_cnt--; +} + +void *bh_trampo_get_return_address(void) { + bh_trampo_stack_t *stack = (bh_trampo_stack_t *)pthread_getspecific(bh_trampo_tls_key); + if (0 == stack->frames_cnt) abort(); // called in a non-hook status? + bh_trampo_frame_t *frame = &stack->frames[stack->frames_cnt - 1]; + + return frame->return_address; +} diff --git a/app/src/main/cpp/bytehook/bh_trampo.h b/app/src/main/cpp/bytehook/bh_trampo.h new file mode 100644 index 00000000..8c6d271b --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_trampo.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Li Zhang (zhangli.foxleezh@bytedance.com) on 2020-06-21. + +#pragma once +#include "bh_hook.h" + +extern void *bh_trampo_data; + +void bh_trampo_template(void); + +int bh_trampo_init(void); + +void *bh_trampo_create(bh_hook_t *hook); + +void *bh_trampo_get_prev_func(void *func); + +void bh_trampo_pop_stack(void *return_address); + +void *bh_trampo_get_return_address(void); diff --git a/app/src/main/cpp/shadowhook/common/sh_log.c b/app/src/main/cpp/bytehook/bh_trampo_arm.c similarity index 55% rename from app/src/main/cpp/shadowhook/common/sh_log.c rename to app/src/main/cpp/bytehook/bh_trampo_arm.c index 0eb5ae1e..6e0cac2d 100644 --- a/app/src/main/cpp/shadowhook/common/sh_log.c +++ b/app/src/main/cpp/bytehook/bh_trampo_arm.c @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,35 +19,40 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Li Zhang (zhangli.foxleezh@bytedance.com) on 2020-06-21. -#include "sh_log.h" +#if defined(__arm__) -#include -#include +#include "bh_trampo.h" -#include "sh_config.h" +__attribute__((naked)) void bh_trampo_template(void) { + __asm__( + // Save caller-saved registers + "push { r0 - r3, lr } \n" -android_LogPriority sh_log_priority = -#ifdef SH_CONFIG_DEBUG - ANDROID_LOG_INFO -#else - ANDROID_LOG_SILENT -#endif - ; + // Call bh_trampo_push_stack() + "ldr r0, .L_hook_ptr \n" + "mov r1, lr \n" + "ldr ip, .L_push_stack \n" + "blx ip \n" + + // Save the hook function's address to IP register + "mov ip, r0 \n" -bool sh_log_get_debuggable(void) { - return sh_log_priority <= ANDROID_LOG_INFO; + // Restore caller-saved registers + "pop { r0 - r3, lr } \n" + + // Call hook function + "bx ip \n" + + "bh_trampo_data:" + ".global bh_trampo_data;" + ".L_push_stack:" + ".word 0;" + ".L_hook_ptr:" + ".word 0;"); } -void sh_log_set_debuggable(bool debuggable) { -#ifdef SH_CONFIG_DEBUG - (void)debuggable; - sh_log_priority = ANDROID_LOG_INFO; #else - if (__predict_false(debuggable)) - sh_log_priority = ANDROID_LOG_INFO; - else - sh_log_priority = ANDROID_LOG_SILENT; +typedef int make_iso_happy; #endif -} diff --git a/app/src/main/cpp/bytehook/bh_trampo_arm64.c b/app/src/main/cpp/bytehook/bh_trampo_arm64.c new file mode 100644 index 00000000..1f3119b6 --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_trampo_arm64.c @@ -0,0 +1,76 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Li Zhang (zhangli.foxleezh@bytedance.com) on 2020-06-21. + +#if defined(__aarch64__) + +#include "bh_trampo.h" + +__attribute__((naked)) void bh_trampo_template() { + __asm__( + // Save caller-saved registers + "sub sp, sp, #0xd0 \n" + "stp q0, q1, [sp, #0xb0] \n" + "stp q2, q3, [sp, #0x90] \n" + "stp q4, q5, [sp, #0x70] \n" + "stp q6, q7, [sp, #0x50] \n" + "stp x0, x1, [sp, #0x40] \n" + "stp x2, x3, [sp, #0x30] \n" + "stp x4, x5, [sp, #0x20] \n" + "stp x6, x7, [sp, #0x10] \n" + "stp x8, lr, [sp] \n" + + // Call bh_trampo_push_stack() + "ldr x0, .L_hook_ptr \n" + "mov x1, lr \n" + "ldr x16, .L_push_stack \n" + "blr x16 \n" + + // Save the hook function's address to IP register + "mov x16, x0 \n" + + // Restore caller-saved registers + "ldp x8, lr, [sp] \n" + "ldp x6, x7, [sp, #0x10] \n" + "ldp x4, x5, [sp, #0x20] \n" + "ldp x2, x3, [sp, #0x30] \n" + "ldp x0, x1, [sp, #0x40] \n" + "ldp q6, q7, [sp, #0x50] \n" + "ldp q4, q5, [sp, #0x70] \n" + "ldp q2, q3, [sp, #0x90] \n" + "ldp q0, q1, [sp, #0xb0] \n" + "add sp, sp, #0xd0 \n" + + // Call hook function + "br x16 \n" + + "bh_trampo_data:" + ".global bh_trampo_data;" + ".L_push_stack:" + ".quad 0;" + ".L_hook_ptr:" + ".quad 0;"); +} + +#else +typedef int make_iso_happy; +#endif diff --git a/app/src/main/cpp/bytehook/bh_trampo_x86.c b/app/src/main/cpp/bytehook/bh_trampo_x86.c new file mode 100644 index 00000000..22c5a9dc --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_trampo_x86.c @@ -0,0 +1,65 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-21. + +#if defined(__i386__) + +#include "bh_trampo.h" + +__attribute__((naked)) void bh_trampo_template(void) { + __asm__( + "pushl %ebp \n" + "movl %esp, %ebp \n" + + // the second param for bh_trampo_push_stack(): return address + "pushl 4(%ebp) \n" + + // the first param for bh_trampo_push_stack(): .L_hook_ptr + "call .L_pic_trampo \n" + ".L_pic_trampo: \n" + "popl %ecx \n" + "addl $(.L_hook_ptr - .L_pic_trampo), %ecx\n" + "movl (%ecx), %eax \n" + "pushl %eax \n" + + // Call bh_trampo_push_stack() + "addl $(.L_push_stack - .L_hook_ptr), %ecx\n" + "movl (%ecx), %eax \n" + "call *%eax \n" + + "movl %ebp, %esp \n" + "popl %ebp \n" + + // Call hook function + "jmp *%eax\n" + + "bh_trampo_data:" + ".global bh_trampo_data;" + ".L_push_stack:" + ".word 0; .word 0;" + ".L_hook_ptr:" + ".word 0; .word 0;"); +} + +#else +typedef int make_iso_happy; +#endif diff --git a/app/src/main/cpp/bytehook/bh_trampo_x86_64.c b/app/src/main/cpp/bytehook/bh_trampo_x86_64.c new file mode 100644 index 00000000..4b2e6adb --- /dev/null +++ b/app/src/main/cpp/bytehook/bh_trampo_x86_64.c @@ -0,0 +1,95 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-21. + +#if defined(__x86_64__) + +#include "bh_trampo.h" + +__attribute__((naked)) void bh_trampo_template(void) { + __asm__( + "pushq %rbp \n" + "movq %rsp, %rbp \n" + + // Save caller-saved registers + "subq $192, %rsp \n" + "movupd %xmm0, 176(%rsp) \n" + "movupd %xmm1, 160(%rsp) \n" + "movupd %xmm2, 144(%rsp) \n" + "movupd %xmm3, 128(%rsp) \n" + "movupd %xmm4, 112(%rsp) \n" + "movupd %xmm5, 96(%rsp) \n" + "movupd %xmm6, 80(%rsp) \n" + "movupd %xmm7, 64(%rsp) \n" + "movq %rax, 56(%rsp) \n" + "movq %rdi, 48(%rsp) \n" + "movq %rsi, 40(%rsp) \n" + "movq %rdx, 32(%rsp) \n" + "movq %rcx, 24(%rsp) \n" + "movq %r8, 16(%rsp) \n" + "movq %r9, 8(%rsp) \n" + "movq %r10, (%rsp) \n" + + // Call bh_trampo_push_stack() + "movq .L_hook_ptr(%rip), %rdi \n" + "movq 8(%rbp), %rsi \n" + "call *.L_push_stack(%rip) \n" + + // Save the hook function's address to IP register + "movq %rax, %r11 \n" + + // Restore caller-saved registers + "movupd 176(%rsp), %xmm0 \n" + "movupd 160(%rsp), %xmm1 \n" + "movupd 144(%rsp), %xmm2 \n" + "movupd 128(%rsp), %xmm3 \n" + "movupd 112(%rsp), %xmm4 \n" + "movupd 96(%rsp), %xmm5 \n" + "movupd 80(%rsp), %xmm6 \n" + "movupd 64(%rsp), %xmm7 \n" + "movq 56(%rsp), %rax \n" + "movq 48(%rsp), %rdi \n" + "movq 40(%rsp), %rsi \n" + "movq 32(%rsp), %rdx \n" + "movq 24(%rsp), %rcx \n" + "movq 16(%rsp), %r8 \n" + "movq 8(%rsp), %r9 \n" + "movq (%rsp), %r10 \n" + "addq $192, %rsp \n" + + "movq %rbp, %rsp \n" + "popq %rbp \n" + + // Call hook function + "jmp *%r11 \n" + + "bh_trampo_data:" + ".global bh_trampo_data;" + ".L_push_stack:" + ".quad 0;" + ".L_hook_ptr:" + ".quad 0;"); +} + +#else +typedef int make_iso_happy; +#endif diff --git a/app/src/main/cpp/shadowhook/common/sh_util.c b/app/src/main/cpp/bytehook/bh_util.c similarity index 63% rename from app/src/main/cpp/shadowhook/common/sh_util.c rename to app/src/main/cpp/bytehook/bh_util.c index e480b279..51981b1b 100644 --- a/app/src/main/cpp/shadowhook/common/sh_util.c +++ b/app/src/main/cpp/bytehook/bh_util.c @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,114 +19,94 @@ // SOFTWARE. // -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. -#include "sh_util.h" +#include "bh_util.h" +#include +#include +#include +#include +#include +#include +#include +#include #include -#include #include +#include #include #include #include -#include +#include +#include +#include +#include #include +#include -#include "sh_log.h" -#include "sh_sig.h" -#include "shadowhook.h" - -static size_t sh_util_page_size = 0; - -__attribute__((constructor)) static void sh_util_ctor(void) { - sh_util_page_size = (size_t)getpagesize(); -} - -size_t sh_util_get_page_size(void) { - return sh_util_page_size; -} - -uintptr_t sh_util_page_start(uintptr_t x) { - return x & ~(sh_util_page_size - 1); -} - -uintptr_t sh_util_page_end(uintptr_t x) { - return sh_util_page_start(x + sh_util_page_size - 1); -} +#ifndef PAGE_SIZE +#define PAGE_SIZE sysconf(_SC_PAGESIZE) +#endif -int sh_util_mprotect(uintptr_t addr, size_t len, int prot) { - uintptr_t start = sh_util_page_start(addr); - uintptr_t end = sh_util_page_end(addr + len - 1); +#ifndef PAGE_MASK +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#endif +#define PAGE_START(addr) ((addr) & (uintptr_t)PAGE_MASK) - return mprotect((void *)start, end - start, prot); -} +int bh_util_set_addr_protect(void *addr, int prot) { + uintptr_t start_addr = PAGE_START((uintptr_t)addr); + uintptr_t end_addr = PAGE_START((uintptr_t)addr + sizeof(uintptr_t) - 1) + PAGE_SIZE; + size_t size = end_addr - start_addr; -void sh_util_clear_cache(uintptr_t addr, size_t len) { - __builtin___clear_cache((char *)addr, (char *)(addr + len)); + if (0 != mprotect((void *)start_addr, size, prot)) return -1; + return 0; } -bool sh_util_is_thumb32(uintptr_t target_addr) { - uint16_t opcode = *((uint16_t *)target_addr); - int tmp = opcode >> 11u; - return (tmp == 0x1d) || (tmp == 0x1e) || (tmp == 0x1f); -} +int bh_util_set_protect(void *start, void *end, int prot) { + uintptr_t start_addr = PAGE_START((uintptr_t)start); + uintptr_t end_addr = PAGE_START((uintptr_t)end - 1) + PAGE_SIZE; + size_t size = end_addr - start_addr; -static uint32_t sh_util_ror(uint32_t val, uint32_t n, uint32_t shift) { - uint32_t m = shift % n; - return (val >> m) | (val << (n - m)); + if (0 != mprotect((void *)start_addr, size, prot)) return -1; + return 0; } -uint32_t sh_util_arm_expand_imm(uint32_t opcode) { - uint32_t imm = SH_UTIL_GET_BITS_32(opcode, 7, 0); - uint32_t amt = 2 * SH_UTIL_GET_BITS_32(opcode, 11, 8); +bool bh_util_starts_with(const char *str, const char *start) { + while (*str && *str == *start) { + str++; + start++; + } - return amt == 0 ? imm : sh_util_ror(imm, 32, amt); + return '\0' == *start; } -int sh_util_write_inst(uintptr_t target_addr, void *inst, size_t inst_len) { - if (0 != sh_util_mprotect(target_addr, inst_len, PROT_READ | PROT_WRITE | PROT_EXEC)) - return SHADOWHOOK_ERRNO_MPROT; - - SH_SIG_TRY(SIGSEGV, SIGBUS) { - if ((4 == inst_len) && (0 == target_addr % 4)) - __atomic_store_n((uint32_t *)target_addr, *((uint32_t *)inst), __ATOMIC_SEQ_CST); - else if ((8 == inst_len) && (0 == target_addr % 8)) - __atomic_store_n((uint64_t *)target_addr, *((uint64_t *)inst), __ATOMIC_SEQ_CST); -#ifdef __LP64__ - else if ((16 == inst_len) && (0 == target_addr % 16)) - __atomic_store_n((__int128 *)target_addr, *((__int128 *)inst), __ATOMIC_SEQ_CST); -#endif - else - memcpy((void *)target_addr, inst, inst_len); +bool bh_util_ends_with(const char *str, const char *ending) { + size_t str_len = strlen(str); + size_t ending_len = strlen(ending); - sh_util_clear_cache(target_addr, inst_len); - } - SH_SIG_CATCH() { - return SHADOWHOOK_ERRNO_WRITE_CRASH; - } - SH_SIG_EXIT + if (ending_len > str_len) return 0; - return 0; // OK + return 0 == strcmp(str + (str_len - ending_len), ending) ? true : false; } -static bool sh_util_starts_with(const char *str, const char *start) { - while (*str && *str == *start) { - str++; - start++; +size_t bh_util_trim_ending(char *start) { + char *end = start + strlen(start); + while (start < end && isspace((int)(*(end - 1)))) { + end--; + *end = '\0'; } - - return '\0' == *start; + return (size_t)(end - start); } -static int sh_util_get_api_level_from_build_prop(void) { +static int bh_util_get_api_level_from_build_prop(void) { char buf[128]; int api_level = -1; FILE *fp = fopen("/system/build.prop", "r"); - if (__predict_false(NULL == fp)) goto end; + if (NULL == fp) goto end; while (fgets(buf, sizeof(buf), fp)) { - if (__predict_false(sh_util_starts_with(buf, "ro.build.version.sdk="))) { + if (bh_util_starts_with(buf, "ro.build.version.sdk=")) { api_level = atoi(buf + 21); break; } @@ -137,22 +117,21 @@ static int sh_util_get_api_level_from_build_prop(void) { return (api_level > 0) ? api_level : -1; } -int sh_util_get_api_level(void) { - static int xdl_util_api_level = -1; +int bh_util_get_api_level(void) { + static int bh_util_api_level = -1; - if (__predict_false(xdl_util_api_level < 0)) { + if (bh_util_api_level < 0) { int api_level = android_get_device_api_level(); - if (__predict_false(api_level < 0)) - api_level = sh_util_get_api_level_from_build_prop(); // compatible with unusual models - if (__predict_false(api_level < __ANDROID_API_J__)) api_level = __ANDROID_API_J__; + if (api_level < 0) api_level = bh_util_get_api_level_from_build_prop(); // compatible with unusual models + if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__; - __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST); + __atomic_store_n(&bh_util_api_level, api_level, __ATOMIC_SEQ_CST); } - return xdl_util_api_level; + return bh_util_api_level; } -int sh_util_write(int fd, const char *buf, size_t buf_len) { +int bh_util_write(int fd, const char *buf, size_t buf_len) { if (fd < 0) return -1; const char *ptr = buf; @@ -176,14 +155,14 @@ int sh_util_write(int fd, const char *buf, size_t buf_len) { /* Nonzero if YEAR is a leap year (every 4 years, except every 100th isn't, and every 400th is). */ -#define SH_UTIL_ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#define BH_UTIL_ISLEAP(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) -#define SH_UTIL_SECS_PER_HOUR (60 * 60) -#define SH_UTIL_SECS_PER_DAY (SH_UTIL_SECS_PER_HOUR * 24) -#define SH_UTIL_DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) -#define SH_UTIL_LEAPS_THRU_END_OF(y) (SH_UTIL_DIV(y, 4) - SH_UTIL_DIV(y, 100) + SH_UTIL_DIV(y, 400)) +#define BH_UTIL_SECS_PER_HOUR (60 * 60) +#define BH_UTIL_SECS_PER_DAY (BH_UTIL_SECS_PER_HOUR * 24) +#define BH_UTIL_DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define BH_UTIL_LEAPS_THRU_END_OF(y) (BH_UTIL_DIV(y, 4) - BH_UTIL_DIV(y, 100) + BH_UTIL_DIV(y, 400)) -static const unsigned short int sh_util_mon_yday[2][13] = { +static const unsigned short int bh_util_mon_yday[2][13] = { /* Normal years. */ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, /* Leap years. */ @@ -193,7 +172,7 @@ static const unsigned short int sh_util_mon_yday[2][13] = { offset GMTOFF seconds east of UTC, and store year, yday, mon, mday, wday, hour, min, sec into *RESULT. Return RESULT if successful. */ -struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result) { +struct tm *bh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result) { time_t days, rem, y; const unsigned short int *ip; @@ -201,32 +180,32 @@ struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *resu result->tm_gmtoff = gmtoff; - days = ((*timep) / SH_UTIL_SECS_PER_DAY); - rem = ((*timep) % SH_UTIL_SECS_PER_DAY); + days = ((*timep) / BH_UTIL_SECS_PER_DAY); + rem = ((*timep) % BH_UTIL_SECS_PER_DAY); rem += gmtoff; while (rem < 0) { - rem += SH_UTIL_SECS_PER_DAY; + rem += BH_UTIL_SECS_PER_DAY; --days; } - while (rem >= SH_UTIL_SECS_PER_DAY) { - rem -= SH_UTIL_SECS_PER_DAY; + while (rem >= BH_UTIL_SECS_PER_DAY) { + rem -= BH_UTIL_SECS_PER_DAY; ++days; } - result->tm_hour = (int)(rem / SH_UTIL_SECS_PER_HOUR); - rem %= SH_UTIL_SECS_PER_HOUR; - result->tm_min = (int)(rem / 60L); - result->tm_sec = (int)(rem % 60L); + result->tm_hour = (int)(rem / BH_UTIL_SECS_PER_HOUR); + rem %= BH_UTIL_SECS_PER_HOUR; + result->tm_min = (int)(rem / 60); + result->tm_sec = rem % 60; /* January 1, 1970 was a Thursday. */ - result->tm_wday = (int)(4 + days) % 7; + result->tm_wday = (4 + days) % 7; if (result->tm_wday < 0) result->tm_wday += 7; y = 1970; - while (days < 0 || days >= (SH_UTIL_ISLEAP(y) ? 366 : 365)) { + while (days < 0 || days >= (BH_UTIL_ISLEAP(y) ? 366 : 365)) { /* Guess a corrected year, assuming 365 days per year. */ time_t yg = y + days / 365 - (days % 365 < 0); /* Adjust DAYS and Y to match the guessed year. */ - days -= ((yg - y) * 365 + SH_UTIL_LEAPS_THRU_END_OF(yg - 1) - SH_UTIL_LEAPS_THRU_END_OF(y - 1)); + days -= ((yg - y) * 365 + BH_UTIL_LEAPS_THRU_END_OF(yg - 1) - BH_UTIL_LEAPS_THRU_END_OF(y - 1)); y = yg; } @@ -237,7 +216,7 @@ struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *resu return NULL; } result->tm_yday = (int)days; - ip = sh_util_mon_yday[SH_UTIL_ISLEAP(y)]; + ip = bh_util_mon_yday[BH_UTIL_ISLEAP(y)]; for (y = 11; days < (long int)ip[y]; --y) continue; days -= ip[y]; result->tm_mon = (int)y; @@ -245,7 +224,7 @@ struct tm *sh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *resu return result; } -static unsigned sh_util_parse_decimal(const char *format, int *ppos) { +static unsigned bh_util_parse_decimal(const char *format, int *ppos) { const char *p = format + *ppos; unsigned result = 0; for (;;) { @@ -261,7 +240,7 @@ static unsigned sh_util_parse_decimal(const char *format, int *ppos) { return result; } -static void sh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, int base, int caps) { +static void bh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, int base, int caps) { char *p = buf; char *end = buf + buf_size - 1; @@ -297,7 +276,7 @@ static void sh_util_format_unsigned(char *buf, size_t buf_size, uint64_t value, } } -static void sh_util_format_integer(char *buf, size_t buf_size, uint64_t value, char conversion) { +static void bh_util_format_integer(char *buf, size_t buf_size, uint64_t value, char conversion) { // Decode the conversion specifier. int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o'); int base = 10; @@ -313,7 +292,7 @@ static void sh_util_format_integer(char *buf, size_t buf_size, uint64_t value, c buf_size -= 1; value = (uint64_t)(-(int64_t)(value)); } - sh_util_format_unsigned(buf, buf_size, value, base, caps); + bh_util_format_unsigned(buf, buf_size, value, base, caps); } // format stream @@ -321,9 +300,9 @@ typedef struct { size_t total; char *pos; size_t avail; -} sh_util_stream_t; +} bh_util_stream_t; -static void sh_util_stream_init(sh_util_stream_t *self, char *buffer, size_t buffer_size) { +static void bh_util_stream_init(bh_util_stream_t *self, char *buffer, size_t buffer_size) { self->total = 0; self->pos = buffer; self->avail = buffer_size; @@ -331,11 +310,11 @@ static void sh_util_stream_init(sh_util_stream_t *self, char *buffer, size_t buf if (self->avail > 0) self->pos[0] = '\0'; } -static size_t sh_util_stream_total(sh_util_stream_t *self) { +static size_t bh_util_stream_total(bh_util_stream_t *self) { return self->total; } -static void sh_util_stream_send(sh_util_stream_t *self, const char *data, int len) { +static void bh_util_stream_send(bh_util_stream_t *self, const char *data, int len) { if (len < 0) { len = (int)strlen(data); } @@ -356,7 +335,7 @@ static void sh_util_stream_send(sh_util_stream_t *self, const char *data, int le self->avail -= (size_t)len; } -static void sh_util_stream_send_repeat(sh_util_stream_t *self, char ch, int count) { +static void bh_util_stream_send_repeat(bh_util_stream_t *self, char ch, int count) { char pad[8]; memset(pad, ch, sizeof(pad)); @@ -366,12 +345,12 @@ static void sh_util_stream_send_repeat(sh_util_stream_t *self, char ch, int coun if (avail > pad_size) { avail = pad_size; } - sh_util_stream_send(self, pad, avail); + bh_util_stream_send(self, pad, avail); count -= avail; } } -static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, va_list args) { +static void bh_util_stream_vformat(bh_util_stream_t *self, const char *format, va_list args) { int nn = 0; for (;;) { @@ -394,7 +373,7 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v mm++; } while (1); if (mm > nn) { - sh_util_stream_send(self, format + nn, mm - nn); + bh_util_stream_send(self, format + nn, mm - nn); nn = mm; } @@ -410,7 +389,7 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v if (c == '\0') { // single trailing '%' ? c = '%'; - sh_util_stream_send(self, &c, 1); + bh_util_stream_send(self, &c, 1); return; } else if (c == '0') { padZero = 1; @@ -428,13 +407,13 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v // parse field width if ((c >= '0' && c <= '9')) { nn--; - width = (int)(sh_util_parse_decimal(format, &nn)); + width = (int)(bh_util_parse_decimal(format, &nn)); c = format[nn++]; } // parse precision if (c == '.') { - prec = (int)(sh_util_parse_decimal(format, &nn)); + prec = (int)(bh_util_parse_decimal(format, &nn)); c = format[nn++]; } @@ -484,7 +463,7 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v uint64_t value = (uintptr_t)(va_arg(args, void *)); buffer[0] = '0'; buffer[1] = 'x'; - sh_util_format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x'); + bh_util_format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x'); } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') { // integers - first read value from stack uint64_t value; @@ -512,7 +491,7 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v value = (uint64_t)(((int64_t)(value << shift)) >> shift); } // format the number properly into our buffer - sh_util_format_integer(buffer, sizeof(buffer), value, c); + bh_util_format_integer(buffer, sizeof(buffer), value, c); } else if (c == '%') { buffer[0] = '%'; buffer[1] = '\0'; @@ -530,27 +509,27 @@ static void sh_util_stream_vformat(sh_util_stream_t *self, const char *format, v } if (slen < width && !padLeft) { char padChar = padZero ? '0' : ' '; - sh_util_stream_send_repeat(self, padChar, width - slen); + bh_util_stream_send_repeat(self, padChar, width - slen); } - sh_util_stream_send(self, str, slen); + bh_util_stream_send(self, str, slen); if (slen < width && padLeft) { char padChar = padZero ? '0' : ' '; - sh_util_stream_send_repeat(self, padChar, width - slen); + bh_util_stream_send_repeat(self, padChar, width - slen); } } } -size_t sh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args) { - sh_util_stream_t stream; - sh_util_stream_init(&stream, buffer, buffer_size); - sh_util_stream_vformat(&stream, format, args); - return sh_util_stream_total(&stream); +size_t bh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args) { + bh_util_stream_t stream; + bh_util_stream_init(&stream, buffer, buffer_size); + bh_util_stream_vformat(&stream, format, args); + return bh_util_stream_total(&stream); } -size_t sh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...) { +size_t bh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...) { va_list args; va_start(args, format); - size_t buffer_len = sh_util_vsnprintf(buffer, buffer_size, format, args); + size_t buffer_len = bh_util_vsnprintf(buffer, buffer_size, format, args); va_end(args); return buffer_len; } diff --git a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h b/app/src/main/cpp/bytehook/bh_util.h similarity index 57% rename from app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h rename to app/src/main/cpp/bytehook/bh_util.h index 8641d1ae..0a2a0a1c 100644 --- a/app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h +++ b/app/src/main/cpp/bytehook/bh_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 HexHacking Team +// Copyright (c) 2020-2022 ByteDance, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -19,31 +19,25 @@ // SOFTWARE. // -// Created by caikelun on 2020-10-04. - -#ifndef IO_GITHUB_HEXHACKING_XDL_UTIL -#define IO_GITHUB_HEXHACKING_XDL_UTIL +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. +#pragma once +#include #include +#include +#include #include #include +#include +#include -#ifndef __LP64__ -#define XDL_UTIL_LINKER_BASENAME "linker" -#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker" -#define XDL_UTIL_APP_PROCESS_BASENAME "app_process32" -#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32" -#define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process" -#define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process" +#if defined(__LP64__) +#define BH_UTIL_PRIxADDR "016" PRIxPTR #else -#define XDL_UTIL_LINKER_BASENAME "linker64" -#define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64" -#define XDL_UTIL_APP_PROCESS_BASENAME "app_process64" -#define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64" +#define BH_UTIL_PRIxADDR "08" PRIxPTR #endif -#define XDL_UTIL_VDSO_BASENAME "[vdso]" -#define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \ +#define BH_UTIL_TEMP_FAILURE_RETRY(exp) \ ({ \ __typeof__(exp) _rc; \ do { \ @@ -53,19 +47,19 @@ _rc; \ }) -#ifdef __cplusplus -extern "C" { -#endif +int bh_util_set_addr_protect(void *addr, int prot); +int bh_util_set_protect(void *start, void *end, int prot); -bool xdl_util_starts_with(const char *str, const char *start); -bool xdl_util_ends_with(const char *str, const char *ending); +bool bh_util_starts_with(const char *str, const char *start); +bool bh_util_ends_with(const char *str, const char *ending); -size_t xdl_util_trim_ending(char *start); +size_t bh_util_trim_ending(char *start); -int xdl_util_get_api_level(void); +int bh_util_get_api_level(void); -#ifdef __cplusplus -} -#endif +int bh_util_write(int fd, const char *buf, size_t buf_len); -#endif +struct tm *bh_util_localtime_r(const time_t *timep, long gmtoff, struct tm *result); + +size_t bh_util_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list args); +size_t bh_util_snprintf(char *buffer, size_t buffer_size, const char *format, ...); diff --git a/app/src/main/cpp/bytehook/bytehook.c b/app/src/main/cpp/bytehook/bytehook.c new file mode 100644 index 00000000..fa27aec8 --- /dev/null +++ b/app/src/main/cpp/bytehook/bytehook.c @@ -0,0 +1,124 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2020-06-02. + +#include "bytehook.h" + +#include +#include +#include +#include +#include + +#include "bh_core.h" +#include "bh_recorder.h" + +const char *bytehook_get_version(void) { + return "bytehook version " BYTEHOOK_VERSION; +} + +int bytehook_init(int mode, bool debug) { + return bh_core_init(mode, debug); +} + +bytehook_stub_t bytehook_hook_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg) { + const void *caller_addr = __builtin_return_address(0); + return bh_core_hook_single(caller_path_name, callee_path_name, sym_name, new_func, hooked, hooked_arg, + (uintptr_t)caller_addr); +} + +bytehook_stub_t bytehook_hook_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg) { + const void *caller_addr = __builtin_return_address(0); + return bh_core_hook_partial(caller_allow_filter, caller_allow_filter_arg, callee_path_name, sym_name, + new_func, hooked, hooked_arg, (uintptr_t)caller_addr); +} + +bytehook_stub_t bytehook_hook_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg) { + const void *caller_addr = __builtin_return_address(0); + return bh_core_hook_all(callee_path_name, sym_name, new_func, hooked, hooked_arg, (uintptr_t)caller_addr); +} + +int bytehook_unhook(bytehook_stub_t stub) { + const void *caller_addr = __builtin_return_address(0); + return bh_core_unhook(stub, (uintptr_t)caller_addr); +} + +int bytehook_add_ignore(const char *caller_path_name) { + return bh_core_add_ignore(caller_path_name); +} + +bool bytehook_get_debug(void) { + return bh_core_get_debug(); +} + +void bytehook_set_debug(bool debug) { + bh_core_set_debug(debug); +} + +bool bytehook_get_recordable(void) { + return bh_core_get_recordable(); +} + +void bytehook_set_recordable(bool recordable) { + bh_core_set_recordable(recordable); +} + +char *bytehook_get_records(uint32_t item_flags) { + return bh_recorder_get(item_flags); +} + +void bytehook_dump_records(int fd, uint32_t item_flags) { + bh_recorder_dump(fd, item_flags); +} + +void *bytehook_get_prev_func(void *func) { + if (__predict_false(BYTEHOOK_MODE_MANUAL == bh_core_get_mode())) abort(); + return bh_core_get_prev_func(func); +} + +void *bytehook_get_return_address(void) { + if (__predict_false(BYTEHOOK_MODE_MANUAL == bh_core_get_mode())) abort(); + return bh_core_get_return_address(); +} + +void bytehook_pop_stack(void *return_address) { + if (__predict_false(BYTEHOOK_MODE_MANUAL == bh_core_get_mode())) abort(); + bh_core_pop_stack(return_address); +} + +int bytehook_get_mode(void) { + return bh_core_get_mode(); +} + +void bytehook_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + bh_core_add_dlopen_callback(pre, post, data); +} + +void bytehook_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data) { + bh_core_del_dlopen_callback(pre, post, data); +} diff --git a/app/src/main/cpp/bytehook/bytehook.map.txt b/app/src/main/cpp/bytehook/bytehook.map.txt new file mode 100644 index 00000000..66381375 --- /dev/null +++ b/app/src/main/cpp/bytehook/bytehook.map.txt @@ -0,0 +1,26 @@ +{ + global: + JNI_OnLoad; + bytehook_get_version; + bytehook_init; + bytehook_hook_single; + bytehook_hook_all; + bytehook_hook_partial; + bytehook_unhook; + bytehook_add_ignore; + bytehook_get_debug; + bytehook_set_debug; + bytehook_get_recordable; + bytehook_set_recordable; + bytehook_get_records; + bytehook_dump_records; + bytehook_get_prev_func; + bytehook_pop_stack; + bytehook_get_return_address; + bytehook_get_mode; + bytehook_add_dlopen_callback; + bytehook_del_dlopen_callback; + + local: + *; +}; diff --git a/app/src/main/cpp/shadowhook/common/bytesig.c b/app/src/main/cpp/bytehook/bytesig.c similarity index 99% rename from app/src/main/cpp/shadowhook/common/bytesig.c rename to app/src/main/cpp/bytehook/bytesig.c index 174ac0b0..8568279f 100644 --- a/app/src/main/cpp/shadowhook/common/bytesig.c +++ b/app/src/main/cpp/bytehook/bytesig.c @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2021-2023 ByteDance Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/app/src/main/cpp/shadowhook/common/bytesig.h b/app/src/main/cpp/bytehook/bytesig.h similarity index 99% rename from app/src/main/cpp/shadowhook/common/bytesig.h rename to app/src/main/cpp/bytehook/bytesig.h index b8fcd758..a7de5268 100644 --- a/app/src/main/cpp/shadowhook/common/bytesig.h +++ b/app/src/main/cpp/bytehook/bytesig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2024 ByteDance Inc. +// Copyright (c) 2021-2023 ByteDance Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/app/src/main/cpp/bytehook/include/bytehook.h b/app/src/main/cpp/bytehook/include/bytehook.h new file mode 100644 index 00000000..8ac16b67 --- /dev/null +++ b/app/src/main/cpp/bytehook/include/bytehook.h @@ -0,0 +1,192 @@ +// Copyright (c) 2020-2022 ByteDance, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// ByteHook is now credited to all its contributors. +// +// Before ByteHook was open sourced in August 2021, it was designed, implemented and +// improved by the ByteHook Development Team from ByteDance IES, GIP and Client Infra. +// The team members (alphabetical order): +// +// Hongkai Liu (liuhongkai@bytedance.com) +// Jinshi Song (songjinshi@bytedance.com) +// Kelun Cai (caikelun@bytedance.com) +// Li Han (hanli.lee@bytedance.com) +// Li Zhang (zhangli.foxleezh@bytedance.com) +// Minghao Li (liminghao.mingo@bytedance.com) +// Nian Sun (sunnian@bytedance.com) +// Quanfei Li (liquanfei@bytedance.com) +// Shujie Wang (wangshujie.matt@bytedance.com) +// Tianzhou Shen (shentianzhou@bytedance.com) +// Xiangyu Pang (pangxiangyu@bytedance.com) +// Yingmin Piao (piaoyingmin@bytedance.com) +// Yonggang Sun (sunyonggang@bytedance.com) +// Zhi Guo (guozhi.kevin@bytedance.com) +// Zhi Xu (xuzhi@bytedance.com) +// + +#ifndef BYTEDANCE_BYTEHOOK_H +#define BYTEDANCE_BYTEHOOK_H 1 + +#include +#include + +#define BYTEHOOK_VERSION "1.0.10" + +#define BYTEHOOK_STATUS_CODE_OK 0 +#define BYTEHOOK_STATUS_CODE_UNINIT 1 +#define BYTEHOOK_STATUS_CODE_INITERR_INVALID_ARG 2 +#define BYTEHOOK_STATUS_CODE_INITERR_SYM 3 +#define BYTEHOOK_STATUS_CODE_INITERR_TASK 4 +#define BYTEHOOK_STATUS_CODE_INITERR_HOOK 5 +#define BYTEHOOK_STATUS_CODE_INITERR_ELF 6 +#define BYTEHOOK_STATUS_CODE_INITERR_ELF_REFR 7 +#define BYTEHOOK_STATUS_CODE_INITERR_TRAMPO 8 +#define BYTEHOOK_STATUS_CODE_INITERR_SIG 9 +#define BYTEHOOK_STATUS_CODE_INITERR_DLMTR 10 +#define BYTEHOOK_STATUS_CODE_INVALID_ARG 11 +#define BYTEHOOK_STATUS_CODE_UNMATCH_ORIG_FUNC 12 +#define BYTEHOOK_STATUS_CODE_NOSYM 13 +#define BYTEHOOK_STATUS_CODE_GET_PROT 14 +#define BYTEHOOK_STATUS_CODE_SET_PROT 15 +#define BYTEHOOK_STATUS_CODE_SET_GOT 16 +#define BYTEHOOK_STATUS_CODE_NEW_TRAMPO 17 +#define BYTEHOOK_STATUS_CODE_APPEND_TRAMPO 18 +#define BYTEHOOK_STATUS_CODE_GOT_VERIFY 19 +#define BYTEHOOK_STATUS_CODE_REPEATED_FUNC 20 +#define BYTEHOOK_STATUS_CODE_READ_ELF 21 +#define BYTEHOOK_STATUS_CODE_CFI_HOOK_FAILED 22 +#define BYTEHOOK_STATUS_CODE_ORIG_ADDR 23 +#define BYTEHOOK_STATUS_CODE_INITERR_CFI 24 +#define BYTEHOOK_STATUS_CODE_IGNORE 25 +#define BYTEHOOK_STATUS_CODE_MAX 255 + +#define BYTEHOOK_MODE_AUTOMATIC 0 +#define BYTEHOOK_MODE_MANUAL 1 + +#ifdef __cplusplus +extern "C" { +#endif + +const char *bytehook_get_version(void); + +typedef void *bytehook_stub_t; + +typedef void (*bytehook_hooked_t)(bytehook_stub_t task_stub, int status_code, const char *caller_path_name, + const char *sym_name, void *new_func, void *prev_func, void *arg); + +typedef bool (*bytehook_caller_allow_filter_t)(const char *caller_path_name, void *arg); + +int bytehook_init(int mode, bool debug); + +bytehook_stub_t bytehook_hook_single(const char *caller_path_name, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg); + +bytehook_stub_t bytehook_hook_partial(bytehook_caller_allow_filter_t caller_allow_filter, + void *caller_allow_filter_arg, const char *callee_path_name, + const char *sym_name, void *new_func, bytehook_hooked_t hooked, + void *hooked_arg); + +bytehook_stub_t bytehook_hook_all(const char *callee_path_name, const char *sym_name, void *new_func, + bytehook_hooked_t hooked, void *hooked_arg); + +int bytehook_unhook(bytehook_stub_t stub); + +int bytehook_add_ignore(const char *caller_path_name); + +int bytehook_get_mode(void); +bool bytehook_get_debug(void); +void bytehook_set_debug(bool debug); +bool bytehook_get_recordable(void); +void bytehook_set_recordable(bool recordable); + +// get operation records +#define BYTEHOOK_RECORD_ITEM_ALL 0xFF // 0b11111111 +#define BYTEHOOK_RECORD_ITEM_TIMESTAMP (1 << 0) +#define BYTEHOOK_RECORD_ITEM_CALLER_LIB_NAME (1 << 1) +#define BYTEHOOK_RECORD_ITEM_OP (1 << 2) +#define BYTEHOOK_RECORD_ITEM_LIB_NAME (1 << 3) +#define BYTEHOOK_RECORD_ITEM_SYM_NAME (1 << 4) +#define BYTEHOOK_RECORD_ITEM_NEW_ADDR (1 << 5) +#define BYTEHOOK_RECORD_ITEM_ERRNO (1 << 6) +#define BYTEHOOK_RECORD_ITEM_STUB (1 << 7) +char *bytehook_get_records(uint32_t item_flags); +void bytehook_dump_records(int fd, uint32_t item_flags); + +// for internal use +void *bytehook_get_prev_func(void *func); + +// for internal use +void bytehook_pop_stack(void *return_address); + +// for internal use +void *bytehook_get_return_address(void); + +typedef void (*bytehook_pre_dlopen_t)(const char *filename, void *data); + +typedef void (*bytehook_post_dlopen_t)(const char *filename, + int result, // 0: OK -1: Failed + void *data); + +void bytehook_add_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); + +void bytehook_del_dlopen_callback(bytehook_pre_dlopen_t pre, bytehook_post_dlopen_t post, void *data); + +#ifdef __cplusplus +} +#endif + +// call previous function in hook-function +#ifdef __cplusplus +#define BYTEHOOK_CALL_PREV(func, ...) ((decltype(&(func)))bytehook_get_prev_func((void *)(func)))(__VA_ARGS__) +#else +#define BYTEHOOK_CALL_PREV(func, func_sig, ...) \ + ((func_sig)bytehook_get_prev_func((void *)(func)))(__VA_ARGS__) +#endif + +// get return address in hook-function +#define BYTEHOOK_RETURN_ADDRESS() \ + ((void *)(BYTEHOOK_MODE_AUTOMATIC == bytehook_get_mode() ? bytehook_get_return_address() \ + : __builtin_return_address(0))) + +// pop stack in hook-function (for C/C++) +#define BYTEHOOK_POP_STACK() \ + do { \ + if (BYTEHOOK_MODE_AUTOMATIC == bytehook_get_mode()) bytehook_pop_stack(__builtin_return_address(0)); \ + } while (0) + +// pop stack in hook-function (for C++ only) +#ifdef __cplusplus +class BytehookStackScope { + public: + BytehookStackScope(void *return_address) : return_address_(return_address) {} + + ~BytehookStackScope() { + if (BYTEHOOK_MODE_AUTOMATIC == bytehook_get_mode()) bytehook_pop_stack(return_address_); + } + + private: + void *return_address_; +}; +#define BYTEHOOK_STACK_SCOPE() BytehookStackScope bytehook_stack_scope_obj(__builtin_return_address(0)) +#endif + +#endif diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/queue.h b/app/src/main/cpp/bytehook/third_party/bsd/queue.h similarity index 99% rename from app/src/main/cpp/shadowhook/third_party/bsd/queue.h rename to app/src/main/cpp/bytehook/third_party/bsd/queue.h index f550f6c2..a68a9fc2 100644 --- a/app/src/main/cpp/shadowhook/third_party/bsd/queue.h +++ b/app/src/main/cpp/bytehook/third_party/bsd/queue.h @@ -25,6 +25,9 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $ */ #ifndef QUEUE_H diff --git a/app/src/main/cpp/shadowhook/third_party/bsd/tree.h b/app/src/main/cpp/bytehook/third_party/bsd/tree.h similarity index 99% rename from app/src/main/cpp/shadowhook/third_party/bsd/tree.h rename to app/src/main/cpp/bytehook/third_party/bsd/tree.h index e6db6a90..9b1b3dd4 100644 --- a/app/src/main/cpp/shadowhook/third_party/bsd/tree.h +++ b/app/src/main/cpp/bytehook/third_party/bsd/tree.h @@ -1,3 +1,7 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */ + /*- * Copyright 2002 Niels Provos * All rights reserved. diff --git a/app/src/main/cpp/shadowhook/third_party/lss/LICENSE b/app/src/main/cpp/bytehook/third_party/lss/LICENSE similarity index 100% rename from app/src/main/cpp/shadowhook/third_party/lss/LICENSE rename to app/src/main/cpp/bytehook/third_party/lss/LICENSE diff --git a/app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h b/app/src/main/cpp/bytehook/third_party/lss/linux_syscall_support.h similarity index 100% rename from app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h rename to app/src/main/cpp/bytehook/third_party/lss/linux_syscall_support.h diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index e81d1426..00083850 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -5,7 +5,7 @@ #include #include #include "zygisk.hpp" -#include "shadowhook.h" +#include "bytehook.h" #include "cJSON.h" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__) @@ -91,36 +91,28 @@ static void modify_callback(void *cookie, const char *name, const char *value, u return o_callback(cookie, name, value, serial); } -static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *) = nullptr; - static void my_system_property_read_callback(const prop_info *pi, T_Callback callback, void *cookie) { if (pi && callback && cookie) o_callback = callback; - return o_system_property_read_callback(pi, modify_callback, cookie); + + return BYTEHOOK_CALL_PREV(my_system_property_read_callback, pi, modify_callback, cookie); } static bool doHook() { - shadowhook_init(SHADOWHOOK_MODE_UNIQUE, false); - { - auto libc_handle = shadowhook_dlopen("libc.so"); - if (!libc_handle) { - LOGE("error loading libc.so library!"); - goto exit; - } - auto handle = shadowhook_dlsym(libc_handle, "__system_property_read_callback"); - if (!handle) { - LOGE("error resolving __system_property_read_callback symbol!"); - goto exit; - } - if (shadowhook_hook_sym_addr(handle, (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback)) { - LOGD("hook __system_property_read_callback success at %p", handle); - return true; - } + int init = bytehook_init(BYTEHOOK_MODE_AUTOMATIC, true); + if (init != BYTEHOOK_STATUS_CODE_OK) { + LOGE("Failed to initialize bytehook: %d", init); + return false; + } + + void *handle = bytehook_hook_all(nullptr, "__system_property_read_callback", (void *) my_system_property_read_callback, nullptr, nullptr); + if (handle == nullptr) { + LOGE("Failed to hook __system_property_read_callback."); + return false; } - exit: - LOGE("hook __system_property_read_callback failed!"); - return false; + + LOGD("Hooked __system_property_read_callback."); + return true; } class PlayIntegrityFix : public zygisk::ModuleBase { diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c deleted file mode 100644 index cffd120f..00000000 --- a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright (c) 2021-2024 ByteDance Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. - -#include "sh_a32.h" - -#include -#include -#include - -#include "sh_log.h" - -// https://developer.arm.com/documentation/ddi0406/latest -// https://developer.arm.com/documentation/ddi0597/latest - -typedef enum { - IGNORED = 0, - B_A1, - BX_A1, - BL_IMM_A1, - BLX_IMM_A2, - ADD_REG_A1, - ADD_REG_PC_A1, - SUB_REG_A1, - SUB_REG_PC_A1, - ADR_A1, - ADR_A2, - MOV_REG_A1, - MOV_REG_PC_A1, - LDR_LIT_A1, - LDR_LIT_PC_A1, - LDRB_LIT_A1, - LDRD_LIT_A1, - LDRH_LIT_A1, - LDRSB_LIT_A1, - LDRSH_LIT_A1, - LDR_REG_A1, - LDR_REG_PC_A1, - LDRB_REG_A1, - LDRD_REG_A1, - LDRH_REG_A1, - LDRSB_REG_A1, - LDRSH_REG_A1 -} sh_a32_type_t; - -static sh_a32_type_t sh_a32_get_type(uint32_t inst) { - if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000)) - return B_A1; - else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000)) - return BX_A1; - else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000)) - return BL_IMM_A1; - else if ((inst & 0xFE000000) == 0xFA000000) - return BLX_IMM_A2; - else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && - (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) - return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1; - else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && - (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) - return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1; - else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ADR_A1; - else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ADR_A2; - else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x0010F000u) != 0x0010F000) && - (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000)))) - return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1; - else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1; - else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRB_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRD_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRH_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRSB_LIT_A1; - else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000)) - return LDRSH_LIT_A1; - else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1; - else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRB_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRD_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRH_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRSB_REG_A1; - else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) && - ((inst & 0x01200000u) != 0x00200000)) - return LDRSH_REG_A1; - else - return IGNORED; -} - -size_t sh_a32_get_rewrite_inst_len(uint32_t inst) { - static uint8_t map[] = { - 4, // IGNORED - 12, // B_A1 - 12, // BX_A1 - 16, // BL_IMM_A1 - 16, // BLX_IMM_A2 - 32, // ADD_REG_A1 - 32, // ADD_REG_PC_A1 - 32, // SUB_REG_A1 - 32, // SUB_REG_PC_A1 - 12, // ADR_A1 - 12, // ADR_A2 - 32, // MOV_REG_A1 - 12, // MOV_REG_PC_A1 - 24, // LDR_LIT_A1 - 36, // LDR_LIT_PC_A1 - 24, // LDRB_LIT_A1 - 24, // LDRD_LIT_A1 - 24, // LDRH_LIT_A1 - 24, // LDRSB_LIT_A1 - 24, // LDRSH_LIT_A1 - 32, // LDR_REG_A1 - 36, // LDR_REG_PC_A1 - 32, // LDRB_REG_A1 - 32, // LDRD_REG_A1 - 32, // LDRH_REG_A1 - 32, // LDRSB_REG_A1 - 32 // LDRSH_REG_A1 - }; - - return (size_t)(map[sh_a32_get_type(inst)]); -} - -static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { - return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr); -} - -static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { - if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) { - uintptr_t cursor_addr = rinfo->overwrite_start_addr; - size_t offset = 0; - for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) { - if (cursor_addr >= addr) break; - cursor_addr += 4; - offset += rinfo->rewrite_inst_lens[i]; - } - uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset; - SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); - return fixed_addr; - } - - return addr; -} - -static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond; - if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1) - cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - else - // type == BLX_IMM_A2 - cond = 0xE; // 1110 None (AL) - - uint32_t addr; - if (type == B_A1 || type == BL_IMM_A1) { - uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); - uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u); - addr = pc + imm32; // arm -> arm - } else if (type == BLX_IMM_A2) { - uint32_t h = SH_UTIL_GET_BIT_32(inst, 24); - uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); - uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u); - addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb - } else { - // type == BX_A1 - // BX PC - // PC must be even, and the "arm" instruction must be at a 4-byte aligned address, - // so the instruction set must keep "arm" unchanged. - addr = pc; // arm -> arm - } - addr = sh_a32_fix_addr(addr, rinfo); - - size_t idx = 0; - if (type == BL_IMM_A1 || type == BLX_IMM_A2) { - buf[idx++] = 0x028FE008u | (cond << 28u); // ADD LR, PC, #8 - } - buf[idx++] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] - buf[idx++] = 0xEA000000; // B #0 - buf[idx++] = addr; - return idx * 4; // 12 or 16 -} - -static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) { - // ADD{S} , , PC{, } or ADD{S} , PC, {, } - // SUB{S} , , PC{, } or SUB{S} , PC, {, } - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16); - uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); - - uint32_t rx; // r0 - r3 - for (rx = 3;; --rx) - if (rx != rn && rx != rm && rx != rd) break; - - if (rd == 0xF) // Rd == PC - { - uint32_t ry; // r0 - r4 - for (ry = 4;; --ry) - if (ry != rn && ry != rm && ry != rd && ry != rx) break; - - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - if (rn == 0xF) - // Rn == PC - buf[4] = - (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, } - else - // Rm == PC - buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, } - buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] - buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} - buf[7] = pc; - return 32; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - if (rn == 0xF) - // Rn == PC - buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, } - else - // Rm == PC - buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, } - buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} - buf[6] = 0xEA000000; // B #0 - buf[7] = pc; - return 32; - } -} - -static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15 - uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0); - uint32_t imm32 = sh_util_arm_expand_imm(imm12); - uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); - if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed - - buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR Rd, [PC, #0] - buf[1] = 0xEA000000; // B #0 - buf[2] = addr; - return 12; -} - -static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) { - // MOV{S} , PC - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); - uint32_t rx = (rd == 0) ? 1 : 0; - - if (rd == 0xF) // Rd == PC (MOV PC, PC) - { - buf[0] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] - buf[1] = 0xEA000000; // B #0 - buf[2] = pc; - return 12; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] - buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, #/RRX} - buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} - buf[6] = 0xEA000000; // B #0 - buf[7] = pc; - return 32; - } -} - -static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, - sh_a32_rewrite_info_t *rinfo) { - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t u = SH_UTIL_GET_BIT_32(inst, 23); - uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); - - uint32_t imm32; - if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1) - imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0); - else - imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0); - uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); - if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed - - if (type == LDR_LIT_PC_A1 && rt == 0xF) { - // Rt == PC - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000006; // B #24 - buf[2] = 0xE92D0003; // PUSH {R0, R1} - buf[3] = 0xE59F0000; // LDR R0, [PC, #0] - buf[4] = 0xEA000000; // B #0 - buf[5] = addr; // - buf[6] = 0xE5900000; // LDR R0, [R0] - buf[7] = 0xE58D0004; // STR R0, [SP, #4] - buf[8] = 0xE8BD8001; // POP {R0, PC} - return 36; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000003; // B #12 - buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0] - buf[3] = 0xEA000000; // B #0 - buf[4] = addr; // -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wswitch" - switch (type) { - case LDR_LIT_A1: - buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt] - break; - case LDRB_LIT_A1: - buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt] - break; - case LDRD_LIT_A1: - buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt] - break; - case LDRH_LIT_A1: - buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt] - break; - case LDRSB_LIT_A1: - buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt] - break; - case LDRSH_LIT_A1: - buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt] - break; - } -#pragma clang diagnostic pop - return 24; - } -} - -static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) { - // LDR , [PC,+/-{, }]{!} - // ...... - uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); - uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); - uint32_t rt2 = rt + 1; - uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0); - uint32_t rx; // r0 - r3 - for (rx = 3;; --rx) - if (rx != rt && rx != rt2 && rx != rm) break; - - if (type == LDR_REG_PC_A1 && rt == 0xF) { - // Rt == PC - uint32_t ry; // r0 - r4 - for (ry = 4;; --ry) - if (ry != rt && ry != rt2 && ry != rm && ry != rx) break; - - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000006; // B #24 - buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} - buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #8] - buf[4] = 0xEA000000; // B #0 - buf[5] = pc; - buf[6] = - (inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u); // LDRxx Ry, [Rx],+/-Rm{, } - buf[7] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] - buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} - return 36; - } else { - buf[0] = 0x0A000000u | (cond << 28u); // B #0 - buf[1] = 0xEA000005; // B #20 - buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} - buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #0] - buf[4] = 0xEA000000; // B #0 - buf[5] = pc; - buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // LDRxx Rt, [Rx],+/-Rm{, } - buf[7] = 0xE49D0004 | (rx << 12u); // POP {Rx} - return 32; - } -} - -size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) { - sh_a32_type_t type = sh_a32_get_type(inst); - SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst); - - // We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage. - // In this case, the add/sub/mov/ldr_reg instruction does not need to consider - // the problem of PC in the coverage area when rewriting. - - if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2) - return sh_a32_rewrite_b(buf, inst, pc, type, rinfo); - else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1) - return sh_a32_rewrite_add_or_sub(buf, inst, pc); - else if (type == ADR_A1 || type == ADR_A2) - return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo); - else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1) - return sh_a32_rewrite_mov(buf, inst, pc); - else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 || - type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1) - return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo); - else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 || - type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1) - return sh_a32_rewrite_ldr_reg(buf, inst, pc, type); - else { - // IGNORED - buf[0] = inst; - return 4; - } -} - -size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) { - buf[0] = 0xE51FF004; // LDR PC, [PC, #-4] - buf[1] = addr; - return 8; -} - -size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) { - buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u); // B