From 153097f9d8dd08741bb6dc9a74b265564a91782b Mon Sep 17 00:00:00 2001 From: ThePedroo Date: Mon, 1 Sep 2025 19:43:54 -0300 Subject: [PATCH] update: rewrite ART method and module related code to C This commit rewrites both ART method and module related code to C, following the same behavior, aside from the module's `on load` now be called globally, allowing a better flexibility. This will not impact any module. --- loader/src/include/api.hpp | 395 --------- loader/src/injector/art_method.h | 107 +++ loader/src/injector/art_method.hpp | 83 -- loader/src/injector/gen_jni_hooks.py | 4 +- loader/src/injector/hook.cpp | 299 ++++--- loader/src/injector/jni_helper.hpp | 1116 -------------------------- loader/src/injector/jni_hooks.hpp | 40 +- loader/src/injector/module.h | 252 ++++++ loader/src/injector/module.hpp | 240 ------ 9 files changed, 556 insertions(+), 1980 deletions(-) delete mode 100644 loader/src/include/api.hpp create mode 100644 loader/src/injector/art_method.h delete mode 100644 loader/src/injector/art_method.hpp delete mode 100644 loader/src/injector/jni_helper.hpp create mode 100644 loader/src/injector/module.h delete mode 100644 loader/src/injector/module.hpp diff --git a/loader/src/include/api.hpp b/loader/src/include/api.hpp deleted file mode 100644 index e4a00bd..0000000 --- a/loader/src/include/api.hpp +++ /dev/null @@ -1,395 +0,0 @@ -/* Copyright 2022 John "topjohnwu" Wu - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -// This is the public API for Zygisk modules. -// DO NOT MODIFY ANY CODE IN THIS HEADER. - -// WARNING: this file may contain changes that are not finalized. -// Always use the following published header for development: -// https://github.com/topjohnwu/zygisk-module-sample/blob/master/module/jni/zygisk.hpp - -#pragma once - -#include - -#define ZYGISK_API_VERSION 4 - -/* - -*************** -* Introduction -*************** - -On Android, all app processes are forked from a special daemon called "Zygote". -For each new app process, zygote will fork a new process and perform "specialization". -This specialization operation enforces the Android security sandbox on the newly forked -process to make sure that 3rd party application code is only loaded after it is being -restricted within a sandbox. - -On Android, there is also this special process called "system_server". This single -process hosts a significant portion of system services, which controls how the -Android operating system and apps interact with each other. - -The Zygisk framework provides a way to allow developers to build modules and run custom -code before and after system_server and any app processes' specialization. -This enable developers to inject code and alter the behavior of system_server and app processes. - -Please note that modules will only be loaded after zygote has forked the child process. -THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON! - -********************* -* Development Guide -********************* - -Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. -Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. - -Example code: - -static jint (*orig_logger_entry_max)(JNIEnv *env); -static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } - -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(env, "android/util/Log", methods, 1); - *(void **) &orig_logger_entry_max = methods[0].fnPtr; - } -private: - zygisk::Api *api; - JNIEnv *env; -}; - -REGISTER_ZYGISK_MODULE(ExampleModule) - ------------------------------------------------------------------------------------------ - -Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize, -or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class -never runs in a true superuser environment. - -If your module require access to superuser permissions, you can create and register -a root companion handler function. This function runs in a separate root companion -daemon process, and an Unix domain socket is provided to allow you to perform IPC between -your target process and the root companion process. - -Example code: - -static void example_handler(int socket) { ... } - -REGISTER_ZYGISK_COMPANION(example_handler) - -*/ - -namespace zygisk { - - struct Api; - struct AppSpecializeArgs; - struct ServerSpecializeArgs; - - class ModuleBase { - public: - - // This method is called as soon as the module is loaded into the target process. - // A Zygisk API handle will be passed as an argument. - virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {} - - // This method is called before the app process is specialized. - // At this point, the process just got forked from zygote, but no app specific specialization - // is applied. This means that the process does not have any sandbox restrictions and - // still runs with the same privilege of zygote. - // - // All the arguments that will be sent and used for app specialization is passed as a single - // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app - // process will be specialized. - // - // If you need to run some operations as superuser, you can call Api::connectCompanion() to - // get a socket to do IPC calls with a root companion process. - // See Api::connectCompanion() for more info. - virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {} - - // This method is called after the app process is specialized. - // At this point, the process has all sandbox restrictions enabled for this application. - // This means that this method runs with the same privilege of the app's own code. - virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {} - - // This method is called before the system server process is specialized. - // See preAppSpecialize(args) for more info. - virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {} - - // This method is called after the system server process is specialized. - // At this point, the process runs with the privilege of system_server. - virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {} - }; - - struct AppSpecializeArgs { - // Required arguments. These arguments are guaranteed to exist on all Android versions. - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jobjectArray &rlimits; - jint &mount_external; - jstring &se_info; - jstring &nice_name; - jstring &instruction_set; - jstring &app_data_dir; - - // Optional arguments. Please check whether the pointer is null before de-referencing - jintArray *const fds_to_ignore; - jboolean *const is_child_zygote; - jboolean *const is_top_app; - jobjectArray *const pkg_data_info_list; - jobjectArray *const whitelisted_data_info_list; - jboolean *const mount_data_dirs; - jboolean *const mount_storage_dirs; - jboolean *const mount_sysprop_overrides; - - AppSpecializeArgs() = delete; - }; - - struct ServerSpecializeArgs { - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jlong &permitted_capabilities; - jlong &effective_capabilities; - - ServerSpecializeArgs() = delete; - }; - - namespace internal { - 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. - // - // Set this option to force all Magisk and modules' files to be unmounted from the - // mount namespace of the process, regardless of the denylist enforcement status. - 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 from memory. - // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS. - DLCLOSE_MODULE_LIBRARY = 1, - }; - -// Bit masks of the return value of Api::getFlags() - enum StateFlag : uint32_t { - // The user has granted root access to the current process - PROCESS_GRANTED_ROOT = (1u << 0), - - // The current process was added on the denylist - PROCESS_ON_DENYLIST = (1u << 1), - }; - -// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded -// from the specialized process afterwards. - struct Api { - - // Connect to a root companion process and get a Unix domain socket for IPC. - // - // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions. - // - // The pre[XXX]Specialize methods run with the same privilege of zygote. - // If you would like to do some operations with superuser permissions, register a handler - // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). - // Another good use case for a companion process is that if you want to share some resources - // across multiple processes, hold the resources in the companion process and pass it over. - // - // The root companion process is ABI aware; that is, when calling this method from a 32-bit - // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. - // - // Returns a file descriptor to a socket that is connected to the socket passed to your - // 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 methods. - // Accessing the directory returned is only possible in the pre[XXX]Specialize methods - // 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 method accepts one single option at a time. - // Check zygisk::Option for the full list of options available. - void setOption(Option opt); - - // Get information about the current process. - // Returns bitwise-or'd zygisk::StateFlag values. - uint32_t getFlags(); - - // Exempt the provided file descriptor from being automatically closed. - // - // This API only make sense in preAppSpecialize; calling this method in any other situation - // is either a no-op (returns true) or an error (returns false). - // - // When false is returned, the provided file descriptor will eventually be closed by zygote. - bool exemptFd(int fd); - - // Hook JNI native methods for a class - // - // Lookup all registered JNI native methods and replace it with your own methods. - // 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(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); - - // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory. - // - // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example: - // - //
- // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64 - // (More details: https://man7.org/linux/man-pages/man5/proc.5.html) - // - // The `dev` and `inode` pair uniquely identifies a file being mapped into memory. - // For matching ELFs loaded in memory, replace function `symbol` with `newFunc`. - // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. - void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc); - - // Commit all the hooks that was previously registered. - // Returns false if an error occurred. - bool pltHookCommit(); - - private: - internal::api_table *tbl; - template friend void internal::entry_impl(internal::api_table *, JNIEnv *); - }; - -// Register a class as a Zygisk module - -#define REGISTER_ZYGISK_MODULE(clazz) \ -void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ - zygisk::internal::entry_impl(table, env); \ -} - -// Register a root companion request handler function for your module -// -// The function runs in a superuser daemon process and handles a root companion request from -// your module running in a target process. The function has to accept an integer value, -// which is a Unix domain socket that is connected to the target process. -// See Api::connectCompanion() for more info. -// -// NOTE: the function can run concurrently on multiple threads. -// Be aware of race conditions if you have globally shared resources. - -#define REGISTER_ZYGISK_COMPANION(func) \ -void zygisk_companion_entry(int client) { func(client); } - -/********************************************************* - * The following is internal ABI implementation detail. - * You do not have to understand what it is doing. - *********************************************************/ - - namespace internal { - - struct module_abi { - long api_version; - ModuleBase *impl; - - void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); - void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); - void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); - void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); - - module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) { - preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); }; - postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); }; - preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); }; - postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); }; - } - }; - - struct api_table { - // Base - void *impl; - bool (*registerModule)(api_table *, module_abi *); - - void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); - void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); - bool (*exemptFd)(int); - bool (*pltHookCommit)(); - int (*connectCompanion)(void * /* impl */); - void (*setOption)(void * /* impl */, Option); - int (*getModuleDir)(void * /* impl */); - uint32_t (*getFlags)(void * /* impl */); - }; - - template - void entry_impl(api_table *table, JNIEnv *env) { - ModuleBase *module = new T(); - if (!table->registerModule(table, new module_abi(module))) - return; - auto api = new Api(); - api->tbl = table; - module->onLoad(api, env); - } - - } // namespace internal - - inline int Api::connectCompanion() { - return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1; - } - inline int Api::getModuleDir() { - return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1; - } - inline void Api::setOption(Option opt) { - if (tbl->setOption) tbl->setOption(tbl->impl, opt); - } - inline uint32_t Api::getFlags() { - return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0; - } - inline bool Api::exemptFd(int fd) { - return tbl->exemptFd != nullptr && tbl->exemptFd(fd); - } - inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { - if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods); - } - inline void Api::pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc) { - if (tbl->pltHookRegister) tbl->pltHookRegister(dev, inode, symbol, newFunc, oldFunc); - } - inline bool Api::pltHookCommit() { - return tbl->pltHookCommit != nullptr && tbl->pltHookCommit(); - } - -} // namespace zygisk - -extern "C" { - -[[gnu::visibility("default")]] [[gnu::used]] -void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); - -[[gnu::visibility("default")]] [[gnu::used]] -void zygisk_companion_entry(int); - -} // extern "C" diff --git a/loader/src/injector/art_method.h b/loader/src/injector/art_method.h new file mode 100644 index 0000000..221754d --- /dev/null +++ b/loader/src/injector/art_method.h @@ -0,0 +1,107 @@ +#ifndef ART_METHOD_H +#define ART_METHOD_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "logging.h" + +inline static jfieldID art_method_field = nullptr; +inline static size_t art_method_size = 0; +inline static size_t entry_point_offset = 0; +inline static size_t data_offset = 0; + +void *amethod_from_reflected_method(JNIEnv *env, jobject method); + +bool amethod_init(JNIEnv *env) { + jclass clazz = env->FindClass("java/lang/reflect/Executable"); + if (!clazz) { + LOGE("Failed to found Executable"); + + return false; + } + + if (art_method_field = env->GetFieldID(clazz, "artMethod", "J"); !art_method_field) { + LOGE("Failed to find artMethod field"); + + env->DeleteLocalRef(clazz); + + return false; + } + + jclass throwable = env->FindClass("java/lang/Throwable"); + if (!throwable) { + LOGE("Failed to found Executable"); + + env->DeleteLocalRef(clazz); + + return false; + } + + jclass clz = env->FindClass("java/lang/Class"); + if (!clz) { + LOGE("Failed to found Class"); + + env->DeleteLocalRef(clazz); + env->DeleteLocalRef(throwable); + + return false; + } + + jmethodID get_declared_constructors = env->GetMethodID(clz, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;"); + env->DeleteLocalRef(clz); + + const auto constructors = (jobjectArray) env->CallObjectMethod(throwable, get_declared_constructors); + env->DeleteLocalRef(throwable); + if (!constructors || env->GetArrayLength(constructors) < 2) { + LOGE("Throwable has less than 2 constructors"); + + env->DeleteLocalRef(clazz); + + return false; + } + + jobject first_ctor = env->GetObjectArrayElement(constructors, 0); + jobject second_ctor = env->GetObjectArrayElement(constructors, 1); + + uintptr_t first = (uintptr_t)amethod_from_reflected_method(env, first_ctor); + uintptr_t second = (uintptr_t)amethod_from_reflected_method(env, second_ctor); + + env->DeleteLocalRef(first_ctor); + env->DeleteLocalRef(second_ctor); + env->DeleteLocalRef(constructors); + + art_method_size = (size_t)(second - first); + LOGD("ArtMethod size: %zu", art_method_size); + if ((4 * 9 + 3 * sizeof(void *)) < art_method_size) { + LOGE("ArtMethod size exceeds maximum assume. There may be something wrong."); + + return false; + } + + entry_point_offset = art_method_size - sizeof(void *); + data_offset = entry_point_offset - sizeof(void *); + LOGD("ArtMethod entrypoint offset: %zu", entry_point_offset); + LOGD("ArtMethod data offset: %zu", data_offset); + + return true; +} + +void *amethod_get_data(uintptr_t self) { + return *(void **)((uintptr_t)self + data_offset); +} + +void *amethod_from_reflected_method(JNIEnv *env, jobject method) { + if (art_method_field) { + return (void *)env->GetLongField(method, art_method_field); + } else { + return (void *)env->FromReflectedMethod(method); + } +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ART_METHOD_H */ \ No newline at end of file diff --git a/loader/src/injector/art_method.hpp b/loader/src/injector/art_method.hpp deleted file mode 100644 index 271e01a..0000000 --- a/loader/src/injector/art_method.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "logging.h" -#include "jni_helper.hpp" - -template -constexpr inline auto RoundUpTo(T v, size_t size) { - return v + size - 1 - ((v + size - 1) & (size - 1)); -} - -inline static constexpr auto kPointerSize = sizeof(void *); - -namespace lsplant::art { - - class ArtMethod { - - public: - void *GetData() { - return *reinterpret_cast(reinterpret_cast(this) + data_offset); - } - - static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) { - if (art_method_field) [[likely]] { - return reinterpret_cast( - JNI_GetLongField(env, method, art_method_field)); - } else { - return reinterpret_cast(env->FromReflectedMethod(method)); - } - } - - static bool Init(JNIEnv *env) { - ScopedLocalRef executable{env, nullptr}; - executable = JNI_FindClass(env, "java/lang/reflect/Executable"); - if (!executable) { - LOGE("Failed to found Executable"); - return false; - } - - if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J"); - !art_method_field) { - LOGE("Failed to find artMethod field"); - return false; - } - - auto throwable = JNI_FindClass(env, "java/lang/Throwable"); - if (!throwable) { - LOGE("Failed to found Executable"); - return false; - } - auto clazz = JNI_FindClass(env, "java/lang/Class"); - static_assert(std::is_same_v); - jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors", - "()[Ljava/lang/reflect/Constructor;"); - const auto constructors = - JNI_Cast(JNI_CallObjectMethod(env, throwable, get_declared_constructors)); - if (constructors.size() < 2) { - LOGE("Throwable has less than 2 constructors"); - return false; - } - auto &first_ctor = constructors[0]; - auto &second_ctor = constructors[1]; - auto *first = FromReflectedMethod(env, first_ctor.get()); - auto *second = FromReflectedMethod(env, second_ctor.get()); - art_method_size = reinterpret_cast(second) - reinterpret_cast(first); - LOGD("ArtMethod size: %zu", art_method_size); - if (RoundUpTo(4 * 9, kPointerSize) + kPointerSize * 3 < art_method_size) [[unlikely]] { - LOGW("ArtMethod size exceeds maximum assume. There may be something wrong."); - } - entry_point_offset = art_method_size - kPointerSize; - data_offset = entry_point_offset - kPointerSize; - LOGD("ArtMethod::entrypoint offset: %zu", entry_point_offset); - LOGD("ArtMethod::data offset: %zu", data_offset); - return true; - } - - private: - inline static jfieldID art_method_field = nullptr; - inline static size_t art_method_size = 0; - inline static size_t entry_point_offset = 0; - inline static size_t data_offset = 0; - }; - -} // namespace lsplant::art diff --git a/loader/src/injector/gen_jni_hooks.py b/loader/src/injector/gen_jni_hooks.py index bdf0eeb..69320ce 100644 --- a/loader/src/injector/gen_jni_hooks.py +++ b/loader/src/injector/gen_jni_hooks.py @@ -88,7 +88,7 @@ class ForkAndSpec(JNIHook): return 'nativeForkAndSpecialize' def init_args(self): - return 'AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);' + return 'struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir);' def body(self): decl = '' @@ -117,7 +117,7 @@ class ForkServer(ForkAndSpec): return 'nativeForkSystemServer' def init_args(self): - return 'ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);' + return 'struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities);' # Common args uid = Argument('uid', jint) diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index b29ce23..7c3b834 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -21,12 +22,12 @@ #include "daemon.h" #include "zygisk.hpp" -#include "module.hpp" +#include "module.h" #include "misc.h" #include "solist.h" -#include "art_method.hpp" +#include "art_method.h" using namespace std; @@ -61,8 +62,8 @@ struct ZygiskContext { JNIEnv *env; union { void *ptr; - AppSpecializeArgs_v5 *app; - ServerSpecializeArgs_v1 *server; + struct app_specialize_args_v5 *app; + struct server_specialize_args_v1 *server; } args; const char *process; @@ -125,7 +126,8 @@ vector> *plt_hook_list; map> *jni_hook_list; bool modules_loaded = false; -list modules; +struct rezygisk_module *zygisk_modules = NULL; +size_t zygisk_module_length = 0; bool should_unmap_zygisk = false; bool enable_unloader = false; @@ -245,6 +247,10 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) { if (should_unmap_zygisk) { unhook_functions(); + /* INFO: Modules might use libzygisk.so after postAppSpecialize. We can only + free it when we are really before our unmap. */ + free(zygisk_modules); + lsplt_free_resources(); if (should_unmap_zygisk) { @@ -326,21 +332,23 @@ void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods nm.fnPtr = nullptr; continue; } - auto method = lsplant::JNI_ToReflectedMethod(env, clazz, mid, is_static); - auto modifier = lsplant::JNI_CallIntMethod(env, method, member_getModifiers); + auto method = env->ToReflectedMethod(clazz, mid, is_static); + auto modifier = env->CallIntMethod(method, member_getModifiers); if ((modifier & MODIFIER_NATIVE) == 0) { nm.fnPtr = nullptr; continue; } - auto artMethod = lsplant::art::ArtMethod::FromReflectedMethod(env, method); + auto artMethod = amethod_from_reflected_method(env, method); hooks.push_back(nm); - auto orig = artMethod->GetData(); + auto orig = amethod_get_data((uintptr_t)artMethod); LOGV("replaced %s %s orig %p", clz, nm.name, orig); nm.fnPtr = orig; } if (hooks.empty()) return; env->RegisterNatives(clazz, hooks.data(), hooks.size()); + + env->DeleteLocalRef(clazz); } // JNI method hook definitions, auto generated @@ -386,16 +394,21 @@ void initialize_jni_hook() { res = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (res != JNI_OK || env == nullptr) return; - auto classMember = lsplant::JNI_FindClass(env, "java/lang/reflect/Member"); - if (classMember != nullptr) member_getModifiers = lsplant::JNI_GetMethodID(env, classMember, "getModifiers", "()I"); - auto classModifier = lsplant::JNI_FindClass(env, "java/lang/reflect/Modifier"); + auto classMember = env->FindClass("java/lang/reflect/Member"); + if (classMember != nullptr) member_getModifiers = env->GetMethodID(classMember, "getModifiers", "()I"); + auto classModifier = env->FindClass("java/lang/reflect/Modifier"); if (classModifier != nullptr) { - auto fieldId = lsplant::JNI_GetStaticFieldID(env, classModifier, "NATIVE", "I"); - if (fieldId != nullptr) MODIFIER_NATIVE = lsplant::JNI_GetStaticIntField(env, classModifier, fieldId); + auto fieldId = env->GetStaticFieldID(classModifier, "NATIVE", "I"); + if (fieldId != nullptr) MODIFIER_NATIVE = env->GetStaticIntField(classModifier, fieldId); } + + env->DeleteLocalRef(classMember); + env->DeleteLocalRef(classModifier); + if (member_getModifiers == nullptr || MODIFIER_NATIVE == 0) return; - if (!lsplant::art::ArtMethod::Init(env)) { - LOGE("failed to init ArtMethod"); + if (!amethod_init(env)) { + LOGE("failed to init amethod"); + return; } @@ -405,51 +418,101 @@ void initialize_jni_hook() { // ----------------------------------------------------------------- -ZygiskModule::ZygiskModule(int id, void *handle, void *entry) -: id(id), handle(handle), entry{entry}, api{}, mod{nullptr} { - // Make sure all pointers are null - memset(&api, 0, sizeof(api)); - api.base.impl = this; - api.base.registerModule = &ZygiskModule::RegisterModuleImpl; -} +bool rezygisk_module_register(struct rezygisk_api *api, struct rezygisk_abi *module) { + LOGD("Registering module with API version %ld", module->api_version); -bool ZygiskModule::RegisterModuleImpl(ApiTable *api, long *module) { if (api == nullptr || module == nullptr) return false; - long api_version = *module; - // Unsupported version - if (api_version > ZYGISK_API_VERSION) + if (module->api_version > REZYGISK_API_VERSION) return false; - // Set the actual module_abi* - api->base.impl->mod = { module }; + struct rezygisk_module *m = &zygisk_modules[(size_t)api->impl]; + m->abi = *module; + m->api = *api; + + api->hook_jni_native_methods = hookJniNativeMethods; + if (module->api_version >= 4) { + api->plt_hook_register_v4 = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) { + LOGD("plt_hook_register_v4 called for dev=%lu, inode=%lu, symbol=%s, fn=%p, backup=%p", + (unsigned long)dev, (unsigned long)inode, symbol, fn, backup); + if (dev == 0 || inode == 0 || symbol == NULL || fn == NULL) { + LOGE("Invalid arguments to plt_hook_register"); - // Fill in API accordingly with module API version - if (api_version >= 1) { - api->v1.hookJniNativeMethods = hookJniNativeMethods; - api->v1.pltHookRegister = [](auto a, auto b, auto c, auto d) { - if (g_ctx) g_ctx->plt_hook_register(a, b, c, d); - }; - api->v1.pltHookExclude = [](auto a, auto b) { - if (g_ctx) g_ctx->plt_hook_exclude(a, b); - }; - api->v1.pltHookCommit = []() { return g_ctx && g_ctx->plt_hook_commit(); }; - api->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); }; - api->v1.setOption = [](ZygiskModule *m, auto opt) { m->setOption(opt); }; - } - if (api_version >= 2) { - api->v2.getModuleDir = [](ZygiskModule *m) { return m->getModuleDir(); }; - api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); }; - } - if (api_version >= 4) { - api->v4.pltHookCommit = []() { return lsplt_commit_hook(); }; - api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) { - if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr) return; + } + lsplt_register_hook(dev, inode, symbol, fn, backup); }; - api->v4.exemptFd = [](int fd) { return g_ctx && g_ctx->exempt_fd(fd); }; + api->exempt_fd = [](int fd) { + LOGD("exempt_fd called for fd=%d", fd); + if (g_ctx) g_ctx->exempt_fd(fd); + }; + api->plt_hook_commit = []() { + LOGD("plt_hook_commit called"); + return lsplt_commit_hook(); + }; + } else { + api->plt_hook_register = [](const char *regex, const char *symbol, void *fn, void **backup) { + if (g_ctx) g_ctx->plt_hook_register(regex, symbol, fn, backup); + }; + api->plt_hook_exclude = [](const char *regex, const char *symbol) { + if (g_ctx) g_ctx->plt_hook_exclude(regex, symbol); + }; + api->plt_hook_commit = []() { + return g_ctx && g_ctx->plt_hook_commit(); + }; + } + api->connect_companion = [](void *id) { + LOGD("connect_companion called for id=%p", id); + if ((size_t)id >= zygisk_module_length) { + LOGE("Invalid module id %zu", (size_t)id); + + return -1; + } + + return rezygiskd_connect_companion((size_t)id); + }; + api->set_option = [](void *id, enum rezygisk_options opt) { + if (!g_ctx) return; + + LOGD("set_option called for id=%p, opt=%d", id, opt); + + if ((size_t)id >= zygisk_module_length) { + LOGE("Invalid module id %zu", (size_t)id); + + return; + } + + switch (opt) { + case FORCE_DENYLIST_UNMOUNT: { + g_ctx->flags[DO_REVERT_UNMOUNT] = true; + + break; + } + case DLCLOSE_MODULE_LIBRARY: { + struct rezygisk_module *m = &zygisk_modules[(size_t)id]; + m->unload = true; + + break; + } + } + }; + if (module->api_version >= 2) { + api->get_module_dir = [](void *id) { + LOGD("get_module_dir called for id=%p", id); + if ((size_t)id >= zygisk_module_length) { + LOGE("Invalid module id %zu", (size_t)id); + + return -1; + } + + return rezygiskd_get_module_dir((size_t)id); + }; + api->get_flags = []() { + LOGD("get_flags called"); + return g_ctx ? (g_ctx->info_flags & ~PRIVATE_MASK) : 0; + }; } return true; @@ -524,49 +587,6 @@ bool ZygiskContext::plt_hook_commit() { return lsplt_commit_hook(); } - -bool ZygiskModule::valid() const { - if (mod.api_version == nullptr) - return false; - switch (*mod.api_version) { - case 4: - case 3: - case 2: - case 1: - return mod.v1->impl && mod.v1->preAppSpecialize && mod.v1->postAppSpecialize && - mod.v1->preServerSpecialize && mod.v1->postServerSpecialize; - default: - return false; - } -} - -/* Zygisksu changed: Use own zygiskd */ -int ZygiskModule::connectCompanion() const { - return rezygiskd_connect_companion(id); -} - -/* Zygisksu changed: Use own zygiskd */ -int ZygiskModule::getModuleDir() const { - return rezygiskd_get_module_dir(id); -} - -void ZygiskModule::setOption(zygisk::Option opt) { - if (g_ctx == nullptr) - return; - switch (opt) { - case zygisk::FORCE_DENYLIST_UNMOUNT: - g_ctx->flags[DO_REVERT_UNMOUNT] = true; - break; - case zygisk::DLCLOSE_MODULE_LIBRARY: - unload = true; - break; - } -} - -uint32_t ZygiskModule::getFlags() { - return g_ctx ? (g_ctx->info_flags & ~PRIVATE_MASK) : 0; -} - // ----------------------------------------------------------------- int sigmask(int how, int signum) { @@ -630,8 +650,14 @@ void ZygiskContext::sanitize_fds() { allowed_fds[fd] = true; } } + + jintArray old_array = *args.app->fds_to_ignore; + *args.app->fds_to_ignore = array; flags[SKIP_FD_SANITIZATION] = true; + + env->DeleteLocalRef(old_array); + return array; }; @@ -692,6 +718,15 @@ bool ZygiskContext::load_modules_only() { return false; } + zygisk_modules = (struct rezygisk_module *)malloc(ms.modules_count * sizeof(struct rezygisk_module)); + if (!zygisk_modules) { + LOGE("Failed to allocate memory for modules"); + + free_modules(&ms); + + return false; + } + for (size_t i = 0; i < ms.modules_count; i++) { char *lib_path = ms.modules[i]; @@ -711,7 +746,18 @@ bool ZygiskContext::load_modules_only() { continue; } - modules.emplace_back(i, handle, entry); + zygisk_modules[zygisk_module_length].api.register_module = rezygisk_module_register; + zygisk_modules[zygisk_module_length].api.impl = (void *)zygisk_module_length; + + zygisk_modules[zygisk_module_length].handle = handle; + zygisk_modules[zygisk_module_length].zygisk_module_entry = (void (*)(void *, void *))entry; + + zygisk_modules[zygisk_module_length].unload = false; + + zygisk_module_length++; + + /* INFO: The module will call register module function, so by then, it must be fully registered. */ + rezygisk_module_call_on_load(&zygisk_modules[zygisk_module_length - 1], env); } free_modules(&ms); @@ -721,11 +767,9 @@ bool ZygiskContext::load_modules_only() { /* Zygisksu changed: Load module fds */ void ZygiskContext::run_modules_pre() { - for (auto &m : modules) { - m.onLoad(env); - - if (flags[APP_SPECIALIZE]) m.preAppSpecialize(args.app); - else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.preServerSpecialize(args.server); + for (size_t i = 0; i < zygisk_module_length; i++) { + if (flags[APP_SPECIALIZE]) rezygisk_module_call_pre_app_specialize(&zygisk_modules[i], args.app); + else if (flags[SERVER_FORK_AND_SPECIALIZE]) rezygisk_module_call_pre_server_specialize(&zygisk_modules[i], args.server); } } @@ -733,25 +777,29 @@ void ZygiskContext::run_modules_post() { flags[POST_SPECIALIZE] = true; size_t modules_unloaded = 0; - for (const auto &m : modules) { - if (flags[APP_SPECIALIZE]) m.postAppSpecialize(args.app); - else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.postServerSpecialize(args.server); + for (size_t i = 0; i < zygisk_module_length; i++) { + struct rezygisk_module *m = &zygisk_modules[i]; + + if (flags[APP_SPECIALIZE]) rezygisk_module_call_post_app_specialize(m, args.app); + else if (flags[SERVER_FORK_AND_SPECIALIZE]) rezygisk_module_call_post_server_specialize(m, args.server); /* INFO: If module is unloaded by dlclose, there's no need to hide it from soinfo manually. */ - if (m.tryUnload()) modules_unloaded++; - else { - bool has_dropped = solist_drop_so_path(m.getEntry(), false); + if (m->unload && dlclose(m->handle) == 0) modules_unloaded++; + else if (m->unload) { + PLOGE("Failed to unload module %zu", i); + } else { + bool has_dropped = solist_drop_so_path((void *)m->zygisk_module_entry, false); if (!has_dropped) continue; - LOGD("Dropped solist record for %p", m.getEntry()); + LOGD("Dropped solist record for %p", (void *)m->zygisk_module_entry); } } - if (modules.size() > 0) { - LOGD("Modules unloaded: %zu/%zu", modules_unloaded, modules.size()); + if (zygisk_module_length > 0) { + LOGD("Modules unloaded: %zu/%zu", modules_unloaded, zygisk_module_length); - solist_reset_counters(modules.size(), modules_unloaded); + solist_reset_counters(zygisk_module_length, modules_unloaded); LOGD("Returned global counters to their original values"); } @@ -773,13 +821,13 @@ void ZygiskContext::app_specialize_pre() { and even if so, it should not impact in detections, performance or any area. */ - uid_t uid = args.app->uid; + uid_t uid = *args.app->uid; if (IS_ISOLATED_SERVICE(uid) && args.app->app_data_dir) { /* INFO: If the app is an isolated service, we use the UID of the app's process data directory, which is the UID of the app itself, which root implementations actually use. */ - const char *data_dir = env->GetStringUTFChars(args.app->app_data_dir, NULL); + const char *data_dir = env->GetStringUTFChars(*args.app->app_data_dir, NULL); if (!data_dir) { LOGE("Failed to get app data directory"); @@ -790,7 +838,7 @@ void ZygiskContext::app_specialize_pre() { if (stat(data_dir, &st) == -1) { PLOGE("Failed to stat app data directory [%s]", data_dir); - env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir); + env->ReleaseStringUTFChars(*args.app->app_data_dir, data_dir); return; } @@ -799,7 +847,7 @@ void ZygiskContext::app_specialize_pre() { LOGD("Isolated service being related to UID %d, app data dir: %s", uid, data_dir); - env->ReleaseStringUTFChars(args.app->app_data_dir, data_dir); + env->ReleaseStringUTFChars(*args.app->app_data_dir, data_dir); } info_flags = rezygiskd_get_process_flags(uid, (const char *const)process); @@ -869,7 +917,7 @@ void ZygiskContext::app_specialize_post() { run_modules_post(); // Cleanups - env->ReleaseStringUTFChars(args.app->nice_name, process); + env->ReleaseStringUTFChars(*args.app->nice_name, process); g_ctx = nullptr; } @@ -885,7 +933,7 @@ bool ZygiskContext::exempt_fd(int fd) { // ----------------------------------------------------------------- void ZygiskContext::nativeSpecializeAppProcess_pre() { - process = env->GetStringUTFChars(args.app->nice_name, nullptr); + process = env->GetStringUTFChars(*args.app->nice_name, nullptr); LOGV("pre specialize [%s]", process); // App specialize does not check FD flags[SKIP_FD_SANITIZATION] = true; @@ -926,7 +974,7 @@ void ZygiskContext::nativeForkSystemServer_post() { } void ZygiskContext::nativeForkAndSpecialize_pre() { - process = env->GetStringUTFChars(args.app->nice_name, nullptr); + process = env->GetStringUTFChars(*args.app->nice_name, nullptr); LOGV("pre forkAndSpecialize [%s]", process); flags[APP_FORK_AND_SPECIALIZE] = true; @@ -960,19 +1008,22 @@ ZygiskContext::~ZygiskContext() { // Unhook JNI methods for (const auto &[clz, methods] : *jni_hook_list) { - if (!methods.empty() && env->RegisterNatives( - env->FindClass(clz.data()), methods.data(), - static_cast(methods.size())) != 0) { - LOGE("Failed to restore JNI hook of class [%s]", clz.data()); - should_unmap_zygisk = false; + jclass jc = env->FindClass(clz.data()); + if (jc) { + if (!methods.empty() && env->RegisterNatives(jc, methods.data(), + static_cast(methods.size())) != 0) { + LOGE("Failed to restore JNI hook of class [%s]", clz.data()); + should_unmap_zygisk = false; + } + env->DeleteLocalRef(jc); } } delete jni_hook_list; jni_hook_list = nullptr; // Strip out all API function pointers - for (auto &m : modules) { - m.clearApi(); + for (size_t i = 0; i < zygisk_module_length; i++) { + memset(&zygisk_modules[i], 0, sizeof(zygisk_modules[i])); } enable_unloader = true; diff --git a/loader/src/injector/jni_helper.hpp b/loader/src/injector/jni_helper.hpp deleted file mode 100644 index fef32b9..0000000 --- a/loader/src/injector/jni_helper.hpp +++ /dev/null @@ -1,1116 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winvalid-partial-specialization" -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" - -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &) = delete; \ - void operator=(const TypeName &) = delete - -namespace lsplant { - template class> - struct is_instance : public std::false_type {}; - - template class U> - struct is_instance, U> : public std::true_type {}; - - template class U> - inline constexpr bool is_instance_v = is_instance::value; - - template - concept JObject = std::is_base_of_v, std::remove_pointer_t>; - - template - class ScopedLocalRef { - public: - using BaseType [[maybe_unused]] = T; - - ScopedLocalRef(JNIEnv *env, T local_ref) : env_(env), local_ref_(nullptr) { reset(local_ref); } - - ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, s.release()) {} - - template - ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} - - explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} - - ~ScopedLocalRef() { reset(); } - - void reset(T ptr = nullptr) { - if (ptr != local_ref_) { - if (local_ref_ != nullptr) { - env_->DeleteLocalRef(local_ref_); - } - local_ref_ = ptr; - } - } - - [[nodiscard]] T release() { - T localRef = local_ref_; - local_ref_ = nullptr; - return localRef; - } - - T get() const { return local_ref_; } - - operator T() const { return local_ref_; } - - // We do not expose an empty constructor as it can easily lead to errors - // using common idioms, e.g.: - // ScopedLocalRef<...> ref; - // ref.reset(...); - // Move assignment operator. - ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { - reset(s.release()); - env_ = s.env_; - return *this; - } - - operator bool() const { return local_ref_; } - - template - friend class ScopedLocalRef; - - friend class JUTFString; - - private: - JNIEnv *env_; - T local_ref_; - DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); - }; - - template - concept JArray = std::is_base_of_v, std::remove_pointer_t>; - - template - class ScopedLocalRef; - - class JNIScopeFrame { - JNIEnv *env_; - - DISALLOW_COPY_AND_ASSIGN(JNIScopeFrame); - - public: - JNIScopeFrame(JNIEnv *env, jint size) : env_(env) { env_->PushLocalFrame(size); } - - ~JNIScopeFrame() { env_->PopLocalFrame(nullptr); } - }; - - class JNIMonitor { - JNIEnv *env_; - jobject obj_; - - DISALLOW_COPY_AND_ASSIGN(JNIMonitor); - - public: - JNIMonitor(JNIEnv *env, jobject obj) : env_(env), obj_(obj) { env_->MonitorEnter(obj_); } - - ~JNIMonitor() { env_->MonitorExit(obj_); } - }; - - template - concept ScopeOrRaw = std::is_convertible_v || - (is_instance_v, ScopedLocalRef> - &&std::is_convertible_v::BaseType, U>); - - template - concept ScopeOrClass = ScopeOrRaw; - - template - concept ScopeOrObject = ScopeOrRaw; - - inline ScopedLocalRef ClearException(JNIEnv *env) { - if (auto exception = env->ExceptionOccurred()) { - env->ExceptionClear(); - static jclass log = (jclass)env->NewGlobalRef(env->FindClass("android/util/Log")); - static jmethodID toString = env->GetStaticMethodID( - log, "getStackTraceString", "(Ljava/lang/Throwable;)Ljava/lang/String;"); - auto str = (jstring)env->CallStaticObjectMethod(log, toString, exception); - env->DeleteLocalRef(exception); - return {env, str}; - } - return {env, nullptr}; - } - - template - [[maybe_unused]] inline auto UnwrapScope(T &&x) { - if constexpr (std::is_same_v, std::string_view>) - return x.data(); - else if constexpr (is_instance_v, ScopedLocalRef>) - return x.get(); - else - return std::forward(x); - } - - template - [[maybe_unused]] inline auto WrapScope(JNIEnv *env, T &&x) { - if constexpr (std::is_convertible_v) { - return ScopedLocalRef(env, std::forward(x)); - } else - return x; - } - - template - [[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x, - std::index_sequence) { - return std::make_tuple(WrapScope(env, std::forward(std::get(x)))...); - } - - template - [[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple &&x) { - return WrapScope(env, std::forward>(x), - std::make_index_sequence()); - } - - inline auto JNI_NewStringUTF(JNIEnv *env, std::string_view sv) { - return ScopedLocalRef(env, env->NewStringUTF(sv.data())); - } - - class JUTFString { - public: - inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {} - - inline JUTFString(const ScopedLocalRef &jstr) - : JUTFString(jstr.env_, jstr.local_ref_, nullptr) {} - - inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) - : env_(env), jstr_(jstr) { - if (env_ && jstr_) - cstr_ = env_->GetStringUTFChars(jstr, nullptr); - else - cstr_ = default_cstr; - } - - inline operator const char *() const { return cstr_; } - - inline operator const std::string() const { return cstr_; } - - inline operator const bool() const { return cstr_ != nullptr; } - - inline auto get() const { return cstr_; } - - inline ~JUTFString() { - if (env_ && jstr_) env_->ReleaseStringUTFChars(jstr_, cstr_); - } - - JUTFString(JUTFString &&other) - : env_(std::move(other.env_)), - jstr_(std::move(other.jstr_)), - cstr_(std::move(other.cstr_)) { - other.cstr_ = nullptr; - } - - JUTFString &operator=(JUTFString &&other) { - if (&other != this) { - env_ = std::move(other.env_); - jstr_ = std::move(other.jstr_); - cstr_ = std::move(other.cstr_); - other.cstr_ = nullptr; - } - return *this; - } - - private: - JNIEnv *env_; - jstring jstr_; - const char *cstr_; - - JUTFString(const JUTFString &) = delete; - - JUTFString &operator=(const JUTFString &) = delete; - }; - - template - requires(std::is_function_v) - [[maybe_unused]] inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&...args) { - struct finally { - finally(JNIEnv *env) : env_(env) {} - - ~finally() { - if (auto exception = ClearException(env_)) { - __android_log_print(ANDROID_LOG_ERROR, -#ifdef LOG_TAG - LOG_TAG, -#else - "JNIHelper", -#endif - "%s", JUTFString(env_, exception.get()).get()); - } - } - - JNIEnv *env_; - } _(env); - - if constexpr (!std::is_same_v(args)))...>>) - return WrapScope(env, (env->*f)(UnwrapScope(std::forward(args))...)); - else - (env->*f)(UnwrapScope(std::forward(args))...); - } - -// functions to class - - [[maybe_unused]] inline auto JNI_FindClass(JNIEnv *env, std::string_view name) { - return JNI_SafeInvoke(env, &JNIEnv::FindClass, name); - } - - template - [[maybe_unused]] inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) { - return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj); - } - -// functions to field - - template - [[maybe_unused]] inline auto JNI_GetFieldID(JNIEnv *env, Class &&clazz, std::string_view name, - std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, std::forward(clazz), name, sig); - } - -// getters - - template - [[maybe_unused]] inline auto JNI_GetObjectField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetBooleanField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetBooleanField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetByteField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetByteField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetCharField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetCharField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetShortField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetShortField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetIntField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetIntField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetLongField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetLongField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetFloatField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetFloatField, std::forward(obj), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetDoubleField(JNIEnv *env, Object &&obj, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetDoubleField, std::forward(obj), fieldId); - } - -// setters - - template - [[maybe_unused]] inline auto JNI_SetObjectField(JNIEnv *env, Object &&obj, jfieldID fieldId, - const Value &value) { - return JNI_SafeInvoke(env, &JNIEnv::SetObjectField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetBooleanField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jboolean value) { - return JNI_SafeInvoke(env, &JNIEnv::SetBooleanField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetByteField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jbyte value) { - return JNI_SafeInvoke(env, &JNIEnv::SetByteField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetCharField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jchar value) { - return JNI_SafeInvoke(env, &JNIEnv::SetCharField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetShortField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jshort value) { - return JNI_SafeInvoke(env, &JNIEnv::SetShortField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetIntField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jint value) { - return JNI_SafeInvoke(env, &JNIEnv::SetIntField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetLongField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jlong value) { - return JNI_SafeInvoke(env, &JNIEnv::SetLongField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetFloatField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jfloat value) { - return JNI_SafeInvoke(env, &JNIEnv::SetFloatField, std::forward(obj), fieldId, value); - } - - template - [[maybe_unused]] inline auto JNI_SetDoubleField(JNIEnv *env, Object &&obj, jfieldID fieldId, - jdouble value) { - return JNI_SafeInvoke(env, &JNIEnv::SetDoubleField, std::forward(obj), fieldId, value); - } - -// functions to static field - - template - [[maybe_unused]] inline auto JNI_GetStaticFieldID(JNIEnv *env, Class &&clazz, std::string_view name, - std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, std::forward(clazz), name, sig); - } - -// getters - - template - [[maybe_unused]] inline auto JNI_GetStaticObjectField(JNIEnv *env, Class &&clazz, - jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticBooleanField(JNIEnv *env, Class &&clazz, - jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticBooleanField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticByteField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticByteField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticCharField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticCharField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticShortField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticShortField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticIntField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticIntField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticLongField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticLongField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticFloatField(JNIEnv *env, Class &&clazz, jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticFloatField, std::forward(clazz), fieldId); - } - - template - [[maybe_unused]] inline auto JNI_GetStaticDoubleField(JNIEnv *env, Class &&clazz, - jfieldID fieldId) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticDoubleField, std::forward(clazz), fieldId); - } - -// setters - - template - [[maybe_unused]] inline auto JNI_SetStaticObjectField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - const Object &value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticObjectField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticBooleanField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jboolean value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticBooleanField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticByteField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jbyte value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticByteField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticCharField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jchar value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticCharField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticShortField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jshort value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticShortField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticIntField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jint value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticIntField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticLongField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jlong value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticLongField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticFloatField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jfloat value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticFloatField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_SetStaticDoubleField(JNIEnv *env, Class &&clazz, jfieldID fieldId, - jdouble value) { - return JNI_SafeInvoke(env, &JNIEnv::SetStaticDoubleField, std::forward(clazz), fieldId, - value); - } - - template - [[maybe_unused]] inline auto JNI_ToReflectedMethod(JNIEnv *env, Class &&clazz, jmethodID method, - jboolean isStatic = JNI_FALSE) { - return JNI_SafeInvoke(env, &JNIEnv::ToReflectedMethod, std::forward(clazz), method, - isStatic); - } - -// functions to method - -// virtual methods - - template - [[maybe_unused]] inline auto JNI_GetMethodID(JNIEnv *env, Class &&clazz, std::string_view name, - std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, std::forward(clazz), name, sig); - } - - template - [[maybe_unused]] inline auto JNI_CallVoidMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallObjectMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallBooleanMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallByteMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallByteMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCharMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallCharMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallShortMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallShortMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallIntMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallIntMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallLongMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallLongMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallFloatMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallFloatMethod, std::forward(obj), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallDoubleMethod(JNIEnv *env, Object &&obj, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallDoubleMethod, std::forward(obj), method, - std::forward(args)...); - } - -// static methods - - template - [[maybe_unused]] inline auto JNI_GetStaticMethodID(JNIEnv *env, Class &&clazz, - std::string_view name, std::string_view sig) { - return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, std::forward(clazz), name, sig); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticVoidMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticObjectMethod(JNIEnv *env, Class &&clazz, - jmethodID method, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, Class &&clazz, - jmethodID method, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticBooleanMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticByteMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticByteMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticCharMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticCharMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticShortMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticShortMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticIntMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticLongMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticLongMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticFloatMethod(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticFloatMethod, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallStaticDoubleMethod(JNIEnv *env, Class &&clazz, - jmethodID method, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallStaticDoubleMethod, std::forward(clazz), method, - std::forward(args)...); - } - -// non-vritual methods - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualVoidMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualVoidMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualObjectMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualObjectMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualBooleanMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualBooleanMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualByteMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualByteMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualCharMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualCharMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualShortMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualShortMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualIntMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualIntMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualLongMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualLongMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualFloatMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualFloatMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_CallCallNonvirtualDoubleMethod(JNIEnv *env, Object &&obj, - Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::CallNonvirtualDoubleMethod, std::forward(obj), - std::forward(clazz), method, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, Class &&clazz, jmethodID method, - Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::NewObject, std::forward(clazz), method, - std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, Args &&...args) { - return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, std::forward(args)...); - } - - template - [[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, Class &&clazz, - const JNINativeMethod *methods, jint size) { - return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, std::forward(clazz), methods, size); - } - - template - [[maybe_unused]] inline auto JNI_IsInstanceOf(JNIEnv *env, Object &&obj, Class &&clazz) { - return JNI_SafeInvoke(env, &JNIEnv::IsInstanceOf, std::forward(obj), - std::forward(clazz)); - } - - template - [[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) { - return (decltype(UnwrapScope(std::forward(x))))env->NewGlobalRef( - UnwrapScope(std::forward(x))); - } - - template - [[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef &&x) requires( - std::is_convertible_v) { - return ScopedLocalRef(std::move(x)); - } - - [[maybe_unused]] inline auto JNI_NewDirectByteBuffer(JNIEnv *env, void *address, jlong capacity) { - return JNI_SafeInvoke(env, &JNIEnv::NewDirectByteBuffer, address, capacity); - } - - template - struct JArrayUnderlyingTypeHelper; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = ScopedLocalRef; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jboolean; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jbyte; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jchar; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jshort; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jint; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jlong; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jfloat; - }; - - template <> - struct JArrayUnderlyingTypeHelper { - using Type = jdouble; - }; - - template - using JArrayUnderlyingType = typename JArrayUnderlyingTypeHelper::Type; - - template - class ScopedLocalRef { - ScopedLocalRef(JNIEnv *env, T local_ref, size_t size, JArrayUnderlyingType *elements, - bool modified) noexcept - : env_(env), local_ref_(local_ref), size_(size), elements_(elements), modified_(modified) {} - - public: - class Iterator { - friend class ScopedLocalRef; - Iterator(JArrayUnderlyingType *e) : e_(e) {} - JArrayUnderlyingType *e_; - - public: - auto &operator*() { return *e_; } - auto *operator->() { return e_; } - Iterator &operator++() { return ++e_, *this; } - Iterator &operator--() { return --e_, *this; } - Iterator operator++(int) { return Iterator(e_++); } - Iterator operator--(int) { return Iterator(e_--); } - bool operator==(const Iterator &other) const { return other.e_ == e_; } - bool operator!=(const Iterator &other) const { return other.e_ != e_; } - }; - - class ConstIterator { - friend class ScopedLocalRef; - ConstIterator(const JArrayUnderlyingType *e) : e_(e) {} - const JArrayUnderlyingType *e_; - - public: - const auto &operator*() { return *e_; } - const auto *operator->() { return e_; } - ConstIterator &operator++() { return ++e_, *this; } - ConstIterator &operator--() { return --e_, *this; } - ConstIterator operator++(int) { return ConstIterator(e_++); } - ConstIterator operator--(int) { return ConstIterator(e_--); } - bool operator==(const ConstIterator &other) const { return other.e_ == e_; } - bool operator!=(const ConstIterator &other) const { return other.e_ != e_; } - }; - - auto begin() { - modified_ = true; - return Iterator(elements_); - } - - auto end() { - modified_ = true; - return Iterator(elements_ + size_); - } - - auto begin() const { return ConstIterator(elements_); } - - auto end() const { return ConstIterator(elements_ + size_); } - - auto cbegin() const { return ConstIterator(elements_); } - - auto cend() const { return ConstIterator(elements_ + size_); } - - using BaseType [[maybe_unused]] = T; - - ScopedLocalRef(JNIEnv *env, T local_ref) noexcept : env_(env), local_ref_(nullptr) { - reset(local_ref); - } - - ScopedLocalRef(ScopedLocalRef &&s) noexcept - : ScopedLocalRef(s.env_, s.local_ref_, s.size_, s.elements_, s.modified_) { - s.local_ref_ = nullptr; - s.size_ = 0; - s.elements_ = nullptr; - s.modified_ = false; - } - - template - ScopedLocalRef(ScopedLocalRef &&s) noexcept : ScopedLocalRef(s.env_, (T)s.release()) {} - - explicit ScopedLocalRef(JNIEnv *env) noexcept : ScopedLocalRef(env, T{nullptr}) {} - - ~ScopedLocalRef() { env_->DeleteLocalRef(release()); } - - void reset(T ptr = nullptr) { - if (ptr != local_ref_) { - if (local_ref_ != nullptr) { - ReleaseElements(modified_ ? 0 : JNI_ABORT); - env_->DeleteLocalRef(local_ref_); - if constexpr (std::is_same_v) { - for (size_t i = 0; i < size_; ++i) { - elements_[i].~ScopedLocalRef(); - } - operator delete[](elements_); - } - elements_ = nullptr; - } - local_ref_ = ptr; - size_ = local_ref_ ? env_->GetArrayLength(local_ref_) : 0; - if (!local_ref_) return; - if constexpr (std::is_same_v) { - elements_ = static_cast *>(operator new[]( - sizeof(ScopedLocalRef) * size_)); - for (size_t i = 0; i < size_; ++i) { - new (&elements_[i]) ScopedLocalRef( - JNI_SafeInvoke(env_, &JNIEnv::GetObjectArrayElement, local_ref_, i)); - } - } else if constexpr (std::is_same_v) { - elements_ = env_->GetBooleanArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetByteArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetCharArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetShortArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetIntArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetLongArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetFloatArrayElements(local_ref_, nullptr); - } else if constexpr (std::is_same_v) { - elements_ = env_->GetDoubleArrayElements(local_ref_, nullptr); - } - } - } - - [[nodiscard]] T release() { - T localRef = local_ref_; - size_ = 0; - local_ref_ = nullptr; - ReleaseElements(modified_ ? 0 : JNI_ABORT); - if constexpr (std::is_same_v) { - for (size_t i = 0; i < size_; ++i) { - elements_[i].~ScopedLocalRef(); - } - operator delete[](elements_); - } - elements_ = nullptr; - return localRef; - } - - T get() const { return local_ref_; } - - explicit operator T() const { return local_ref_; } - - JArrayUnderlyingType &operator[](size_t index) { - modified_ = true; - return elements_[index]; - } - - const JArrayUnderlyingType &operator[](size_t index) const { return elements_[index]; } - - void commit() { - ReleaseElements(JNI_COMMIT); - modified_ = false; - } - - // We do not expose an empty constructor as it can easily lead to errors - // using common idioms, e.g.: - // ScopedLocalRef<...> ref; - // ref.reset(...); - // Move assignment operator. - ScopedLocalRef &operator=(ScopedLocalRef &&s) noexcept { - env_ = s.env_; - local_ref_ = s.local_ref_; - size_ = s.size_; - elements_ = s.elements_; - modified_ = s.modified_; - s.elements_ = nullptr; - s.size_ = 0; - s.modified_ = false; - s.local_ref_ = nullptr; - return *this; - } - - size_t size() const { return size_; } - - operator bool() const { return local_ref_; } - - template - friend class ScopedLocalRef; - - friend class JUTFString; - - private: - void ReleaseElements(jint mode) { - if (!local_ref_ || !elements_) return; - if constexpr (std::is_same_v) { - for (size_t i = 0; i < size_; ++i) { - JNI_SafeInvoke(env_, &JNIEnv::SetObjectArrayElement, local_ref_, i, elements_[i]); - } - } else if constexpr (std::is_same_v) { - env_->ReleaseBooleanArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseByteArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseCharArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseShortArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseIntArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseLongArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseFloatArrayElements(local_ref_, elements_, mode); - } else if constexpr (std::is_same_v) { - env_->ReleaseDoubleArrayElements(local_ref_, elements_, mode); - } - } - - JNIEnv *env_; - T local_ref_; - size_t size_; - JArrayUnderlyingType *elements_{nullptr}; - bool modified_ = false; - DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); - }; - -// functions to array - - template Array> - [[maybe_unused]] inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) { - return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array); - } - -// newers - - template - [[maybe_unused]] inline auto JNI_NewObjectArray(JNIEnv *env, jsize len, Class &&clazz, - const Object &init) { - return JNI_SafeInvoke(env, &JNIEnv::NewObjectArray, len, std::forward(clazz), init); - } - - [[maybe_unused]] inline auto JNI_NewBooleanArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewBooleanArray, len); - } - - [[maybe_unused]] inline auto JNI_NewByteArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewByteArray, len); - } - - [[maybe_unused]] inline auto JNI_NewCharArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewCharArray, len); - } - - [[maybe_unused]] inline auto JNI_NewShortArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewShortArray, len); - } - - [[maybe_unused]] inline auto JNI_NewIntArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewIntArray, len); - } - - [[maybe_unused]] inline auto JNI_NewLongArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewLongArray, len); - } - - [[maybe_unused]] inline auto JNI_NewFloatArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewFloatArray, len); - } - - [[maybe_unused]] inline auto JNI_NewDoubleArray(JNIEnv *env, jsize len) { - return JNI_SafeInvoke(env, &JNIEnv::NewDoubleArray, len); - } - - template - [[maybe_unused]] inline auto JNI_GetObjectFieldOf(JNIEnv *env, Object &&object, - std::string_view field_name, - std::string_view field_class) { - auto &&o = std::forward(object); - return JNI_GetObjectField( - env, o, JNI_GetFieldID(env, JNI_GetObjectClass(env, o), field_name, field_class)); - } - -} // namespace lsplant - -#undef DISALLOW_COPY_AND_ASSIGN - -#pragma clang diagnostic pop diff --git a/loader/src/injector/jni_hooks.hpp b/loader/src/injector/jni_hooks.hpp index 177b139..a8d98fc 100644 --- a/loader/src/injector/jni_hooks.hpp +++ b/loader/src/injector/jni_hooks.hpp @@ -4,7 +4,7 @@ namespace { void *nativeForkAndSpecialize_orig = nullptr; [[clang::no_stack_protector]] jint nativeForkAndSpecialize_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); ZygiskContext ctx(env, &args); ctx.nativeForkAndSpecialize_pre(); reinterpret_cast(nativeForkAndSpecialize_orig)( @@ -14,7 +14,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; ZygiskContext ctx(env, &args); ctx.nativeForkAndSpecialize_pre(); @@ -25,7 +25,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_p(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; ZygiskContext ctx(env, &args); @@ -37,7 +37,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_q_alt(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; @@ -50,7 +50,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_r(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; @@ -67,7 +67,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; @@ -85,7 +85,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_m(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); ZygiskContext ctx(env, &args); ctx.nativeForkAndSpecialize_pre(); reinterpret_cast(nativeForkAndSpecialize_orig)( @@ -95,7 +95,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_n(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); ZygiskContext ctx(env, &args); ctx.nativeForkAndSpecialize_pre(); reinterpret_cast(nativeForkAndSpecialize_orig)( @@ -105,7 +105,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; ZygiskContext ctx(env, &args); ctx.nativeForkAndSpecialize_pre(); @@ -116,7 +116,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_p(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; ZygiskContext ctx(env, &args); @@ -128,7 +128,7 @@ void *nativeForkAndSpecialize_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkAndSpecialize_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jlongArray _13) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.fds_to_ignore = &fds_to_ignore; args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; @@ -205,7 +205,7 @@ std::array nativeForkAndSpecialize_methods = { void *nativeSpecializeAppProcess_orig = nullptr; [[clang::no_stack_protector]] void nativeSpecializeAppProcess_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; ZygiskContext ctx(env, &args); ctx.nativeSpecializeAppProcess_pre(); @@ -215,7 +215,7 @@ void *nativeSpecializeAppProcess_orig = nullptr; ctx.nativeSpecializeAppProcess_post(); } [[clang::no_stack_protector]] void nativeSpecializeAppProcess_q_alt(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; ZygiskContext ctx(env, &args); @@ -226,7 +226,7 @@ void *nativeSpecializeAppProcess_orig = nullptr; ctx.nativeSpecializeAppProcess_post(); } [[clang::no_stack_protector]] void nativeSpecializeAppProcess_r(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; args.pkg_data_info_list = &pkg_data_info_list; @@ -241,7 +241,7 @@ void *nativeSpecializeAppProcess_orig = nullptr; ctx.nativeSpecializeAppProcess_post(); } [[clang::no_stack_protector]] void nativeSpecializeAppProcess_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; args.pkg_data_info_list = &pkg_data_info_list; @@ -257,7 +257,7 @@ void *nativeSpecializeAppProcess_orig = nullptr; ctx.nativeSpecializeAppProcess_post(); } [[clang::no_stack_protector]] void nativeSpecializeAppProcess_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; ZygiskContext ctx(env, &args); ctx.nativeSpecializeAppProcess_pre(); @@ -267,7 +267,7 @@ void *nativeSpecializeAppProcess_orig = nullptr; ctx.nativeSpecializeAppProcess_post(); } [[clang::no_stack_protector]] void nativeSpecializeAppProcess_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides, jlongArray _14) { - AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); + struct app_specialize_args_v5 args(&uid, &gid, &gids, &runtime_flags, &rlimits, &mount_external, &se_info, &nice_name, &instruction_set, &app_data_dir); args.is_child_zygote = &is_child_zygote; args.is_top_app = &is_top_app; args.pkg_data_info_list = &pkg_data_info_list; @@ -317,7 +317,7 @@ std::array nativeSpecializeAppProcess_methods = { void *nativeForkSystemServer_orig = nullptr; [[clang::no_stack_protector]] jint nativeForkSystemServer_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) { - ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities); + struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities); ZygiskContext ctx(env, &args); ctx.nativeForkSystemServer_pre(); reinterpret_cast(nativeForkSystemServer_orig)( @@ -327,7 +327,7 @@ void *nativeForkSystemServer_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkSystemServer_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) { - ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities); + struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities); ZygiskContext ctx(env, &args); ctx.nativeForkSystemServer_pre(); reinterpret_cast(nativeForkSystemServer_orig)( @@ -337,7 +337,7 @@ void *nativeForkSystemServer_orig = nullptr; return ctx.pid; } [[clang::no_stack_protector]] jint nativeForkSystemServer_grapheneos_u(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) { - ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities); + struct server_specialize_args_v1 args(&uid, &gid, &gids, &runtime_flags, &permitted_capabilities, &effective_capabilities); ZygiskContext ctx(env, &args); ctx.nativeForkSystemServer_pre(); reinterpret_cast(nativeForkSystemServer_orig)( diff --git a/loader/src/injector/module.h b/loader/src/injector/module.h new file mode 100644 index 0000000..c678542 --- /dev/null +++ b/loader/src/injector/module.h @@ -0,0 +1,252 @@ +#ifndef MODULE_H +#define MODULE_H + +#include "logging.h" + +#define REZYGISK_API_VERSION 5 + +enum rezygiskd_flags : uint32_t { + PROCESS_GRANTED_ROOT = (1u << 0), + PROCESS_ON_DENYLIST = (1u << 1), + + PROCESS_IS_MANAGER = (1u << 27), + PROCESS_ROOT_IS_APATCH = (1u << 28), + PROCESS_ROOT_IS_KSU = (1u << 29), + PROCESS_ROOT_IS_MAGISK = (1u << 30), + PROCESS_IS_FIRST_STARTED = (1u << 31), + + PRIVATE_MASK = PROCESS_IS_FIRST_STARTED +}; + +struct app_specialize_args_v1 { + 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; + + jboolean *is_child_zygote; + jboolean *is_top_app; + jobjectArray *pkg_data_info_list; + jobjectArray *whitelisted_data_info_list; + jboolean *mount_data_dirs; + jboolean *mount_storage_dirs; +}; + +struct app_specialize_args_v4 { + jint *uid; + jint *gid; + jintArray *gids; + jint *runtime_flags; + jobjectArray *rlimits; + jint *mount_external; + jstring *se_info; + jstring *nice_name; + jstring *instruction_set; + jstring *app_data_dir; + + jintArray *fds_to_ignore; + jboolean *is_child_zygote; + jboolean *is_top_app; + jobjectArray *pkg_data_info_list; + jobjectArray *whitelisted_data_info_list; + jboolean *mount_data_dirs; + jboolean *mount_storage_dirs; +}; + +struct app_specialize_args_v5 { + jint *uid; + jint *gid; + jintArray *gids; + jint *runtime_flags; + jobjectArray *rlimits; + jint *mount_external; + jstring *se_info; + jstring *nice_name; + jstring *instruction_set; + jstring *app_data_dir; + + jintArray *fds_to_ignore; + jboolean *is_child_zygote; + jboolean *is_top_app; + jobjectArray *pkg_data_info_list; + jobjectArray *whitelisted_data_info_list; + jboolean *mount_data_dirs; + jboolean *mount_storage_dirs; + + jboolean *mount_sysprop_overrides; +}; + +struct server_specialize_args_v1 { + jint *uid; + jint *gid; + jintArray *gids; + jint *runtime_flags; + jlong *permitted_capabilities; + jlong *effective_capabilities; +}; + +enum rezygisk_options : uint32_t { + /* INFO: Force ReZygisk to umount the root related mounts on this process. This option + will only take effect if set in pre...Specialize, as ReZygisk umounts at + that point. + + ReZygisk Umount System will not umount all root related mounts, read ReZygiskd + umount_root function in utils.c file to understand how it selects the ones + to umount. + */ + FORCE_DENYLIST_UNMOUNT = 0, + + /* INFO: Once set, ReZygisk will dlclose your library from the process, this is assured to + happen after post...Specialize, but not at a specific moment due to different + implementations. + + You should not use this option if you leave references in the process such as hooks, + which will try to execute unitialized memory. + */ + DLCLOSE_MODULE_LIBRARY = 1 +}; + +struct rezygisk_api { + void *impl; + bool (*register_module)(struct rezygisk_api *, struct rezygisk_abi *); + + void (*hook_jni_native_methods)(JNIEnv *, const char *, JNINativeMethod *, int); + union { + void (*plt_hook_register)(const char *, const char *, void *, void **); /* INFO: v3 and below */ + void (*plt_hook_register_v4)(dev_t, ino_t, const char *, void *, void **); /* INFO: v4 */ + }; + union { + void (*plt_hook_exclude)(const char *, const char *); /* INFO: v3 and below */ + void (*exempt_fd)(int); /* INFO: v4 */ + }; + bool (*plt_hook_commit)(); + int (*connect_companion)(void *); + void (*set_option)(void *, enum rezygisk_options opt); + int (*get_module_dir)(void *); + uint32_t (*get_flags)(); +}; + +struct rezygisk_abi { + long api_version; + void *impl; + + void (*pre_app_specialize)(void *, void *); + void (*post_app_specialize)(void *, const void *); + void (*pre_server_specialize)(void *, void *); + void (*post_server_specialize)(void *, const void *); +}; + +struct rezygisk_module { + struct rezygisk_abi abi; + struct rezygisk_api api; + + void *handle; + void (*zygisk_module_entry)(void *, void *); + + bool unload; +}; + +void rezygisk_module_call_on_load(struct rezygisk_module *m, void *env) { + m->zygisk_module_entry((void *)&m->api, env); +} + +void rezygisk_module_call_pre_app_specialize(struct rezygisk_module *m, struct app_specialize_args_v5 *args) { + switch (m->abi.api_version) { + case 1: + case 2: { + struct app_specialize_args_v1 versioned_args = { + .uid = args->uid, + .gid = args->gid, + .gids = args->gids, + .runtime_flags = args->runtime_flags, + .mount_external = args->mount_external, + .se_info = args->se_info, + .nice_name = args->nice_name, + .instruction_set = args->instruction_set, + .app_data_dir = args->app_data_dir, + .is_child_zygote = args->is_child_zygote, + .is_top_app = args->is_top_app, + .pkg_data_info_list = args->pkg_data_info_list, + .whitelisted_data_info_list = args->whitelisted_data_info_list, + .mount_data_dirs = args->mount_data_dirs, + .mount_storage_dirs = args->mount_storage_dirs + }; + + m->abi.pre_app_specialize(m->abi.impl, &versioned_args); + + break; + } + case 3: + case 4: { + struct app_specialize_args_v4 versioned_args; + memcpy(&versioned_args, args, sizeof(struct app_specialize_args_v4)); + + m->abi.pre_app_specialize(m->abi.impl, &versioned_args); + + break; + } + case 5: { + m->abi.pre_app_specialize(m->abi.impl, args); + + break; + } + } +} + +void rezygisk_module_call_post_app_specialize(struct rezygisk_module *m, const struct app_specialize_args_v5 *args) { + switch (m->abi.api_version) { + case 1: + case 2: { + struct app_specialize_args_v1 versioned_args = { + .uid = args->uid, + .gid = args->gid, + .gids = args->gids, + .runtime_flags = args->runtime_flags, + .mount_external = args->mount_external, + .se_info = args->se_info, + .nice_name = args->nice_name, + .instruction_set = args->instruction_set, + .app_data_dir = args->app_data_dir, + .is_child_zygote = args->is_child_zygote, + .is_top_app = args->is_top_app, + .pkg_data_info_list = args->pkg_data_info_list, + .whitelisted_data_info_list = args->whitelisted_data_info_list, + .mount_data_dirs = args->mount_data_dirs, + .mount_storage_dirs = args->mount_storage_dirs + }; + + m->abi.post_app_specialize(m->abi.impl, &versioned_args); + + break; + } + case 3: + case 4: { + struct app_specialize_args_v4 versioned_args; + memcpy(&versioned_args, args, sizeof(struct app_specialize_args_v4)); + + m->abi.post_app_specialize(m->abi.impl, &versioned_args); + + break; + } + case 5: { + m->abi.post_app_specialize(m->abi.impl, args); + + break; + } + } +} + +void rezygisk_module_call_pre_server_specialize(struct rezygisk_module *m, struct server_specialize_args_v1 *args) { + m->abi.pre_server_specialize(m->abi.impl, args); +} + +void rezygisk_module_call_post_server_specialize(struct rezygisk_module *m, const struct server_specialize_args_v1 *args) { + m->abi.post_server_specialize(m->abi.impl, args); +} + +#endif /* MODULE_H */ diff --git a/loader/src/injector/module.hpp b/loader/src/injector/module.hpp deleted file mode 100644 index b3007a8..0000000 --- a/loader/src/injector/module.hpp +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once - -#include -#include -#include "api.hpp" - -namespace { - - struct ZygiskContext; - struct ZygiskModule; - - struct AppSpecializeArgs_v1; - using AppSpecializeArgs_v2 = AppSpecializeArgs_v1; - struct AppSpecializeArgs_v3; - using AppSpecializeArgs_v4 = AppSpecializeArgs_v3; - struct AppSpecializeArgs_v5; - - struct module_abi_v1; - using module_abi_v2 = module_abi_v1; - using module_abi_v3 = module_abi_v1; - using module_abi_v4 = module_abi_v1; - using module_abi_v5 = module_abi_v1; - - struct api_abi_v1; - struct api_abi_v2; - using api_abi_v3 = api_abi_v2; - struct api_abi_v4; - using api_abi_v5 = api_abi_v4; - - union ApiTable; - - struct AppSpecializeArgs_v3 { - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jobjectArray &rlimits; - jint &mount_external; - jstring &se_info; - jstring &nice_name; - jstring &instruction_set; - jstring &app_data_dir; - - jintArray *fds_to_ignore = nullptr; - 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; - - AppSpecializeArgs_v3( - jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, - jobjectArray &rlimits, 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), rlimits(rlimits), - mount_external(mount_external), se_info(se_info), nice_name(nice_name), - instruction_set(instruction_set), app_data_dir(app_data_dir) {} - }; - - struct AppSpecializeArgs_v5 : public AppSpecializeArgs_v3 { - jboolean *mount_sysprop_overrides = nullptr; - - AppSpecializeArgs_v5( - jint &uid, jint &gid, jintArray &gids, jint &runtime_flags, - jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name, - jstring &instruction_set, jstring &app_data_dir) : AppSpecializeArgs_v3( - uid, gid, gids, runtime_flags, rlimits, mount_external, - se_info, nice_name, instruction_set, app_data_dir) {} - }; - - struct AppSpecializeArgs_v1 { - 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; - - jboolean *const is_child_zygote; - jboolean *const is_top_app; - jobjectArray *const pkg_data_info_list; - jobjectArray *const whitelisted_data_info_list; - jboolean *const mount_data_dirs; - jboolean *const mount_storage_dirs; - - AppSpecializeArgs_v1(const AppSpecializeArgs_v5 *a) : - uid(a->uid), gid(a->gid), gids(a->gids), runtime_flags(a->runtime_flags), - mount_external(a->mount_external), se_info(a->se_info), nice_name(a->nice_name), - instruction_set(a->instruction_set), app_data_dir(a->app_data_dir), - is_child_zygote(a->is_child_zygote), is_top_app(a->is_top_app), - pkg_data_info_list(a->pkg_data_info_list), - whitelisted_data_info_list(a->whitelisted_data_info_list), - mount_data_dirs(a->mount_data_dirs), mount_storage_dirs(a->mount_storage_dirs) {} - }; - - struct ServerSpecializeArgs_v1 { - jint &uid; - jint &gid; - jintArray &gids; - jint &runtime_flags; - jlong &permitted_capabilities; - jlong &effective_capabilities; - - ServerSpecializeArgs_v1( - 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) {} - }; - - struct module_abi_v1 { - long api_version; - void *impl; - void (*preAppSpecialize)(void *, void *); - void (*postAppSpecialize)(void *, const void *); - void (*preServerSpecialize)(void *, void *); - void (*postServerSpecialize)(void *, const void *); - }; - - enum : uint32_t { - PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT, - PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST, - - PROCESS_IS_MANAGER = (1u << 27), - PROCESS_ROOT_IS_APATCH = (1u << 28), - PROCESS_ROOT_IS_KSU = (1u << 29), - PROCESS_ROOT_IS_MAGISK = (1u << 30), - PROCESS_IS_FIRST_STARTED = (1u << 31), - - PRIVATE_MASK = PROCESS_IS_FIRST_STARTED - }; - - struct api_abi_base { - ZygiskModule *impl; - bool (*registerModule)(ApiTable *, long *); - }; - - struct api_abi_v1 : public api_abi_base { - /* 0 */ void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); - /* 1 */ void (*pltHookRegister)(const char *, const char *, void *, void **); - /* 2 */ void (*pltHookExclude)(const char *, const char *); - /* 3 */ bool (*pltHookCommit)(); - /* 4 */ int (*connectCompanion)(ZygiskModule *); - /* 5 */ void (*setOption)(ZygiskModule *, zygisk::Option); - }; - - struct api_abi_v2 : public api_abi_v1 { - /* 6 */ int (*getModuleDir)(ZygiskModule *); - /* 7 */ uint32_t (*getFlags)(ZygiskModule *); - }; - - struct api_abi_v4 : public api_abi_base { - /* 0 */ void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); - /* 1 */ void (*pltHookRegister)(dev_t, ino_t, const char *, void *, void **); - /* 2 */ bool (*exemptFd)(int); - /* 3 */ bool (*pltHookCommit)(); - /* 4 */ int (*connectCompanion)(ZygiskModule *); - /* 5 */ void (*setOption)(ZygiskModule *, zygisk::Option); - /* 6 */ int (*getModuleDir)(ZygiskModule *); - /* 7 */ uint32_t (*getFlags)(ZygiskModule *); - }; - - union ApiTable { - api_abi_base base; - api_abi_v1 v1; - api_abi_v2 v2; - api_abi_v4 v4; - }; - -#define call_app(method) \ -switch (*mod.api_version) { \ -case 1: \ -case 2: { \ - AppSpecializeArgs_v1 a(args); \ - mod.v1->method(mod.v1->impl, &a); \ - break; \ -} \ -case 3: \ -case 4: \ -case 5: \ - mod.v1->method(mod.v1->impl, args);\ - break; \ -} - - struct ZygiskModule { - - void onLoad(void *env) { - entry.fn(&api, env); - } - void preAppSpecialize(AppSpecializeArgs_v5 *args) const { - call_app(preAppSpecialize) - } - void postAppSpecialize(const AppSpecializeArgs_v5 *args) const { - call_app(postAppSpecialize) - } - void preServerSpecialize(ServerSpecializeArgs_v1 *args) const { - mod.v1->preServerSpecialize(mod.v1->impl, args); - } - void postServerSpecialize(const ServerSpecializeArgs_v1 *args) const { - mod.v1->postServerSpecialize(mod.v1->impl, args); - } - - bool valid() const; - int connectCompanion() const; - int getModuleDir() const; - void setOption(zygisk::Option opt); - static uint32_t getFlags(); - bool tryUnload() const { return unload && dlclose(handle) == 0; }; - void clearApi() { memset(&api, 0, sizeof(api)); } - int getId() const { return id; } - void *getEntry() const { return entry.ptr; } - - ZygiskModule(int id, void *handle, void *entry); - - static bool RegisterModuleImpl(ApiTable *api, long *module); - - private: - const int id; - bool unload = false; - - void * const handle; - union { - void * const ptr; - void (* const fn)(void *, void *); - } entry; - - ApiTable api; - - union { - long *api_version; - module_abi_v1 *v1; - } mod; - }; - -} // namespace