From 21d7db09591740a212e42372167a415a00d64684 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 14 Jan 2022 03:10:02 -0800 Subject: [PATCH] Add new Zygisk API to get module dir --- native/jni/core/module.cpp | 41 +++++++--------------------------- native/jni/include/daemon.hpp | 10 ++++++++- native/jni/zygisk/api.hpp | 16 ++++++++++++- native/jni/zygisk/entry.cpp | 33 +++++++++++++++++++++++++-- native/jni/zygisk/hook.cpp | 42 ++++++++++++++++++++++++++--------- native/jni/zygisk/module.hpp | 4 ++++ native/jni/zygisk/zygisk.hpp | 1 + 7 files changed, 100 insertions(+), 47 deletions(-) diff --git a/native/jni/core/module.cpp b/native/jni/core/module.cpp index 737289da3..0a47cb5ce 100644 --- a/native/jni/core/module.cpp +++ b/native/jni/core/module.cpp @@ -541,15 +541,7 @@ static void inject_magisk_bins(root_node *system) { delete bin->extract(init_applet[i]); } -struct module_info { - string name; - int z32 = -1; -#if defined(__LP64__) - int z64 = -1; -#endif -}; - -static vector *modules; +vector *module_list; int app_process_32 = -1; int app_process_64 = -1; @@ -577,8 +569,8 @@ void magic_mount() { char buf[4096]; LOGI("* Loading modules\n"); - if (modules) { - for (const auto &m : *modules) { + if (module_list) { + for (const auto &m : *module_list) { const char *module = m.name.data(); char *b = buf + sprintf(buf, "%s/" MODULEMNT "/%s/", MAGISKTMP.data(), module); @@ -732,7 +724,7 @@ static void collect_modules(bool open_zygisk) { } } info.name = entry->d_name; - modules->push_back(info); + module_list->push_back(info); }); if (open_zygisk && zygisk_enabled) { bool use_memfd = true; @@ -752,7 +744,7 @@ static void collect_modules(bool open_zygisk) { } return fd; }; - std::for_each(modules->begin(), modules->end(), [&](module_info &info) { + std::for_each(module_list->begin(), module_list->end(), [&](module_info &info) { info.z32 = convert_to_memfd(info.z32); #if defined(__LP64__) info.z64 = convert_to_memfd(info.z64); @@ -762,13 +754,13 @@ static void collect_modules(bool open_zygisk) { } void handle_modules() { - default_new(modules); + default_new(module_list); prepare_modules(); collect_modules(false); exec_module_scripts("post-fs-data"); // Recollect modules (module scripts could remove itself) - modules->clear(); + module_list->clear(); collect_modules(true); } @@ -789,24 +781,7 @@ void remove_modules() { void exec_module_scripts(const char *stage) { vector module_names; - std::transform(modules->begin(), modules->end(), std::back_inserter(module_names), + std::transform(module_list->begin(), module_list->end(), std::back_inserter(module_names), [](const module_info &info) -> string_view { return info.name; }); exec_module_scripts(stage, module_names); } - -vector zygisk_module_fds(bool is_64_bit) { - vector fds; - // All fds passed to send_fds have to be valid file descriptors. - // To workaround this issue, send over STDOUT_FILENO as an indicator of an - // invalid fd as it will always be /dev/null in magiskd - if (is_64_bit) { -#if defined(__LP64__) - std::transform(modules->begin(), modules->end(), std::back_inserter(fds), - [](const module_info &info) { return info.z64 < 0 ? STDOUT_FILENO : info.z64; }); -#endif - } else { - std::transform(modules->begin(), modules->end(), std::back_inserter(fds), - [](const module_info &info) { return info.z32 < 0 ? STDOUT_FILENO : info.z32; }); - } - return fds; -} diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index b1f3b8049..5968d6df8 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -42,9 +42,18 @@ enum : int { DAEMON_LAST }; +struct module_info { + std::string name; + int z32 = -1; +#if defined(__LP64__) + int z64 = -1; +#endif +}; + extern bool zygisk_enabled; extern int app_process_32; extern int app_process_64; +extern std::vector *module_list; int connect_daemon(bool create = false); @@ -69,7 +78,6 @@ void boot_complete(int client); void denylist_handler(int client, const sock_cred *cred); void su_daemon_handler(int client, const sock_cred *cred); void zygisk_handler(int client, const sock_cred *cred); -std::vector zygisk_module_fds(bool is_64_bit); // Denylist void initialize_denylist(); diff --git a/native/jni/zygisk/api.hpp b/native/jni/zygisk/api.hpp index 4e438615f..76455f51f 100644 --- a/native/jni/zygisk/api.hpp +++ b/native/jni/zygisk/api.hpp @@ -9,7 +9,7 @@ #include -#define ZYGISK_API_VERSION 1 +#define ZYGISK_API_VERSION 2 /* @@ -173,6 +173,16 @@ struct Api { // module's companion request handler. Returns -1 if the connection attempt failed. int connectCompanion(); + // Get the file descriptor of the root folder of the current module. + // + // This API only works in the pre[XXX]Specialize functions. + // Accessing the directory returned is only possible in the pre[XXX]Specialize functions + // or in the root companion process (assuming that you sent the fd over the socket). + // Both restrictions are due to SELinux and UID. + // + // Returns -1 if errors occurred. + int getModuleDir(); + // Set various options for your module. // Please note that this function accepts one single option at a time. // Check zygisk::Option for the full list of options available. @@ -261,6 +271,7 @@ struct api_table { // Zygisk functions int (*connectCompanion)(void * /* _this */); void (*setOption)(void * /* _this */, Option); + int (*getModuleDir)(void * /* _this */); }; template @@ -278,6 +289,9 @@ void entry_impl(api_table *table, JNIEnv *env) { inline int Api::connectCompanion() { return impl->connectCompanion ? impl->connectCompanion(impl->_this) : -1; } +inline int Api::getModuleDir() { + return impl->getModuleDir ? impl->getModuleDir(impl->_this) : -1; +} inline void Api::setOption(Option opt) { if (impl->setOption) impl->setOption(impl->_this, opt); } diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index dd55e5db6..29d97857a 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -185,6 +185,23 @@ std::vector remote_get_info(int uid, const char *process, AppInfo *info) { // The following code runs in magiskd +static vector get_module_fds(bool is_64_bit) { + vector fds; + // All fds passed to send_fds have to be valid file descriptors. + // To workaround this issue, send over STDOUT_FILENO as an indicator of an + // invalid fd as it will always be /dev/null in magiskd + if (is_64_bit) { +#if defined(__LP64__) + std::transform(module_list->begin(), module_list->end(), std::back_inserter(fds), + [](const module_info &info) { return info.z64 < 0 ? STDOUT_FILENO : info.z64; }); +#endif + } else { + std::transform(module_list->begin(), module_list->end(), std::back_inserter(fds), + [](const module_info &info) { return info.z32 < 0 ? STDOUT_FILENO : info.z32; }); + } + return fds; +} + static bool get_exe(int pid, char *buf, size_t sz) { snprintf(buf, sz, "/proc/%d/exe", pid); return xreadlink(buf, buf, sz) > 0; @@ -221,7 +238,7 @@ static void connect_companion(int client, bool is_64_bit) { exit(-1); } close(fds[1]); - vector module_fds = zygisk_module_fds(is_64_bit); + vector module_fds = get_module_fds(is_64_bit); send_fds(zygiskd_socket, module_fds.data(), module_fds.size()); // Wait for ack if (read_int(zygiskd_socket) != 0) { @@ -342,7 +359,7 @@ static void get_process_info(int client, const sock_cred *cred) { if (!info.on_denylist) { char buf[256]; get_exe(cred->pid, buf, sizeof(buf)); - vector fds = zygisk_module_fds(str_ends(buf, "64")); + vector fds = get_module_fds(str_ends(buf, "64")); send_fds(client, fds.data(), fds.size()); } } @@ -357,6 +374,15 @@ static void send_log_pipe(int fd) { } } +static void get_moddir(int client) { + int id = read_int(client); + char buf[4096]; + snprintf(buf, sizeof(buf), MODULEROOT "/%s", module_list->operator[](id).name.data()); + int dfd = xopen(buf, O_RDONLY | O_CLOEXEC); + send_fd(client, dfd); + close(dfd); +} + void zygisk_handler(int client, const sock_cred *cred) { int code = read_int(client); char buf[256]; @@ -377,6 +403,9 @@ void zygisk_handler(int client, const sock_cred *cred) { get_exe(cred->pid, buf, sizeof(buf)); connect_companion(client, str_ends(buf, "64")); break; + case ZYGISK_GET_MODDIR: + get_moddir(client); + break; } close(client); } diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index 1154eeb9c..986d712d5 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -294,16 +294,26 @@ bool ZygiskModule::RegisterModule(ApiTable *table, long *module) { table->module->ver = module; // Fill in API accordingly with module API version - table->v1.hookJniNativeMethods = &hookJniNativeMethods; - table->v1.pltHookRegister = [](const char *path, const char *symbol, void *n, void **o) { - xhook_register(path, symbol, n, o); - }; - table->v1.pltHookExclude = [](const char *path, const char *symbol) { - xhook_ignore(path, symbol); - }; - table->v1.pltHookCommit = []{ bool r = xhook_refresh(0) == 0; xhook_clear(); return r; }; - table->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); }; - table->v1.setOption = [](ZygiskModule *m, auto opt) { m->setOption(opt); }; + switch (ver) { + case 2: + table->v2.getModuleDir = [](ZygiskModule *m) { return m->getModuleDir(); }; + [[fallthrough]]; + case 1: + table->v1.hookJniNativeMethods = &hookJniNativeMethods; + table->v1.pltHookRegister = [](const char *p, const char *s, void *n, void **o) { + xhook_register(p, s, n, o); + }; + table->v1.pltHookExclude = [](const char *p, const char *s) { + xhook_ignore(p, s); + }; + table->v1.pltHookCommit = []{ bool r = xhook_refresh(0) == 0; xhook_clear(); return r; }; + table->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); }; + table->v1.setOption = [](ZygiskModule *m, auto opt) { m->setOption(opt); }; + break; + default: + // Unknown version number + return false; + } return true; } @@ -318,6 +328,18 @@ int ZygiskModule::connectCompanion() const { return -1; } +int ZygiskModule::getModuleDir() const { + if (int fd = connect_daemon(); fd >= 0) { + write_int(fd, ZYGISK_REQUEST); + write_int(fd, ZYGISK_GET_MODDIR); + write_int(fd, id); + int dfd = recv_fd(fd); + close(fd); + return dfd; + } + return -1; +} + void ZygiskModule::setOption(zygisk::Option opt) { if (g_ctx == nullptr) return; diff --git a/native/jni/zygisk/module.hpp b/native/jni/zygisk/module.hpp index 215602fac..513ec7652 100644 --- a/native/jni/zygisk/module.hpp +++ b/native/jni/zygisk/module.hpp @@ -81,6 +81,9 @@ struct ApiTable { int (*connectCompanion)(ZygiskModule *); void (*setOption)(ZygiskModule *, zygisk::Option); } v1; + struct { + int (*getModuleDir)(ZygiskModule *); + } v2; ApiTable(ZygiskModule *m); }; @@ -100,6 +103,7 @@ struct ZygiskModule { } int connectCompanion() const; + int getModuleDir() const; void setOption(zygisk::Option opt); void doUnload() const { if (unload) dlclose(handle); } diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index ae26aebd8..9b150ef8a 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -12,6 +12,7 @@ enum : int { ZYGISK_GET_INFO, ZYGISK_GET_LOG_PIPE, ZYGISK_CONNECT_COMPANION, + ZYGISK_GET_MODDIR, }; #if defined(__LP64__)