diff --git a/native/src/core/zygisk/hook.cpp b/native/src/core/zygisk/hook.cpp index e52d6a0a8..6afb5fe68 100644 --- a/native/src/core/zygisk/hook.cpp +++ b/native/src/core/zygisk/hook.cpp @@ -37,7 +37,7 @@ static bool should_unmap_zygisk = false; ret (*old_##func)(__VA_ARGS__); \ ret new_##func(__VA_ARGS__) -DCL_HOOK_FUNC(void, androidSetCreateThreadFunc, void *func) { +DCL_HOOK_FUNC(static void, androidSetCreateThreadFunc, void *func) { ZLOGD("androidSetCreateThreadFunc\n"); hook_jni_env(); old_androidSetCreateThreadFunc(func); @@ -49,14 +49,14 @@ DCL_HOOK_FUNC(int, fork) { } // Unmount stuffs in the process's private mount namespace -DCL_HOOK_FUNC(int, unshare, int flags) { +DCL_HOOK_FUNC(static int, unshare, int flags) { int res = old_unshare(flags); if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 && // For some unknown reason, unmounting app_process in SysUI can break. // This is reproducible on the official AVD running API 26 and 27. // Simply avoid doing any unmounts for SysUI to avoid potential issues. (g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) { - if (g_ctx->flags[DO_REVERT_UNMOUNT]) { + if (g_ctx->flags & DO_REVERT_UNMOUNT) { revert_unmount(); } // Restore errno back to 0 @@ -66,7 +66,7 @@ DCL_HOOK_FUNC(int, unshare, int flags) { } // This is the last moment before the secontext of the process changes -DCL_HOOK_FUNC(int, selinux_android_setcontext, +DCL_HOOK_FUNC(static int, selinux_android_setcontext, uid_t uid, bool isSystemServer, const char *seinfo, const char *pkgname) { // Pre-fetch logd before secontext transition zygisk_get_logd(); @@ -74,8 +74,8 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext, } // Close file descriptors to prevent crashing -DCL_HOOK_FUNC(void, android_log_close) { - if (g_ctx == nullptr || !g_ctx->flags[SKIP_CLOSE_LOG_PIPE]) { +DCL_HOOK_FUNC(static void, android_log_close) { + if (g_ctx == nullptr || !(g_ctx->flags & SKIP_CLOSE_LOG_PIPE)) { // This happens during forks like nativeForkApp, nativeForkUsap, // nativeForkSystemServer, and nativeForkAndSpecialize. zygisk_close_logd(); @@ -86,7 +86,7 @@ DCL_HOOK_FUNC(void, android_log_close) { // We cannot directly call `dlclose` to unload ourselves, otherwise when `dlclose` returns, // it will return to our code which has been unmapped, causing segmentation fault. // Instead, we hook `pthread_attr_destroy` which will be called when VM daemon threads start. -DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) { +DCL_HOOK_FUNC(static int, pthread_attr_destroy, void *target) { int res = old_pthread_attr_destroy((pthread_attr_t *)target); // Only perform unloading on the main thread @@ -111,7 +111,7 @@ DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) { } // it should be safe to assume all dlclose's in libnativebridge are for zygisk_loader -DCL_HOOK_FUNC(int, dlclose, void *handle) { +DCL_HOOK_FUNC(static int, dlclose, void *handle) { static bool kDone = false; if (!kDone) { ZLOGV("dlclose zygisk_loader\n"); @@ -126,7 +126,7 @@ DCL_HOOK_FUNC(int, dlclose, void *handle) { // ----------------------------------------------------------------- HookContext::HookContext(JNIEnv *env, void *args) : - env(env), args{args}, process(nullptr), pid(-1), info_flags(0), + env(env), args{args}, process(nullptr), pid(-1), flags(0), info_flags(0), hook_info_lock(PTHREAD_MUTEX_INITIALIZER) { g_ctx = this; } HookContext::~HookContext() { diff --git a/native/src/core/zygisk/module.cpp b/native/src/core/zygisk/module.cpp index ba54186be..b3c311d32 100644 --- a/native/src/core/zygisk/module.cpp +++ b/native/src/core/zygisk/module.cpp @@ -98,7 +98,7 @@ void ZygiskModule::setOption(zygisk::Option opt) { return; switch (opt) { case zygisk::FORCE_DENYLIST_UNMOUNT: - g_ctx->flags[DO_REVERT_UNMOUNT] = true; + g_ctx->flags |= DO_REVERT_UNMOUNT; break; case zygisk::DLCLOSE_MODULE_LIBRARY: unload = true; @@ -203,45 +203,6 @@ bool HookContext::plt_hook_commit() { // ----------------------------------------------------------------- -static int sigmask(int how, int signum) { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, signum); - return sigprocmask(how, &set, nullptr); -} - -void HookContext::fork_pre() { - // Do our own fork before loading any 3rd party code - // First block SIGCHLD, unblock after original fork is done - sigmask(SIG_BLOCK, SIGCHLD); - pid = old_fork(); - - if (!is_child()) - return; - - // Record all open fds - auto dir = xopen_dir("/proc/self/fd"); - for (dirent *entry; (entry = xreaddir(dir.get()));) { - int fd = parse_int(entry->d_name); - if (fd < 0 || fd >= MAX_FD_SIZE) { - close(fd); - continue; - } - allowed_fds[fd] = true; - } - // The dirfd will be closed once out of scope - allowed_fds[dirfd(dir.get())] = false; - // logd_fd should be handled separately - if (int fd = zygisk_get_logd(); fd >= 0) { - allowed_fds[fd] = false; - } -} - -void HookContext::fork_post() { - // Unblock SIGCHLD in case the original method didn't - sigmask(SIG_UNBLOCK, SIGCHLD); -} - void HookContext::sanitize_fds() { zygisk_close_logd(); @@ -296,7 +257,7 @@ void HookContext::sanitize_fds() { } bool HookContext::exempt_fd(int fd) { - if (flags[POST_SPECIALIZE] || flags[SKIP_CLOSE_LOG_PIPE]) + if ((flags & POST_SPECIALIZE) || (flags & SKIP_CLOSE_LOG_PIPE)) return true; if (!can_exempt_fd()) return false; @@ -304,6 +265,49 @@ bool HookContext::exempt_fd(int fd) { return true; } +bool HookContext::can_exempt_fd() const { + return (flags & APP_FORK_AND_SPECIALIZE) && args.app->fds_to_ignore; +} + +static int sigmask(int how, int signum) { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, signum); + return sigprocmask(how, &set, nullptr); +} + +void HookContext::fork_pre() { + // Do our own fork before loading any 3rd party code + // First block SIGCHLD, unblock after original fork is done + sigmask(SIG_BLOCK, SIGCHLD); + pid = old_fork(); + + if (!is_child()) + return; + + // Record all open fds + auto dir = xopen_dir("/proc/self/fd"); + for (dirent *entry; (entry = xreaddir(dir.get()));) { + int fd = parse_int(entry->d_name); + if (fd < 0 || fd >= MAX_FD_SIZE) { + close(fd); + continue; + } + allowed_fds[fd] = true; + } + // The dirfd will be closed once out of scope + allowed_fds[dirfd(dir.get())] = false; + // logd_fd should be handled separately + if (int fd = zygisk_get_logd(); fd >= 0) { + allowed_fds[fd] = false; + } +} + +void HookContext::fork_post() { + // Unblock SIGCHLD in case the original method didn't + sigmask(SIG_UNBLOCK, SIGCHLD); +} + void HookContext::run_modules_pre(const vector &fds) { for (int i = 0; i < fds.size(); ++i) { struct stat s{}; @@ -312,14 +316,14 @@ void HookContext::run_modules_pre(const vector &fds) { continue; } android_dlextinfo info { - .flags = ANDROID_DLEXT_USE_LIBRARY_FD, - .library_fd = fds[i], + .flags = ANDROID_DLEXT_USE_LIBRARY_FD, + .library_fd = fds[i], }; if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) { if (void *e = dlsym(h, "zygisk_module_entry")) { modules.emplace_back(i, h, e); } - } else if (g_ctx->flags[SERVER_FORK_AND_SPECIALIZE]) { + } else if (flags & SERVER_FORK_AND_SPECIALIZE) { ZLOGW("Failed to dlopen zygisk module: %s\n", dlerror()); } close(fds[i]); @@ -335,20 +339,20 @@ void HookContext::run_modules_pre(const vector &fds) { } for (auto &m : modules) { - if (flags[APP_SPECIALIZE]) { + if (flags & APP_SPECIALIZE) { m.preAppSpecialize(args.app); - } else if (flags[SERVER_FORK_AND_SPECIALIZE]) { + } else if (flags & SERVER_FORK_AND_SPECIALIZE) { m.preServerSpecialize(args.server); } } } void HookContext::run_modules_post() { - flags[POST_SPECIALIZE] = true; + flags |= POST_SPECIALIZE; for (const auto &m : modules) { - if (flags[APP_SPECIALIZE]) { + if (flags & APP_SPECIALIZE) { m.postAppSpecialize(args.app); - } else if (flags[SERVER_FORK_AND_SPECIALIZE]) { + } else if (flags & SERVER_FORK_AND_SPECIALIZE) { m.postServerSpecialize(args.server); } m.tryUnload(); @@ -356,7 +360,7 @@ void HookContext::run_modules_post() { } void HookContext::app_specialize_pre() { - flags[APP_SPECIALIZE] = true; + flags |= APP_SPECIALIZE; vector module_fds; int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds); @@ -369,7 +373,7 @@ void HookContext::app_specialize_pre() { } if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) { ZLOGI("[%s] is on the denylist\n", process); - flags[DO_REVERT_UNMOUNT] = true; + flags |= DO_REVERT_UNMOUNT; } else if (fd >= 0) { run_modules_pre(module_fds); } @@ -419,7 +423,7 @@ void HookContext::nativeSpecializeAppProcess_pre() { process = env->GetStringUTFChars(args.app->nice_name, nullptr); ZLOGV("pre specialize [%s]\n", process); // App specialize does not check FD - flags[SKIP_CLOSE_LOG_PIPE] = true; + flags |= SKIP_CLOSE_LOG_PIPE; app_specialize_pre(); } @@ -430,7 +434,7 @@ void HookContext::nativeSpecializeAppProcess_post() { void HookContext::nativeForkSystemServer_pre() { ZLOGV("pre forkSystemServer\n"); - flags[SERVER_FORK_AND_SPECIALIZE] = true; + flags |= SERVER_FORK_AND_SPECIALIZE; fork_pre(); if (is_child()) { @@ -450,7 +454,7 @@ void HookContext::nativeForkSystemServer_post() { void HookContext::nativeForkAndSpecialize_pre() { process = env->GetStringUTFChars(args.app->nice_name, nullptr); ZLOGV("pre forkAndSpecialize [%s]\n", process); - flags[APP_FORK_AND_SPECIALIZE] = true; + flags |= APP_FORK_AND_SPECIALIZE; fork_pre(); if (is_child()) { diff --git a/native/src/core/zygisk/module.hpp b/native/src/core/zygisk/module.hpp index 977d77381..2e1a2aacd 100644 --- a/native/src/core/zygisk/module.hpp +++ b/native/src/core/zygisk/module.hpp @@ -202,15 +202,13 @@ private: extern HookContext *g_ctx; extern int (*old_fork)(void); -enum { - POST_SPECIALIZE, - APP_FORK_AND_SPECIALIZE, - APP_SPECIALIZE, - SERVER_FORK_AND_SPECIALIZE, - DO_REVERT_UNMOUNT, - SKIP_CLOSE_LOG_PIPE, - - FLAG_MAX +enum : uint32_t { + POST_SPECIALIZE = (1u << 0), + APP_FORK_AND_SPECIALIZE = (1u << 1), + APP_SPECIALIZE = (1u << 2), + SERVER_FORK_AND_SPECIALIZE = (1u << 3), + DO_REVERT_UNMOUNT = (1u << 4), + SKIP_CLOSE_LOG_PIPE = (1u << 5), }; #define MAX_FD_SIZE 1024 @@ -231,7 +229,7 @@ struct HookContext { std::list modules; int pid; - std::bitset flags; + uint32_t flags; uint32_t info_flags; std::bitset allowed_fds; std::vector exempted_fds; @@ -266,8 +264,8 @@ struct HookContext { void sanitize_fds(); bool exempt_fd(int fd); + bool can_exempt_fd() const; bool is_child() const { return pid <= 0; } - bool can_exempt_fd() const { return flags[APP_FORK_AND_SPECIALIZE] && args.app->fds_to_ignore; } // Compatibility shim void plt_hook_register(const char *regex, const char *symbol, void *fn, void **backup);