From fbe17dde0334da0629cdd088bc1eef8ab190bf43 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 21 Jan 2022 04:43:27 -0800 Subject: [PATCH] Add flag for unloaded Zygisk modules --- native/jni/core/module.cpp | 1 + native/jni/utils/misc.hpp | 19 +++++++++++++++++++ native/jni/zygisk/entry.cpp | 28 ++++++++++++++++++++++++---- native/jni/zygisk/hook.cpp | 32 ++++++++++++++++++++++++++++---- native/jni/zygisk/module.hpp | 1 + native/jni/zygisk/zygisk.hpp | 2 +- 6 files changed, 74 insertions(+), 9 deletions(-) diff --git a/native/jni/core/module.cpp b/native/jni/core/module.cpp index 10e8c6e79..d6eee32dd 100644 --- a/native/jni/core/module.cpp +++ b/native/jni/core/module.cpp @@ -715,6 +715,7 @@ static void collect_modules(bool open_zygisk) { #else #error Unsupported ABI #endif + unlinkat(modfd, "zygisk/unloaded", 0); } } else { // Ignore zygisk modules when zygisk is not enabled diff --git a/native/jni/utils/misc.hpp b/native/jni/utils/misc.hpp index e00542bbd..1de8c4395 100644 --- a/native/jni/utils/misc.hpp +++ b/native/jni/utils/misc.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #define UID_ROOT 0 #define UID_SHELL 2000 @@ -79,6 +80,24 @@ public: bool operator!=(const stateless_allocator&) { return false; } }; +class dynamic_bitset { +private: + using long_bits = std::bitset; + std::vector bits; +public: + constexpr static int slot_size = sizeof(unsigned long); + long_bits::reference operator[] (size_t pos) { + size_t slot = pos / slot_size; + size_t index = pos % slot_size; + if (bits.size() <= slot) { + bits.resize(slot + 1); + } + return bits[slot][index]; + } + size_t slots() const { return bits.size(); } + unsigned long to_ulong(size_t slot) const { return bits[slot].to_ulong(); } +}; + int parse_int(std::string_view s); using thread_entry = void *(*)(void *); diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index 930c20129..4250f81bc 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -167,8 +167,7 @@ static int zygisk_log(int prio, const char *fmt, va_list ap) { return ret; } -std::vector remote_get_info(int uid, const char *process, uint32_t *flags) { - vector fds; +int remote_get_info(int uid, const char *process, uint32_t *flags, vector &fds) { if (int fd = connect_daemon(); fd >= 0) { write_int(fd, ZYGISK_REQUEST); write_int(fd, ZYGISK_GET_INFO); @@ -179,9 +178,9 @@ std::vector remote_get_info(int uid, const char *process, uint32_t *flags) if ((*flags & UNMOUNT_MASK) != UNMOUNT_MASK) { fds = recv_fds(fd); } - close(fd); + return fd; } - return fds; + return -1; } // The following code runs in magiskd @@ -358,6 +357,27 @@ static void get_process_info(int client, const sock_cred *cred) { vector fds = get_module_fds(str_ends(buf, "64")); send_fds(client, fds.data(), fds.size()); } + + // The following will only happen for system_server + int slots = read_int(client); + int id = 0; + for (int i = 0; i < slots; ++i) { + unsigned long l = 0; + xxread(client, &l, sizeof(l)); + bitset bits(l); + for (int j = 0; id < module_list->size(); ++j, ++id) { + if (!bits[j]) { + // Either not a zygisk module, or incompatible + char buf[4096]; + snprintf(buf, sizeof(buf), MODULEROOT "/%s/zygisk", + module_list->operator[](id).name.data()); + if (int dirfd = open(buf, O_RDONLY | O_CLOEXEC); dirfd >= 0) { + close(xopenat(dirfd, "unloaded", O_CREAT | O_RDONLY, 0644)); + close(dirfd); + } + } + } + } } static void send_log_pipe(int fd) { diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index 1c7d00054..6a7d0e2ed 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -450,14 +450,16 @@ void HookContext::nativeSpecializeAppProcess_pre() { ZLOGV("pre specialize [%s]\n", process); } - auto module_fds = remote_get_info(args->uid, process, &flags); + vector module_fds; + int fd = remote_get_info(args->uid, process, &flags, module_fds); if ((flags & UNMOUNT_MASK) == UNMOUNT_MASK) { - // TODO: Handle MOUNT_EXTERNAL_NONE on older platforms ZLOGI("[%s] is on the denylist\n", process); state[DO_UNMOUNT] = true; - } else { + } else if (fd >= 0) { run_modules_pre(module_fds); + write_int(fd, 0); } + close(fd); close_fds(); android_logging(); @@ -486,7 +488,29 @@ void HookContext::nativeForkSystemServer_pre() { state[SERVER_SPECIALIZE] = true; if (pid == 0) { ZLOGV("pre forkSystemServer\n"); - run_modules_pre(remote_get_info(1000, "system_server", &flags)); + vector module_fds; + int fd = remote_get_info(1000, "system_server", &flags, module_fds); + if (fd >= 0) { + if (module_fds.empty()) { + write_int(fd, 0); + } else { + run_modules_pre(module_fds); + + // Send the bitset of module status back to magiskd from system_server + dynamic_bitset bits; + // Pre-allocate enough bits + bits[module_fds.size() - 1] = false; + for (const auto &m : modules) { + bits[m.getId()] = true; + } + write_int(fd, bits.slots()); + for (int i = 0; i < bits.slots(); ++i) { + unsigned long l = bits.to_ulong(i); + xwrite(fd, &l, sizeof(l)); + } + } + close(fd); + } close_fds(); android_logging(); } diff --git a/native/jni/zygisk/module.hpp b/native/jni/zygisk/module.hpp index d0db06516..b4fa1ae24 100644 --- a/native/jni/zygisk/module.hpp +++ b/native/jni/zygisk/module.hpp @@ -119,6 +119,7 @@ struct ZygiskModule { void setOption(zygisk::Option opt); static uint32_t getFlags(); void doUnload() const { if (unload) dlclose(handle); } + int getId() const { return id; } ZygiskModule(int id, void *handle, void *entry); diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index 2b07ef884..39266d678 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -41,5 +41,5 @@ extern void *self_handle; void unload_first_stage(); void hook_functions(); -std::vector remote_get_info(int uid, const char *process, uint32_t *flags); +int remote_get_info(int uid, const char *process, uint32_t *flags, std::vector &fds); int remote_request_unmount();