diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index 15000c64e..6c224596f 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -20,7 +20,6 @@ using namespace std; // 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; @@ -60,21 +59,6 @@ static void hide_sensitive_props() { } } -/* - * Bionic's atoi runs through strtol(). - * Use our own implementation for faster conversion. - */ -static inline int parse_int(const char *s) { - int val = 0; - char c; - while ((c = *(s++))) { - if (c > '9' || c < '0') - return -1; - val = val * 10 + c - '0'; - } - return val; -} - // Leave /proc fd opened as we're going to read from it repeatedly static DIR *procfp; void crawl_procfs(const function &fn) { @@ -148,22 +132,12 @@ static void kill_process(int uid) { }); } -static int add_pkg_uid(const char *pkg) { +static int get_pkg_uid(const char *pkg) { char path[4096]; struct stat st; const char *data = SDK_INT >= 24 ? "/data/user_de/0" : "/data/data"; sprintf(path, "%s/%s", data, pkg); - if (stat(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()); + return stat(path, &st) ? -1 : st.st_uid; } void clean_magisk_props() { @@ -192,7 +166,7 @@ int add_list(const char *pkg) { { MutexGuard lock(list_lock); hide_list.emplace_back(pkg); - uid = add_pkg_uid(pkg); + uid = get_pkg_uid(pkg); } kill_process(uid); @@ -235,17 +209,17 @@ int rm_list(int client) { int ret = rm_list(pkg); free(pkg); if (ret == DAEMON_SUCCESS) - update_inotify_mask(true); + update_inotify_mask(); return ret; } static 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); + int uid = get_pkg_uid(*data); if (strcmp(*data, SAFETYNET_PKG) == 0) gms_uid = uid; + kill_process(uid); return 0; } diff --git a/native/jni/magiskhide/magiskhide.h b/native/jni/magiskhide/magiskhide.h index 8699c419a..ea3b3b464 100644 --- a/native/jni/magiskhide/magiskhide.h +++ b/native/jni/magiskhide/magiskhide.h @@ -26,7 +26,7 @@ int rm_list(int client); void ls_list(int client); // Update APK list for inotify -void update_inotify_mask(bool refresh = false); +void update_inotify_mask(); // Process monitor void proc_monitor(); @@ -34,7 +34,6 @@ void proc_monitor(); // Utility functions void manage_selinux(); void clean_magisk_props(); -void refresh_uid(); void crawl_procfs(const std::function &fn); static inline int get_uid(const int pid) { @@ -49,10 +48,24 @@ static inline int get_uid(const int pid) { return st.st_uid % 100000; } +/* + * Bionic's atoi runs through strtol(). + * Use our own implementation for faster conversion. + */ +static inline int parse_int(const char *s) { + int val = 0; + char c; + while ((c = *(s++))) { + if (c > '9' || c < '0') + return -1; + val = val * 10 + c - '0'; + } + return val; +} + 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 { diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp index a1afe3991..25da0ee63 100644 --- a/native/jni/magiskhide/proc_monitor.cpp +++ b/native/jni/magiskhide/proc_monitor.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ using namespace std; extern char *system_block, *vendor_block, *data_block; static int inotify_fd = -1; +static set hide_uid; // Workaround for the lack of pthread_cancel static void term_thread(int) { @@ -189,102 +191,67 @@ static int xinotify_add_watch(int fd, const char* path, uint32_t mask) { return ret; } -static char *append_path(char *eof, const char *name) { - *(eof++) = '/'; - char c; - while ((c = *(name++))) - *(eof++) = c; - *eof = '\0'; - return eof; -} - -#define DATA_APP "/data/app" static int new_inotify; -static int data_app_wd; -static vector app_in_data; -static void find_apks(char *path, char *eof) { - DIR *dir = opendir(path); - if (dir == nullptr) - return; +static const string_view APK_EXT(".apk"); - struct dirent *entry; - char *dash; - for (; (entry = xreaddir(dir)); *eof = '\0') { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if (entry->d_type == DT_DIR) { - find_apks(path, append_path(eof, entry->d_name)); - } else if (strend(entry->d_name, ".apk") == 0) { - append_path(eof, entry->d_name); - /* Supported path will be in either format: - * /data/app/[pkg]-[hash or 1 or 2]/base.apk - * /data/app/[pkg]-[1 or 2].apk */ - if ((dash = strchr(path, '-')) == nullptr) - continue; - *dash = '\0'; - for (int i = 0; i < hide_list.size(); ++i) { - if (hide_list[i] == path + sizeof(DATA_APP)) { - *dash = '-'; - append_path(eof, entry->d_name); - xinotify_add_watch(new_inotify, path, IN_OPEN | IN_DELETE); - app_in_data[i] = true; - break; +static bool parse_packages_xml(string_view &s) { + if (!str_starts(s, " */ + char *start = (char *) s.data(); + start[s.length() - 2] = '\0'; /* Remove trailing '>' */ + char key[32], value[1024]; + char *tok; + start += 9; /* Skip 'd_name, APK_EXT)) { + strcpy(value + value_view.length(), "/"); + strcpy(value + value_view.length() + 1, entry->d_name); + xinotify_add_watch(new_inotify, value, IN_OPEN); + break; + } } + closedir(dir); } - *dash = '-'; - break; + } else if (strcmp(key, "userId") == 0 || strcmp(key, "sharedUserId") == 0) { + hide_uid.insert(parse_int(value)); } } - closedir(dir); + return true; } -// Iterate through /data/app and search all .apk files -void update_inotify_mask(bool refresh) { - char buf[4096]; - +void update_inotify_mask() { new_inotify = inotify_init(); if (new_inotify < 0) { LOGE("proc_monitor: Cannot initialize inotify: %s\n", strerror(errno)); term_thread(TERM_THREAD); } + fcntl(new_inotify, F_SETFD, FD_CLOEXEC); LOGD("proc_monitor: Updating inotify list\n"); - strcpy(buf, DATA_APP); - app_in_data.clear(); - bool reinstall = false; { MutexGuard lock(list_lock); - app_in_data.resize(hide_list.size(), false); - find_apks(buf, buf + sizeof(DATA_APP) - 1); - // Stop monitoring /data/app - if (inotify_fd >= 0) - inotify_rm_watch(inotify_fd, data_app_wd); - // All apps on the hide list should be installed in data - auto it = hide_list.begin(); - for (bool in_data : app_in_data) { - if (!in_data) { - if (reinstall_apk(it->c_str()) != 0) { - // Reinstallation failed, remove from hide list - hide_list.erase(it); - refresh = true; - continue; - } - reinstall = true; - } - it++; - } - if (refresh && !reinstall) - refresh_uid(); - } - if (reinstall) { - // Rerun detection - close(new_inotify); - update_inotify_mask(refresh); - return; + hide_uid.clear(); + file_readline("/data/system/packages.xml", parse_packages_xml, true); } - // Add /data/app itself to the watch list to detect app (un)installations/updates - data_app_wd = xinotify_add_watch(new_inotify, DATA_APP, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE); + // Add /data/system to monitor /data/system/packages.xml + xinotify_add_watch(new_inotify, "/data/system", IN_CLOSE_WRITE); int tmp = inotify_fd; inotify_fd = new_inotify; @@ -306,20 +273,19 @@ void proc_monitor() { // Read inotify events ssize_t len; - char buf[4096]; + char buf[512]; auto event = reinterpret_cast(buf); while ((len = read(inotify_fd, buf, sizeof(buf))) >= 0) { if (len < sizeof(*event)) continue; - if (event->mask & IN_OPEN) { // Since we're just watching files, // extracting file name is not possible from querying event MutexGuard lock(list_lock); crawl_procfs(process_pid); - } else if (!(event->mask & IN_IGNORED)) { - LOGD("proc_monitor: inotify: /data/app change detected\n"); - update_inotify_mask(true); + } else if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) { + LOGD("proc_monitor: /data/system/packages.xml updated\n"); + update_inotify_mask(); } } PLOGE("proc_monitor: read inotify"); diff --git a/native/jni/utils/include/utils.h b/native/jni/utils/include/utils.h index d03bb3573..ff20b4260 100644 --- a/native/jni/utils/include/utils.h +++ b/native/jni/utils/include/utils.h @@ -201,6 +201,8 @@ int exec_command_sync(Args &&...args) { return exec_command_sync(exec, args...); } +bool ends_with(const std::string_view &s1, const std::string_view &s2); + #endif #endif diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index 8eac4b48a..e6b51c6af 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -236,3 +236,9 @@ void set_nice_name(const char *name) { strlcpy(argv0, name, name_len); prctl(PR_SET_NAME, name); } + +bool ends_with(const std::string_view &s1, const std::string_view &s2) { + unsigned l1 = s1.length(); + unsigned l2 = s2.length(); + return l1 < l2 ? false : s1.compare(l1 - l2, l2, s2) == 0; +}