From c8ac6c07b0ae4c633904ffd295124d1698c316c4 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 13 Oct 2021 04:52:02 -0700 Subject: [PATCH] Load Zygisk modules --- native/jni/core/core.hpp | 2 +- native/jni/core/module.cpp | 82 +++++++++++---- native/jni/core/scripting.cpp | 19 ++-- native/jni/core/socket.cpp | 5 +- native/jni/include/daemon.hpp | 1 + native/jni/zygisk/api.hpp | 8 +- native/jni/zygisk/entry.cpp | 65 +++++++----- native/jni/zygisk/hook.cpp | 182 +++++++++++++++++++++++----------- native/jni/zygisk/module.hpp | 117 ++++++++++++++++++++++ native/jni/zygisk/zygisk.hpp | 5 +- 10 files changed, 366 insertions(+), 120 deletions(-) create mode 100644 native/jni/zygisk/module.hpp diff --git a/native/jni/core/core.hpp b/native/jni/core/core.hpp index 2cc5ba1ee..68049197d 100644 --- a/native/jni/core/core.hpp +++ b/native/jni/core/core.hpp @@ -31,6 +31,6 @@ void exec_module_scripts(const char *stage); // Scripting void exec_script(const char *script); void exec_common_scripts(const char *stage); -void exec_module_scripts(const char *stage, const std::vector &module_list); +void exec_module_scripts(const char *stage, const std::vector &modules); void install_apk(const char *apk); [[noreturn]] void install_module(const char *file); diff --git a/native/jni/core/module.cpp b/native/jni/core/module.cpp index aabb6592f..1a0b7a9de 100644 --- a/native/jni/core/module.cpp +++ b/native/jni/core/module.cpp @@ -22,8 +22,6 @@ using namespace std; #define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */ #define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT) -static vector module_list; - class node_entry; class dir_node; class inter_node; @@ -543,12 +541,22 @@ 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; + #define mount_zygisk(bit) \ if (access("/system/bin/app_process" #bit, F_OK) == 0) { \ string zbin = zygisk_bin + "/app_process" #bit; \ string mbin = MAGISKTMP + "/magisk" #bit; \ - int src = xopen(mbin.data(), O_RDONLY); \ - int out = xopen(zbin.data(), O_CREAT | O_WRONLY, 0); \ + int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \ + int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \ xsendfile(out, src, nullptr, INT_MAX); \ close(src); \ close(out); \ @@ -566,8 +574,8 @@ void magic_mount() { char buf[4096]; LOGI("* Loading modules\n"); - for (const auto &m : module_list) { - auto module = m.data(); + for (const auto &m : *modules) { + const char *module = m.name.data(); char *b = buf + sprintf(buf, "%s/" MODULEMNT "/%s/", MAGISKTMP.data(), module); // Read props @@ -676,8 +684,8 @@ static void foreach_module(Func fn) { } } -static void collect_modules() { - foreach_module([](int dfd, dirent *entry, int modfd) { +static void collect_modules(bool open_zygisk) { + foreach_module([=](int dfd, dirent *entry, int modfd) { if (faccessat(modfd, "remove", F_OK, 0) == 0) { LOGI("%s: remove\n", entry->d_name); auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh"; @@ -690,24 +698,42 @@ static void collect_modules() { unlinkat(modfd, "update", 0); if (faccessat(modfd, "disable", F_OK, 0) == 0) return; - // Riru and its modules are not compatible with zygisk - if (zygisk_enabled && ( - entry->d_name == "riru-core"sv || - faccessat(modfd, "riru", F_OK, 0) == 0)) - return; - module_list.emplace_back(entry->d_name); + module_info info; + if (zygisk_enabled) { + // Riru and its modules are not compatible with zygisk + if (entry->d_name == "riru-core"sv || faccessat(modfd, "riru", F_OK, 0) == 0) + return; + if (open_zygisk) { +#if defined(__arm__) + info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC); +#elif defined(__aarch64__) + info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC); + info.z64 = openat(modfd, "zygisk/arm64-v8a.so", O_RDONLY | O_CLOEXEC); +#elif defined(__i386__) + info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC); +#elif defined(__x86_64__) + info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC); + info.z64 = openat(modfd, "zygisk/x86_64.so", O_RDONLY | O_CLOEXEC); +#else +#error Unsupported ABI +#endif + } + } + info.name = entry->d_name; + modules->push_back(info); }); } void handle_modules() { + default_new(modules); prepare_modules(); - collect_modules(); + collect_modules(false); exec_module_scripts("post-fs-data"); // Recollect modules (module scripts could remove itself) - module_list.clear(); - collect_modules(); + modules->clear(); + collect_modules(true); } void disable_modules() { @@ -726,5 +752,25 @@ void remove_modules() { } void exec_module_scripts(const char *stage) { - exec_module_scripts(stage, module_list); + vector module_names; + std::transform(modules->begin(), modules->end(), std::back_inserter(module_names), + [](const module_info &info) { 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/core/scripting.cpp b/native/jni/core/scripting.cpp index 7cbcac317..388f80849 100644 --- a/native/jni/core/scripting.cpp +++ b/native/jni/core/scripting.cpp @@ -107,16 +107,15 @@ void exec_common_scripts(const char *stage) { PFS_DONE() } -// Return if a > b -static bool timespec_larger(timespec *a, timespec *b) { - if (a->tv_sec != b->tv_sec) - return a->tv_sec > b->tv_sec; - return a->tv_nsec > b->tv_nsec; +static bool operator>(const timespec &a, const timespec &b) { + if (a.tv_sec != b.tv_sec) + return a.tv_sec > b.tv_sec; + return a.tv_nsec > b.tv_nsec; } -void exec_module_scripts(const char *stage, const vector &module_list) { +void exec_module_scripts(const char *stage, const vector &modules) { LOGI("* Running module %s scripts\n", stage); - if (module_list.empty()) + if (modules.empty()) return; bool pfs = stage == "post-fs-data"sv; @@ -124,15 +123,15 @@ void exec_module_scripts(const char *stage, const vector &module_list) { timespec now{}; clock_gettime(CLOCK_MONOTONIC, &now); // If we had already timed out, treat it as service mode - if (timespec_larger(&now, &pfs_timeout)) + if (now > pfs_timeout) pfs = false; } int timer_pid = -1; PFS_SETUP() char path[4096]; - for (auto &m : module_list) { - const char* module = m.data(); + for (auto &m : modules) { + const char *module = m.data(); sprintf(path, MODULEROOT "/%s/%s.sh", module, stage); if (access(path, F_OK) == -1) continue; diff --git a/native/jni/core/socket.cpp b/native/jni/core/socket.cpp index 9cf1fcc70..08166de0a 100644 --- a/native/jni/core/socket.cpp +++ b/native/jni/core/socket.cpp @@ -93,14 +93,17 @@ static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) { } vector recv_fds(int sockfd) { + vector results; + // Peek fd count to allocate proper buffer int cnt; recv(sockfd, &cnt, sizeof(cnt), MSG_PEEK); + if (cnt == 0) + return results; vector cmsgbuf; cmsgbuf.resize(CMSG_SPACE(sizeof(int) * cnt)); - vector results; void *data = recv_fds(sockfd, cmsgbuf.data(), cmsgbuf.size(), cnt); if (data == nullptr) return results; diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index cb88a38e1..07a7ede53 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -65,6 +65,7 @@ void boot_complete(int client); void denylist_handler(int client, ucred *cred); void su_daemon_handler(int client, ucred *credential); void zygisk_handler(int client, ucred *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 d690dbd74..bdf6b6cb7 100644 --- a/native/jni/zygisk/api.hpp +++ b/native/jni/zygisk/api.hpp @@ -172,7 +172,7 @@ struct Api { // The original function pointer will be saved in each JNINativeMethod's fnPtr. // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr // will be set to nullptr. - void hookJniNativeMethods(const char *className, JNINativeMethod *methods, int numMethods); + void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); // For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`. // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. @@ -241,7 +241,7 @@ struct api_table { bool (*registerModule)(api_table *, module_abi *); // Utility functions - void (*hookJniNativeMethods)(const char *, JNINativeMethod *, int); + void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); void (*pltHookRegister)(const char *, const char *, void *, void **); void (*pltHookExclude)(const char *, const char *); bool (*pltHookCommit)(); @@ -269,8 +269,8 @@ int Api::connectCompanion() { void Api::forceDenyListUnmount() { impl->forceDenyListUnmount(impl->_this); } -void Api::hookJniNativeMethods(const char *className, JNINativeMethod *methods, int numMethods) { - impl->hookJniNativeMethods(className, methods, numMethods); +void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { + impl->hookJniNativeMethods(env, className, methods, numMethods); } void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { impl->pltHookRegister(regex, symbol, newFunc, oldFunc); diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index d34584044..510094b4a 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -213,17 +213,21 @@ static int zygisk_log(int prio, const char *fmt, va_list ap) { return ret; } -void remote_get_app_info(int uid, const char *process, AppInfo *info) { +std::vector remote_get_info(int uid, const char *process, AppInfo *info) { + vector fds; if (int fd = connect_daemon(); fd >= 0) { write_int(fd, ZYGISK_REQUEST); - write_int(fd, ZYGISK_GET_APPINFO); + write_int(fd, ZYGISK_GET_INFO); write_int(fd, uid); write_string(fd, process); xxread(fd, info, sizeof(*info)); - + if (!info->on_denylist) { + fds = recv_fds(fd); + } close(fd); } + return fds; } int remote_request_unmount() { @@ -261,12 +265,12 @@ static void setup_files(int client, ucred *cred) { int cached_manager_app_id = -1; static time_t last_modified = 0; -static void get_app_info(int client) { +static void get_process_info(int client, ucred *cred) { AppInfo info{}; int uid = read_int(client); string process = read_string(client); - // This function is called on every single zygote app process specialization, + // This function is called on every single zygote process specialization, // so performance is critical. get_manager_app_id() is expensive as it goes // through a SQLite query and potentially multiple filesystem stats, so we // really want to cache the app ID value. Check the last modify timestamp of @@ -275,29 +279,40 @@ static void get_app_info(int client) { // If denylist is enabled, inotify will invalidate the app ID cache for us. // In this case, we can skip the timestamp check all together. - int manager_app_id = cached_manager_app_id; + if (uid != 1000) { + int manager_app_id = cached_manager_app_id; - // Denylist not enabled, check packages.xml timestamp - if (!denylist_enabled && manager_app_id > 0) { - struct stat st{}; - stat("/data/system/packages.xml", &st); - if (st.st_atim.tv_sec > last_modified) { - manager_app_id = -1; - last_modified = st.st_atim.tv_sec; + // Denylist not enabled, check packages.xml timestamp + if (!denylist_enabled && manager_app_id > 0) { + struct stat st{}; + stat("/data/system/packages.xml", &st); + if (st.st_atim.tv_sec > last_modified) { + manager_app_id = -1; + last_modified = st.st_atim.tv_sec; + } + } + + if (manager_app_id < 0) { + manager_app_id = get_manager_app_id(); + cached_manager_app_id = manager_app_id; + } + + if (to_app_id(uid) == manager_app_id) { + info.is_magisk_app = true; + } else if (denylist_enabled) { + info.on_denylist = is_deny_target(uid, process); } } - if (manager_app_id < 0) { - manager_app_id = get_manager_app_id(); - cached_manager_app_id = manager_app_id; - } - - if (to_app_id(uid) == manager_app_id) { - info.is_magisk_app = true; - } else if (denylist_enabled) { - info.on_denylist = is_deny_target(uid, process); - } xwrite(client, &info, sizeof(info)); + + if (!info.on_denylist) { + char buf[256]; + snprintf(buf, sizeof(buf), "/proc/%d/exe", cred->pid); + xreadlink(buf, buf, sizeof(buf)); + vector fds = zygisk_module_fds(str_ends(buf, "64")); + send_fds(client, fds.data(), fds.size()); + } } static void do_unmount(int client, ucred *cred) { @@ -325,8 +340,8 @@ void zygisk_handler(int client, ucred *cred) { case ZYGISK_SETUP: setup_files(client, cred); break; - case ZYGISK_GET_APPINFO: - get_app_info(client); + case ZYGISK_GET_INFO: + get_process_info(client, cred); break; case ZYGISK_UNMOUNT: do_unmount(client, cred); diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index f5e0accd3..8d04a982c 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -8,7 +8,7 @@ #include "zygisk.hpp" #include "memory.hpp" -#include "api.hpp" +#include "module.hpp" using namespace std; using jni_hook::hash_map; @@ -22,57 +22,13 @@ using xstring = jni_hook::string; namespace { enum { - DENY_FLAG, + UNMOUNT_FLAG, FORK_AND_SPECIALIZE, APP_SPECIALIZE, SERVER_SPECIALIZE, FLAG_MAX }; -struct AppSpecializeArgsImpl { - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jint &mount_external; - jstring &se_info; - jstring &nice_name; - jstring &instruction_set; - jstring &app_data_dir; - - /* Optional */ - jboolean *is_child_zygote = nullptr; - jboolean *is_top_app = nullptr; - jobjectArray *pkg_data_info_list = nullptr; - jobjectArray *whitelisted_data_info_list = nullptr; - jboolean *mount_data_dirs = nullptr; - jboolean *mount_storage_dirs = nullptr; - - AppSpecializeArgsImpl( - jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, - jint &mount_external, jstring &se_info, jstring &nice_name, - jstring &instruction_set, jstring &app_data_dir) : - uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags), - mount_external(mount_external), se_info(se_info), nice_name(nice_name), - instruction_set(instruction_set), app_data_dir(app_data_dir) {} -}; - -struct ServerSpecializeArgsImpl { - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jlong &permitted_capabilities; - jlong &effective_capabilities; - - ServerSpecializeArgsImpl( - jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, - jlong &permitted_capabilities, jlong &effective_capabilities) : - uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags), - permitted_capabilities(permitted_capabilities), - effective_capabilities(effective_capabilities) {} -}; - #define DCL_PRE_POST(name) \ void name##_pre(); \ void name##_post(); @@ -88,14 +44,16 @@ struct HookContext { int pid; bitset flags; AppInfo info; + vector modules; HookContext() : pid(-1), info{} {} static void close_fds(); + void toggle_unmount(); DCL_PRE_POST(fork) - DCL_PRE_POST(run_modules) - + void run_modules_pre(const vector &fds); + void run_modules_post(); DCL_PRE_POST(nativeForkAndSpecialize) DCL_PRE_POST(nativeSpecializeAppProcess) DCL_PRE_POST(nativeForkSystemServer) @@ -184,7 +142,7 @@ DCL_HOOK_FUNC(int, fork) { // This is the latest point where we can still connect to the magiskd main socket DCL_HOOK_FUNC(int, selinux_android_setcontext, uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) { - if (g_ctx && g_ctx->flags[DENY_FLAG]) { + if (g_ctx && g_ctx->flags[UNMOUNT_FLAG]) { if (remote_request_unmount() == 0) { LOGD("zygisk: mount namespace cleaned up\n"); } @@ -267,13 +225,121 @@ DCL_HOOK_FUNC(void, setArgv0, void *self, const char *argv0, bool setProcName) { // ----------------------------------------------------------------- -void HookContext::run_modules_pre() { /* TODO */ } -void HookContext::run_modules_post() { /* TODO */ } +void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) { + auto class_map = jni_method_map->find(clz); + if (class_map == jni_method_map->end()) { + for (int i = 0; i < numMethods; ++i) { + methods[i].fnPtr = nullptr; + } + return; + } + + vector hooks; + for (int i = 0; i < numMethods; ++i) { + auto method_map = class_map->second.find(methods[i].name); + if (method_map != class_map->second.end()) { + auto it = method_map->second.find(methods[i].signature); + if (it != method_map->second.end()) { + // Copy the JNINativeMethod + hooks.push_back(methods[i]); + // Save the original function pointer + methods[i].fnPtr = it->second; + // Do not allow double hook, remove method from map + method_map->second.erase(it); + continue; + } + } + // No matching method found, set fnPtr to null + methods[i].fnPtr = nullptr; + } + + if (hooks.empty()) + return; + + old_jniRegisterNativeMethods(env, clz, hooks.data(), hooks.size()); +} + +bool ZygiskModule::registerModule(ApiTable *table, long *module) { + long ver = *module; + // Unsupported version + if (ver > ZYGISK_API_VERSION) + return false; + + // Set the actual module_abi* + 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 = []() { return xhook_refresh(0) == 0; }; + table->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); }; + table->v1.forceDenyListUnmount = [](auto) { ZygiskModule::forceDenyListUnmount(); }; + + return true; +} + +int ZygiskModule::connectCompanion() { + // TODO + (void) id; + return -1; +} + +void ZygiskModule::forceDenyListUnmount() { + if (g_ctx == nullptr) + return; + g_ctx->toggle_unmount(); +} + +void HookContext::run_modules_pre(const vector &fds) { + char buf[256]; + for (int i = 0; i < fds.size(); ++i) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fds[i]); + if (void *h = dlopen(buf, RTLD_LAZY)) { + void (*module_entry)(void *, void *); + *(void **) &module_entry = dlsym(h, "zygisk_module_entry"); + if (module_entry) { + modules.emplace_back(i); + auto api = new ApiTable(&modules.back()); + module_entry(api, env); + } + } + close(fds[i]); + } + for (auto &m : modules) { + if (flags[APP_SPECIALIZE]) { + m.preAppSpecialize(args); + } else if (flags[SERVER_SPECIALIZE]) { + m.preServerSpecialize(server_args); + } + } +} + +void HookContext::run_modules_post() { + for (auto &m : modules) { + if (flags[APP_SPECIALIZE]) { + m.postAppSpecialize(args); + } else if (flags[SERVER_SPECIALIZE]) { + m.postServerSpecialize(server_args); + } + } +} void HookContext::close_fds() { close(logd_fd.exchange(-1)); } +void HookContext::toggle_unmount() { + if (flags[APP_SPECIALIZE]) { + // TODO: Handle MOUNT_EXTERNAL_NONE + flags[UNMOUNT_FLAG] = args->mount_external != 0; + } +} + // ----------------------------------------------------------------- void HookContext::nativeSpecializeAppProcess_pre() { @@ -286,14 +352,12 @@ void HookContext::nativeSpecializeAppProcess_pre() { VLOG("zygisk: pre specialize [%s]\n", process); } - remote_get_app_info(args->uid, process, &info); - - /* TODO: Handle MOUNT_EXTERNAL_NONE */ - if (args->mount_external != 0 && info.on_denylist) { + auto module_fds = remote_get_info(args->uid, process, &info); + if (info.on_denylist) { LOGI("zygisk: [%s] is on the denylist\n", process); - flags[DENY_FLAG] = true; + toggle_unmount(); } else { - run_modules_pre(); + run_modules_pre(module_fds); } } @@ -305,7 +369,7 @@ void HookContext::nativeSpecializeAppProcess_post() { } env->ReleaseStringUTFChars(args->nice_name, process); - if (flags[DENY_FLAG]) { + if (info.on_denylist) { self_unload(); } else { run_modules_post(); @@ -325,7 +389,7 @@ void HookContext::nativeForkSystemServer_pre() { flags[SERVER_SPECIALIZE] = true; if (pid == 0) { VLOG("zygisk: pre forkSystemServer\n"); - run_modules_pre(); + run_modules_pre(remote_get_info(1000, "system_server", &info)); close_fds(); } } diff --git a/native/jni/zygisk/module.hpp b/native/jni/zygisk/module.hpp new file mode 100644 index 000000000..e7d729359 --- /dev/null +++ b/native/jni/zygisk/module.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include "api.hpp" + +namespace { + +using module_abi_v1 = zygisk::internal::module_abi; +struct HookContext; +struct ApiTable; + +struct AppSpecializeArgsImpl { + jint &uid; + jint &gid; + jintArray &gids; + jint &runtime_flags; + jint &mount_external; + jstring &se_info; + jstring &nice_name; + jstring &instruction_set; + jstring &app_data_dir; + + /* Optional */ + jboolean *is_child_zygote = nullptr; + jboolean *is_top_app = nullptr; + jobjectArray *pkg_data_info_list = nullptr; + jobjectArray *whitelisted_data_info_list = nullptr; + jboolean *mount_data_dirs = nullptr; + jboolean *mount_storage_dirs = nullptr; + + AppSpecializeArgsImpl( + jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, + jint &mount_external, jstring &se_info, jstring &nice_name, + jstring &instruction_set, jstring &app_data_dir) : + uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags), + mount_external(mount_external), se_info(se_info), nice_name(nice_name), + instruction_set(instruction_set), app_data_dir(app_data_dir) {} +}; + +struct ServerSpecializeArgsImpl { + jint &uid; + jint &gid; + jintArray &gids; + jint &runtime_flags; + jlong &permitted_capabilities; + jlong &effective_capabilities; + + ServerSpecializeArgsImpl( + jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, + jlong &permitted_capabilities, jlong &effective_capabilities) : + uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags), + permitted_capabilities(permitted_capabilities), + effective_capabilities(effective_capabilities) {} +}; + +template +struct force_cast_wrapper { + template + operator U() const { return reinterpret_cast(mX); } + force_cast_wrapper(T &&x) : mX(std::forward(x)) {} + force_cast_wrapper &operator=(const force_cast_wrapper &) = delete; +private: + T &&mX; +}; + +template +force_cast_wrapper force_cast(R &&x) { + return force_cast_wrapper(std::forward(x)); +} + +struct ZygiskModule { + void preAppSpecialize(AppSpecializeArgsImpl *args) { + v1->preAppSpecialize(v1->_this, force_cast(args)); + } + void postAppSpecialize(const AppSpecializeArgsImpl *args) { + v1->postAppSpecialize(v1->_this, force_cast(args)); + } + void preServerSpecialize(ServerSpecializeArgsImpl *args) { + v1->preServerSpecialize(v1->_this, force_cast(args)); + } + void postServerSpecialize(const ServerSpecializeArgsImpl *args) { + v1->postServerSpecialize(v1->_this, force_cast(args)); + } + + int connectCompanion(); + static void forceDenyListUnmount(); + static bool registerModule(ApiTable *table, long *module); + + ZygiskModule(int id) : id(id) {} + +private: + int id; + union { + long *ver = nullptr; + module_abi_v1 *v1; + }; +}; + +struct ApiTable { + ZygiskModule *module; + bool (*registerModule)(ApiTable *, long *); + + union { + void *padding[6] = {}; + struct { + void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); + void (*pltHookRegister)(const char *, const char *, void *, void **); + void (*pltHookExclude)(const char *, const char *); + bool (*pltHookCommit)(); + + int (*connectCompanion)(ZygiskModule *); + void (*forceDenyListUnmount)(ZygiskModule *); + } v1; + }; + ApiTable(ZygiskModule *m) : module(m), registerModule(&ZygiskModule::registerModule) {} +}; + +} // namespace diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index d6c218b0e..78cc646ee 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -2,13 +2,14 @@ #include #include +#include #define INJECT_ENV_1 "MAGISK_INJ_1" #define INJECT_ENV_2 "MAGISK_INJ_2" enum : int { ZYGISK_SETUP, - ZYGISK_GET_APPINFO, + ZYGISK_GET_INFO, ZYGISK_UNMOUNT, ZYGISK_GET_LOG_PIPE, }; @@ -33,5 +34,5 @@ struct AppInfo { void self_unload(); void hook_functions(); bool unhook_functions(); -void remote_get_app_info(int uid, const char *process, AppInfo *info); +std::vector remote_get_info(int uid, const char *process, AppInfo *info); int remote_request_unmount();