Magisk/native/jni/magiskhide/proc_monitor.c

241 lines
5.7 KiB
C
Raw Normal View History

2017-07-10 17:39:33 +02:00
/* proc_monitor.c - Monitor am_proc_start events and unmount
2017-08-01 09:34:16 +02:00
*
2017-07-10 17:39:33 +02:00
* 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
2017-04-06 00:12:29 +02:00
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
2017-07-10 17:39:33 +02:00
#include <sys/mount.h>
2017-04-06 00:12:29 +02:00
#include "magisk.h"
#include "utils.h"
2016-12-30 19:44:24 +01:00
#include "magiskhide.h"
2017-07-10 17:39:33 +02:00
static char init_ns[32], zygote_ns[2][32], cache_block[256];
static int zygote_num, has_cache = 1, pipefd[2] = { -1, -1 };
2017-04-07 01:50:02 +02:00
2017-04-06 00:12:29 +02:00
// Workaround for the lack of pthread_cancel
static void term_thread(int sig) {
LOGD("proc_monitor: running cleanup\n");
2017-04-20 16:45:56 +02:00
destroy_list();
hideEnabled = 0;
// Unregister listener
log_events[HIDE_EVENT].fd = -1;
close(pipefd[0]);
close(pipefd[1]);
pipefd[0] = pipefd[1] = -1;
2017-04-21 18:54:08 +02:00
pthread_mutex_destroy(&hide_lock);
2017-05-07 21:11:14 +02:00
pthread_mutex_destroy(&file_lock);
LOGD("proc_monitor: terminating...\n");
pthread_exit(NULL);
2017-04-06 00:12:29 +02:00
}
2017-01-01 11:54:13 +01:00
2017-07-10 17:39:33 +02:00
static int read_namespace(const int pid, char* target, const size_t size) {
2017-07-02 19:02:11 +02:00
char path[32];
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
2017-07-10 17:39:33 +02:00
if (access(path, R_OK) == -1)
return 1;
2017-07-02 19:02:11 +02:00
xreadlink(path, target, size);
2017-07-10 17:39:33 +02:00
return 0;
2017-07-02 19:02:11 +02:00
}
2017-04-07 01:50:02 +02:00
static void store_zygote_ns(int pid) {
2017-04-18 13:32:50 +02:00
if (zygote_num == 2) return;
2017-04-07 01:50:02 +02:00
do {
usleep(500);
read_namespace(pid, zygote_ns[zygote_num], 32);
} while (strcmp(zygote_ns[zygote_num], init_ns) == 0);
++zygote_num;
}
2017-07-10 17:39:33 +02:00
static void lazy_unmount(const char* mountpoint) {
2018-01-11 17:23:38 +01:00
if (umount2(mountpoint, MNT_DETACH) != -1)
2017-07-10 17:39:33 +02:00
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
}
static void hide_daemon(int pid, int ppid) {
2017-07-10 17:39:33 +02:00
LOGD("hide_daemon: start unmount for pid=[%d]\n", pid);
2017-12-15 21:40:54 +01:00
strcpy(argv0, "hide_daemon");
2017-07-10 17:39:33 +02:00
char *line, buffer[PATH_MAX];
2017-07-10 17:39:33 +02:00
struct vector mount_list;
manage_selinux();
2017-07-18 06:26:23 +02:00
clean_magisk_props();
2017-07-10 17:39:33 +02:00
if (switch_mnt_ns(pid))
goto exit;
2017-07-10 17:39:33 +02:00
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid);
2017-07-10 17:39:33 +02:00
vec_init(&mount_list);
file_to_vector(buffer, &mount_list);
// Find the cache block name if not found yet
2017-09-13 09:45:07 +02:00
if (has_cache && cache_block[0] == '\0') {
2017-07-10 17:39:33 +02:00
vec_for_each(&mount_list, line) {
if (strstr(line, " /cache ")) {
sscanf(line, "%256s", cache_block);
break;
}
}
2017-09-13 09:45:07 +02:00
if (strlen(cache_block) == 0)
has_cache = 0;
2017-07-10 17:39:33 +02:00
}
2017-09-13 09:45:07 +02:00
// Unmout cache mounts
if (has_cache) {
vec_for_each(&mount_list, line) {
if (strstr(line, cache_block) && (strstr(line, " /system/") || strstr(line, " /vendor/"))) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
}
}
2017-11-22 09:04:24 +01:00
// Unmount dummy skeletons, /sbin links
2017-07-10 17:39:33 +02:00
vec_for_each(&mount_list, line) {
2017-11-22 09:04:24 +01:00
if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin")) {
2017-07-10 17:39:33 +02:00
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);
2017-07-10 17:39:33 +02:00
vec_init(&mount_list);
file_to_vector(buffer, &mount_list);
// Unmount any loop mounts
2017-07-10 17:39:33 +02:00
vec_for_each(&mount_list, line) {
if (strstr(line, "/dev/block/loop")) {
2017-07-10 17:39:33 +02:00
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
exit:
// Send resume signal
kill(pid, SIGCONT);
// Free up memory
2017-07-10 17:39:33 +02:00
vec_destroy(&mount_list);
_exit(0);
2017-07-10 17:39:33 +02:00
}
2017-04-21 18:54:08 +02:00
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);
2017-04-06 00:12:29 +02:00
// Register the cancel signal
2017-05-07 21:11:14 +02:00
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = term_thread;
sigaction(TERM_THREAD, &act, NULL);
2017-05-07 21:11:14 +02:00
2017-07-10 17:39:33 +02:00
cache_block[0] = '\0';
2017-04-06 00:12:29 +02:00
// Get the mount namespace of init
2017-07-10 17:39:33 +02:00
if (read_namespace(1, init_ns, 32)) {
LOGE("proc_monitor: Your kernel doesn't support mount namespace :(\n");
term_thread(TERM_THREAD);
2017-07-10 17:39:33 +02:00
}
2017-04-06 00:12:29 +02:00
LOGI("proc_monitor: init ns=%s\n", init_ns);
// Get the mount namespace of zygote
2017-07-02 17:04:57 +02:00
zygote_num = 0;
2017-04-17 10:36:49 +02:00
while(!zygote_num) {
// Check zygote every 10 ms
usleep(10000);
2017-04-17 10:36:49 +02:00
ps_filter_proc_name("zygote", store_zygote_ns);
}
2017-05-03 20:58:37 +02:00
ps_filter_proc_name("zygote64", store_zygote_ns);
2017-04-06 00:12:29 +02:00
switch(zygote_num) {
case 1:
LOGI("proc_monitor: zygote ns=%s\n", zygote_ns[0]);
break;
case 2:
LOGI("proc_monitor: zygote ns=%s zygote64 ns=%s\n", zygote_ns[0], zygote_ns[1]);
2017-04-06 00:12:29 +02:00
break;
}
// Register our listener to logcat monitor
xpipe2(pipefd, O_CLOEXEC);
log_events[HIDE_EVENT].fd = pipefd[1];
2017-07-02 17:04:57 +02:00
for (char *log, *line;; free(log)) {
if (read(pipefd[0], &log, sizeof(log)) != sizeof(log)) {
/* It might be interrupted */
log = NULL;
continue;
}
char *ss = strchr(log, '[');
int pid, ret, comma = 0;
char *pos = ss, processName[256], ns[32];
while(1) {
pos = strchr(pos, ',');
if(pos == NULL)
break;
pos[0] = ' ';
++comma;
}
if (comma == 6)
ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName);
else
ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName);
2016-12-30 19:44:24 +01:00
if(ret != 2)
continue;
// Critical region
pthread_mutex_lock(&hide_lock);
vec_for_each(hide_list, line) {
if (strcmp(processName, line) == 0) {
while(1) {
ret = 1;
for (int i = 0; i < zygote_num; ++i) {
read_namespace(pid, ns, sizeof(ns));
if (strcmp(ns, zygote_ns[i]) == 0) {
usleep(50);
ret = 0;
break;
2017-01-01 11:54:13 +01:00
}
}
if (ret) break;
}
2017-01-01 11:54:13 +01:00
// Send pause signal ASAP
if (kill(pid, SIGSTOP) == -1) continue;
2017-07-10 17:39:33 +02:00
LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns);
2017-07-10 17:39:33 +02:00
/*
* The setns system call do not support multithread processes
* We have to fork a new process, setns, then do the unmounts
*/
int selfpid = getpid();
if (fork_dont_care() == 0)
hide_daemon(pid, selfpid);
break;
2016-12-30 19:44:24 +01:00
}
}
pthread_mutex_unlock(&hide_lock);
2017-06-02 22:31:01 +02:00
}
}