From 003fea52b1857015bfc3988de56eb1a6d3049a7f Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 18 Aug 2021 02:01:54 -0700 Subject: [PATCH] Remove all non-Magisk hiding code Magisk no longer interferes with any signals/info that were not created or caused by Magisk itself. --- native/jni/core/bootstages.cpp | 6 +- native/jni/include/daemon.hpp | 4 +- native/jni/magiskhide/hide_policy.cpp | 79 +---- native/jni/magiskhide/hide_utils.cpp | 174 +++++------ native/jni/magiskhide/magiskhide.cpp | 39 ++- native/jni/magiskhide/magiskhide.hpp | 29 +- native/jni/magiskhide/proc_monitor.cpp | 409 ------------------------- native/jni/utils/misc.hpp | 3 + native/jni/zygisk/hook.cpp | 3 - 9 files changed, 110 insertions(+), 636 deletions(-) delete mode 100644 native/jni/magiskhide/proc_monitor.cpp diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 336a4b0ea..d0a578d74 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -298,10 +298,10 @@ void post_fs_data(int client) { safe_mode = true; // Disable all modules and magiskhide so next boot will be clean disable_modules(); - stop_magiskhide(); + disable_hide(); } else { exec_common_scripts("post-fs-data"); - auto_start_magiskhide(false); + check_enable_hide(); handle_modules(); } @@ -350,7 +350,7 @@ void boot_complete(int client) { if (access(SECURE_DIR, F_OK) != 0) xmkdir(SECURE_DIR, 0700); - auto_start_magiskhide(true); + check_enable_hide(); if (!check_manager()) { if (access(MANAGERAPK, F_OK) == 0) { diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index 53dc6afc9..635d3a846 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -55,5 +55,5 @@ void magiskhide_handler(int client, ucred *cred); void su_daemon_handler(int client, ucred *credential); // MagiskHide -void auto_start_magiskhide(bool late_props); -int stop_magiskhide(); +void check_enable_hide(); +int disable_hide(); diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp index 977bdb96e..85e8e07e5 100644 --- a/native/jni/magiskhide/hide_policy.cpp +++ b/native/jni/magiskhide/hide_policy.cpp @@ -9,69 +9,6 @@ using namespace std; -static const char *prop_key[] = - { "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked", - "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit", - "ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", - "ro.vendor.boot.warranty_bit", "ro.vendor.warranty_bit", - "vendor.boot.vbmeta.device_state", nullptr }; - -static const char *prop_val[] = - { "locked", "green", "1", - "enforcing", "0", "0", - "0", "1", "user", "release-keys", - "0", "0", - "locked", nullptr }; - -static const char *late_prop_key[] = - { "vendor.boot.verifiedbootstate", nullptr }; - -static const char *late_prop_val[] = - { "green", nullptr }; - -void hide_sensitive_props() { - LOGI("hide: Hiding sensitive props\n"); - - for (int i = 0; prop_key[i]; ++i) { - auto value = getprop(prop_key[i]); - if (!value.empty() && value != prop_val[i]) - setprop(prop_key[i], prop_val[i], false); - } - - // Hide that we booted from recovery when magisk is in recovery mode - auto bootmode = getprop("ro.bootmode"); - if (!bootmode.empty() && str_contains(bootmode, "recovery")) - setprop("ro.bootmode", "unknown", false); - bootmode = getprop("ro.boot.mode"); - if (!bootmode.empty() && str_contains(bootmode, "recovery")) - setprop("ro.boot.mode", "unknown", false); - bootmode = getprop("vendor.boot.mode"); - if (!bootmode.empty() && str_contains(bootmode, "recovery")) - setprop("vendor.boot.mode", "unknown", false); - - // Xiaomi cross region flash - auto hwc = getprop("ro.boot.hwc"); - if (!hwc.empty() && str_contains(hwc, "CN")) - setprop("ro.boot.hwc", "GLOBAL", false); - auto hwcountry = getprop("ro.boot.hwcountry"); - if (!hwcountry.empty() && str_contains(hwcountry, "China")) - setprop("ro.boot.hwcountry", "GLOBAL", false); - - auto selinux = getprop("ro.build.selinux"); - if (!selinux.empty()) - delprop("ro.build.selinux"); -} - -void hide_late_sensitive_props() { - LOGI("hide: Hiding sensitive props (late)\n"); - - for (int i = 0; late_prop_key[i]; ++i) { - auto value = getprop(late_prop_key[i]); - if (!value.empty() && value != late_prop_val[i]) - setprop(late_prop_key[i], late_prop_val[i], false); - } -} - static void lazy_unmount(const char* mountpoint) { if (umount2(mountpoint, MNT_DETACH) != -1) LOGD("hide: Unmounted (%s)\n", mountpoint); @@ -86,8 +23,7 @@ void hide_daemon(int pid) { } } -#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && \ -strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0) +#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && str_starts(mentry->mnt_dir, "/" #dir)) void hide_unmount(int pid) { if (pid > 0 && switch_mnt_ns(pid)) @@ -95,16 +31,6 @@ void hide_unmount(int pid) { LOGD("hide: handling PID=[%d]\n", pid); - char val; - int fd = xopen(SELINUX_ENFORCE, O_RDONLY); - xxread(fd, &val, sizeof(val)); - close(fd); - // Permissive - if (val == '0') { - chmod(SELINUX_ENFORCE, 0640); - chmod(SELINUX_POLICY, 0440); - } - vector targets; // Unmount dummy skeletons and /sbin links @@ -121,7 +47,7 @@ void hide_unmount(int pid) { // Unmount all Magisk created mounts parse_mnt("/proc/self/mounts", [&](mntent *mentry) { - if (strstr(mentry->mnt_fsname, BLOCKDIR)) + if (str_contains(mentry->mnt_fsname, BLOCKDIR)) targets.emplace_back(mentry->mnt_dir); return true; }); @@ -129,4 +55,3 @@ void hide_unmount(int pid) { for (auto &s : reversed(targets)) lazy_unmount(s.data()); } - diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index a18d8ef60..bd3c1110c 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -13,16 +13,16 @@ using namespace std; -static bool hide_state = false; -static set> hide_set; /* set of pair */ -map> uid_proc_map; /* uid -> list of process */ +static set> *hide_set; /* set of pair */ +static map> *uid_proc_map; /* uid -> list of process */ // Locks the variables above -pthread_mutex_t hide_state_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t hide_data_lock = PTHREAD_MUTEX_INITIALIZER; -void update_uid_map() { - mutex_guard lock(hide_state_lock); - uid_proc_map.clear(); +static atomic hide_state = false; + +static void rebuild_uid_map() { + uid_proc_map->clear(); string data_path(APP_DATA_DIR); size_t len = data_path.length(); auto dir = open_dir(APP_DATA_DIR); @@ -34,17 +34,17 @@ void update_uid_map() { data_path += '/'; size_t user_len = data_path.length(); struct stat st; - for (auto &hide : hide_set) { + for (const auto &hide : *hide_set) { if (hide.first == ISOLATED_MAGIC) { if (!first_iter) continue; // Setup isolated processes - uid_proc_map[-1].emplace_back(hide.second); + (*uid_proc_map)[-1].emplace_back(hide.second); } data_path.resize(user_len); data_path += hide.first; if (stat(data_path.data(), &st)) continue; - uid_proc_map[st.st_uid].emplace_back(hide.second); + (*uid_proc_map)[st.st_uid].emplace_back(hide.second); } first_iter = false; } @@ -52,26 +52,19 @@ void update_uid_map() { // Leave /proc fd opened as we're going to read from it repeatedly static DIR *procfp; -void crawl_procfs(const function &fn) { - rewinddir(procfp); - crawl_procfs(procfp, fn); -} -void crawl_procfs(DIR *dir, const function &fn) { - struct dirent *dp; +template +static void crawl_procfs(F &&fn) { + rewinddir(procfp); + dirent *dp; int pid; - while ((dp = readdir(dir))) { + while ((dp = readdir(procfp))) { pid = parse_int(dp->d_name); if (pid > 0 && !fn(pid)) break; } } -bool hide_enabled() { - mutex_guard g(hide_state_lock); - return hide_state; -} - template static bool proc_name_match(int pid, const char *name) { char buf[4019]; @@ -86,12 +79,12 @@ static bool proc_name_match(int pid, const char *name) { return false; } -static inline bool str_eql(string_view s, string_view ss) { return s == ss; } +static inline bool str_eql(string_view a, string_view b) { return a == b; } -static void kill_process(const char *name, bool multi = false, - bool (*filter)(int, const char *) = proc_name_match<&str_eql>) { +template +static void kill_process(const char *name, bool multi = false) { crawl_procfs([=](int pid) -> bool { - if (filter(pid, name)) { + if (proc_name_match(pid, name)) { kill(pid, SIGKILL); return multi; } @@ -137,10 +130,10 @@ static bool validate(const char *pkg, const char *proc) { static void add_hide_set(const char *pkg, const char *proc) { LOGI("hide_list add: [%s/%s]\n", pkg, proc); - hide_set.emplace(pkg, proc); + hide_set->emplace(pkg, proc); if (strcmp(pkg, ISOLATED_MAGIC) == 0) { // Kill all matching isolated processes - kill_process(proc, true, proc_name_match<&str_starts>); + kill_process<&str_starts>(proc, true); } else { kill_process(proc); } @@ -153,9 +146,15 @@ static int add_list(const char *pkg, const char *proc) { if (!validate(pkg, proc)) return HIDE_INVALID_PKG; - for (auto &hide : hide_set) - if (hide.first == pkg && hide.second == proc) - return HIDE_ITEM_EXIST; + { + // Critical region + mutex_guard lock(hide_data_lock); + for (const auto &hide : *hide_set) + if (hide.first == pkg && hide.second == proc) + return HIDE_ITEM_EXIST; + add_hide_set(pkg, proc); + rebuild_uid_map(); + } // Add to database char sql[4096]; @@ -163,42 +162,33 @@ static int add_list(const char *pkg, const char *proc) { "INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc); char *err = db_exec(sql); db_err_cmd(err, return DAEMON_ERROR); - - { - // Critical region - mutex_guard lock(hide_state_lock); - add_hide_set(pkg, proc); - } - return DAEMON_SUCCESS; } int add_list(int client) { string pkg = read_string(client); string proc = read_string(client); - int ret = add_list(pkg.data(), proc.data()); - if (ret == DAEMON_SUCCESS) - update_uid_map(); - return ret; + return add_list(pkg.data(), proc.data()); } static int rm_list(const char *pkg, const char *proc) { bool remove = false; { // Critical region - mutex_guard lock(hide_state_lock); - for (auto it = hide_set.begin(); it != hide_set.end();) { + mutex_guard lock(hide_data_lock); + for (auto it = hide_set->begin(); it != hide_set->end();) { if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) { remove = true; LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data()); - it = hide_set.erase(it); + it = hide_set->erase(it); } else { ++it; } } + if (!remove) + return HIDE_ITEM_NOT_EXIST; + rebuild_uid_map(); } - if (!remove) - return HIDE_ITEM_NOT_EXIST; char sql[4096]; if (proc[0] == '\0') @@ -207,17 +197,14 @@ static int rm_list(const char *pkg, const char *proc) { snprintf(sql, sizeof(sql), "DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc); char *err = db_exec(sql); - db_err(err); + db_err_cmd(err, return DAEMON_ERROR); return DAEMON_SUCCESS; } int rm_list(int client) { string pkg = read_string(client); string proc = read_string(client); - int ret = rm_list(pkg.data(), proc.data()); - if (ret == DAEMON_SUCCESS) - update_uid_map(); - return ret; + return rm_list(pkg.data(), proc.data()); } static bool str_ends_safe(string_view s, string_view ss) { @@ -227,9 +214,6 @@ static bool str_ends_safe(string_view s, string_view ss) { return str_ends(s, ss); } -#define SNET_PROC "com.google.android.gms.unstable" -#define GMS_PKG "com.google.android.gms" - static bool init_list() { LOGD("hide: initialize\n"); @@ -243,27 +227,22 @@ static bool init_list() { if (SDK_INT >= 29) { kill_process("usap32", true); kill_process("usap64", true); - kill_process("_zygote", true, proc_name_match<&str_ends_safe>); + kill_process<&str_ends_safe>("_zygote", true); } - // Add SafetyNet by default - add_hide_set(GMS_PKG, SNET_PROC); - - // We also need to hide the default GMS process if MAGISKTMP != /sbin - // The snet process communicates with the main process and get additional info - if (MAGISKTMP != "/sbin") - add_hide_set(GMS_PKG, GMS_PKG); - return true; } void ls_list(int client) { write_int(client, DAEMON_SUCCESS); - for (auto &hide : hide_set) { - write_int(client, hide.first.size() + hide.second.size() + 1); - xwrite(client, hide.first.data(), hide.first.size()); - xwrite(client, "|", 1); - xwrite(client, hide.second.data(), hide.second.size()); + { + mutex_guard lock(hide_data_lock); + for (const auto &hide : *hide_set) { + write_int(client, hide.first.size() + hide.second.size() + 1); + xwrite(client, hide.first.data(), hide.first.size()); + xwrite(client, "|", 1); + xwrite(client, hide.second.data(), hide.second.size()); + } } write_int(client, 0); close(client); @@ -272,14 +251,12 @@ void ls_list(int client) { static void update_hide_config() { char sql[64]; sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)", - DB_SETTING_KEYS[HIDE_CONFIG], hide_state); + DB_SETTING_KEYS[HIDE_CONFIG], hide_state.load()); char *err = db_exec(sql); db_err(err); } -int launch_magiskhide(bool late_props) { - mutex_guard lock(hide_state_lock); - +int enable_hide() { if (hide_state) return HIDE_IS_ENABLED; @@ -291,31 +268,30 @@ int launch_magiskhide(bool late_props) { LOGI("* Enable MagiskHide\n"); + mutex_guard lock(hide_data_lock); + default_new(hide_set); + default_new(uid_proc_map); + // Initialize the hide list if (!init_list()) return DAEMON_ERROR; - hide_sensitive_props(); - if (late_props) - hide_late_sensitive_props(); - hide_state = true; update_hide_config(); - // Unlock here or else we'll be stuck in deadlock - lock.unlock(); - - update_uid_map(); + rebuild_uid_map(); return DAEMON_SUCCESS; } -int stop_magiskhide() { - mutex_guard g(hide_state_lock); +int disable_hide() { + mutex_guard lock(hide_data_lock); if (hide_state) { LOGI("* Disable MagiskHide\n"); - uid_proc_map.clear(); - hide_set.clear(); + delete uid_proc_map; + delete hide_set; + uid_proc_map = nullptr; + hide_set = nullptr; } hide_state = false; @@ -323,43 +299,41 @@ int stop_magiskhide() { return DAEMON_SUCCESS; } -void auto_start_magiskhide(bool late_props) { - if (hide_enabled()) { - hide_late_sensitive_props(); - } else { +void check_enable_hide() { + if (!hide_state) { db_settings dbs; get_db_settings(dbs, HIDE_CONFIG); if (dbs[HIDE_CONFIG]) - launch_magiskhide(late_props); + enable_hide(); } } -bool is_hide_target(int uid, string_view process, int max_len) { - mutex_guard lock(hide_state_lock); +bool is_hide_target(int uid, string_view process) { + mutex_guard lock(hide_data_lock); if (uid % 100000 >= 90000) { // Isolated processes - auto it = uid_proc_map.find(-1); - if (it == uid_proc_map.end()) + auto it = uid_proc_map->find(-1); + if (it == uid_proc_map->end()) return false; for (auto &s : it->second) { - if (s.length() > max_len && process.length() > max_len && str_starts(s, process)) - return true; if (str_starts(process, s)) return true; } } else { - auto it = uid_proc_map.find(uid); - if (it == uid_proc_map.end()) + auto it = uid_proc_map->find(uid); + if (it == uid_proc_map->end()) return false; for (auto &s : it->second) { - if (s.length() > max_len && process.length() > max_len && str_starts(s, process)) - return true; if (s == process) return true; } } return false; } + +bool hide_enabled() { + return hide_state; +} diff --git a/native/jni/magiskhide/magiskhide.cpp b/native/jni/magiskhide/magiskhide.cpp index 54f024cd0..e095163f4 100644 --- a/native/jni/magiskhide/magiskhide.cpp +++ b/native/jni/magiskhide/magiskhide.cpp @@ -20,9 +20,6 @@ using namespace std; " ls Print the current hide list\n" " exec CMDs... Execute commands in isolated mount\n" " namespace and do all hide unmounts\n" -#ifdef MAGISK_DEBUG - " test Run process monitor test\n" -#endif , arg0); exit(1); } @@ -32,10 +29,10 @@ void magiskhide_handler(int client, ucred *cred) { int res = DAEMON_ERROR; switch (req) { - case STOP_MAGISKHIDE: - case ADD_HIDELIST: - case RM_HIDELIST: - case LS_HIDELIST: + case DISABLE_HIDE: + case ADD_LIST: + case RM_LIST: + case LS_LIST: if (!hide_enabled()) { write_int(client, HIDE_NOT_ENABLED); close(client); @@ -44,19 +41,19 @@ void magiskhide_handler(int client, ucred *cred) { } switch (req) { - case LAUNCH_MAGISKHIDE: - res = launch_magiskhide(true); + case ENABLE_HIDE: + res = enable_hide(); break; - case STOP_MAGISKHIDE: - res = stop_magiskhide(); + case DISABLE_HIDE: + res = disable_hide(); break; - case ADD_HIDELIST: + case ADD_LIST: res = add_list(client); break; - case RM_HIDELIST: + case RM_LIST: res = rm_list(client); break; - case LS_HIDELIST: + case LS_LIST: ls_list(client); return; case HIDE_STATUS: @@ -79,15 +76,15 @@ int magiskhide_main(int argc, char *argv[]) { int req; if (opt == "enable"sv) - req = LAUNCH_MAGISKHIDE; + req = ENABLE_HIDE; else if (opt == "disable"sv) - req = STOP_MAGISKHIDE; + req = DISABLE_HIDE; else if (opt == "add"sv) - req = ADD_HIDELIST; + req = ADD_LIST; else if (opt == "rm"sv) - req = RM_HIDELIST; + req = RM_LIST; else if (opt == "ls"sv) - req = LS_HIDELIST; + req = LS_LIST; else if (opt == "status"sv) req = HIDE_STATUS; else if (opt == "exec"sv && argc > 2) { @@ -104,7 +101,7 @@ int magiskhide_main(int argc, char *argv[]) { int fd = connect_daemon(); write_int(fd, MAGISKHIDE); write_int(fd, req); - if (req == ADD_HIDELIST || req == RM_HIDELIST) { + if (req == ADD_LIST || req == RM_LIST) { write_string(fd, argv[2]); write_string(fd, argv[3] ? argv[3] : ""); } @@ -141,7 +138,7 @@ int magiskhide_main(int argc, char *argv[]) { return DAEMON_ERROR; } - if (req == LS_HIDELIST) { + if (req == LS_LIST) { string res; for (;;) { read_string(fd, res); diff --git a/native/jni/magiskhide/magiskhide.hpp b/native/jni/magiskhide/magiskhide.hpp index 453ee040e..12ffa7dc6 100644 --- a/native/jni/magiskhide/magiskhide.hpp +++ b/native/jni/magiskhide/magiskhide.hpp @@ -1,48 +1,35 @@ #pragma once -#include -#include #include -#include -#include #include #include #include #include -#define SIGTERMTHRD SIGUSR1 #define ISOLATED_MAGIC "isolated" // CLI entries -int launch_magiskhide(bool late_props); -int stop_magiskhide(); +int enable_hide(); +int disable_hide(); int add_list(int client); int rm_list(int client); void ls_list(int client); // Utility functions -void crawl_procfs(const std::function &fn); -void crawl_procfs(DIR *dir, const std::function &fn); bool hide_enabled(); -void update_uid_map(); -bool is_hide_target(int uid, std::string_view process, int max_len = 1024); +bool is_hide_target(int uid, std::string_view process); // Hide policies void hide_daemon(int pid); void hide_unmount(int pid = -1); -void hide_sensitive_props(); -void hide_late_sensitive_props(); - -extern pthread_mutex_t hide_state_lock; -extern std::map> uid_proc_map; enum { - LAUNCH_MAGISKHIDE, - STOP_MAGISKHIDE, - ADD_HIDELIST, - RM_HIDELIST, - LS_HIDELIST, + ENABLE_HIDE, + DISABLE_HIDE, + ADD_LIST, + RM_LIST, + LS_LIST, HIDE_STATUS, }; diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp deleted file mode 100644 index fa43b981a..000000000 --- a/native/jni/magiskhide/proc_monitor.cpp +++ /dev/null @@ -1,409 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "magiskhide.hpp" - -using namespace std; - -static int inotify_fd = -1; - -static void new_zygote(int pid); - -pthread_t monitor_thread; - -/****************** - * Data structures - ******************/ - -#define PID_MAX 32768 -struct pid_set { - bitset::const_reference operator[](size_t pos) const { return set[pos - 1]; } - bitset::reference operator[](size_t pos) { return set[pos - 1]; } - void reset() { set.reset(); } -private: - bitset set; -}; - -// true if pid is monitored -static pid_set attaches; - -// zygote pid -> mnt ns -static map zygote_map; - -/******** - * Utils - ********/ - -static inline int read_ns(const int pid, struct stat *st) { - char path[32]; - sprintf(path, "/proc/%d/ns/mnt", pid); - return stat(path, st); -} - -static int parse_ppid(int pid) { - char path[32]; - int ppid; - - sprintf(path, "/proc/%d/stat", pid); - - auto stat = open_file(path, "re"); - if (!stat) - return -1; - - // PID COMM STATE PPID ..... - fscanf(stat.get(), "%*d %*s %*c %d", &ppid); - - return ppid; -} - -static bool is_zygote_done() { -#ifdef __LP64__ - return zygote_map.size() >= 2; -#else - return zygote_map.size() >= 1; -#endif -} - -static void check_zygote() { - crawl_procfs([](int pid) -> bool { - char buf[512]; - snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); - if (FILE *f = fopen(buf, "re")) { - fgets(buf, sizeof(buf), f); - if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1) - new_zygote(pid); - fclose(f); - } - return true; - }); - if (is_zygote_done()) { - // Stop periodic scanning - timeval val { .tv_sec = 0, .tv_usec = 0 }; - itimerval interval { .it_interval = val, .it_value = val }; - setitimer(ITIMER_REAL, &interval, nullptr); - } -} - -#define APP_PROC "/system/bin/app_process" - -static void setup_inotify() { - inotify_fd = xinotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - return; - - // Setup inotify asynchronous I/O - fcntl(inotify_fd, F_SETFL, O_ASYNC); - struct f_owner_ex ex = { - .type = F_OWNER_TID, - .pid = gettid() - }; - fcntl(inotify_fd, F_SETOWN_EX, &ex); - - // Monitor packages.xml - inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE); - - // Monitor app_process - if (access(APP_PROC "32", F_OK) == 0) { - inotify_add_watch(inotify_fd, APP_PROC "32", IN_ACCESS); - if (access(APP_PROC "64", F_OK) == 0) - inotify_add_watch(inotify_fd, APP_PROC "64", IN_ACCESS); - } else { - inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS); - } -} - -/************************ - * Async signal handlers - ************************/ - -static void inotify_event(int) { - // Make sure we can actually read stuffs - // or else the whole thread will be blocked. - struct pollfd pfd = { - .fd = inotify_fd, - .events = POLLIN, - .revents = 0 - }; - if (poll(&pfd, 1, 0) <= 0) - return; // Nothing to read - char buf[512]; - auto event = reinterpret_cast(buf); - read(inotify_fd, buf, sizeof(buf)); - if ((event->mask & IN_CLOSE_WRITE) && event->name == "packages.xml"sv) - update_uid_map(); - check_zygote(); -} - -static void term_thread(int) { - LOGD("proc_monitor: cleaning up\n"); - zygote_map.clear(); - attaches.reset(); - close(inotify_fd); - inotify_fd = -1; - // Restore all signal handlers that was set - sigset_t set; - sigfillset(&set); - pthread_sigmask(SIG_BLOCK, &set, nullptr); - struct sigaction act{}; - act.sa_handler = SIG_DFL; - sigaction(SIGTERMTHRD, &act, nullptr); - sigaction(SIGIO, &act, nullptr); - sigaction(SIGALRM, &act, nullptr); - LOGD("proc_monitor: terminate\n"); - pthread_exit(nullptr); -} - -/****************** - * Ptrace Madness - ******************/ - -// Ptrace is super tricky, preserve all excessive logging in code -// but disable when actually building for usage (you won't want -// your logcat spammed with new thread events from all apps) - -//#define PTRACE_LOG(fmt, args...) LOGD("PID=[%d] " fmt, pid, ##args) -#define PTRACE_LOG(...) - -static void detach_pid(int pid, int signal = 0) { - attaches[pid] = false; - ptrace(PTRACE_DETACH, pid, 0, signal); - PTRACE_LOG("detach\n"); -} - -static bool check_pid(int pid) { - char path[128]; - char cmdline[1024]; - struct stat st; - - sprintf(path, "/proc/%d", pid); - if (stat(path, &st)) { - // Process died unexpectedly, ignore - detach_pid(pid); - return true; - } - - int uid = st.st_uid; - - // UID hasn't changed - if (uid == 0) - return false; - - sprintf(path, "/proc/%d/cmdline", pid); - if (auto f = open_file(path, "re")) { - fgets(cmdline, sizeof(cmdline), f.get()); - } else { - // Process died unexpectedly, ignore - detach_pid(pid); - return true; - } - - if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv || - cmdline == "usap32"sv || cmdline == "usap64"sv) - return false; - - if (!is_hide_target(uid, cmdline, 95)) - goto not_target; - - // Ensure ns is separated - read_ns(pid, &st); - for (auto &zit : zygote_map) { - if (zit.second.st_ino == st.st_ino && - zit.second.st_dev == st.st_dev) { - // ns not separated, abort - LOGW("proc_monitor: skip [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid); - goto not_target; - } - } - - // Detach but the process should still remain stopped - // The hide daemon will resume the process after hiding it - LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid); - detach_pid(pid, SIGSTOP); - hide_daemon(pid); - return true; - -not_target: - PTRACE_LOG("[%s] is not our target\n", cmdline); - detach_pid(pid); - return true; -} - -static bool is_process(int pid) { - char buf[128]; - char key[32]; - int tgid; - sprintf(buf, "/proc/%d/status", pid); - auto fp = open_file(buf, "re"); - // PID is dead - if (!fp) - return false; - while (fgets(buf, sizeof(buf), fp.get())) { - sscanf(buf, "%s", key); - if (key == "Tgid:"sv) { - sscanf(buf, "%*s %d", &tgid); - return tgid == pid; - } - } - return false; -} - -static void new_zygote(int pid) { - struct stat st; - if (read_ns(pid, &st)) - return; - - auto it = zygote_map.find(pid); - if (it != zygote_map.end()) { - // Update namespace info - it->second = st; - return; - } - - LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid); - zygote_map[pid] = st; - - xptrace(PTRACE_ATTACH, pid); - - waitpid(pid, nullptr, __WALL | __WNOTHREAD); - xptrace(PTRACE_SETOPTIONS, pid, nullptr, - PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXIT); - xptrace(PTRACE_CONT, pid); -} - -#define DETACH_AND_CONT { detach_pid(pid); continue; } - -void proc_monitor() { - monitor_thread = pthread_self(); - - // Backup original mask - sigset_t orig_mask; - pthread_sigmask(SIG_SETMASK, nullptr, &orig_mask); - - sigset_t unblock_set; - sigemptyset(&unblock_set); - sigaddset(&unblock_set, SIGTERMTHRD); - sigaddset(&unblock_set, SIGIO); - sigaddset(&unblock_set, SIGALRM); - - struct sigaction act{}; - sigfillset(&act.sa_mask); - act.sa_handler = SIG_IGN; - sigaction(SIGTERMTHRD, &act, nullptr); - sigaction(SIGIO, &act, nullptr); - sigaction(SIGALRM, &act, nullptr); - - // Temporary unblock to clear pending signals - pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr); - pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr); - - act.sa_handler = term_thread; - sigaction(SIGTERMTHRD, &act, nullptr); - act.sa_handler = inotify_event; - sigaction(SIGIO, &act, nullptr); - act.sa_handler = [](int){ check_zygote(); }; - sigaction(SIGALRM, &act, nullptr); - - setup_inotify(); - - // First try find existing zygotes - check_zygote(); - if (!is_zygote_done()) { - // Periodic scan every 250ms - timeval val { .tv_sec = 0, .tv_usec = 250000 }; - itimerval interval { .it_interval = val, .it_value = val }; - setitimer(ITIMER_REAL, &interval, nullptr); - } - - for (int status;;) { - pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr); - - const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD); - if (pid < 0) { - if (errno == ECHILD) { - // Nothing to wait yet, sleep and wait till signal interruption - LOGD("proc_monitor: nothing to monitor, wait for signal\n"); - struct timespec ts = { - .tv_sec = INT_MAX, - .tv_nsec = 0 - }; - nanosleep(&ts, nullptr); - } - continue; - } - - pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr); - - if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */) - DETACH_AND_CONT; - - int event = WEVENT(status); - int signal = WSTOPSIG(status); - - if (signal == SIGTRAP && event) { - unsigned long msg; - xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg); - if (zygote_map.count(pid)) { - // Zygote event - switch (event) { - case PTRACE_EVENT_FORK: - case PTRACE_EVENT_VFORK: - PTRACE_LOG("zygote forked: [%lu]\n", msg); - attaches[msg] = true; - break; - case PTRACE_EVENT_EXIT: - PTRACE_LOG("zygote exited with status: [%lu]\n", msg); - [[fallthrough]]; - default: - zygote_map.erase(pid); - DETACH_AND_CONT; - } - } else { - switch (event) { - case PTRACE_EVENT_CLONE: - PTRACE_LOG("create new threads: [%lu]\n", msg); - if (attaches[pid] && check_pid(pid)) - continue; - break; - case PTRACE_EVENT_EXEC: - case PTRACE_EVENT_EXIT: - PTRACE_LOG("exit or execve\n"); - [[fallthrough]]; - default: - DETACH_AND_CONT; - } - } - xptrace(PTRACE_CONT, pid); - } else if (signal == SIGSTOP) { - if (!attaches[pid]) { - // Double check if this is actually a process - attaches[pid] = is_process(pid); - } - if (attaches[pid]) { - // This is a process, continue monitoring - PTRACE_LOG("SIGSTOP from child\n"); - xptrace(PTRACE_SETOPTIONS, pid, nullptr, - PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT); - xptrace(PTRACE_CONT, pid); - } else { - // This is a thread, do NOT monitor - PTRACE_LOG("SIGSTOP from thread\n"); - DETACH_AND_CONT; - } - } else { - // Not caused by us, resend signal - xptrace(PTRACE_CONT, pid, nullptr, signal); - PTRACE_LOG("signal [%d]\n", signal); - } - } -} diff --git a/native/jni/utils/misc.hpp b/native/jni/utils/misc.hpp index 4e28b83d5..da78bbe06 100644 --- a/native/jni/utils/misc.hpp +++ b/native/jni/utils/misc.hpp @@ -58,6 +58,9 @@ reversed_container reversed(T &base) { return reversed_container(base); } +template +static inline void default_new(T *&p) { p = new T(); } + template class stateless_allocator { public: diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index 6a8281edb..1c9f6657b 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -326,9 +326,6 @@ int hook_register(const char *path, const char *symbol, void *new_func, void **o } // namespace -template -static inline void default_new(T *&p) { p = new T(); } - #define XHOOK_REGISTER_SYM(PATH_REGEX, SYM, NAME) \ hook_register(PATH_REGEX, SYM, (void*) new_##NAME, (void **) &old_##NAME)