mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-06-04 20:28:23 +00:00
Load Zygisk modules
This commit is contained in:
parent
27814e3015
commit
c8ac6c07b0
@ -31,6 +31,6 @@ void exec_module_scripts(const char *stage);
|
|||||||
// Scripting
|
// Scripting
|
||||||
void exec_script(const char *script);
|
void exec_script(const char *script);
|
||||||
void exec_common_scripts(const char *stage);
|
void exec_common_scripts(const char *stage);
|
||||||
void exec_module_scripts(const char *stage, const std::vector<std::string> &module_list);
|
void exec_module_scripts(const char *stage, const std::vector<std::string_view> &modules);
|
||||||
void install_apk(const char *apk);
|
void install_apk(const char *apk);
|
||||||
[[noreturn]] void install_module(const char *file);
|
[[noreturn]] void install_module(const char *file);
|
||||||
|
@ -22,8 +22,6 @@ using namespace std;
|
|||||||
#define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */
|
#define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */
|
||||||
#define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT)
|
#define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT)
|
||||||
|
|
||||||
static vector<string> module_list;
|
|
||||||
|
|
||||||
class node_entry;
|
class node_entry;
|
||||||
class dir_node;
|
class dir_node;
|
||||||
class inter_node;
|
class inter_node;
|
||||||
@ -543,12 +541,22 @@ static void inject_magisk_bins(root_node *system) {
|
|||||||
delete bin->extract(init_applet[i]);
|
delete bin->extract(init_applet[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct module_info {
|
||||||
|
string name;
|
||||||
|
int z32 = -1;
|
||||||
|
#if defined(__LP64__)
|
||||||
|
int z64 = -1;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static vector<module_info> *modules;
|
||||||
|
|
||||||
#define mount_zygisk(bit) \
|
#define mount_zygisk(bit) \
|
||||||
if (access("/system/bin/app_process" #bit, F_OK) == 0) { \
|
if (access("/system/bin/app_process" #bit, F_OK) == 0) { \
|
||||||
string zbin = zygisk_bin + "/app_process" #bit; \
|
string zbin = zygisk_bin + "/app_process" #bit; \
|
||||||
string mbin = MAGISKTMP + "/magisk" #bit; \
|
string mbin = MAGISKTMP + "/magisk" #bit; \
|
||||||
int src = xopen(mbin.data(), O_RDONLY); \
|
int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \
|
||||||
int out = xopen(zbin.data(), O_CREAT | O_WRONLY, 0); \
|
int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \
|
||||||
xsendfile(out, src, nullptr, INT_MAX); \
|
xsendfile(out, src, nullptr, INT_MAX); \
|
||||||
close(src); \
|
close(src); \
|
||||||
close(out); \
|
close(out); \
|
||||||
@ -566,8 +574,8 @@ void magic_mount() {
|
|||||||
|
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
LOGI("* Loading modules\n");
|
LOGI("* Loading modules\n");
|
||||||
for (const auto &m : module_list) {
|
for (const auto &m : *modules) {
|
||||||
auto module = m.data();
|
const char *module = m.name.data();
|
||||||
char *b = buf + sprintf(buf, "%s/" MODULEMNT "/%s/", MAGISKTMP.data(), module);
|
char *b = buf + sprintf(buf, "%s/" MODULEMNT "/%s/", MAGISKTMP.data(), module);
|
||||||
|
|
||||||
// Read props
|
// Read props
|
||||||
@ -676,8 +684,8 @@ static void foreach_module(Func fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_modules() {
|
static void collect_modules(bool open_zygisk) {
|
||||||
foreach_module([](int dfd, dirent *entry, int modfd) {
|
foreach_module([=](int dfd, dirent *entry, int modfd) {
|
||||||
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
|
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
|
||||||
LOGI("%s: remove\n", entry->d_name);
|
LOGI("%s: remove\n", entry->d_name);
|
||||||
auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh";
|
auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh";
|
||||||
@ -690,24 +698,42 @@ static void collect_modules() {
|
|||||||
unlinkat(modfd, "update", 0);
|
unlinkat(modfd, "update", 0);
|
||||||
if (faccessat(modfd, "disable", F_OK, 0) == 0)
|
if (faccessat(modfd, "disable", F_OK, 0) == 0)
|
||||||
return;
|
return;
|
||||||
// Riru and its modules are not compatible with zygisk
|
|
||||||
if (zygisk_enabled && (
|
|
||||||
entry->d_name == "riru-core"sv ||
|
|
||||||
faccessat(modfd, "riru", F_OK, 0) == 0))
|
|
||||||
return;
|
|
||||||
|
|
||||||
module_list.emplace_back(entry->d_name);
|
module_info info;
|
||||||
|
if (zygisk_enabled) {
|
||||||
|
// Riru and its modules are not compatible with zygisk
|
||||||
|
if (entry->d_name == "riru-core"sv || faccessat(modfd, "riru", F_OK, 0) == 0)
|
||||||
|
return;
|
||||||
|
if (open_zygisk) {
|
||||||
|
#if defined(__arm__)
|
||||||
|
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
info.z64 = openat(modfd, "zygisk/arm64-v8a.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
#elif defined(__i386__)
|
||||||
|
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
info.z64 = openat(modfd, "zygisk/x86_64.so", O_RDONLY | O_CLOEXEC);
|
||||||
|
#else
|
||||||
|
#error Unsupported ABI
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.name = entry->d_name;
|
||||||
|
modules->push_back(info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_modules() {
|
void handle_modules() {
|
||||||
|
default_new(modules);
|
||||||
prepare_modules();
|
prepare_modules();
|
||||||
collect_modules();
|
collect_modules(false);
|
||||||
exec_module_scripts("post-fs-data");
|
exec_module_scripts("post-fs-data");
|
||||||
|
|
||||||
// Recollect modules (module scripts could remove itself)
|
// Recollect modules (module scripts could remove itself)
|
||||||
module_list.clear();
|
modules->clear();
|
||||||
collect_modules();
|
collect_modules(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable_modules() {
|
void disable_modules() {
|
||||||
@ -726,5 +752,25 @@ void remove_modules() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void exec_module_scripts(const char *stage) {
|
void exec_module_scripts(const char *stage) {
|
||||||
exec_module_scripts(stage, module_list);
|
vector<string_view> module_names;
|
||||||
|
std::transform(modules->begin(), modules->end(), std::back_inserter(module_names),
|
||||||
|
[](const module_info &info) { return info.name; });
|
||||||
|
exec_module_scripts(stage, module_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<int> zygisk_module_fds(bool is_64_bit) {
|
||||||
|
vector<int> fds;
|
||||||
|
// All fds passed to send_fds have to be valid file descriptors.
|
||||||
|
// To workaround this issue, send over STDOUT_FILENO as an indicator of an
|
||||||
|
// invalid fd as it will always be /dev/null in magiskd
|
||||||
|
if (is_64_bit) {
|
||||||
|
#if defined(__LP64__)
|
||||||
|
std::transform(modules->begin(), modules->end(), std::back_inserter(fds),
|
||||||
|
[](const module_info &info) { return info.z64 < 0 ? STDOUT_FILENO : info.z64; });
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
std::transform(modules->begin(), modules->end(), std::back_inserter(fds),
|
||||||
|
[](const module_info &info) { return info.z32 < 0 ? STDOUT_FILENO : info.z32; });
|
||||||
|
}
|
||||||
|
return fds;
|
||||||
}
|
}
|
||||||
|
@ -107,16 +107,15 @@ void exec_common_scripts(const char *stage) {
|
|||||||
PFS_DONE()
|
PFS_DONE()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if a > b
|
static bool operator>(const timespec &a, const timespec &b) {
|
||||||
static bool timespec_larger(timespec *a, timespec *b) {
|
if (a.tv_sec != b.tv_sec)
|
||||||
if (a->tv_sec != b->tv_sec)
|
return a.tv_sec > b.tv_sec;
|
||||||
return a->tv_sec > b->tv_sec;
|
return a.tv_nsec > b.tv_nsec;
|
||||||
return a->tv_nsec > b->tv_nsec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void exec_module_scripts(const char *stage, const vector<string> &module_list) {
|
void exec_module_scripts(const char *stage, const vector<string_view> &modules) {
|
||||||
LOGI("* Running module %s scripts\n", stage);
|
LOGI("* Running module %s scripts\n", stage);
|
||||||
if (module_list.empty())
|
if (modules.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool pfs = stage == "post-fs-data"sv;
|
bool pfs = stage == "post-fs-data"sv;
|
||||||
@ -124,15 +123,15 @@ void exec_module_scripts(const char *stage, const vector<string> &module_list) {
|
|||||||
timespec now{};
|
timespec now{};
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
// If we had already timed out, treat it as service mode
|
// If we had already timed out, treat it as service mode
|
||||||
if (timespec_larger(&now, &pfs_timeout))
|
if (now > pfs_timeout)
|
||||||
pfs = false;
|
pfs = false;
|
||||||
}
|
}
|
||||||
int timer_pid = -1;
|
int timer_pid = -1;
|
||||||
PFS_SETUP()
|
PFS_SETUP()
|
||||||
|
|
||||||
char path[4096];
|
char path[4096];
|
||||||
for (auto &m : module_list) {
|
for (auto &m : modules) {
|
||||||
const char* module = m.data();
|
const char *module = m.data();
|
||||||
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
|
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
|
||||||
if (access(path, F_OK) == -1)
|
if (access(path, F_OK) == -1)
|
||||||
continue;
|
continue;
|
||||||
|
@ -93,14 +93,17 @@ static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vector<int> recv_fds(int sockfd) {
|
vector<int> recv_fds(int sockfd) {
|
||||||
|
vector<int> results;
|
||||||
|
|
||||||
// Peek fd count to allocate proper buffer
|
// Peek fd count to allocate proper buffer
|
||||||
int cnt;
|
int cnt;
|
||||||
recv(sockfd, &cnt, sizeof(cnt), MSG_PEEK);
|
recv(sockfd, &cnt, sizeof(cnt), MSG_PEEK);
|
||||||
|
if (cnt == 0)
|
||||||
|
return results;
|
||||||
|
|
||||||
vector<char> cmsgbuf;
|
vector<char> cmsgbuf;
|
||||||
cmsgbuf.resize(CMSG_SPACE(sizeof(int) * cnt));
|
cmsgbuf.resize(CMSG_SPACE(sizeof(int) * cnt));
|
||||||
|
|
||||||
vector<int> results;
|
|
||||||
void *data = recv_fds(sockfd, cmsgbuf.data(), cmsgbuf.size(), cnt);
|
void *data = recv_fds(sockfd, cmsgbuf.data(), cmsgbuf.size(), cnt);
|
||||||
if (data == nullptr)
|
if (data == nullptr)
|
||||||
return results;
|
return results;
|
||||||
|
@ -65,6 +65,7 @@ void boot_complete(int client);
|
|||||||
void denylist_handler(int client, ucred *cred);
|
void denylist_handler(int client, ucred *cred);
|
||||||
void su_daemon_handler(int client, ucred *credential);
|
void su_daemon_handler(int client, ucred *credential);
|
||||||
void zygisk_handler(int client, ucred *cred);
|
void zygisk_handler(int client, ucred *cred);
|
||||||
|
std::vector<int> zygisk_module_fds(bool is_64_bit);
|
||||||
|
|
||||||
// Denylist
|
// Denylist
|
||||||
void initialize_denylist();
|
void initialize_denylist();
|
||||||
|
@ -172,7 +172,7 @@ struct Api {
|
|||||||
// 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.
|
||||||
void hookJniNativeMethods(const char *className, JNINativeMethod *methods, int numMethods);
|
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
|
||||||
|
|
||||||
// For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`.
|
// For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`.
|
||||||
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
|
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
|
||||||
@ -241,7 +241,7 @@ struct api_table {
|
|||||||
bool (*registerModule)(api_table *, module_abi *);
|
bool (*registerModule)(api_table *, module_abi *);
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
void (*hookJniNativeMethods)(const char *, JNINativeMethod *, int);
|
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||||
void (*pltHookRegister)(const char *, const char *, void *, void **);
|
void (*pltHookRegister)(const char *, const char *, void *, void **);
|
||||||
void (*pltHookExclude)(const char *, const char *);
|
void (*pltHookExclude)(const char *, const char *);
|
||||||
bool (*pltHookCommit)();
|
bool (*pltHookCommit)();
|
||||||
@ -269,8 +269,8 @@ int Api::connectCompanion() {
|
|||||||
void Api::forceDenyListUnmount() {
|
void Api::forceDenyListUnmount() {
|
||||||
impl->forceDenyListUnmount(impl->_this);
|
impl->forceDenyListUnmount(impl->_this);
|
||||||
}
|
}
|
||||||
void Api::hookJniNativeMethods(const char *className, JNINativeMethod *methods, int numMethods) {
|
void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
|
||||||
impl->hookJniNativeMethods(className, methods, numMethods);
|
impl->hookJniNativeMethods(env, className, methods, numMethods);
|
||||||
}
|
}
|
||||||
void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
|
void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
|
||||||
impl->pltHookRegister(regex, symbol, newFunc, oldFunc);
|
impl->pltHookRegister(regex, symbol, newFunc, oldFunc);
|
||||||
|
@ -213,17 +213,21 @@ static int zygisk_log(int prio, const char *fmt, va_list ap) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remote_get_app_info(int uid, const char *process, AppInfo *info) {
|
std::vector<int> remote_get_info(int uid, const char *process, AppInfo *info) {
|
||||||
|
vector<int> fds;
|
||||||
if (int fd = connect_daemon(); fd >= 0) {
|
if (int fd = connect_daemon(); fd >= 0) {
|
||||||
write_int(fd, ZYGISK_REQUEST);
|
write_int(fd, ZYGISK_REQUEST);
|
||||||
write_int(fd, ZYGISK_GET_APPINFO);
|
write_int(fd, ZYGISK_GET_INFO);
|
||||||
|
|
||||||
write_int(fd, uid);
|
write_int(fd, uid);
|
||||||
write_string(fd, process);
|
write_string(fd, process);
|
||||||
xxread(fd, info, sizeof(*info));
|
xxread(fd, info, sizeof(*info));
|
||||||
|
if (!info->on_denylist) {
|
||||||
|
fds = recv_fds(fd);
|
||||||
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
return fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
int remote_request_unmount() {
|
int remote_request_unmount() {
|
||||||
@ -261,12 +265,12 @@ static void setup_files(int client, ucred *cred) {
|
|||||||
int cached_manager_app_id = -1;
|
int cached_manager_app_id = -1;
|
||||||
static time_t last_modified = 0;
|
static time_t last_modified = 0;
|
||||||
|
|
||||||
static void get_app_info(int client) {
|
static void get_process_info(int client, ucred *cred) {
|
||||||
AppInfo info{};
|
AppInfo info{};
|
||||||
int uid = read_int(client);
|
int uid = read_int(client);
|
||||||
string process = read_string(client);
|
string process = read_string(client);
|
||||||
|
|
||||||
// This function is called on every single zygote app process specialization,
|
// This function is called on every single zygote process specialization,
|
||||||
// so performance is critical. get_manager_app_id() is expensive as it goes
|
// so performance is critical. get_manager_app_id() is expensive as it goes
|
||||||
// through a SQLite query and potentially multiple filesystem stats, so we
|
// through a SQLite query and potentially multiple filesystem stats, so we
|
||||||
// really want to cache the app ID value. Check the last modify timestamp of
|
// really want to cache the app ID value. Check the last modify timestamp of
|
||||||
@ -275,29 +279,40 @@ static void get_app_info(int client) {
|
|||||||
// If denylist is enabled, inotify will invalidate the app ID cache for us.
|
// If denylist is enabled, inotify will invalidate the app ID cache for us.
|
||||||
// In this case, we can skip the timestamp check all together.
|
// In this case, we can skip the timestamp check all together.
|
||||||
|
|
||||||
int manager_app_id = cached_manager_app_id;
|
if (uid != 1000) {
|
||||||
|
int manager_app_id = cached_manager_app_id;
|
||||||
|
|
||||||
// Denylist not enabled, check packages.xml timestamp
|
// Denylist not enabled, check packages.xml timestamp
|
||||||
if (!denylist_enabled && manager_app_id > 0) {
|
if (!denylist_enabled && manager_app_id > 0) {
|
||||||
struct stat st{};
|
struct stat st{};
|
||||||
stat("/data/system/packages.xml", &st);
|
stat("/data/system/packages.xml", &st);
|
||||||
if (st.st_atim.tv_sec > last_modified) {
|
if (st.st_atim.tv_sec > last_modified) {
|
||||||
manager_app_id = -1;
|
manager_app_id = -1;
|
||||||
last_modified = st.st_atim.tv_sec;
|
last_modified = st.st_atim.tv_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager_app_id < 0) {
|
||||||
|
manager_app_id = get_manager_app_id();
|
||||||
|
cached_manager_app_id = manager_app_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_app_id(uid) == manager_app_id) {
|
||||||
|
info.is_magisk_app = true;
|
||||||
|
} else if (denylist_enabled) {
|
||||||
|
info.on_denylist = is_deny_target(uid, process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager_app_id < 0) {
|
|
||||||
manager_app_id = get_manager_app_id();
|
|
||||||
cached_manager_app_id = manager_app_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to_app_id(uid) == manager_app_id) {
|
|
||||||
info.is_magisk_app = true;
|
|
||||||
} else if (denylist_enabled) {
|
|
||||||
info.on_denylist = is_deny_target(uid, process);
|
|
||||||
}
|
|
||||||
xwrite(client, &info, sizeof(info));
|
xwrite(client, &info, sizeof(info));
|
||||||
|
|
||||||
|
if (!info.on_denylist) {
|
||||||
|
char buf[256];
|
||||||
|
snprintf(buf, sizeof(buf), "/proc/%d/exe", cred->pid);
|
||||||
|
xreadlink(buf, buf, sizeof(buf));
|
||||||
|
vector<int> fds = zygisk_module_fds(str_ends(buf, "64"));
|
||||||
|
send_fds(client, fds.data(), fds.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_unmount(int client, ucred *cred) {
|
static void do_unmount(int client, ucred *cred) {
|
||||||
@ -325,8 +340,8 @@ void zygisk_handler(int client, ucred *cred) {
|
|||||||
case ZYGISK_SETUP:
|
case ZYGISK_SETUP:
|
||||||
setup_files(client, cred);
|
setup_files(client, cred);
|
||||||
break;
|
break;
|
||||||
case ZYGISK_GET_APPINFO:
|
case ZYGISK_GET_INFO:
|
||||||
get_app_info(client);
|
get_process_info(client, cred);
|
||||||
break;
|
break;
|
||||||
case ZYGISK_UNMOUNT:
|
case ZYGISK_UNMOUNT:
|
||||||
do_unmount(client, cred);
|
do_unmount(client, cred);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "zygisk.hpp"
|
#include "zygisk.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "api.hpp"
|
#include "module.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using jni_hook::hash_map;
|
using jni_hook::hash_map;
|
||||||
@ -22,57 +22,13 @@ using xstring = jni_hook::string;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DENY_FLAG,
|
UNMOUNT_FLAG,
|
||||||
FORK_AND_SPECIALIZE,
|
FORK_AND_SPECIALIZE,
|
||||||
APP_SPECIALIZE,
|
APP_SPECIALIZE,
|
||||||
SERVER_SPECIALIZE,
|
SERVER_SPECIALIZE,
|
||||||
FLAG_MAX
|
FLAG_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AppSpecializeArgsImpl {
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* Optional */
|
|
||||||
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;
|
|
||||||
|
|
||||||
AppSpecializeArgsImpl(
|
|
||||||
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) :
|
|
||||||
uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags),
|
|
||||||
mount_external(mount_external), se_info(se_info), nice_name(nice_name),
|
|
||||||
instruction_set(instruction_set), app_data_dir(app_data_dir) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ServerSpecializeArgsImpl {
|
|
||||||
jint &uid;
|
|
||||||
jint &gid;
|
|
||||||
jintArray &gids;
|
|
||||||
jint &runtime_flags;
|
|
||||||
jlong &permitted_capabilities;
|
|
||||||
jlong &effective_capabilities;
|
|
||||||
|
|
||||||
ServerSpecializeArgsImpl(
|
|
||||||
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) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DCL_PRE_POST(name) \
|
#define DCL_PRE_POST(name) \
|
||||||
void name##_pre(); \
|
void name##_pre(); \
|
||||||
void name##_post();
|
void name##_post();
|
||||||
@ -88,14 +44,16 @@ struct HookContext {
|
|||||||
int pid;
|
int pid;
|
||||||
bitset<FLAG_MAX> flags;
|
bitset<FLAG_MAX> flags;
|
||||||
AppInfo info;
|
AppInfo info;
|
||||||
|
vector<ZygiskModule> modules;
|
||||||
|
|
||||||
HookContext() : pid(-1), info{} {}
|
HookContext() : pid(-1), info{} {}
|
||||||
|
|
||||||
static void close_fds();
|
static void close_fds();
|
||||||
|
void toggle_unmount();
|
||||||
|
|
||||||
DCL_PRE_POST(fork)
|
DCL_PRE_POST(fork)
|
||||||
DCL_PRE_POST(run_modules)
|
void run_modules_pre(const vector<int> &fds);
|
||||||
|
void run_modules_post();
|
||||||
DCL_PRE_POST(nativeForkAndSpecialize)
|
DCL_PRE_POST(nativeForkAndSpecialize)
|
||||||
DCL_PRE_POST(nativeSpecializeAppProcess)
|
DCL_PRE_POST(nativeSpecializeAppProcess)
|
||||||
DCL_PRE_POST(nativeForkSystemServer)
|
DCL_PRE_POST(nativeForkSystemServer)
|
||||||
@ -184,7 +142,7 @@ DCL_HOOK_FUNC(int, fork) {
|
|||||||
// This is the latest point where we can still connect to the magiskd main socket
|
// This is the latest point where we can still connect to the magiskd main socket
|
||||||
DCL_HOOK_FUNC(int, selinux_android_setcontext,
|
DCL_HOOK_FUNC(int, selinux_android_setcontext,
|
||||||
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
|
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
|
||||||
if (g_ctx && g_ctx->flags[DENY_FLAG]) {
|
if (g_ctx && g_ctx->flags[UNMOUNT_FLAG]) {
|
||||||
if (remote_request_unmount() == 0) {
|
if (remote_request_unmount() == 0) {
|
||||||
LOGD("zygisk: mount namespace cleaned up\n");
|
LOGD("zygisk: mount namespace cleaned up\n");
|
||||||
}
|
}
|
||||||
@ -267,13 +225,121 @@ DCL_HOOK_FUNC(void, setArgv0, void *self, const char *argv0, bool setProcName) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
void HookContext::run_modules_pre() { /* TODO */ }
|
void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) {
|
||||||
void HookContext::run_modules_post() { /* TODO */ }
|
auto class_map = jni_method_map->find(clz);
|
||||||
|
if (class_map == jni_method_map->end()) {
|
||||||
|
for (int i = 0; i < numMethods; ++i) {
|
||||||
|
methods[i].fnPtr = nullptr;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<JNINativeMethod> hooks;
|
||||||
|
for (int i = 0; i < numMethods; ++i) {
|
||||||
|
auto method_map = class_map->second.find(methods[i].name);
|
||||||
|
if (method_map != class_map->second.end()) {
|
||||||
|
auto it = method_map->second.find(methods[i].signature);
|
||||||
|
if (it != method_map->second.end()) {
|
||||||
|
// Copy the JNINativeMethod
|
||||||
|
hooks.push_back(methods[i]);
|
||||||
|
// Save the original function pointer
|
||||||
|
methods[i].fnPtr = it->second;
|
||||||
|
// Do not allow double hook, remove method from map
|
||||||
|
method_map->second.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No matching method found, set fnPtr to null
|
||||||
|
methods[i].fnPtr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hooks.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
old_jniRegisterNativeMethods(env, clz, hooks.data(), hooks.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZygiskModule::registerModule(ApiTable *table, long *module) {
|
||||||
|
long ver = *module;
|
||||||
|
// Unsupported version
|
||||||
|
if (ver > ZYGISK_API_VERSION)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Set the actual module_abi*
|
||||||
|
table->module->ver = module;
|
||||||
|
|
||||||
|
// Fill in API accordingly with module API version
|
||||||
|
table->v1.hookJniNativeMethods = &hookJniNativeMethods;
|
||||||
|
table->v1.pltHookRegister = [](const char *path, const char *symbol, void *n, void **o) {
|
||||||
|
xhook_register(path, symbol, n, o);
|
||||||
|
};
|
||||||
|
table->v1.pltHookExclude = [](const char *path, const char *symbol) {
|
||||||
|
xhook_ignore(path, symbol);
|
||||||
|
};
|
||||||
|
table->v1.pltHookCommit = []() { return xhook_refresh(0) == 0; };
|
||||||
|
table->v1.connectCompanion = [](ZygiskModule *m) { return m->connectCompanion(); };
|
||||||
|
table->v1.forceDenyListUnmount = [](auto) { ZygiskModule::forceDenyListUnmount(); };
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ZygiskModule::connectCompanion() {
|
||||||
|
// TODO
|
||||||
|
(void) id;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZygiskModule::forceDenyListUnmount() {
|
||||||
|
if (g_ctx == nullptr)
|
||||||
|
return;
|
||||||
|
g_ctx->toggle_unmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HookContext::run_modules_pre(const vector<int> &fds) {
|
||||||
|
char buf[256];
|
||||||
|
for (int i = 0; i < fds.size(); ++i) {
|
||||||
|
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fds[i]);
|
||||||
|
if (void *h = dlopen(buf, RTLD_LAZY)) {
|
||||||
|
void (*module_entry)(void *, void *);
|
||||||
|
*(void **) &module_entry = dlsym(h, "zygisk_module_entry");
|
||||||
|
if (module_entry) {
|
||||||
|
modules.emplace_back(i);
|
||||||
|
auto api = new ApiTable(&modules.back());
|
||||||
|
module_entry(api, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fds[i]);
|
||||||
|
}
|
||||||
|
for (auto &m : modules) {
|
||||||
|
if (flags[APP_SPECIALIZE]) {
|
||||||
|
m.preAppSpecialize(args);
|
||||||
|
} else if (flags[SERVER_SPECIALIZE]) {
|
||||||
|
m.preServerSpecialize(server_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HookContext::run_modules_post() {
|
||||||
|
for (auto &m : modules) {
|
||||||
|
if (flags[APP_SPECIALIZE]) {
|
||||||
|
m.postAppSpecialize(args);
|
||||||
|
} else if (flags[SERVER_SPECIALIZE]) {
|
||||||
|
m.postServerSpecialize(server_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HookContext::close_fds() {
|
void HookContext::close_fds() {
|
||||||
close(logd_fd.exchange(-1));
|
close(logd_fd.exchange(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HookContext::toggle_unmount() {
|
||||||
|
if (flags[APP_SPECIALIZE]) {
|
||||||
|
// TODO: Handle MOUNT_EXTERNAL_NONE
|
||||||
|
flags[UNMOUNT_FLAG] = args->mount_external != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|
||||||
void HookContext::nativeSpecializeAppProcess_pre() {
|
void HookContext::nativeSpecializeAppProcess_pre() {
|
||||||
@ -286,14 +352,12 @@ void HookContext::nativeSpecializeAppProcess_pre() {
|
|||||||
VLOG("zygisk: pre specialize [%s]\n", process);
|
VLOG("zygisk: pre specialize [%s]\n", process);
|
||||||
}
|
}
|
||||||
|
|
||||||
remote_get_app_info(args->uid, process, &info);
|
auto module_fds = remote_get_info(args->uid, process, &info);
|
||||||
|
if (info.on_denylist) {
|
||||||
/* TODO: Handle MOUNT_EXTERNAL_NONE */
|
|
||||||
if (args->mount_external != 0 && info.on_denylist) {
|
|
||||||
LOGI("zygisk: [%s] is on the denylist\n", process);
|
LOGI("zygisk: [%s] is on the denylist\n", process);
|
||||||
flags[DENY_FLAG] = true;
|
toggle_unmount();
|
||||||
} else {
|
} else {
|
||||||
run_modules_pre();
|
run_modules_pre(module_fds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +369,7 @@ void HookContext::nativeSpecializeAppProcess_post() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(args->nice_name, process);
|
env->ReleaseStringUTFChars(args->nice_name, process);
|
||||||
if (flags[DENY_FLAG]) {
|
if (info.on_denylist) {
|
||||||
self_unload();
|
self_unload();
|
||||||
} else {
|
} else {
|
||||||
run_modules_post();
|
run_modules_post();
|
||||||
@ -325,7 +389,7 @@ void HookContext::nativeForkSystemServer_pre() {
|
|||||||
flags[SERVER_SPECIALIZE] = true;
|
flags[SERVER_SPECIALIZE] = true;
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
VLOG("zygisk: pre forkSystemServer\n");
|
VLOG("zygisk: pre forkSystemServer\n");
|
||||||
run_modules_pre();
|
run_modules_pre(remote_get_info(1000, "system_server", &info));
|
||||||
close_fds();
|
close_fds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
117
native/jni/zygisk/module.hpp
Normal file
117
native/jni/zygisk/module.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "api.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using module_abi_v1 = zygisk::internal::module_abi;
|
||||||
|
struct HookContext;
|
||||||
|
struct ApiTable;
|
||||||
|
|
||||||
|
struct AppSpecializeArgsImpl {
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* Optional */
|
||||||
|
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;
|
||||||
|
|
||||||
|
AppSpecializeArgsImpl(
|
||||||
|
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) :
|
||||||
|
uid(uid), gid(gid), gids(gids), runtime_flags(runtime_flags),
|
||||||
|
mount_external(mount_external), se_info(se_info), nice_name(nice_name),
|
||||||
|
instruction_set(instruction_set), app_data_dir(app_data_dir) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ServerSpecializeArgsImpl {
|
||||||
|
jint &uid;
|
||||||
|
jint &gid;
|
||||||
|
jintArray &gids;
|
||||||
|
jint &runtime_flags;
|
||||||
|
jlong &permitted_capabilities;
|
||||||
|
jlong &effective_capabilities;
|
||||||
|
|
||||||
|
ServerSpecializeArgsImpl(
|
||||||
|
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) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct force_cast_wrapper {
|
||||||
|
template<typename U>
|
||||||
|
operator U() const { return reinterpret_cast<U>(mX); }
|
||||||
|
force_cast_wrapper(T &&x) : mX(std::forward<T>(x)) {}
|
||||||
|
force_cast_wrapper &operator=(const force_cast_wrapper &) = delete;
|
||||||
|
private:
|
||||||
|
T &&mX;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
force_cast_wrapper<R> force_cast(R &&x) {
|
||||||
|
return force_cast_wrapper<R>(std::forward<R>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ZygiskModule {
|
||||||
|
void preAppSpecialize(AppSpecializeArgsImpl *args) {
|
||||||
|
v1->preAppSpecialize(v1->_this, force_cast(args));
|
||||||
|
}
|
||||||
|
void postAppSpecialize(const AppSpecializeArgsImpl *args) {
|
||||||
|
v1->postAppSpecialize(v1->_this, force_cast(args));
|
||||||
|
}
|
||||||
|
void preServerSpecialize(ServerSpecializeArgsImpl *args) {
|
||||||
|
v1->preServerSpecialize(v1->_this, force_cast(args));
|
||||||
|
}
|
||||||
|
void postServerSpecialize(const ServerSpecializeArgsImpl *args) {
|
||||||
|
v1->postServerSpecialize(v1->_this, force_cast(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
int connectCompanion();
|
||||||
|
static void forceDenyListUnmount();
|
||||||
|
static bool registerModule(ApiTable *table, long *module);
|
||||||
|
|
||||||
|
ZygiskModule(int id) : id(id) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int id;
|
||||||
|
union {
|
||||||
|
long *ver = nullptr;
|
||||||
|
module_abi_v1 *v1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApiTable {
|
||||||
|
ZygiskModule *module;
|
||||||
|
bool (*registerModule)(ApiTable *, long *);
|
||||||
|
|
||||||
|
union {
|
||||||
|
void *padding[6] = {};
|
||||||
|
struct {
|
||||||
|
void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
|
||||||
|
void (*pltHookRegister)(const char *, const char *, void *, void **);
|
||||||
|
void (*pltHookExclude)(const char *, const char *);
|
||||||
|
bool (*pltHookCommit)();
|
||||||
|
|
||||||
|
int (*connectCompanion)(ZygiskModule *);
|
||||||
|
void (*forceDenyListUnmount)(ZygiskModule *);
|
||||||
|
} v1;
|
||||||
|
};
|
||||||
|
ApiTable(ZygiskModule *m) : module(m), registerModule(&ZygiskModule::registerModule) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define INJECT_ENV_1 "MAGISK_INJ_1"
|
#define INJECT_ENV_1 "MAGISK_INJ_1"
|
||||||
#define INJECT_ENV_2 "MAGISK_INJ_2"
|
#define INJECT_ENV_2 "MAGISK_INJ_2"
|
||||||
|
|
||||||
enum : int {
|
enum : int {
|
||||||
ZYGISK_SETUP,
|
ZYGISK_SETUP,
|
||||||
ZYGISK_GET_APPINFO,
|
ZYGISK_GET_INFO,
|
||||||
ZYGISK_UNMOUNT,
|
ZYGISK_UNMOUNT,
|
||||||
ZYGISK_GET_LOG_PIPE,
|
ZYGISK_GET_LOG_PIPE,
|
||||||
};
|
};
|
||||||
@ -33,5 +34,5 @@ struct AppInfo {
|
|||||||
void self_unload();
|
void self_unload();
|
||||||
void hook_functions();
|
void hook_functions();
|
||||||
bool unhook_functions();
|
bool unhook_functions();
|
||||||
void remote_get_app_info(int uid, const char *process, AppInfo *info);
|
std::vector<int> remote_get_info(int uid, const char *process, AppInfo *info);
|
||||||
int remote_request_unmount();
|
int remote_request_unmount();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user