diff --git a/native/jni/zygisk/api.hpp b/native/jni/zygisk/api.hpp index d2412cecb..dbd11d2ff 100644 --- a/native/jni/zygisk/api.hpp +++ b/native/jni/zygisk/api.hpp @@ -30,16 +30,18 @@ class ExampleModule : public zygisk::ModuleBase { public: void onLoad(zygisk::Api *api, JNIEnv *env) override { this->api = api; + this->env = env; } void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { JNINativeMethod methods[] = { { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, }; - api->hookJniNativeMethods("android/util/Log", methods, 1); + api->hookJniNativeMethods(env, "android/util/Log", methods, 1); *(void **) &orig_logger_entry_max = methods[0].fnPtr; } private: zygisk::Api *api; + JNIEnv *env; }; REGISTER_ZYGISK_MODULE(ExampleModule) @@ -129,6 +131,27 @@ struct api_table; template void entry_impl(api_table *, JNIEnv *); } +// These values are used in Api::setOption(Option) +enum Option : int { + // Force Magisk's denylist unmount routines to run on this process. + // + // Setting this option only makes sense in preAppSpecialize. + // The actual unmounting happens during app process specialization. + // + // Processes added to Magisk's denylist will have all Magisk and its modules' files unmounted + // from its mount namespace. In addition, all Zygisk code will be unloaded from memory, which + // also implies that no Zygisk modules (including yours) are loaded. + // + // However, if for any reason your module still wants the unmount part of the denylist + // operation to be enabled EVEN IF THE PROCESS IS NOT ON THE DENYLIST, set this option. + FORCE_DENYLIST_UNMOUNT = 0, + + // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize. + // Be aware that after dlclose-ing your module, all of your code will be unmapped. + // YOU SHOULD NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTION IN THE PROCESS. + DLCLOSE_MODULE_LIBRARY = 1, +}; + struct Api { // Connect to a root companion process and get a Unix domain socket for IPC. @@ -145,22 +168,10 @@ struct Api { // module's companion request handler. Returns -1 if the connection attempt failed. int connectCompanion(); - // Force Magisk's denylist unmount routines to run on this process. - // - // This API only works in preAppSpecialize. - // - // Processes added to Magisk's denylist will have all Magisk and its modules' files unmounted - // from its mount namespace. In addition, all Zygisk code will be unloaded from memory, which - // also implies that no Zygisk modules (including yours) are loaded. - // - // However, if for any reason your module still wants the unmount part of the denylist - // operation to be enabled EVEN IF THE PROCESS IS NOT ON THE DENYLIST, call this function. - // No code will be unloaded from memory (including your module) because there is no way to - // guarantee no crashes will occur. - // - // The unmounting does not happen immediately after the function is called. It is actually - // done during app process specialization. - void forceDenyListUnmount(); + // 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. + void setOption(Option opt); // Hook JNI native methods for a class // @@ -244,7 +255,7 @@ struct api_table { // Zygisk functions int (*connectCompanion)(void * /* _this */); - void (*forceDenyListUnmount)(void * /* _this */); + void (*setOption)(void * /* _this */, Option); }; template @@ -262,8 +273,8 @@ void entry_impl(api_table *table, JNIEnv *env) { int Api::connectCompanion() { return impl->connectCompanion(impl->_this); } -void Api::forceDenyListUnmount() { - impl->forceDenyListUnmount(impl->_this); +void Api::setOption(Option opt) { + impl->setOption(impl->_this, opt); } void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { impl->hookJniNativeMethods(env, className, methods, numMethods); diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index d5f7ddaeb..e3915357b 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -282,7 +282,7 @@ bool ZygiskModule::registerModule(ApiTable *table, long *module) { }; table->v1.pltHookCommit = []() { return xhook_refresh(0) == 0; }; table->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); }; - table->v1.forceDenyListUnmount = [](auto) { ZygiskModule::forceDenyListUnmount(); }; + table->v1.setOption = [](ZygiskModule *m, auto opt) { m->setOption(opt); }; return true; } @@ -297,10 +297,17 @@ int ZygiskModule::connectCompanion() const { return -1; } -void ZygiskModule::forceDenyListUnmount() { +void ZygiskModule::setOption(zygisk::Option opt) { if (g_ctx == nullptr) return; - g_ctx->toggle_unmount(); + switch (opt) { + case zygisk::FORCE_DENYLIST_UNMOUNT: + g_ctx->toggle_unmount(); + break; + case zygisk::DLCLOSE_MODULE_LIBRARY: + unload = true; + break; + } } void HookContext::run_modules_pre(const vector &fds) { @@ -311,7 +318,7 @@ void HookContext::run_modules_pre(const vector &fds) { void (*module_entry)(void *, void *); *(void **) &module_entry = dlsym(h, "zygisk_module_entry"); if (module_entry) { - modules.emplace_back(i); + modules.emplace_back(i, h); auto api = new ApiTable(&modules.back()); module_entry(api, env); } @@ -334,6 +341,9 @@ void HookContext::run_modules_post() { } else if (flags[SERVER_SPECIALIZE]) { m.postServerSpecialize(server_args); } + if (m.unload) { + dlclose(m.handle); + } } } diff --git a/native/jni/zygisk/module.hpp b/native/jni/zygisk/module.hpp index ff7f4ccb7..bd4931d30 100644 --- a/native/jni/zygisk/module.hpp +++ b/native/jni/zygisk/module.hpp @@ -82,10 +82,13 @@ struct ZygiskModule { } int connectCompanion() const; - static void forceDenyListUnmount(); + void setOption(zygisk::Option opt); static bool registerModule(ApiTable *table, long *module); - ZygiskModule(int id) : id(id) {} + ZygiskModule(int id, void *handle) : handle(handle), id(id) {} + + void * const handle; + bool unload = false; private: int id; @@ -108,7 +111,7 @@ struct ApiTable { bool (*pltHookCommit)(); int (*connectCompanion)(ZygiskModule *); - void (*forceDenyListUnmount)(ZygiskModule *); + void (*setOption)(ZygiskModule *, zygisk::Option); } v1; }; ApiTable(ZygiskModule *m) : module(m), registerModule(&ZygiskModule::registerModule) {}