diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index e2b611ae2..96debcf1e 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -17,8 +17,13 @@ using namespace std; -vector hide_list; +// Protect access to both hide_list and hide_uid pthread_mutex_t list_lock; +vector hide_list; +set hide_uid; + +// Treat GMS separately as we're only interested in one component +int gms_uid = -1; static pthread_t proc_monitor_thread; @@ -157,6 +162,24 @@ static void kill_process(const char *name) { *slash = '/'; } +static int add_pkg_uid(const char *proc) { + char path[4096]; + struct stat st; + const char *data = SDK_INT >= 24 ? "/data/user_de/0" : "/data/data"; + sprintf(path, "%s/%s", data, proc); + if (xstat(path, &st) == 0) { + hide_uid.insert(st.st_uid); + return st.st_uid; + } + return -1; +} + +void refresh_uid() { + hide_uid.clear(); + for (auto &s : hide_list) + add_pkg_uid(s.c_str()); +} + void clean_magisk_props() { LOGD("hide_utils: Cleaning magisk props\n"); getprop([](const char *name, auto, auto) -> void { @@ -167,25 +190,25 @@ void clean_magisk_props() { int add_list(const char *proc) { for (auto &s : hide_list) { - // They should be unique if (s == proc) return HIDE_ITEM_EXIST; } - LOGI("hide_list add: [%s]\n", proc); - // Add to database char sql[4096]; snprintf(sql, sizeof(sql), "INSERT INTO hidelist (process) VALUES('%s')", proc); char *err = db_exec(sql); db_err_cmd(err, return DAEMON_ERROR); + LOGI("hide_list add: [%s]\n", proc); + // Critical region pthread_mutex_lock(&list_lock); hide_list.emplace_back(proc); - kill_process(proc); + add_pkg_uid(proc); pthread_mutex_unlock(&list_lock); + kill_process(proc); return DAEMON_SUCCESS; } @@ -193,29 +216,27 @@ int add_list(int client) { char *proc = read_string(client); int ret = add_list(proc); free(proc); - - // Update inotify list - update_apk_list(); - + update_inotify_mask(); return ret; } static int rm_list(const char *proc) { - // Update list in critical region - bool do_rm = false; + // Critical region + bool remove = false; pthread_mutex_lock(&list_lock); for (auto it = hide_list.begin(); it != hide_list.end(); ++it) { if (*it == proc) { - do_rm = true; + remove = true; LOGI("hide_list rm: [%s]\n", proc); hide_list.erase(it); - kill_process(proc); break; } } + if (remove) + refresh_uid(); pthread_mutex_unlock(&list_lock); - if (do_rm) { + if (remove) { char sql[4096]; snprintf(sql, sizeof(sql), "DELETE FROM hidelist WHERE process='%s'", proc); char *err = db_exec(sql); @@ -230,26 +251,30 @@ int rm_list(int client) { char *proc = read_string(client); int ret = rm_list(proc); free(proc); - - // Update inotify list - update_apk_list(); - + update_inotify_mask(); return ret; } +int init_list(void *, int, char **data, char**) { + LOGI("hide_list init: [%s]\n", *data); + hide_list.emplace_back(*data); + kill_process(*data); + int uid = add_pkg_uid(*data); + if (strcmp(*data, SAFETYNET_PKG) == 0) + gms_uid = uid; + return 0; +} + +void init_list(const char *proc) { + init_list(nullptr, 0, (char **) &proc, nullptr); +} + #define LEGACY_LIST MODULEROOT "/.core/hidelist" -static int collect_list(void *, int, char **data, char**) { - LOGI("hide_list: [%s]\n", data[0]); - hide_list.push_back(data[0]); - kill_process(data[0]); - return 0; -} - bool init_list() { LOGD("hide_list: initialize\n"); - char *err = db_exec("SELECT process FROM hidelist", collect_list); + char *err = db_exec("SELECT process FROM hidelist", init_list); db_err_cmd(err, return false); // Migrate old hide list into database @@ -260,6 +285,12 @@ bool init_list() { unlink(LEGACY_LIST); } + // Add SafetyNet by default + rm_list(SAFETYNET_PROCESS); + rm_list(SAFETYNET_COMPONENT); + init_list(SAFETYNET_PKG); + + update_inotify_mask(); return true; } @@ -300,11 +331,6 @@ int launch_magiskhide(int client) { if (!init_list()) goto error; - // Add SafetyNet by default - rm_list(SAFETYNET_PROCESS); - rm_list(SAFETYNET_COMPONENT); - add_list(SAFETYNET_PKG); - // Get thread reference proc_monitor_thread = pthread_self(); if (client >= 0) { diff --git a/native/jni/magiskhide/magiskhide.h b/native/jni/magiskhide/magiskhide.h index 6cd158ffa..9e80084aa 100644 --- a/native/jni/magiskhide/magiskhide.h +++ b/native/jni/magiskhide/magiskhide.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "daemon.h" @@ -21,7 +22,7 @@ int rm_list(int client); void ls_list(int client); // Update APK list for inotify -void update_apk_list(); +void update_inotify_mask(); // Process monitor void proc_monitor(); @@ -30,14 +31,13 @@ void proc_monitor(); void manage_selinux(); void hide_sensitive_props(); void clean_magisk_props(); - -// List managements -int add_list(const char *proc); -bool init_list(); +void refresh_uid(); extern bool hide_enabled; extern pthread_mutex_t list_lock; extern std::vector hide_list; +extern std::set hide_uid; +extern int gms_uid; enum { LAUNCH_MAGISKHIDE, diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp index 2653caf4a..a76b2288b 100644 --- a/native/jni/magiskhide/proc_monitor.cpp +++ b/native/jni/magiskhide/proc_monitor.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -33,15 +32,21 @@ using namespace std; extern char *system_block, *vendor_block, *data_block; +static int inotify_fd = -1; + #define EVENT_SIZE sizeof(struct inotify_event) #define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) +#define __ALIGN_EVENT __attribute__ ((aligned(__alignof__(struct inotify_event)))) // Workaround for the lack of pthread_cancel static void term_thread(int) { LOGD("proc_monitor: running cleanup\n"); hide_list.clear(); + hide_uid.clear(); hide_enabled = false; pthread_mutex_destroy(&list_lock); + close(inotify_fd); + inotify_fd = -1; LOGD("proc_monitor: terminating\n"); pthread_exit(nullptr); } @@ -164,21 +169,16 @@ static inline int fast_atoi(const char *str) { static DIR *dfd; // Use unordered map with pid and namespace inode number to avoid time-consuming GC static unordered_map pid_ns_map; -// Use set for slow insertion but fast searching(which we'd encounter a lot more) -static set hide_uid; -// Treat GMS separately as we're only interested in one component -static int gms_uid = -1; static void detect_new_processes() { struct dirent *dp; struct stat ns, pns; int pid, ppid; - bool hide; uid_t uid; - unordered_map::const_iterator pos; // Iterate through /proc and get a process that reads the target APK rewinddir(dfd); + pthread_mutex_lock(&list_lock); while ((dp = readdir(dfd))) { if (!isdigit(dp->d_name[0])) continue; @@ -191,23 +191,16 @@ static void detect_new_processes() { continue; uid = get_uid(pid) % 100000; // Handle multiuser - if (hide_uid.find(uid) != hide_uid.end()) { + bool is_target = hide_uid.count(uid) != 0; + if (is_target) { // Make sure our target is alive if ((ppid = parse_ppid(pid)) < 0 || read_ns(ppid, &pns) || read_ns(pid, &ns)) continue; // Check if it's a process we haven't already hijacked - hide = false; - pos = pid_ns_map.find(pid); - if (pos == pid_ns_map.end()) { - hide = true; - pid_ns_map.insert(pair(pid, ns.st_ino)); - } else if (pos->second != ns.st_ino) { - hide = true; - pid_ns_map[pos->first] = ns.st_ino; - } - - if (hide) { + auto pos = pid_ns_map.find(pid); + if (pos == pid_ns_map.end() || pos->second != ns.st_ino) { + pid_ns_map[pid] = ns.st_ino; if (uid == gms_uid) { // Check /proc/uid/cmdline to see if it's SAFETYNET_PROCESS if (!is_pid_safetynet_process(pid)) @@ -225,15 +218,15 @@ static void detect_new_processes() { * We have to fork a new process, setns, then do the unmounts */ LOGI("proc_monitor: UID=[%ju] PID=[%d] ns=[%llu]\n", - (uintmax_t)uid, pid, ns.st_ino); + (uintmax_t)uid, pid, ns.st_ino); if (fork_dont_care() == 0) hide_daemon(pid); } } } + pthread_mutex_unlock(&list_lock); } -static int inotify_fd = 0; static void listdir_apk(const char *name) { DIR *dir; struct dirent *entry; @@ -260,9 +253,9 @@ static void listdir_apk(const char *name) { // Compare with (path + 10) to trim "/data/app/" if (strncmp(path + 10, s.c_str(), s.length()) == 0) { if (inotify_add_watch(inotify_fd, path, IN_OPEN | IN_DELETE) > 0) { - LOGI("proc_monitor: Monitoring %s\n", path, inotify_fd); + LOGI("proc_monitor: Monitoring %s\n", path); } else { - LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno)); + LOGE("proc_monitor: Failed to monitor %s: %s\n", path, strerror(errno)); } break; } @@ -275,58 +268,12 @@ static void listdir_apk(const char *name) { closedir(dir); } -static void update_pkg_list() { - DIR *dir; - struct dirent *entry; - struct stat st; - char path[4096]; - const char* target; - const char data_path[] = "/data/data"; - - if (!(dir = opendir(data_path))) - return; - - pthread_mutex_lock(&list_lock); - for (auto &s : hide_list) - LOGD("proc_monitor: hide_list: %s\n", s.c_str()); - pthread_mutex_unlock(&list_lock); - - hide_uid.clear(); - - while ((entry = readdir(dir)) != NULL) { - snprintf(path, sizeof(path), "%s/%s", data_path, - entry->d_name); - - if (entry->d_type == DT_DIR) { - pthread_mutex_lock(&list_lock); - for (auto &s : hide_list) { - target = s.c_str(); - if (strcmp(entry->d_name, target) == 0) { - if (stat(path, &st) == -1) - continue; - - LOGI("proc_monitor: %s UID is %d\n", target, st.st_uid); - hide_uid.insert(st.st_uid); - - if (strcmp(entry->d_name, SAFETYNET_PKG) == 0) { - LOGI("proc_monitor: Got GMS: %d\n", st.st_uid); - gms_uid = st.st_uid; - } - } - } - pthread_mutex_unlock(&list_lock); - } - } - - closedir(dir); -} - // Iterate through /data/app and search all .apk files -void update_apk_list() { +void update_inotify_mask() { // Setup inotify const char data_app[] = "/data/app"; - if (inotify_fd) + if (inotify_fd >= 0) close(inotify_fd); inotify_fd = inotify_init(); @@ -344,9 +291,6 @@ void update_apk_list() { } else { LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno)); } - - // Update pkg_uid_map by reading from /data/data - update_pkg_list(); } void proc_monitor() { @@ -366,8 +310,6 @@ void proc_monitor() { term_thread(TERM_THREAD); } - update_apk_list(); - if ((dfd = opendir("/proc")) == NULL) { LOGE("proc_monitor: Unable to open /proc\n"); term_thread(TERM_THREAD); @@ -380,11 +322,11 @@ void proc_monitor() { struct inotify_event *event; ssize_t len; char *p; - char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event)))); + char buffer[EVENT_BUF_LEN] __ALIGN_EVENT; for (;;) { len = read(inotify_fd, buffer, EVENT_BUF_LEN); if (len == -1) { - LOGE("proc_monitor: failed to read from inotify: %s\n", strerror(errno)); + PLOGE("proc_monitor: read inotify"); sleep(1); continue; } @@ -399,7 +341,10 @@ void proc_monitor() { detect_new_processes(); } else { LOGI("proc_monitor: inotify: /data/app change detected\n"); - update_apk_list(); + pthread_mutex_lock(&list_lock); + refresh_uid(); + pthread_mutex_unlock(&list_lock); + update_inotify_mask(); break; } diff --git a/native/jni/utils/file.cpp b/native/jni/utils/file.cpp index 719b6a403..f5b8994a7 100644 --- a/native/jni/utils/file.cpp +++ b/native/jni/utils/file.cpp @@ -385,7 +385,7 @@ void write_zero(int fd, size_t size) { } vector file_to_vector(const char *filename) { - auto arr = vector(); + vector arr; if (access(filename, R_OK) != 0) return arr; char *line = nullptr;