mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-28 04:25:27 +00:00
Completely rework MagiskHide
Previous MagiskHide detects new app launches via listening through logcat and filtering launch info messages. This is extremely inefficient and prone to cause multiple issues both theoratically and practically. Rework this by using inotify to detect open() syscalls to target APKs. This also solves issues related to Zygote-forked caching mechanisms such as OnePlus OxygenOS' embryo. Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
This commit is contained in:
parent
e5940168fe
commit
7384d2d330
@ -35,7 +35,6 @@ LOCAL_SRC_FILES := \
|
|||||||
core/applets.cpp \
|
core/applets.cpp \
|
||||||
core/magisk.cpp \
|
core/magisk.cpp \
|
||||||
core/daemon.cpp \
|
core/daemon.cpp \
|
||||||
core/logcat.cpp \
|
|
||||||
core/bootstages.cpp \
|
core/bootstages.cpp \
|
||||||
core/socket.cpp \
|
core/socket.cpp \
|
||||||
core/db.cpp \
|
core/db.cpp \
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
#include <resetprop.h>
|
#include <resetprop.h>
|
||||||
#include <selinux.h>
|
#include <selinux.h>
|
||||||
#include <logcat.h>
|
|
||||||
#include <flags.h>
|
#include <flags.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -699,8 +698,6 @@ void post_fs_data(int client) {
|
|||||||
unblock_boot_process();
|
unblock_boot_process();
|
||||||
}
|
}
|
||||||
|
|
||||||
start_logcat();
|
|
||||||
|
|
||||||
LOGI("* Running post-fs-data.d scripts\n");
|
LOGI("* Running post-fs-data.d scripts\n");
|
||||||
exec_common_script("post-fs-data");
|
exec_common_script("post-fs-data");
|
||||||
|
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <logcat.h>
|
|
||||||
#include <utils.h>
|
|
||||||
#include <logging.h>
|
|
||||||
#include <magisk.h>
|
|
||||||
|
|
||||||
static std::vector<const char *> log_cmd;
|
|
||||||
static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static time_t LAST_TIMESTAMP = 0;
|
|
||||||
|
|
||||||
bool logcat_started = false;
|
|
||||||
|
|
||||||
struct log_listener {
|
|
||||||
bool enable = false;
|
|
||||||
bool (*filter)(const char *);
|
|
||||||
BlockingQueue<std::string> queue;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct log_listener events[] = {
|
|
||||||
{ /* HIDE_EVENT */
|
|
||||||
.filter = [](auto log) -> bool { return strstr(log, "am_proc_start") != nullptr; }
|
|
||||||
},
|
|
||||||
{ /* LOG_EVENT */
|
|
||||||
.filter = [](auto log) -> bool { return !strstr(log, "am_proc_start"); }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void init_args() {
|
|
||||||
// Construct cmdline
|
|
||||||
log_cmd.push_back(MIRRDIR "/system/bin/logcat");
|
|
||||||
// Test whether these buffers actually works
|
|
||||||
const char *buffers[] = { "main", "events", "crash" };
|
|
||||||
for (auto b : buffers) {
|
|
||||||
if (exec_command_sync(MIRRDIR "/system/bin/logcat", "-b", b, "-d", "-f", "/dev/null") == 0) {
|
|
||||||
log_cmd.push_back("-b");
|
|
||||||
log_cmd.push_back(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chmod("/dev/null", 0666);
|
|
||||||
log_cmd.insert(log_cmd.end(), { "-v", "threadtime", "-s", "am_proc_start", "Magisk" });
|
|
||||||
#ifdef MAGISK_DEBUG
|
|
||||||
log_cmd.push_back("*:F");
|
|
||||||
#endif
|
|
||||||
log_cmd.push_back(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test_logcat() {
|
|
||||||
int test = exec_command_sync(MIRRDIR "/system/bin/logcat", "-d", "-f", "/dev/null");
|
|
||||||
chmod("/dev/null", 0666);
|
|
||||||
return test == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *logcat_gobbler(void *) {
|
|
||||||
int log_pid;
|
|
||||||
char line[4096];
|
|
||||||
struct tm tm{};
|
|
||||||
time_t prev;
|
|
||||||
|
|
||||||
// Set tm year info
|
|
||||||
time_t now = time(nullptr);
|
|
||||||
localtime_r(&now, &tm);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
prev = 0;
|
|
||||||
exec_t exec {
|
|
||||||
.fd = -1,
|
|
||||||
.argv = log_cmd.data()
|
|
||||||
};
|
|
||||||
log_pid = exec_command(exec);
|
|
||||||
FILE *logs = fdopen(exec.fd, "r");
|
|
||||||
while (fgets(line, sizeof(line), logs)) {
|
|
||||||
if (line[0] == '-')
|
|
||||||
continue;
|
|
||||||
// Parse timestamp
|
|
||||||
strptime(line, "%m-%d %H:%M:%S", &tm);
|
|
||||||
now = mktime(&tm);
|
|
||||||
if (now < prev) {
|
|
||||||
/* Log timestamps should be monotonic increasing, if this happens,
|
|
||||||
* it means that we occur the super rare case: crossing year boundary
|
|
||||||
* (e.g 2019 -> 2020). Reset and reparse timestamp */
|
|
||||||
now = time(nullptr);
|
|
||||||
localtime_r(&now, &tm);
|
|
||||||
strptime(line, "%m-%d %H:%M:%S", &tm);
|
|
||||||
now = mktime(&tm);
|
|
||||||
}
|
|
||||||
// Skip old logs
|
|
||||||
if (now < LAST_TIMESTAMP)
|
|
||||||
continue;
|
|
||||||
LAST_TIMESTAMP = prev = now;
|
|
||||||
pthread_mutex_lock(&event_lock);
|
|
||||||
for (auto &event : events) {
|
|
||||||
if (event.enable && event.filter(line))
|
|
||||||
event.queue.emplace_back(line);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&event_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(logs);
|
|
||||||
kill(log_pid, SIGTERM);
|
|
||||||
waitpid(log_pid, nullptr, 0);
|
|
||||||
|
|
||||||
LOGI("logcat: unexpected output EOF");
|
|
||||||
|
|
||||||
// Wait a few seconds and retry
|
|
||||||
sleep(2);
|
|
||||||
if (!test_logcat()) {
|
|
||||||
// Cancel all events and terminate
|
|
||||||
logcat_started = false;
|
|
||||||
for (auto &event : events)
|
|
||||||
event.queue.cancel();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *log_writer(void *) {
|
|
||||||
rename(LOGFILE, LOGFILE ".bak");
|
|
||||||
FILE *log = xfopen(LOGFILE, "ae");
|
|
||||||
setbuf(log, nullptr);
|
|
||||||
auto &queue = start_logging(LOG_EVENT);
|
|
||||||
while (true) {
|
|
||||||
fprintf(log, "%s", queue.take().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockingQueue<std::string> &start_logging(logcat_event event) {
|
|
||||||
pthread_mutex_lock(&event_lock);
|
|
||||||
events[event].enable = true;
|
|
||||||
pthread_mutex_unlock(&event_lock);
|
|
||||||
return events[event].queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop_logging(logcat_event event) {
|
|
||||||
pthread_mutex_lock(&event_lock);
|
|
||||||
events[event].enable = false;
|
|
||||||
events[event].queue.clear();
|
|
||||||
pthread_mutex_unlock(&event_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_logcat() {
|
|
||||||
if (logcat_started)
|
|
||||||
return true;
|
|
||||||
if (!test_logcat())
|
|
||||||
return false;
|
|
||||||
init_args();
|
|
||||||
pthread_t t;
|
|
||||||
pthread_create(&t, nullptr, log_writer, nullptr);
|
|
||||||
pthread_detach(t);
|
|
||||||
pthread_create(&t, nullptr, logcat_gobbler, nullptr);
|
|
||||||
pthread_detach(t);
|
|
||||||
logcat_started = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <BlockingQueue.h>
|
|
||||||
|
|
||||||
enum logcat_event {
|
|
||||||
HIDE_EVENT,
|
|
||||||
LOG_EVENT
|
|
||||||
};
|
|
||||||
|
|
||||||
extern bool logcat_started;
|
|
||||||
|
|
||||||
BlockingQueue<std::string> &start_logging(logcat_event event);
|
|
||||||
void stop_logging(logcat_event event);
|
|
||||||
bool start_logcat();
|
|
@ -12,7 +12,6 @@
|
|||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
#include <resetprop.h>
|
#include <resetprop.h>
|
||||||
#include <db.h>
|
#include <db.h>
|
||||||
#include <logcat.h>
|
|
||||||
|
|
||||||
#include "magiskhide.h"
|
#include "magiskhide.h"
|
||||||
|
|
||||||
@ -197,6 +196,10 @@ int add_list(int client) {
|
|||||||
char *proc = read_string(client);
|
char *proc = read_string(client);
|
||||||
int ret = add_list(proc);
|
int ret = add_list(proc);
|
||||||
free(proc);
|
free(proc);
|
||||||
|
|
||||||
|
// Update inotify list
|
||||||
|
update_apk_list();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +233,10 @@ int rm_list(int client) {
|
|||||||
char *proc = read_string(client);
|
char *proc = read_string(client);
|
||||||
int ret = rm_list(proc);
|
int ret = rm_list(proc);
|
||||||
free(proc);
|
free(proc);
|
||||||
|
|
||||||
|
// Update inotify list
|
||||||
|
update_apk_list();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,9 +290,6 @@ int launch_magiskhide(int client) {
|
|||||||
if (hide_enabled)
|
if (hide_enabled)
|
||||||
return HIDE_IS_ENABLED;
|
return HIDE_IS_ENABLED;
|
||||||
|
|
||||||
if (!logcat_started)
|
|
||||||
return LOGCAT_DISABLED;
|
|
||||||
|
|
||||||
hide_enabled = true;
|
hide_enabled = true;
|
||||||
set_hide_config();
|
set_hide_config();
|
||||||
LOGI("* Starting MagiskHide\n");
|
LOGI("* Starting MagiskHide\n");
|
||||||
@ -328,8 +332,6 @@ int stop_magiskhide() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void auto_start_magiskhide() {
|
void auto_start_magiskhide() {
|
||||||
if (!start_logcat())
|
|
||||||
return;
|
|
||||||
db_settings dbs;
|
db_settings dbs;
|
||||||
get_db_settings(&dbs, HIDE_CONFIG);
|
get_db_settings(&dbs, HIDE_CONFIG);
|
||||||
if (dbs[HIDE_CONFIG]) {
|
if (dbs[HIDE_CONFIG]) {
|
||||||
|
@ -110,9 +110,6 @@ int magiskhide_main(int argc, char *argv[]) {
|
|||||||
switch (code) {
|
switch (code) {
|
||||||
case DAEMON_SUCCESS:
|
case DAEMON_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case LOGCAT_DISABLED:
|
|
||||||
fprintf(stderr, "Logcat is disabled, cannot start MagiskHide\n");
|
|
||||||
break;
|
|
||||||
case HIDE_NOT_ENABLED:
|
case HIDE_NOT_ENABLED:
|
||||||
fprintf(stderr, "MagiskHide is not enabled\n");
|
fprintf(stderr, "MagiskHide is not enabled\n");
|
||||||
break;
|
break;
|
||||||
|
@ -16,6 +16,9 @@ int add_list(int client);
|
|||||||
int rm_list(int client);
|
int rm_list(int client);
|
||||||
void ls_list(int client);
|
void ls_list(int client);
|
||||||
|
|
||||||
|
// Update APK list for inotify
|
||||||
|
void update_apk_list();
|
||||||
|
|
||||||
// Process monitor
|
// Process monitor
|
||||||
void proc_monitor();
|
void proc_monitor();
|
||||||
|
|
||||||
@ -42,8 +45,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
LOGCAT_DISABLED = DAEMON_LAST,
|
HIDE_IS_ENABLED = DAEMON_LAST,
|
||||||
HIDE_IS_ENABLED,
|
|
||||||
HIDE_NOT_ENABLED,
|
HIDE_NOT_ENABLED,
|
||||||
HIDE_ITEM_EXIST,
|
HIDE_ITEM_EXIST,
|
||||||
HIDE_ITEM_NOT_EXIST
|
HIDE_ITEM_NOT_EXIST
|
||||||
|
@ -1,26 +1,31 @@
|
|||||||
/* proc_monitor.cpp - Monitor am_proc_start events and unmount
|
/* proc_monitor.cpp - Monitor am_proc_start events and unmount
|
||||||
*
|
*
|
||||||
* We monitor the logcat am_proc_start events. When a target starts up,
|
* We monitor the listed APK files from /data/app until they get opened
|
||||||
* we pause it ASAP, and fork a new process to join its mount namespace
|
* via inotify to detect a new app launch.
|
||||||
* and do all the unmounting/mocking
|
*
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <magisk.h>
|
#include <magisk.h>
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
#include <logcat.h>
|
|
||||||
|
|
||||||
#include "magiskhide.h"
|
#include "magiskhide.h"
|
||||||
|
|
||||||
@ -28,10 +33,12 @@ using namespace std;
|
|||||||
|
|
||||||
extern char *system_block, *vendor_block, *data_block;
|
extern char *system_block, *vendor_block, *data_block;
|
||||||
|
|
||||||
|
#define EVENT_SIZE sizeof(struct inotify_event)
|
||||||
|
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
|
||||||
|
|
||||||
// Workaround for the lack of pthread_cancel
|
// Workaround for the lack of pthread_cancel
|
||||||
static void term_thread(int) {
|
static void term_thread(int) {
|
||||||
LOGD("proc_monitor: running cleanup\n");
|
LOGD("proc_monitor: running cleanup\n");
|
||||||
stop_logging(HIDE_EVENT);
|
|
||||||
hide_list.clear();
|
hide_list.clear();
|
||||||
hide_enabled = false;
|
hide_enabled = false;
|
||||||
pthread_mutex_destroy(&list_lock);
|
pthread_mutex_destroy(&list_lock);
|
||||||
@ -39,7 +46,7 @@ static void term_thread(int) {
|
|||||||
pthread_exit(nullptr);
|
pthread_exit(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static 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];
|
||||||
sprintf(path, "/proc/%d/ns/mnt", pid);
|
sprintf(path, "/proc/%d/ns/mnt", pid);
|
||||||
return stat(path, st);
|
return stat(path, st);
|
||||||
@ -50,19 +57,33 @@ static inline void lazy_unmount(const char* mountpoint) {
|
|||||||
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
LOGD("hide_daemon: Unmounted (%s)\n", mountpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_ppid(int pid) {
|
static inline int parse_ppid(const int pid) {
|
||||||
char path[32];
|
char path[32];
|
||||||
int ppid;
|
int ppid;
|
||||||
|
|
||||||
sprintf(path, "/proc/%d/stat", pid);
|
sprintf(path, "/proc/%d/stat", pid);
|
||||||
FILE *stat = fopen(path, "re");
|
FILE *stat = fopen(path, "re");
|
||||||
if (stat == nullptr)
|
if (stat == nullptr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* PID COMM STATE PPID ..... */
|
/* PID COMM STATE PPID ..... */
|
||||||
fscanf(stat, "%*d %*s %*c %d", &ppid);
|
fscanf(stat, "%*d %*s %*c %d", &ppid);
|
||||||
fclose(stat);
|
fclose(stat);
|
||||||
|
|
||||||
return ppid;
|
return ppid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uid_t get_uid(const int pid) {
|
||||||
|
char path[16];
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
sprintf(path, "/proc/%d", pid);
|
||||||
|
if (stat(path, &st) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return st.st_uid;
|
||||||
|
}
|
||||||
|
|
||||||
static void hide_daemon(int pid) {
|
static void hide_daemon(int pid) {
|
||||||
LOGD("hide_daemon: handling pid=[%d]\n", pid);
|
LOGD("hide_daemon: handling pid=[%d]\n", pid);
|
||||||
|
|
||||||
@ -106,6 +127,206 @@ exit:
|
|||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bionic's atoi runs through strtol() and fault-tolerence checkings.
|
||||||
|
* Since we don't need it, use our own implementation of atoi()
|
||||||
|
* for faster conversion.
|
||||||
|
*/
|
||||||
|
static inline int fast_atoi(const char *str) {
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
val = val * 10 + (*str++ - '0');
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leave /proc fd opened as we're going to read from it repeatedly
|
||||||
|
static DIR *dfd;
|
||||||
|
// Use unordered map with pid and namespace inode number to avoid time-consuming GC
|
||||||
|
static unordered_map<int, uint64_t> pid_ns_map;
|
||||||
|
// Use set for slow insertion but fast searching(which we'd encounter a lot more)
|
||||||
|
static set<uid_t> hide_uid;
|
||||||
|
|
||||||
|
static void detect_new_processes() {
|
||||||
|
struct dirent *dp;
|
||||||
|
struct stat ns, pns;
|
||||||
|
int pid, ppid;
|
||||||
|
bool hide;
|
||||||
|
uid_t uid;
|
||||||
|
unordered_map<int, uint64_t>::const_iterator pos;
|
||||||
|
|
||||||
|
// Iterate through /proc and get a process that reads the target APK
|
||||||
|
rewinddir(dfd);
|
||||||
|
while ((dp = readdir(dfd))) {
|
||||||
|
if (!isdigit(dp->d_name[0]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// dp->d_name is now the pid
|
||||||
|
pid = fast_atoi(dp->d_name);
|
||||||
|
|
||||||
|
// We're only interested in PIDs > 1000
|
||||||
|
if (pid <= 1000)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uid = get_uid(pid) % 100000; // Handle multiuser
|
||||||
|
if (hide_uid.find(uid) != hide_uid.end()) {
|
||||||
|
// Make sure our target is alive
|
||||||
|
if ((ppid = parse_ppid(pid)) < 0 || read_ns(ppid, &pns) || read_ns(pid, &ns))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if it's a process we haven't already hijacked
|
||||||
|
hide = false;
|
||||||
|
pos = pid_ns_map.find(pid);
|
||||||
|
if (pos == pid_ns_map.end()) {
|
||||||
|
hide = true;
|
||||||
|
pid_ns_map.insert(pair<int, uint64_t>(pid, ns.st_ino));
|
||||||
|
} else if (pos->second != ns.st_ino) {
|
||||||
|
hide = true;
|
||||||
|
pid_ns_map[pos->first] = ns.st_ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hide) {
|
||||||
|
// Send pause signal ASAP
|
||||||
|
if (kill(pid, SIGSTOP) == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The setns system call do not support multithread processes
|
||||||
|
* We have to fork a new process, setns, then do the unmounts
|
||||||
|
*/
|
||||||
|
LOGI("proc_monitor: UID=[%ju] PID=[%d] ns=[%llu]\n",
|
||||||
|
(uintmax_t)uid, pid, ns.st_ino);
|
||||||
|
if (fork_dont_care() == 0)
|
||||||
|
hide_daemon(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inotify_fd = 0;
|
||||||
|
static void listdir_apk(const char *name) {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
const char *ext;
|
||||||
|
char buf[4096];
|
||||||
|
char path[4096];
|
||||||
|
char *ptr;
|
||||||
|
|
||||||
|
if (!(dir = opendir(name)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", name,
|
||||||
|
entry->d_name);
|
||||||
|
|
||||||
|
if (entry->d_type == DT_DIR) {
|
||||||
|
if (strcmp(entry->d_name, ".") == 0
|
||||||
|
|| strcmp(entry->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
listdir_apk(path);
|
||||||
|
} else {
|
||||||
|
ext = &path[strlen(path) - 4];
|
||||||
|
if (!strncmp(".apk", ext, 4)) {
|
||||||
|
pthread_mutex_lock(&list_lock);
|
||||||
|
for (auto &s : hide_list) {
|
||||||
|
// Replace '/' with '\0' to stop reading beyond the actual package name
|
||||||
|
strcpy(buf, s.c_str());
|
||||||
|
if ((ptr = strchr(buf, '/')))
|
||||||
|
ptr[0] = '\0';
|
||||||
|
|
||||||
|
// Compare with (path + 10) to trim "/data/app/"
|
||||||
|
if (strncmp(path + 10, buf, strlen(buf)) == 0) {
|
||||||
|
if (inotify_add_watch(inotify_fd, path, IN_OPEN | IN_DELETE) > 0) {
|
||||||
|
LOGI("proc_monitor: Monitoring %s\n", path, inotify_fd);
|
||||||
|
} else {
|
||||||
|
LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&list_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_pkg_list() {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
struct stat st;
|
||||||
|
char buf[4096];
|
||||||
|
char path[4096];
|
||||||
|
char *ptr;
|
||||||
|
const char data_path[] = "/data/data";
|
||||||
|
|
||||||
|
if (!(dir = opendir(data_path)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&list_lock);
|
||||||
|
for (auto &s : hide_list)
|
||||||
|
LOGD("proc_monitor: hide_list: %s\n", s.c_str());
|
||||||
|
pthread_mutex_unlock(&list_lock);
|
||||||
|
|
||||||
|
hide_uid.clear();
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", data_path,
|
||||||
|
entry->d_name);
|
||||||
|
|
||||||
|
if (entry->d_type == DT_DIR) {
|
||||||
|
pthread_mutex_lock(&list_lock);
|
||||||
|
for (auto &s : hide_list) {
|
||||||
|
// Replace '/' with '\0' to stop reading beyond the actual package name
|
||||||
|
strcpy(buf, s.c_str());
|
||||||
|
if ((ptr = strchr(buf, '/')))
|
||||||
|
ptr[0] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(entry->d_name, buf) == 0) {
|
||||||
|
if (stat(path, &st) == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOGI("proc_monitor: %s UID is %d\n", buf, st.st_uid);
|
||||||
|
hide_uid.insert(st.st_uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&list_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through /data/app and search all .apk files
|
||||||
|
void update_apk_list() {
|
||||||
|
// Setup inotify
|
||||||
|
const char data_app[] = "/data/app";
|
||||||
|
|
||||||
|
if (inotify_fd)
|
||||||
|
close(inotify_fd);
|
||||||
|
|
||||||
|
inotify_fd = inotify_init();
|
||||||
|
if (inotify_fd < 0) {
|
||||||
|
LOGE("proc_monitor: Cannot initialize inotify: %s\n", strerror(errno));
|
||||||
|
term_thread(TERM_THREAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGI("proc_monitor: Updating APK list\n");
|
||||||
|
listdir_apk(data_app);
|
||||||
|
|
||||||
|
// Add /data/app itself to the watch list to detect app (un)installations/updates
|
||||||
|
if (inotify_add_watch(inotify_fd, data_app, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE) > 0) {
|
||||||
|
LOGI("proc_monitor: Monitoring %s\n", data_app, inotify_fd);
|
||||||
|
} else {
|
||||||
|
LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pkg_uid_map by reading from /data/data
|
||||||
|
update_pkg_list();
|
||||||
|
}
|
||||||
|
|
||||||
void proc_monitor() {
|
void proc_monitor() {
|
||||||
// Unblock user signals
|
// Unblock user signals
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
@ -123,57 +344,44 @@ void proc_monitor() {
|
|||||||
term_thread(TERM_THREAD);
|
term_thread(TERM_THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &queue = start_logging(HIDE_EVENT);
|
update_apk_list();
|
||||||
while (true) {
|
|
||||||
char *log;
|
|
||||||
int pid, ppid;
|
|
||||||
struct stat ns, pns;
|
|
||||||
|
|
||||||
string line = queue.take();
|
if ((dfd = opendir("/proc")) == NULL) {
|
||||||
if ((log = strchr(&line[0], '[')) == nullptr)
|
LOGE("proc_monitor: Unable to open /proc\n");
|
||||||
|
term_thread(TERM_THREAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect existing processes for the first time
|
||||||
|
detect_new_processes();
|
||||||
|
|
||||||
|
// Read inotify events
|
||||||
|
struct inotify_event *event;
|
||||||
|
ssize_t len;
|
||||||
|
char *p;
|
||||||
|
char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
|
||||||
|
for (;;) {
|
||||||
|
len = read(inotify_fd, buffer, EVENT_BUF_LEN);
|
||||||
|
if (len == -1) {
|
||||||
|
LOGE("proc_monitor: failed to read from inotify: %s\n", strerror(errno));
|
||||||
|
sleep(1);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract pid
|
for (p = buffer; p < buffer + len; ) {
|
||||||
if (sscanf(log, "[%*d,%d", &pid) != 1)
|
event = (struct inotify_event *)p;
|
||||||
continue;
|
|
||||||
|
|
||||||
// Extract last token (component name)
|
if (event->mask & IN_OPEN) {
|
||||||
const char *tok, *cpnt = "";
|
// Since we're just watching files,
|
||||||
while ((tok = strtok_r(nullptr, ",[]\n", &log)))
|
// extracting file name is not possible from querying event
|
||||||
cpnt = tok;
|
// LOGI("proc_monitor: inotify: APK opened\n");
|
||||||
if (cpnt[0] == '\0')
|
detect_new_processes();
|
||||||
continue;
|
} else {
|
||||||
|
LOGI("proc_monitor: inotify: /data/app change detected\n");
|
||||||
// Make sure our target is alive
|
update_apk_list();
|
||||||
if ((ppid = parse_ppid(pid)) < 0 || read_ns(ppid, &pns))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool hide = false;
|
|
||||||
pthread_mutex_lock(&list_lock);
|
|
||||||
for (auto &s : hide_list) {
|
|
||||||
if (strncmp(cpnt, s.c_str(), s.size() - 1) == 0) {
|
|
||||||
hide = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p += EVENT_SIZE + event->len;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&list_lock);
|
|
||||||
|
|
||||||
if (!hide)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
while (read_ns(pid, &ns) == 0 && ns.st_dev == pns.st_dev && ns.st_ino == pns.st_ino)
|
|
||||||
usleep(500);
|
|
||||||
|
|
||||||
// Send pause signal ASAP
|
|
||||||
if (kill(pid, SIGSTOP) == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The setns system call do not support multithread processes
|
|
||||||
* We have to fork a new process, setns, then do the unmounts
|
|
||||||
*/
|
|
||||||
LOGI("proc_monitor: %s PID=[%d] ns=[%llu]\n", cpnt, pid, ns.st_ino);
|
|
||||||
if (fork_dont_care() == 0)
|
|
||||||
hide_daemon(pid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user