2018-11-01 17:23:12 +00:00
|
|
|
/* proc_monitor.cpp - Monitor am_proc_start events and unmount
|
2017-08-01 07:34:16 +00:00
|
|
|
*
|
2019-02-12 14:12:03 +00:00
|
|
|
* We monitor the listed APK files from /data/app until they get opened
|
|
|
|
* via inotify to detect a new app launch.
|
|
|
|
*
|
|
|
|
* If it's a target we pause it ASAP, and fork a new process to join
|
|
|
|
* its mount namespace and do all the unmounting/mocking.
|
2017-04-05 22:12:29 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2018-07-13 14:14:32 +00:00
|
|
|
#include <fcntl.h>
|
2017-04-05 22:12:29 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <pthread.h>
|
2019-02-12 14:12:03 +00:00
|
|
|
#include <sys/inotify.h>
|
2017-04-05 22:12:29 +00:00
|
|
|
#include <sys/types.h>
|
2017-04-08 23:25:10 +00:00
|
|
|
#include <sys/wait.h>
|
2017-07-10 15:39:33 +00:00
|
|
|
#include <sys/mount.h>
|
2019-01-20 04:59:37 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
2019-02-14 01:16:26 +00:00
|
|
|
#include <map>
|
2019-02-18 08:05:13 +00:00
|
|
|
#include <algorithm>
|
2017-04-05 22:12:29 +00:00
|
|
|
|
2019-02-10 06:05:19 +00:00
|
|
|
#include <magisk.h>
|
|
|
|
#include <utils.h>
|
|
|
|
|
2016-12-30 18:44:24 +00:00
|
|
|
#include "magiskhide.h"
|
|
|
|
|
2019-01-20 04:59:37 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2019-02-12 07:14:57 +00:00
|
|
|
extern char *system_block, *vendor_block, *data_block;
|
2017-04-06 23:50:02 +00:00
|
|
|
|
2019-02-13 11:16:26 +00:00
|
|
|
static int inotify_fd = -1;
|
2019-03-02 08:44:24 +00:00
|
|
|
static void term_thread(int sig = TERM_THREAD);
|
2017-01-01 10:54:13 +00:00
|
|
|
|
2019-02-12 14:12:03 +00:00
|
|
|
static inline int read_ns(const int pid, struct stat *st) {
|
2017-07-02 17:02:11 +00:00
|
|
|
char path[32];
|
2018-06-19 17:20:49 +00:00
|
|
|
sprintf(path, "/proc/%d/ns/mnt", pid);
|
2018-07-11 15:41:38 +00:00
|
|
|
return stat(path, st);
|
2017-07-02 17:02:11 +00:00
|
|
|
}
|
|
|
|
|
2018-11-23 19:32:33 +00:00
|
|
|
static inline void lazy_unmount(const char* mountpoint) {
|
2018-01-11 16:23:38 +00:00
|
|
|
if (umount2(mountpoint, MNT_DETACH) != -1)
|
2017-07-10 15:39:33 +00:00
|
|
|
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
|
|
|
}
|
|
|
|
|
2019-02-12 14:12:03 +00:00
|
|
|
static inline int parse_ppid(const int pid) {
|
2018-11-23 19:32:33 +00:00
|
|
|
char path[32];
|
|
|
|
int ppid;
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2018-06-19 17:20:49 +00:00
|
|
|
sprintf(path, "/proc/%d/stat", pid);
|
2018-11-24 20:53:15 +00:00
|
|
|
FILE *stat = fopen(path, "re");
|
2018-11-23 19:32:33 +00:00
|
|
|
if (stat == nullptr)
|
2018-11-13 07:11:02 +00:00
|
|
|
return -1;
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2018-06-19 17:20:49 +00:00
|
|
|
/* PID COMM STATE PPID ..... */
|
2018-11-23 19:32:33 +00:00
|
|
|
fscanf(stat, "%*d %*s %*c %d", &ppid);
|
2018-11-24 02:15:44 +00:00
|
|
|
fclose(stat);
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2018-06-19 17:20:49 +00:00
|
|
|
return ppid;
|
|
|
|
}
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2018-04-29 07:10:35 +00:00
|
|
|
static void hide_daemon(int pid) {
|
2019-02-19 06:50:41 +00:00
|
|
|
RunFinally fin([=]() -> void {
|
|
|
|
// Send resume signal
|
|
|
|
kill(pid, SIGCONT);
|
|
|
|
_exit(0);
|
|
|
|
});
|
|
|
|
|
2017-07-10 15:39:33 +00:00
|
|
|
if (switch_mnt_ns(pid))
|
2019-02-19 06:50:41 +00:00
|
|
|
return;
|
2017-07-10 15:39:33 +00:00
|
|
|
|
2019-02-18 03:30:23 +00:00
|
|
|
LOGD("hide_daemon: handling PID=[%d]\n", pid);
|
|
|
|
manage_selinux();
|
|
|
|
clean_magisk_props();
|
2019-02-19 06:50:41 +00:00
|
|
|
|
|
|
|
vector<string> targets;
|
2017-07-10 15:39:33 +00:00
|
|
|
|
2018-06-16 21:16:52 +00:00
|
|
|
// Unmount dummy skeletons and /sbin links
|
2019-02-19 06:50:41 +00:00
|
|
|
file_readline("/proc/self/mounts", [&](string_view &s) -> bool {
|
2019-01-20 04:59:37 +00:00
|
|
|
if (str_contains(s, "tmpfs /system/") || str_contains(s, "tmpfs /vendor/") ||
|
|
|
|
str_contains(s, "tmpfs /sbin")) {
|
2019-02-19 06:50:41 +00:00
|
|
|
char *path = (char *) s.data();
|
|
|
|
// Skip first token
|
|
|
|
strtok_r(nullptr, " ", &path);
|
|
|
|
targets.emplace_back(strtok_r(nullptr, " ", &path));
|
2017-07-10 15:39:33 +00:00
|
|
|
}
|
2019-02-18 03:30:23 +00:00
|
|
|
return true;
|
|
|
|
});
|
2017-07-10 15:39:33 +00:00
|
|
|
|
2019-02-19 06:50:41 +00:00
|
|
|
for (auto &s : targets)
|
|
|
|
lazy_unmount(s.data());
|
|
|
|
targets.clear();
|
|
|
|
|
2019-02-12 07:14:57 +00:00
|
|
|
// Unmount everything under /system, /vendor, and data mounts
|
2019-02-19 06:50:41 +00:00
|
|
|
file_readline("/proc/self/mounts", [&](string_view &s) -> bool {
|
2019-01-20 04:59:37 +00:00
|
|
|
if ((str_contains(s, " /system/") || str_contains(s, " /vendor/")) &&
|
2019-02-18 03:30:23 +00:00
|
|
|
(str_contains(s, system_block) || str_contains(s, vendor_block) ||
|
|
|
|
str_contains(s, data_block))) {
|
2019-02-19 06:50:41 +00:00
|
|
|
char *path = (char *) s.data();
|
|
|
|
// Skip first token
|
|
|
|
strtok_r(nullptr, " ", &path);
|
|
|
|
targets.emplace_back(strtok_r(nullptr, " ", &path));
|
2017-07-10 15:39:33 +00:00
|
|
|
}
|
2019-02-18 03:30:23 +00:00
|
|
|
return true;
|
|
|
|
});
|
2017-07-10 15:39:33 +00:00
|
|
|
|
2019-02-19 06:50:41 +00:00
|
|
|
for (auto &s : targets)
|
|
|
|
lazy_unmount(s.data());
|
2017-07-10 15:39:33 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 22:08:08 +00:00
|
|
|
/********************
|
|
|
|
* All the damn maps
|
|
|
|
********************/
|
|
|
|
|
|
|
|
map<string, string> hide_map; /* process -> package_name */
|
2019-03-02 08:44:24 +00:00
|
|
|
static map<int, int> wd_uid_map; /* inotify wd -> uid */
|
2019-03-01 22:08:08 +00:00
|
|
|
static map<int, uint64_t> pid_ns_map; /* pid -> last ns inode */
|
|
|
|
static map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2019-03-01 22:08:08 +00:00
|
|
|
// All maps are protected by this lock
|
|
|
|
pthread_mutex_t map_lock;
|
|
|
|
|
2019-03-02 08:44:24 +00:00
|
|
|
static bool check_pid(int pid, int uid) {
|
2019-02-14 01:16:26 +00:00
|
|
|
// We're only interested in PIDs > 1000
|
|
|
|
if (pid <= 1000)
|
|
|
|
return true;
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2019-03-02 08:44:24 +00:00
|
|
|
// Not our target UID
|
|
|
|
if (uid != get_uid(pid))
|
|
|
|
return true;
|
|
|
|
|
2019-02-16 17:34:37 +00:00
|
|
|
struct stat ns, pns;
|
2019-02-18 08:44:38 +00:00
|
|
|
int ppid;
|
2019-03-02 08:44:24 +00:00
|
|
|
|
|
|
|
// Make sure we can read mount namespace
|
|
|
|
if ((ppid = parse_ppid(pid)) < 0 || read_ns(pid, &ns) || read_ns(ppid, &pns))
|
|
|
|
return true;
|
|
|
|
// mount namespace is not separated, we only unmount once
|
|
|
|
if (ns.st_dev == pns.st_dev && ns.st_ino == pns.st_ino)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Check if it's a process we haven't already hijacked
|
|
|
|
auto pos = pid_ns_map.find(pid);
|
|
|
|
if (pos != pid_ns_map.end() && pos->second == ns.st_ino)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Check whether process name match hide list
|
|
|
|
const char *process = nullptr;
|
|
|
|
for (auto &proc : uid_proc_map[uid])
|
|
|
|
if (proc_name_match(pid, proc.data()))
|
|
|
|
process = proc.data();
|
|
|
|
|
|
|
|
if (!process)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Send pause signal ASAP
|
|
|
|
if (kill(pid, SIGSTOP) == -1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
pid_ns_map[pid] = ns.st_ino;
|
|
|
|
LOGI("proc_monitor: [%s] UID=[%d] PID=[%d] ns=[%llu]\n", process, uid, pid, ns.st_ino);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
return false;
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 05:52:59 +00:00
|
|
|
static int xinotify_add_watch(int fd, const char* path, uint32_t mask) {
|
|
|
|
int ret = inotify_add_watch(fd, path, mask);
|
|
|
|
if (ret >= 0) {
|
2019-02-16 07:30:48 +00:00
|
|
|
LOGD("proc_monitor: Monitoring %s\n", path);
|
2019-02-14 05:52:59 +00:00
|
|
|
} else {
|
|
|
|
PLOGE("proc_monitor: Monitor %s", path);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-18 08:05:13 +00:00
|
|
|
static bool parse_packages_xml(string_view &s) {
|
2019-03-01 22:08:08 +00:00
|
|
|
static const string_view APK_EXT(".apk");
|
2019-02-18 08:05:13 +00:00
|
|
|
if (!str_starts(s, "<package "))
|
|
|
|
return true;
|
|
|
|
/* <package key1="value1" key2="value2"....> */
|
|
|
|
char *start = (char *) s.data();
|
|
|
|
start[s.length() - 2] = '\0'; /* Remove trailing '>' */
|
2019-03-02 08:44:24 +00:00
|
|
|
start += 9; /* Skip '<package ' */
|
|
|
|
|
2019-02-18 08:05:13 +00:00
|
|
|
char key[32], value[1024];
|
2019-03-01 22:08:08 +00:00
|
|
|
char *pkg = nullptr;
|
2019-03-02 08:44:24 +00:00
|
|
|
int wd = -1;
|
|
|
|
|
2019-02-18 08:05:13 +00:00
|
|
|
char *tok;
|
|
|
|
while ((tok = strtok_r(nullptr, " ", &start))) {
|
|
|
|
sscanf(tok, "%[^=]=\"%[^\"]", key, value);
|
2019-03-01 22:08:08 +00:00
|
|
|
string_view key_view(key);
|
2019-02-18 08:05:13 +00:00
|
|
|
string_view value_view(value);
|
2019-03-01 22:08:08 +00:00
|
|
|
if (key_view == "name") {
|
|
|
|
for (auto &hide : hide_map) {
|
|
|
|
if (hide.second == value_view) {
|
|
|
|
pkg = hide.second.data();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!pkg)
|
2019-02-18 08:05:13 +00:00
|
|
|
return true;
|
2019-03-01 22:08:08 +00:00
|
|
|
} else if (key_view == "codePath") {
|
2019-02-18 08:05:13 +00:00
|
|
|
if (ends_with(value_view, APK_EXT)) {
|
|
|
|
// Directly add to inotify list
|
2019-03-02 08:44:24 +00:00
|
|
|
wd = xinotify_add_watch(inotify_fd, value, IN_OPEN);
|
2019-02-18 08:05:13 +00:00
|
|
|
} else {
|
|
|
|
DIR *dir = opendir(value);
|
|
|
|
if (dir == nullptr)
|
|
|
|
return true;
|
|
|
|
struct dirent *entry;
|
|
|
|
while ((entry = xreaddir(dir))) {
|
|
|
|
if (ends_with(entry->d_name, APK_EXT)) {
|
2019-03-01 22:08:08 +00:00
|
|
|
value[value_view.length()] = '/';
|
2019-02-18 08:05:13 +00:00
|
|
|
strcpy(value + value_view.length() + 1, entry->d_name);
|
2019-03-02 08:44:24 +00:00
|
|
|
wd = xinotify_add_watch(inotify_fd, value, IN_OPEN);
|
2019-02-18 08:05:13 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
2019-02-18 08:05:13 +00:00
|
|
|
closedir(dir);
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
2019-03-01 22:08:08 +00:00
|
|
|
} else if (key_view == "userId" || key_view == "sharedUserId") {
|
|
|
|
int uid = parse_int(value);
|
2019-03-02 08:44:24 +00:00
|
|
|
wd_uid_map[wd] = uid;
|
2019-03-01 22:08:08 +00:00
|
|
|
for (auto &hide : hide_map) {
|
|
|
|
if (hide.second == pkg)
|
|
|
|
uid_proc_map[uid].emplace_back(hide.first);
|
|
|
|
}
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-18 08:05:13 +00:00
|
|
|
return true;
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 08:05:13 +00:00
|
|
|
void update_inotify_mask() {
|
2019-03-01 22:08:08 +00:00
|
|
|
int new_inotify = xinotify_init1(IN_CLOEXEC);
|
|
|
|
if (new_inotify < 0)
|
2019-03-02 08:44:24 +00:00
|
|
|
term_thread();
|
2019-02-12 14:12:03 +00:00
|
|
|
|
2019-02-18 08:18:11 +00:00
|
|
|
// Swap out and close old inotify_fd
|
2019-02-14 09:24:30 +00:00
|
|
|
int tmp = inotify_fd;
|
|
|
|
inotify_fd = new_inotify;
|
|
|
|
if (tmp >= 0)
|
2019-02-14 09:08:05 +00:00
|
|
|
close(tmp);
|
2019-02-18 08:18:11 +00:00
|
|
|
|
2019-03-01 22:08:08 +00:00
|
|
|
LOGD("proc_monitor: Updating inotify list\n");
|
|
|
|
{
|
|
|
|
MutexGuard lock(map_lock);
|
|
|
|
uid_proc_map.clear();
|
2019-03-02 08:44:24 +00:00
|
|
|
wd_uid_map.clear();
|
2019-03-01 22:08:08 +00:00
|
|
|
file_readline("/data/system/packages.xml", parse_packages_xml, true);
|
|
|
|
}
|
2019-02-18 08:18:11 +00:00
|
|
|
xinotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE);
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 22:08:08 +00:00
|
|
|
// Workaround for the lack of pthread_cancel
|
|
|
|
static void term_thread(int) {
|
|
|
|
LOGD("proc_monitor: cleaning up\n");
|
|
|
|
hide_map.clear();
|
|
|
|
uid_proc_map.clear();
|
|
|
|
pid_ns_map.clear();
|
2019-03-02 08:44:24 +00:00
|
|
|
wd_uid_map.clear();
|
2019-03-01 22:08:08 +00:00
|
|
|
hide_enabled = false;
|
|
|
|
pthread_mutex_destroy(&map_lock);
|
|
|
|
close(inotify_fd);
|
|
|
|
inotify_fd = -1;
|
|
|
|
LOGD("proc_monitor: terminate\n");
|
|
|
|
pthread_exit(nullptr);
|
|
|
|
}
|
|
|
|
|
2017-04-21 16:54:08 +00:00
|
|
|
void proc_monitor() {
|
2017-11-14 21:15:58 +00:00
|
|
|
// Unblock user signals
|
|
|
|
sigset_t block_set;
|
|
|
|
sigemptyset(&block_set);
|
|
|
|
sigaddset(&block_set, TERM_THREAD);
|
2019-01-20 04:59:37 +00:00
|
|
|
pthread_sigmask(SIG_UNBLOCK, &block_set, nullptr);
|
2017-11-14 21:15:58 +00:00
|
|
|
|
2017-04-05 22:12:29 +00:00
|
|
|
// Register the cancel signal
|
2019-01-20 04:59:37 +00:00
|
|
|
struct sigaction act{};
|
2017-11-08 19:05:01 +00:00
|
|
|
act.sa_handler = term_thread;
|
2019-01-20 04:59:37 +00:00
|
|
|
sigaction(TERM_THREAD, &act, nullptr);
|
2017-05-07 19:11:14 +00:00
|
|
|
|
2019-02-12 14:12:03 +00:00
|
|
|
// Read inotify events
|
|
|
|
ssize_t len;
|
2019-03-02 08:44:24 +00:00
|
|
|
int uid;
|
2019-02-18 08:05:13 +00:00
|
|
|
char buf[512];
|
2019-02-16 07:49:36 +00:00
|
|
|
auto event = reinterpret_cast<inotify_event *>(buf);
|
2019-02-14 09:08:05 +00:00
|
|
|
while ((len = read(inotify_fd, buf, sizeof(buf))) >= 0) {
|
2019-02-16 07:49:36 +00:00
|
|
|
if (len < sizeof(*event))
|
|
|
|
continue;
|
|
|
|
if (event->mask & IN_OPEN) {
|
2019-03-01 22:08:08 +00:00
|
|
|
MutexGuard lock(map_lock);
|
2019-03-02 08:44:24 +00:00
|
|
|
uid = wd_uid_map[event->wd];
|
|
|
|
crawl_procfs([=](int pid) -> bool { return check_pid(pid, uid); });
|
2019-02-18 08:05:13 +00:00
|
|
|
} 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();
|
2019-02-12 14:12:03 +00:00
|
|
|
}
|
2017-06-02 20:31:01 +00:00
|
|
|
}
|
2019-02-14 09:08:05 +00:00
|
|
|
PLOGE("proc_monitor: read inotify");
|
2019-03-02 08:44:24 +00:00
|
|
|
term_thread();
|
2017-03-24 19:45:12 +00:00
|
|
|
}
|