/* proc_monitor.c - Monitor am_proc_start events and unmount * * We monitor the logcat am_proc_start events. When a target starts up, * we pause it ASAP, and fork a new process to join its mount namespace * and do all the unmounting/mocking */ #include #include #include #include #include #include #include #include #include #include #include "magisk.h" #include "daemon.h" #include "utils.h" #include "magiskhide.h" static int sockfd = -1; // Workaround for the lack of pthread_cancel static void term_thread(int sig) { LOGD("proc_monitor: running cleanup\n"); destroy_list(); hideEnabled = 0; close(sockfd); sockfd = -1; pthread_mutex_destroy(&hide_lock); pthread_mutex_destroy(&file_lock); LOGD("proc_monitor: terminating...\n"); pthread_exit(NULL); } static int read_ns(const int pid, struct stat *st) { char path[32]; sprintf(path, "/proc/%d/ns/mnt", pid); return stat(path, st); } static void lazy_unmount(const char* mountpoint) { if (umount2(mountpoint, MNT_DETACH) != -1) LOGD("hide_daemon: Unmounted (%s)\n", mountpoint); } static int parse_ppid(int pid) { char stat[512], path[32]; int fd, ppid; sprintf(path, "/proc/%d/stat", pid); fd = xopen(path, O_RDONLY); xread(fd, stat, sizeof(stat)); close(fd); /* PID COMM STATE PPID ..... */ sscanf(stat, "%*d %*s %*c %d", &ppid); return ppid; } static void hide_daemon(int pid) { LOGD("hide_daemon: start unmount for pid=[%d]\n", pid); char *line, buffer[PATH_MAX]; struct vector mount_list; manage_selinux(); clean_magisk_props(); if (switch_mnt_ns(pid)) goto exit; snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid); vec_init(&mount_list); file_to_vector(buffer, &mount_list); // Unmount dummy skeletons and /sbin links vec_for_each(&mount_list, line) { if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) { sscanf(line, "%*s %4096s", buffer); lazy_unmount(buffer); } free(line); } vec_destroy(&mount_list); // Re-read mount infos snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid); vec_init(&mount_list); file_to_vector(buffer, &mount_list); // Unmount everything under /system, /vendor, and loop mounts vec_for_each(&mount_list, line) { if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) { sscanf(line, "%*s %4096s", buffer); lazy_unmount(buffer); } free(line); } vec_destroy(&mount_list); exit: // Send resume signal kill(pid, SIGCONT); _exit(0); } void proc_monitor() { // Unblock user signals sigset_t block_set; sigemptyset(&block_set); sigaddset(&block_set, TERM_THREAD); pthread_sigmask(SIG_UNBLOCK, &block_set, NULL); // Register the cancel signal struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = term_thread; sigaction(TERM_THREAD, &act, NULL); if (access("/proc/1/ns/mnt", F_OK) != 0) { LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n"); term_thread(TERM_THREAD); } while(1) { // Connect to the log daemon connect_daemon2(LOG_DAEMON, &sockfd); write_int(sockfd, HIDE_CONNECT); FILE *log_in = fdopen(sockfd, "r"); char buf[4096]; while (fgets(buf, sizeof(buf), log_in)) { char *ss = strchr(buf, '['); int pid, ppid, num = 0; char *pos = ss, proc[256]; struct stat ns, pns; while(1) { pos = strchr(pos, ','); if(pos == NULL) break; pos[0] = ' '; ++num; } if(sscanf(ss, num == 6 ? "[%*d %d %*d %*d %256s" : "[%*d %d %*d %256s", &pid, proc) != 2) continue; // Make sure our target is alive if (kill(pid, 0)) continue; // Allow hiding sub-services of applications char *colon = strchr(proc, ':'); if (colon) *colon = '\0'; int hide = 0; pthread_mutex_lock(&hide_lock); char *line; vec_for_each(hide_list, line) { if (strcmp(proc, line) == 0) { hide = 1; break; } } pthread_mutex_unlock(&hide_lock); if (!hide) continue; ppid = parse_ppid(pid); read_ns(ppid, &pns); do { read_ns(pid, &ns); if (ns.st_dev == pns.st_dev && ns.st_ino == pns.st_ino) usleep(50); else break; } while (1); // Send pause signal ASAP if (kill(pid, SIGSTOP) == -1) continue; // Restore the colon so we can log the actual process name if (colon) *colon = ':'; #ifdef MAGISK_DEBUG LOGI("proc_monitor: %s (PID=[%d] ns=%llu)(PPID=[%d] ns=%llu)\n", proc, pid, ns.st_ino, ppid, pns.st_ino); #else LOGI("proc_monitor: %s\n", proc); #endif /* * The setns system call do not support multithread processes * We have to fork a new process, setns, then do the unmounts */ if (fork_dont_care() == 0) hide_daemon(pid); } // The other end EOF, restart the connection } }