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.
This commit is contained in:
ThePedroo
2025-09-01 19:43:54 -03:00
parent 0f27e455e7
commit 153097f9d8
9 changed files with 556 additions and 1980 deletions

View File

@@ -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 <jni.h>
#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 <class T> 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:
//
// <address> <perms> <offset> <dev> <inode> <pathname>
// 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 <class T> 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<clazz>(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 <class T>
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"

View File

@@ -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 */

View File

@@ -1,83 +0,0 @@
#pragma once
#include "logging.h"
#include "jni_helper.hpp"
template <typename T>
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<void **>(reinterpret_cast<uintptr_t>(this) + data_offset);
}
static art::ArtMethod *FromReflectedMethod(JNIEnv *env, jobject method) {
if (art_method_field) [[likely]] {
return reinterpret_cast<art::ArtMethod *>(
JNI_GetLongField(env, method, art_method_field));
} else {
return reinterpret_cast<art::ArtMethod *>(env->FromReflectedMethod(method));
}
}
static bool Init(JNIEnv *env) {
ScopedLocalRef<jclass> 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<decltype(clazz)::BaseType, jclass>);
jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors",
"()[Ljava/lang/reflect/Constructor;");
const auto constructors =
JNI_Cast<jobjectArray>(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<uintptr_t>(second) - reinterpret_cast<uintptr_t>(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

View File

@@ -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)

View File

@@ -1,5 +1,6 @@
#include <sys/mount.h>
#include <dlfcn.h>
#include <link.h>
#include <regex.h>
#include <bitset>
#include <list>
@@ -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<tuple<dev_t, ino_t, const char *, void **>> *plt_hook_list;
map<string, vector<JNINativeMethod>> *jni_hook_list;
bool modules_loaded = false;
list<ZygiskModule> 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<void **>(&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<int>(methods.size())) != 0) {
jclass jc = env->FindClass(clz.data());
if (jc) {
if (!methods.empty() && env->RegisterNatives(jc, methods.data(),
static_cast<jint>(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;

File diff suppressed because it is too large Load Diff

View File

@@ -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<decltype(&nativeForkAndSpecialize_l)>(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<decltype(&nativeForkAndSpecialize_samsung_m)>(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<decltype(&nativeForkAndSpecialize_samsung_n)>(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<decltype(&nativeForkSystemServer_l)>(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<decltype(&nativeForkSystemServer_samsung_q)>(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<decltype(&nativeForkSystemServer_grapheneos_u)>(nativeForkSystemServer_orig)(

View File

@@ -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 */

View File

@@ -1,240 +0,0 @@
#pragma once
#include <cstring>
#include <dlfcn.h>
#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