From a30d510eb1d131c4b28b044eb93001a3554f3f98 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 8 Jan 2021 00:53:24 -0800 Subject: [PATCH] Use xHook to hook functions in PLT --- .gitmodules | 3 ++ native/jni/Android.mk | 5 ++- native/jni/external/Android.mk | 17 +++++++++ native/jni/external/xhook | 1 + native/jni/inject/entry.cpp | 10 ++--- native/jni/inject/hook.cpp | 70 ++++++++++++++++++++++++++++++++++ native/jni/inject/inject.hpp | 4 ++ 7 files changed, 103 insertions(+), 7 deletions(-) create mode 160000 native/jni/external/xhook create mode 100644 native/jni/inject/hook.cpp diff --git a/.gitmodules b/.gitmodules index 7bf937d45..e3d6dbe94 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,6 +25,9 @@ [submodule "pcre"] path = native/jni/external/pcre url = https://android.googlesource.com/platform/external/pcre +[submodule "xhook"] + path = native/jni/external/xhook + url = https://github.com/iqiyi/xHook.git [submodule "termux-elf-cleaner"] path = tools/termux-elf-cleaner url = https://github.com/termux/termux-elf-cleaner.git diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 4382bc00c..732a3b9f8 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -8,7 +8,7 @@ ifdef B_MAGISK include $(CLEAR_VARS) LOCAL_MODULE := magisk -LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils +LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils libxhook LOCAL_C_INCLUDES := jni/include LOCAL_SRC_FILES := \ @@ -32,7 +32,8 @@ LOCAL_SRC_FILES := \ su/pts.cpp \ su/su_daemon.cpp \ inject/entry.cpp \ - inject/utils.cpp + inject/utils.cpp \ + inject/hook.cpp LOCAL_LDLIBS := -llog include $(BUILD_EXECUTABLE) diff --git a/native/jni/external/Android.mk b/native/jni/external/Android.mk index ebad88bd9..1072489ef 100644 --- a/native/jni/external/Android.mk +++ b/native/jni/external/Android.mk @@ -353,6 +353,23 @@ LOCAL_SRC_FILES := \ pcre/dist2/src/pcre2_xclass.c include $(BUILD_STATIC_LIBRARY) +# libxhook.a +include $(CLEAR_VARS) +LOCAL_MODULE:= libxhook +LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/libxhook/jni +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) +LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden +LOCAL_CONLYFLAGS := -std=c11 +LOCAL_SRC_FILES := \ + xhook/libxhook/jni/xh_log.c \ + xhook/libxhook/jni/xh_version.c \ + xhook/libxhook/jni/xh_jni.c \ + xhook/libxhook/jni/xhook.c \ + xhook/libxhook/jni/xh_core.c \ + xhook/libxhook/jni/xh_util.c \ + xhook/libxhook/jni/xh_elf.c +include $(BUILD_STATIC_LIBRARY) + CWD := $(LOCAL_PATH) include $(CWD)/systemproperties/Android.mk include $(CWD)/mincrypt/Android.mk diff --git a/native/jni/external/xhook b/native/jni/external/xhook new file mode 160000 index 000000000..9180bd740 --- /dev/null +++ b/native/jni/external/xhook @@ -0,0 +1 @@ +Subproject commit 9180bd74098fd41f808d3968e2e52b4f5db92c99 diff --git a/native/jni/inject/entry.cpp b/native/jni/inject/entry.cpp index 6dc67d0bd..ffd3fd2f7 100644 --- a/native/jni/inject/entry.cpp +++ b/native/jni/inject/entry.cpp @@ -41,7 +41,10 @@ static void inject_cleanup() { nanosleep(&ts, nullptr); } -static inline void self_unload() { +void self_unload() { + // If unhook failed, do not unload or else it will cause SIGSEGV + if (!unhook_functions()) + return; new_daemon_thread(reinterpret_cast(&dlclose), self_handle); active_threads--; } @@ -95,15 +98,12 @@ static void inject_init() { self_handle = dlopen(INJECT_LIB_2, RTLD_LAZY); dlclose(self_handle); - // TODO: actually inject stuffs here + hook_functions(); // Some cleanup sanitize_environ(); active_threads++; new_daemon_thread(&unload_first_stage); - - // Demonstrate self unloading 2nd stage - self_unload(); } else if (char *env = getenv(INJECT_ENV_1)) { LOGD("zygote: inject 1st stage\n"); diff --git a/native/jni/inject/hook.cpp b/native/jni/inject/hook.cpp new file mode 100644 index 000000000..c86d777d8 --- /dev/null +++ b/native/jni/inject/hook.cpp @@ -0,0 +1,70 @@ +#include + +#include +#include +#include + +#include "inject.hpp" + +using namespace std; + +// Static vector won't work, use a pointer instead +static vector> *hook_list; + +#define DEF_HOOK_FUNC(ret, func, ...) \ + static ret (*old_##func)(__VA_ARGS__); \ + static ret new_##func(__VA_ARGS__) + +DEF_HOOK_FUNC(int, jniRegisterNativeMethods, + JNIEnv *env, const char *className, const JNINativeMethod *methods, int numMethods) { + LOGD("hook: jniRegisterNativeMethods %s", className); + + // TODO: actually do things like replacing JNI native methods + + return old_jniRegisterNativeMethods(env, className, methods, numMethods); +} + +static bool hook_refresh() { + if (xhook_refresh(0) == 0) { + xhook_clear(); + LOGI("hook: xhook success\n"); + return true; + } else { + LOGE("hook: xhook failed\n"); + return false; + } +} + +static int hook_register(const char *path, const char *symbol, void *new_func, void **old_func) { + int ret = xhook_register(path, symbol, new_func, old_func); + if (ret != 0) { + LOGE("hook: Failed to register hook \"%s\"\n", symbol); + return ret; + } + hook_list->emplace_back(path, symbol, old_func); + return 0; +} + +#define XHOOK_REGISTER(PATH_REGEX, NAME) \ + hook_register(PATH_REGEX, #NAME, (void*) new_##NAME, (void **) &old_##NAME) + +void hook_functions() { +#ifdef MAGISK_DEBUG + xhook_enable_debug(1); + xhook_enable_sigsegv_protection(0); +#endif + hook_list = new remove_pointer_t(); + XHOOK_REGISTER(".*\\libandroid_runtime.so$", jniRegisterNativeMethods); + hook_refresh(); +} + +bool unhook_functions() { + for (auto &[path, sym, old_func] : *hook_list) { + if (xhook_register(path, sym, *old_func, nullptr) != 0) { + LOGE("hook: Failed to register hook \"%s\"\n", sym); \ + return false; + } + } + delete hook_list; + return hook_refresh(); +} diff --git a/native/jni/inject/inject.hpp b/native/jni/inject/inject.hpp index 96c843432..574ad72b2 100644 --- a/native/jni/inject/inject.hpp +++ b/native/jni/inject/inject.hpp @@ -15,3 +15,7 @@ uintptr_t get_function_lib(uintptr_t addr, char *lib); // Get library base address with name uintptr_t get_remote_lib(int pid, const char *lib); + +void self_unload(); +void hook_functions(); +bool unhook_functions();