mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 01:27:38 +00:00
Add new API exemptFd
This commit is contained in:
parent
ccf21b0992
commit
44029875a6
@ -23,7 +23,7 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#define ZYGISK_API_VERSION 3
|
||||
#define ZYGISK_API_VERSION 4
|
||||
|
||||
/*
|
||||
|
||||
@ -73,12 +73,11 @@ struct ServerSpecializeArgs;
|
||||
class ModuleBase {
|
||||
public:
|
||||
|
||||
// This function is called when the module is loaded into the target process.
|
||||
// A Zygisk API handle will be sent as an argument; call utility functions or interface
|
||||
// with Zygisk through this handle.
|
||||
// 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 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
|
||||
// is applied. This means that the process does not have any sandbox restrictions and
|
||||
// still runs with the same privilege of zygote.
|
||||
@ -92,16 +91,16 @@ public:
|
||||
// See Api::connectCompanion() for more info.
|
||||
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.
|
||||
// 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) {}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
|
||||
};
|
||||
@ -173,21 +172,21 @@ enum StateFlag : uint32_t {
|
||||
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.
|
||||
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 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
|
||||
// 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 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// This API only works in the pre[XXX]Specialize functions.
|
||||
// Accessing the directory returned is only possible 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 methods
|
||||
// or in the root companion process (assuming that you sent the fd over the socket).
|
||||
// Both restrictions are due to SELinux and UID.
|
||||
//
|
||||
@ -205,7 +204,7 @@ struct Api {
|
||||
int getModuleDir();
|
||||
|
||||
// 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.
|
||||
void setOption(Option opt);
|
||||
|
||||
@ -213,9 +212,17 @@ struct Api {
|
||||
// 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 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.
|
||||
// If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
|
||||
// will be set to nullptr.
|
||||
@ -295,6 +302,7 @@ struct api_table {
|
||||
void (*setOption)(void * /* impl */, Option);
|
||||
int (*getModuleDir)(void * /* impl */);
|
||||
uint32_t (*getFlags)(void * /* impl */);
|
||||
bool (*exemptFd)(int);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@ -321,6 +329,9 @@ inline void Api::setOption(Option 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);
|
||||
}
|
||||
|
@ -28,12 +28,13 @@ static bool unhook_functions();
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
POST_SPECIALIZE,
|
||||
APP_FORK_AND_SPECIALIZE,
|
||||
APP_SPECIALIZE,
|
||||
SERVER_FORK_AND_SPECIALIZE,
|
||||
DO_REVERT_UNMOUNT,
|
||||
CAN_UNLOAD_ZYGISK,
|
||||
SKIP_CLOSE_LOGD_FD,
|
||||
SKIP_FD_SANITIZATION,
|
||||
|
||||
FLAG_MAX
|
||||
};
|
||||
@ -59,10 +60,10 @@ struct HookContext {
|
||||
bitset<FLAG_MAX> flags;
|
||||
uint32_t info_flags;
|
||||
bitset<MAX_FD_SIZE> allowed_fds;
|
||||
vector<int> exempted_fds;
|
||||
|
||||
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_post();
|
||||
DCL_PRE_POST(fork)
|
||||
@ -72,6 +73,8 @@ struct HookContext {
|
||||
DCL_PRE_POST(nativeForkSystemServer)
|
||||
|
||||
void unload_zygisk();
|
||||
void sanitize_fds();
|
||||
bool exempt_fd(int fd);
|
||||
};
|
||||
|
||||
#undef DCL_PRE_POST
|
||||
@ -178,15 +181,16 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
// Close logd_fd if necessary to prevent crashing
|
||||
// For more info, check comments in zygisk_log_write
|
||||
DCL_HOOK_FUNC(void, android_log_close) {
|
||||
if (g_ctx == nullptr || g_ctx->pid > 0) {
|
||||
// This happens either in the zygote daemon or during nativeForkApp, nativeForkUsap etc.
|
||||
if (g_ctx == nullptr) {
|
||||
// Happens during un-managed fork like nativeForkApp, nativeForkUsap
|
||||
close(logd_fd.exchange(-1));
|
||||
// Do NOT revert back to android logging
|
||||
} else if (!g_ctx->flags[SKIP_CLOSE_LOGD_FD]) {
|
||||
} else if (!g_ctx->flags[SKIP_FD_SANITIZATION]) {
|
||||
close(logd_fd.exchange(-1));
|
||||
// Switch to plain old android logging because we cannot talk
|
||||
// to magiskd to fetch our log pipe afterwards anyways.
|
||||
android_logging();
|
||||
if (g_ctx->pid <= 0) {
|
||||
// Switch to plain old android logging because we cannot talk
|
||||
// to magiskd to fetch our log pipe afterwards anyways.
|
||||
android_logging();
|
||||
}
|
||||
}
|
||||
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
|
||||
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 2: {
|
||||
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
|
||||
sigmask(SIG_BLOCK, SIGCHLD);
|
||||
pid = old_fork();
|
||||
if (pid != 0)
|
||||
if (pid != 0 || flags[SKIP_FD_SANITIZATION])
|
||||
return;
|
||||
|
||||
// Record all open fds
|
||||
@ -419,11 +428,30 @@ void HookContext::fork_pre() {
|
||||
}
|
||||
|
||||
void HookContext::sanitize_fds() {
|
||||
if (flags[SKIP_FD_SANITIZATION])
|
||||
return;
|
||||
|
||||
if (flags[APP_FORK_AND_SPECIALIZE]) {
|
||||
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_CLOSE_LOGD_FD] = true;
|
||||
} else if (jintArray fdsToIgnore = *args.app->fds_to_ignore) {
|
||||
auto update_fd_array = [&](int off) -> jintArray {
|
||||
if (exempted_fds.empty())
|
||||
return nullptr;
|
||||
|
||||
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 len = env->GetArrayLength(fdsToIgnore);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
@ -432,24 +460,12 @@ void HookContext::sanitize_fds() {
|
||||
allowed_fds[fd] = true;
|
||||
}
|
||||
}
|
||||
|
||||
jintArray newFdList = nullptr;
|
||||
if (logd_fd >= 0 && (newFdList = env->NewIntArray(len + 1))) {
|
||||
if (jintArray newFdList = update_fd_array(len)) {
|
||||
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);
|
||||
} else {
|
||||
jintArray newFdList = nullptr;
|
||||
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;
|
||||
}
|
||||
update_fd_array(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,6 +526,7 @@ void HookContext::run_modules_pre(const vector<int> &fds) {
|
||||
}
|
||||
|
||||
void HookContext::run_modules_post() {
|
||||
flags[POST_SPECIALIZE] = true;
|
||||
for (const auto &m : modules) {
|
||||
if (flags[APP_SPECIALIZE]) {
|
||||
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() {
|
||||
g_ctx = this;
|
||||
// App specialize does not check FD
|
||||
flags[SKIP_CLOSE_LOGD_FD] = true;
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
ZLOGV("pre specialize [%s]\n", process);
|
||||
g_ctx = this;
|
||||
// App specialize does not check FD
|
||||
flags[SKIP_FD_SANITIZATION] = true;
|
||||
app_specialize_pre();
|
||||
}
|
||||
|
||||
@ -623,8 +649,16 @@ void HookContext::nativeForkSystemServer_post() {
|
||||
|
||||
void HookContext::nativeForkAndSpecialize_pre() {
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
flags[APP_FORK_AND_SPECIALIZE] = true;
|
||||
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();
|
||||
if (pid == 0) {
|
||||
app_specialize_pre();
|
||||
|
@ -10,14 +10,17 @@ struct ZygiskModule;
|
||||
struct AppSpecializeArgs_v1;
|
||||
using AppSpecializeArgs_v2 = AppSpecializeArgs_v1;
|
||||
struct AppSpecializeArgs_v3;
|
||||
using AppSpecializeArgs_v4 = AppSpecializeArgs_v3;
|
||||
|
||||
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;
|
||||
|
||||
struct api_abi_v1;
|
||||
struct api_abi_v2;
|
||||
using api_abi_v3 = api_abi_v2;
|
||||
struct api_abi_v4;
|
||||
|
||||
struct AppSpecializeArgs_v3 {
|
||||
jint &uid;
|
||||
@ -132,6 +135,10 @@ struct api_abi_v2 : public api_abi_v1 {
|
||||
uint32_t (*getFlags)(ZygiskModule *);
|
||||
};
|
||||
|
||||
struct api_abi_v4 : public api_abi_v2 {
|
||||
bool (*exemptFd)(int);
|
||||
};
|
||||
|
||||
#define call_app(method) \
|
||||
switch (*mod.api_version) { \
|
||||
case 1: \
|
||||
@ -141,6 +148,7 @@ case 2: { \
|
||||
break; \
|
||||
} \
|
||||
case 3: \
|
||||
case 4: \
|
||||
mod.v1->method(mod.v1->impl, args);\
|
||||
break; \
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user