mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-01-12 04:43:37 +00:00
Add new API exemptFd
This commit is contained in:
parent
ccf21b0992
commit
44029875a6
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#define ZYGISK_API_VERSION 3
|
#define ZYGISK_API_VERSION 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -73,12 +73,11 @@ struct ServerSpecializeArgs;
|
|||||||
class ModuleBase {
|
class ModuleBase {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// This function is called when the module is loaded into the target process.
|
// This method is called as soon as the module is loaded into the target process.
|
||||||
// A Zygisk API handle will be sent as an argument; call utility functions or interface
|
// A Zygisk API handle will be passed as an argument.
|
||||||
// with Zygisk through this handle.
|
|
||||||
virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
|
virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
|
||||||
|
|
||||||
// This function is called before the app process is specialized.
|
// 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
|
// 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
|
// is applied. This means that the process does not have any sandbox restrictions and
|
||||||
// still runs with the same privilege of zygote.
|
// still runs with the same privilege of zygote.
|
||||||
@ -92,16 +91,16 @@ public:
|
|||||||
// See Api::connectCompanion() for more info.
|
// See Api::connectCompanion() for more info.
|
||||||
virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
|
virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
|
||||||
|
|
||||||
// This function is called after the app process is specialized.
|
// This method is called after the app process is specialized.
|
||||||
// At this point, the process has all sandbox restrictions enabled for this application.
|
// At this point, the process has all sandbox restrictions enabled for this application.
|
||||||
// This means that this function runs as the same privilege of the app's own code.
|
// This means that this method runs as the same privilege of the app's own code.
|
||||||
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
|
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
|
||||||
|
|
||||||
// This function is called before the system server process is specialized.
|
// This method is called before the system server process is specialized.
|
||||||
// See preAppSpecialize(args) for more info.
|
// See preAppSpecialize(args) for more info.
|
||||||
virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
|
virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
|
||||||
|
|
||||||
// This function is called after the system server process is specialized.
|
// This method is called after the system server process is specialized.
|
||||||
// At this point, the process runs with the privilege of system_server.
|
// At this point, the process runs with the privilege of system_server.
|
||||||
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
|
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
|
||||||
};
|
};
|
||||||
@ -173,21 +172,21 @@ enum StateFlag : uint32_t {
|
|||||||
PROCESS_ON_DENYLIST = (1u << 1),
|
PROCESS_ON_DENYLIST = (1u << 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
// All API functions will stop working after post[XXX]Specialize as Zygisk will be unloaded
|
// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded
|
||||||
// from the specialized process afterwards.
|
// from the specialized process afterwards.
|
||||||
struct Api {
|
struct Api {
|
||||||
|
|
||||||
// Connect to a root companion process and get a Unix domain socket for IPC.
|
// Connect to a root companion process and get a Unix domain socket for IPC.
|
||||||
//
|
//
|
||||||
// This API only works in the pre[XXX]Specialize functions due to SELinux restrictions.
|
// This API only works in the pre[XXX]Specialize methods due to SELinux restrictions.
|
||||||
//
|
//
|
||||||
// The pre[XXX]Specialize functions run with the same privilege of zygote.
|
// 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
|
// 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).
|
// 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
|
// 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.
|
// 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 function from a 32-bit
|
// 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.
|
// 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
|
// Returns a file descriptor to a socket that is connected to the socket passed to your
|
||||||
@ -196,8 +195,8 @@ struct Api {
|
|||||||
|
|
||||||
// Get the file descriptor of the root folder of the current module.
|
// Get the file descriptor of the root folder of the current module.
|
||||||
//
|
//
|
||||||
// This API only works in the pre[XXX]Specialize functions.
|
// This API only works in the pre[XXX]Specialize methods.
|
||||||
// Accessing the directory returned is only possible in the pre[XXX]Specialize functions
|
// 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).
|
// or in the root companion process (assuming that you sent the fd over the socket).
|
||||||
// Both restrictions are due to SELinux and UID.
|
// Both restrictions are due to SELinux and UID.
|
||||||
//
|
//
|
||||||
@ -205,7 +204,7 @@ struct Api {
|
|||||||
int getModuleDir();
|
int getModuleDir();
|
||||||
|
|
||||||
// Set various options for your module.
|
// Set various options for your module.
|
||||||
// Please note that this function accepts one single option at a time.
|
// Please note that this method accepts one single option at a time.
|
||||||
// Check zygisk::Option for the full list of options available.
|
// Check zygisk::Option for the full list of options available.
|
||||||
void setOption(Option opt);
|
void setOption(Option opt);
|
||||||
|
|
||||||
@ -213,9 +212,17 @@ struct Api {
|
|||||||
// Returns bitwise-or'd zygisk::StateFlag values.
|
// Returns bitwise-or'd zygisk::StateFlag values.
|
||||||
uint32_t getFlags();
|
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
|
// Hook JNI native methods for a class
|
||||||
//
|
//
|
||||||
// Lookup all registered JNI native methods and replace it with your own functions.
|
// 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.
|
// 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
|
// If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
|
||||||
// will be set to nullptr.
|
// will be set to nullptr.
|
||||||
@ -295,6 +302,7 @@ struct api_table {
|
|||||||
void (*setOption)(void * /* impl */, Option);
|
void (*setOption)(void * /* impl */, Option);
|
||||||
int (*getModuleDir)(void * /* impl */);
|
int (*getModuleDir)(void * /* impl */);
|
||||||
uint32_t (*getFlags)(void * /* impl */);
|
uint32_t (*getFlags)(void * /* impl */);
|
||||||
|
bool (*exemptFd)(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -321,6 +329,9 @@ inline void Api::setOption(Option opt) {
|
|||||||
inline uint32_t Api::getFlags() {
|
inline uint32_t Api::getFlags() {
|
||||||
return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0;
|
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) {
|
inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
|
||||||
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,13 @@ static bool unhook_functions();
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
POST_SPECIALIZE,
|
||||||
APP_FORK_AND_SPECIALIZE,
|
APP_FORK_AND_SPECIALIZE,
|
||||||
APP_SPECIALIZE,
|
APP_SPECIALIZE,
|
||||||
SERVER_FORK_AND_SPECIALIZE,
|
SERVER_FORK_AND_SPECIALIZE,
|
||||||
DO_REVERT_UNMOUNT,
|
DO_REVERT_UNMOUNT,
|
||||||
CAN_UNLOAD_ZYGISK,
|
CAN_UNLOAD_ZYGISK,
|
||||||
SKIP_CLOSE_LOGD_FD,
|
SKIP_FD_SANITIZATION,
|
||||||
|
|
||||||
FLAG_MAX
|
FLAG_MAX
|
||||||
};
|
};
|
||||||
@ -59,10 +60,10 @@ struct HookContext {
|
|||||||
bitset<FLAG_MAX> flags;
|
bitset<FLAG_MAX> flags;
|
||||||
uint32_t info_flags;
|
uint32_t info_flags;
|
||||||
bitset<MAX_FD_SIZE> allowed_fds;
|
bitset<MAX_FD_SIZE> allowed_fds;
|
||||||
|
vector<int> exempted_fds;
|
||||||
|
|
||||||
HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0) {}
|
HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0) {}
|
||||||
|
|
||||||
void sanitize_fds();
|
|
||||||
void run_modules_pre(const vector<int> &fds);
|
void run_modules_pre(const vector<int> &fds);
|
||||||
void run_modules_post();
|
void run_modules_post();
|
||||||
DCL_PRE_POST(fork)
|
DCL_PRE_POST(fork)
|
||||||
@ -72,6 +73,8 @@ struct HookContext {
|
|||||||
DCL_PRE_POST(nativeForkSystemServer)
|
DCL_PRE_POST(nativeForkSystemServer)
|
||||||
|
|
||||||
void unload_zygisk();
|
void unload_zygisk();
|
||||||
|
void sanitize_fds();
|
||||||
|
bool exempt_fd(int fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef DCL_PRE_POST
|
#undef DCL_PRE_POST
|
||||||
@ -178,15 +181,16 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
|
|||||||
// Close logd_fd if necessary to prevent crashing
|
// Close logd_fd if necessary to prevent crashing
|
||||||
// For more info, check comments in zygisk_log_write
|
// For more info, check comments in zygisk_log_write
|
||||||
DCL_HOOK_FUNC(void, android_log_close) {
|
DCL_HOOK_FUNC(void, android_log_close) {
|
||||||
if (g_ctx == nullptr || g_ctx->pid > 0) {
|
if (g_ctx == nullptr) {
|
||||||
// This happens either in the zygote daemon or during nativeForkApp, nativeForkUsap etc.
|
// Happens during un-managed fork like nativeForkApp, nativeForkUsap
|
||||||
close(logd_fd.exchange(-1));
|
close(logd_fd.exchange(-1));
|
||||||
// Do NOT revert back to android logging
|
} else if (!g_ctx->flags[SKIP_FD_SANITIZATION]) {
|
||||||
} else if (!g_ctx->flags[SKIP_CLOSE_LOGD_FD]) {
|
|
||||||
close(logd_fd.exchange(-1));
|
close(logd_fd.exchange(-1));
|
||||||
// Switch to plain old android logging because we cannot talk
|
if (g_ctx->pid <= 0) {
|
||||||
// to magiskd to fetch our log pipe afterwards anyways.
|
// Switch to plain old android logging because we cannot talk
|
||||||
android_logging();
|
// to magiskd to fetch our log pipe afterwards anyways.
|
||||||
|
android_logging();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
old_android_log_close();
|
old_android_log_close();
|
||||||
}
|
}
|
||||||
@ -318,6 +322,11 @@ bool ZygiskModule::RegisterModuleImpl(api_abi_base *api, long *module) {
|
|||||||
|
|
||||||
// Fill in API accordingly with module API version
|
// Fill in API accordingly with module API version
|
||||||
switch (api_version) {
|
switch (api_version) {
|
||||||
|
case 4: {
|
||||||
|
auto v4 = static_cast<api_abi_v4 *>(api);
|
||||||
|
v4->exemptFd = [](int fd) { return g_ctx != nullptr && g_ctx->exempt_fd(fd); };
|
||||||
|
}
|
||||||
|
// fallthrough
|
||||||
case 3:
|
case 3:
|
||||||
case 2: {
|
case 2: {
|
||||||
auto v2 = static_cast<api_abi_v2 *>(api);
|
auto v2 = static_cast<api_abi_v2 *>(api);
|
||||||
@ -401,7 +410,7 @@ void HookContext::fork_pre() {
|
|||||||
// First block SIGCHLD, unblock after original fork is done
|
// First block SIGCHLD, unblock after original fork is done
|
||||||
sigmask(SIG_BLOCK, SIGCHLD);
|
sigmask(SIG_BLOCK, SIGCHLD);
|
||||||
pid = old_fork();
|
pid = old_fork();
|
||||||
if (pid != 0)
|
if (pid != 0 || flags[SKIP_FD_SANITIZATION])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Record all open fds
|
// Record all open fds
|
||||||
@ -419,11 +428,30 @@ void HookContext::fork_pre() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HookContext::sanitize_fds() {
|
void HookContext::sanitize_fds() {
|
||||||
|
if (flags[SKIP_FD_SANITIZATION])
|
||||||
|
return;
|
||||||
|
|
||||||
if (flags[APP_FORK_AND_SPECIALIZE]) {
|
if (flags[APP_FORK_AND_SPECIALIZE]) {
|
||||||
if (args.app->fds_to_ignore == nullptr) {
|
auto update_fd_array = [&](int off) -> jintArray {
|
||||||
// The field fds_to_ignore don't exist before Android 8.0, which FDs are not checked
|
if (exempted_fds.empty())
|
||||||
flags[SKIP_CLOSE_LOGD_FD] = true;
|
return nullptr;
|
||||||
} else if (jintArray fdsToIgnore = *args.app->fds_to_ignore) {
|
|
||||||
|
jintArray array = env->NewIntArray(off + exempted_fds.size());
|
||||||
|
if (array == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
env->SetIntArrayRegion(array, off, exempted_fds.size(), exempted_fds.data());
|
||||||
|
for (int fd : exempted_fds) {
|
||||||
|
if (fd >= 0 && fd < MAX_FD_SIZE) {
|
||||||
|
allowed_fds[fd] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*args.app->fds_to_ignore = array;
|
||||||
|
flags[SKIP_FD_SANITIZATION] = true;
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (jintArray fdsToIgnore = *args.app->fds_to_ignore) {
|
||||||
int *arr = env->GetIntArrayElements(fdsToIgnore, nullptr);
|
int *arr = env->GetIntArrayElements(fdsToIgnore, nullptr);
|
||||||
int len = env->GetArrayLength(fdsToIgnore);
|
int len = env->GetArrayLength(fdsToIgnore);
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
@ -432,24 +460,12 @@ void HookContext::sanitize_fds() {
|
|||||||
allowed_fds[fd] = true;
|
allowed_fds[fd] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (jintArray newFdList = update_fd_array(len)) {
|
||||||
jintArray newFdList = nullptr;
|
|
||||||
if (logd_fd >= 0 && (newFdList = env->NewIntArray(len + 1))) {
|
|
||||||
env->SetIntArrayRegion(newFdList, 0, len, arr);
|
env->SetIntArrayRegion(newFdList, 0, len, arr);
|
||||||
int fd = logd_fd;
|
|
||||||
env->SetIntArrayRegion(newFdList, len, 1, &fd);
|
|
||||||
*args.app->fds_to_ignore = newFdList;
|
|
||||||
flags[SKIP_CLOSE_LOGD_FD] = true;
|
|
||||||
}
|
}
|
||||||
env->ReleaseIntArrayElements(fdsToIgnore, arr, JNI_ABORT);
|
env->ReleaseIntArrayElements(fdsToIgnore, arr, JNI_ABORT);
|
||||||
} else {
|
} else {
|
||||||
jintArray newFdList = nullptr;
|
update_fd_array(0);
|
||||||
if (logd_fd >= 0 && (newFdList = env->NewIntArray(1))) {
|
|
||||||
int fd = logd_fd;
|
|
||||||
env->SetIntArrayRegion(newFdList, 0, 1, &fd);
|
|
||||||
*args.app->fds_to_ignore = newFdList;
|
|
||||||
flags[SKIP_CLOSE_LOGD_FD] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,6 +526,7 @@ void HookContext::run_modules_pre(const vector<int> &fds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HookContext::run_modules_post() {
|
void HookContext::run_modules_post() {
|
||||||
|
flags[POST_SPECIALIZE] = true;
|
||||||
for (const auto &m : modules) {
|
for (const auto &m : modules) {
|
||||||
if (flags[APP_SPECIALIZE]) {
|
if (flags[APP_SPECIALIZE]) {
|
||||||
m.postAppSpecialize(args.app);
|
m.postAppSpecialize(args.app);
|
||||||
@ -564,14 +581,23 @@ void HookContext::unload_zygisk() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HookContext::exempt_fd(int fd) {
|
||||||
|
if (flags[POST_SPECIALIZE] || flags[SKIP_FD_SANITIZATION])
|
||||||
|
return true;
|
||||||
|
if (!flags[APP_FORK_AND_SPECIALIZE])
|
||||||
|
return false;
|
||||||
|
exempted_fds.push_back(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
void HookContext::nativeSpecializeAppProcess_pre() {
|
void HookContext::nativeSpecializeAppProcess_pre() {
|
||||||
g_ctx = this;
|
|
||||||
// App specialize does not check FD
|
|
||||||
flags[SKIP_CLOSE_LOGD_FD] = true;
|
|
||||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||||
ZLOGV("pre specialize [%s]\n", process);
|
ZLOGV("pre specialize [%s]\n", process);
|
||||||
|
g_ctx = this;
|
||||||
|
// App specialize does not check FD
|
||||||
|
flags[SKIP_FD_SANITIZATION] = true;
|
||||||
app_specialize_pre();
|
app_specialize_pre();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,8 +649,16 @@ void HookContext::nativeForkSystemServer_post() {
|
|||||||
|
|
||||||
void HookContext::nativeForkAndSpecialize_pre() {
|
void HookContext::nativeForkAndSpecialize_pre() {
|
||||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||||
flags[APP_FORK_AND_SPECIALIZE] = true;
|
|
||||||
ZLOGV("pre forkAndSpecialize [%s]\n", process);
|
ZLOGV("pre forkAndSpecialize [%s]\n", process);
|
||||||
|
|
||||||
|
flags[APP_FORK_AND_SPECIALIZE] = true;
|
||||||
|
if (args.app->fds_to_ignore == nullptr) {
|
||||||
|
// The field fds_to_ignore don't exist before Android 8.0, which FDs are not checked
|
||||||
|
flags[SKIP_FD_SANITIZATION] = true;
|
||||||
|
} else if (logd_fd >= 0) {
|
||||||
|
exempted_fds.push_back(logd_fd);
|
||||||
|
}
|
||||||
|
|
||||||
fork_pre();
|
fork_pre();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
app_specialize_pre();
|
app_specialize_pre();
|
||||||
|
@ -10,14 +10,17 @@ struct ZygiskModule;
|
|||||||
struct AppSpecializeArgs_v1;
|
struct AppSpecializeArgs_v1;
|
||||||
using AppSpecializeArgs_v2 = AppSpecializeArgs_v1;
|
using AppSpecializeArgs_v2 = AppSpecializeArgs_v1;
|
||||||
struct AppSpecializeArgs_v3;
|
struct AppSpecializeArgs_v3;
|
||||||
|
using AppSpecializeArgs_v4 = AppSpecializeArgs_v3;
|
||||||
|
|
||||||
struct module_abi_v1;
|
struct module_abi_v1;
|
||||||
using module_abi_v2 = module_abi_v1;
|
using module_abi_v2 = module_abi_v1;
|
||||||
using module_abi_v3 = module_abi_v1;
|
using module_abi_v3 = module_abi_v1;
|
||||||
|
using module_abi_v4 = module_abi_v1;
|
||||||
|
|
||||||
struct api_abi_v1;
|
struct api_abi_v1;
|
||||||
struct api_abi_v2;
|
struct api_abi_v2;
|
||||||
using api_abi_v3 = api_abi_v2;
|
using api_abi_v3 = api_abi_v2;
|
||||||
|
struct api_abi_v4;
|
||||||
|
|
||||||
struct AppSpecializeArgs_v3 {
|
struct AppSpecializeArgs_v3 {
|
||||||
jint &uid;
|
jint &uid;
|
||||||
@ -132,6 +135,10 @@ struct api_abi_v2 : public api_abi_v1 {
|
|||||||
uint32_t (*getFlags)(ZygiskModule *);
|
uint32_t (*getFlags)(ZygiskModule *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct api_abi_v4 : public api_abi_v2 {
|
||||||
|
bool (*exemptFd)(int);
|
||||||
|
};
|
||||||
|
|
||||||
#define call_app(method) \
|
#define call_app(method) \
|
||||||
switch (*mod.api_version) { \
|
switch (*mod.api_version) { \
|
||||||
case 1: \
|
case 1: \
|
||||||
@ -141,6 +148,7 @@ case 2: { \
|
|||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
case 3: \
|
case 3: \
|
||||||
|
case 4: \
|
||||||
mod.v1->method(mod.v1->impl, args);\
|
mod.v1->method(mod.v1->impl, args);\
|
||||||
break; \
|
break; \
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user