From da723b207a46ba732f7dfb45b4a3004f5a14d78d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 10 Jan 2021 01:25:30 -0800 Subject: [PATCH] Allow 3rd party code to load pre-specializing Magisk's policy is to never allow 3rd party code to be loaded in the zygote daemon process so we have 100% control over injection and hiding. However, this makes it impossible for 3rd party modules to run anything before process specialization, which includes the ability to modify the arguments being sent to these original nativeForkAndXXX methods. The trick here is to fork before calling the original nativeForkAndXXX methods, and hook `fork` in libandroid_runtime.so to skip the next invocation; basically, we're moving the responsibility of process forking to our own hands. --- native/jni/inject/hook.cpp | 62 +++++++++++++++++++++++++++++++-- native/jni/inject/jni_hooks.hpp | 4 +-- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/native/jni/inject/hook.cpp b/native/jni/inject/hook.cpp index 93272565b..641784e58 100644 --- a/native/jni/inject/hook.cpp +++ b/native/jni/inject/hook.cpp @@ -19,13 +19,14 @@ using namespace std; // For some reason static vector won't work, use a pointer instead static vector> *hook_list; - static JavaVM *g_jvm; +static int prev_fork_pid = -1; namespace { struct HookContext { int pid; + bool unload; }; // JNI method declarations @@ -76,6 +77,32 @@ DCL_HOOK_FUNC(int, jniRegisterNativeMethods, return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods); } +DCL_HOOK_FUNC(int, fork) { + if (prev_fork_pid < 0) + return old_fork(); + + // Skip an actual fork and return the previous fork result + int pid = prev_fork_pid; + prev_fork_pid = -1; + return pid; +} + +static int sigmask(int how, int signum) { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, signum); + return sigprocmask(how, &set, nullptr); +} + +static int pre_specialize_fork() { + // First block SIGCHLD, unblock after original fork is done + sigmask(SIG_BLOCK, SIGCHLD); + prev_fork_pid = old_fork(); + return prev_fork_pid; +} + +// ----------------------------------------------------------------- + static void nativeForkAndSpecialize_pre(HookContext *ctx, JNIEnv *env, jclass clazz, jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name, @@ -84,13 +111,29 @@ static void nativeForkAndSpecialize_pre(HookContext *ctx, jboolean &is_top_app, jobjectArray &pkg_data_info_list, jobjectArray &whitelisted_data_info_list, jboolean &mount_data_dirs, jboolean &mount_storage_dirs) { + + // Do our own fork before loading any 3rd party code + ctx->pid = pre_specialize_fork(); + if (ctx->pid != 0) + return; + + // TODO: check if we need to do hiding + // Demonstrate self unload in child process + ctx->unload = true; + LOGD("hook: %s\n", __FUNCTION__); } static void nativeForkAndSpecialize_post(HookContext *ctx, JNIEnv *env, jclass clazz) { + // Unblock SIGCHLD in case the original method didn't + sigmask(SIG_UNBLOCK, SIGCHLD); + + if (ctx->pid != 0) + return; + LOGD("hook: %s\n", __FUNCTION__); - // Demonstrate self unload in child process - if (ctx->pid == 0) + + if (ctx->unload) self_unload(); } @@ -115,10 +158,22 @@ static void nativeSpecializeAppProcess_post(HookContext *ctx, JNIEnv *env, jclas static void nativeForkSystemServer_pre(HookContext *ctx, JNIEnv *env, jclass clazz, uid_t &uid, gid_t &gid, jintArray &gids, jint &runtime_flags, jobjectArray &rlimits, jlong &permitted_capabilities, jlong &effective_capabilities) { + + // Do our own fork before loading any 3rd party code + ctx->pid = pre_specialize_fork(); + if (ctx->pid != 0) + return; + LOGD("hook: %s\n", __FUNCTION__); } static void nativeForkSystemServer_post(HookContext *ctx, JNIEnv *env, jclass clazz) { + // Unblock SIGCHLD in case the original method didn't + sigmask(SIG_UNBLOCK, SIGCHLD); + + if (ctx->pid != 0) + return; + LOGD("hook: %s\n", __FUNCTION__); } @@ -155,6 +210,7 @@ void hook_functions() { #endif hook_list = new remove_pointer_t(); XHOOK_REGISTER(".*\\libandroid_runtime.so$", jniRegisterNativeMethods); + XHOOK_REGISTER(".*\\libandroid_runtime.so$", fork); hook_refresh(); } diff --git a/native/jni/inject/jni_hooks.hpp b/native/jni/inject/jni_hooks.hpp index 4019230cd..d93b5e5bb 100644 --- a/native/jni/inject/jni_hooks.hpp +++ b/native/jni/inject/jni_hooks.hpp @@ -37,7 +37,7 @@ static ret name(__VA_ARGS__) mount_data_dirs, mount_storage_dirs) #define orig_fork(ver, ...) \ - ctx.pid = reinterpret_cast \ + reinterpret_cast \ (nativeForkAndSpecialize_orig->fnPtr)(__VA_ARGS__) #define post_fork() \ @@ -300,7 +300,7 @@ DCL_SPECIALIZE_APP(samsung_q, rlimits, permitted_capabilities, effective_capabilities) #define orig_server(ver, ...) \ - ctx.pid = reinterpret_cast \ + reinterpret_cast \ (nativeForkSystemServer_orig->fnPtr)(__VA_ARGS__) #define post_server() \