Create mapping from watch descriptor to UID

This commit is contained in:
topjohnwu 2019-03-02 03:44:24 -05:00
parent b51feffe80
commit 7203e7df5c

View File

@ -33,7 +33,7 @@ using namespace std;
extern char *system_block, *vendor_block, *data_block; extern char *system_block, *vendor_block, *data_block;
static int inotify_fd = -1; static int inotify_fd = -1;
static void term_thread(int); static void term_thread(int sig = TERM_THREAD);
static inline int read_ns(const int pid, struct stat *st) { static inline int read_ns(const int pid, struct stat *st) {
char path[32]; char path[32];
@ -116,58 +116,61 @@ static void hide_daemon(int pid) {
********************/ ********************/
map<string, string> hide_map; /* process -> package_name */ map<string, string> hide_map; /* process -> package_name */
static map<int, int> wd_uid_map; /* inotify wd -> uid */
static map<int, uint64_t> pid_ns_map; /* pid -> last ns inode */ 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 */ static map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
// All maps are protected by this lock // All maps are protected by this lock
pthread_mutex_t map_lock; pthread_mutex_t map_lock;
static bool check_pid(int pid) { static bool check_pid(int pid, int uid) {
// We're only interested in PIDs > 1000 // We're only interested in PIDs > 1000
if (pid <= 1000) if (pid <= 1000)
return true; return true;
// Not our target UID
if (uid != get_uid(pid))
return true;
struct stat ns, pns; struct stat ns, pns;
int ppid; int ppid;
int uid = get_uid(pid);
auto it = uid_proc_map.find(uid);
if (it != uid_proc_map.end()) {
// 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 // Make sure we can read mount namespace
auto pos = pid_ns_map.find(pid); if ((ppid = parse_ppid(pid)) < 0 || read_ns(pid, &ns) || read_ns(ppid, &pns))
if (pos != pid_ns_map.end() && pos->second == ns.st_ino) return true;
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 whether process name match hide list // Check if it's a process we haven't already hijacked
const char *process = nullptr; auto pos = pid_ns_map.find(pid);
for (auto &proc : it->second) if (pos != pid_ns_map.end() && pos->second == ns.st_ino)
if (proc_name_match(pid, proc.data())) return true;
process = proc.data();
if (!process) // Check whether process name match hide list
return true; const char *process = nullptr;
for (auto &proc : uid_proc_map[uid])
if (proc_name_match(pid, proc.data()))
process = proc.data();
// Send pause signal ASAP if (!process)
if (kill(pid, SIGSTOP) == -1) return true;
return true;
pid_ns_map[pid] = ns.st_ino; // Send pause signal ASAP
LOGI("proc_monitor: [%s] UID=[%d] PID=[%d] ns=[%llu]\n", process, uid, pid, ns.st_ino); if (kill(pid, SIGSTOP) == -1)
return true;
/* pid_ns_map[pid] = ns.st_ino;
* The setns system call do not support multithread processes LOGI("proc_monitor: [%s] UID=[%d] PID=[%d] ns=[%llu]\n", process, uid, pid, ns.st_ino);
* We have to fork a new process, setns, then do the unmounts
*/ /*
if (fork_dont_care() == 0) * The setns system call do not support multithread processes
hide_daemon(pid); * We have to fork a new process, setns, then do the unmounts
} */
return true; if (fork_dont_care() == 0)
hide_daemon(pid);
return false;
} }
static int xinotify_add_watch(int fd, const char* path, uint32_t mask) { static int xinotify_add_watch(int fd, const char* path, uint32_t mask) {
@ -187,10 +190,13 @@ static bool parse_packages_xml(string_view &s) {
/* <package key1="value1" key2="value2"....> */ /* <package key1="value1" key2="value2"....> */
char *start = (char *) s.data(); char *start = (char *) s.data();
start[s.length() - 2] = '\0'; /* Remove trailing '>' */ start[s.length() - 2] = '\0'; /* Remove trailing '>' */
start += 9; /* Skip '<package ' */
char key[32], value[1024]; char key[32], value[1024];
char *pkg = nullptr; char *pkg = nullptr;
int wd = -1;
char *tok; char *tok;
start += 9; /* Skip '<package ' */
while ((tok = strtok_r(nullptr, " ", &start))) { while ((tok = strtok_r(nullptr, " ", &start))) {
sscanf(tok, "%[^=]=\"%[^\"]", key, value); sscanf(tok, "%[^=]=\"%[^\"]", key, value);
string_view key_view(key); string_view key_view(key);
@ -207,7 +213,7 @@ static bool parse_packages_xml(string_view &s) {
} else if (key_view == "codePath") { } else if (key_view == "codePath") {
if (ends_with(value_view, APK_EXT)) { if (ends_with(value_view, APK_EXT)) {
// Directly add to inotify list // Directly add to inotify list
xinotify_add_watch(inotify_fd, value, IN_OPEN); wd = xinotify_add_watch(inotify_fd, value, IN_OPEN);
} else { } else {
DIR *dir = opendir(value); DIR *dir = opendir(value);
if (dir == nullptr) if (dir == nullptr)
@ -217,7 +223,7 @@ static bool parse_packages_xml(string_view &s) {
if (ends_with(entry->d_name, APK_EXT)) { if (ends_with(entry->d_name, APK_EXT)) {
value[value_view.length()] = '/'; value[value_view.length()] = '/';
strcpy(value + value_view.length() + 1, entry->d_name); strcpy(value + value_view.length() + 1, entry->d_name);
xinotify_add_watch(inotify_fd, value, IN_OPEN); wd = xinotify_add_watch(inotify_fd, value, IN_OPEN);
break; break;
} }
} }
@ -225,6 +231,7 @@ static bool parse_packages_xml(string_view &s) {
} }
} else if (key_view == "userId" || key_view == "sharedUserId") { } else if (key_view == "userId" || key_view == "sharedUserId") {
int uid = parse_int(value); int uid = parse_int(value);
wd_uid_map[wd] = uid;
for (auto &hide : hide_map) { for (auto &hide : hide_map) {
if (hide.second == pkg) if (hide.second == pkg)
uid_proc_map[uid].emplace_back(hide.first); uid_proc_map[uid].emplace_back(hide.first);
@ -237,7 +244,7 @@ static bool parse_packages_xml(string_view &s) {
void update_inotify_mask() { void update_inotify_mask() {
int new_inotify = xinotify_init1(IN_CLOEXEC); int new_inotify = xinotify_init1(IN_CLOEXEC);
if (new_inotify < 0) if (new_inotify < 0)
term_thread(TERM_THREAD); term_thread();
// Swap out and close old inotify_fd // Swap out and close old inotify_fd
int tmp = inotify_fd; int tmp = inotify_fd;
@ -249,6 +256,7 @@ void update_inotify_mask() {
{ {
MutexGuard lock(map_lock); MutexGuard lock(map_lock);
uid_proc_map.clear(); uid_proc_map.clear();
wd_uid_map.clear();
file_readline("/data/system/packages.xml", parse_packages_xml, true); file_readline("/data/system/packages.xml", parse_packages_xml, true);
} }
xinotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE); xinotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE);
@ -260,6 +268,7 @@ static void term_thread(int) {
hide_map.clear(); hide_map.clear();
uid_proc_map.clear(); uid_proc_map.clear();
pid_ns_map.clear(); pid_ns_map.clear();
wd_uid_map.clear();
hide_enabled = false; hide_enabled = false;
pthread_mutex_destroy(&map_lock); pthread_mutex_destroy(&map_lock);
close(inotify_fd); close(inotify_fd);
@ -282,20 +291,21 @@ void proc_monitor() {
// Read inotify events // Read inotify events
ssize_t len; ssize_t len;
int uid;
char buf[512]; char buf[512];
auto event = reinterpret_cast<inotify_event *>(buf); auto event = reinterpret_cast<inotify_event *>(buf);
while ((len = read(inotify_fd, buf, sizeof(buf))) >= 0) { while ((len = read(inotify_fd, buf, sizeof(buf))) >= 0) {
if (len < sizeof(*event)) if (len < sizeof(*event))
continue; continue;
if (event->mask & IN_OPEN) { if (event->mask & IN_OPEN) {
// Since we're just watching files,
// extracting file name is not possible from querying event
MutexGuard lock(map_lock); MutexGuard lock(map_lock);
crawl_procfs(check_pid); uid = wd_uid_map[event->wd];
crawl_procfs([=](int pid) -> bool { return check_pid(pid, uid); });
} else if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) { } else if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) {
LOGD("proc_monitor: /data/system/packages.xml updated\n"); LOGD("proc_monitor: /data/system/packages.xml updated\n");
update_inotify_mask(); update_inotify_mask();
} }
} }
PLOGE("proc_monitor: read inotify"); PLOGE("proc_monitor: read inotify");
term_thread();
} }