mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-26 12:47:32 +00:00
Preparation for hiding isolated processes
This commit is contained in:
@@ -68,7 +68,7 @@ static void load_overlay_rc(const char *overlay) {
|
||||
// Do not allow overwrite init.rc
|
||||
unlinkat(dfd, "init.rc", 0);
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (strend(entry->d_name, ".rc") == 0) {
|
||||
if (str_ends(entry->d_name, ".rc")) {
|
||||
LOGD("Found rc script [%s]\n", entry->d_name);
|
||||
int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
rc_list.push_back(fd_full_read(rc));
|
||||
|
@@ -48,23 +48,27 @@ void set_hide_state(bool state) {
|
||||
hide_state = state;
|
||||
}
|
||||
|
||||
template <bool str_op(string_view, string_view)>
|
||||
static bool proc_name_match(int pid, const char *name) {
|
||||
char buf[4019];
|
||||
sprintf(buf, "/proc/%d/cmdline", pid);
|
||||
if (FILE *f = fopen(buf, "re")) {
|
||||
fgets(buf, sizeof(buf), f);
|
||||
fclose(f);
|
||||
if (strcmp(buf, name) == 0)
|
||||
if (auto fp = open_file(buf, "re")) {
|
||||
fgets(buf, sizeof(buf), fp.get());
|
||||
if (str_op(buf, name)) {
|
||||
LOGD("hide_utils: kill PID=[%d] (%s)\n", pid, buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void kill_process(const char *name, bool multi = false) {
|
||||
static inline bool str_eql(string_view s, string_view ss) { return s == ss; }
|
||||
|
||||
static void kill_process(const char *name, bool multi = false,
|
||||
bool (*filter)(int, const char *) = proc_name_match<&str_eql>) {
|
||||
crawl_procfs([=](int pid) -> bool {
|
||||
if (proc_name_match(pid, name)) {
|
||||
if (kill(pid, SIGTERM) == 0)
|
||||
LOGD("hide_utils: killed PID=[%d] (%s)\n", pid, name);
|
||||
if (filter(pid, name)) {
|
||||
kill(pid, SIGTERM);
|
||||
return multi;
|
||||
}
|
||||
return true;
|
||||
@@ -72,6 +76,8 @@ static void kill_process(const char *name, bool multi = false) {
|
||||
}
|
||||
|
||||
static bool validate(const char *s) {
|
||||
if (strcmp(s, ISOLATED_MAGIC) == 0)
|
||||
return true;
|
||||
bool dot = false;
|
||||
for (char c; (c = *s); ++s) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
@@ -87,7 +93,18 @@ static bool validate(const char *s) {
|
||||
return dot;
|
||||
}
|
||||
|
||||
static int add_list(const char *pkg, const char *proc = "") {
|
||||
static void add_hide_set(const char *pkg, const char *proc) {
|
||||
LOGI("hide_list add: [%s/%s]\n", pkg, proc);
|
||||
hide_set.emplace(pkg, proc);
|
||||
if (strcmp(pkg, ISOLATED_MAGIC) == 0) {
|
||||
// Kill all matching isolated processes
|
||||
kill_process(proc, true, proc_name_match<&str_starts>);
|
||||
} else {
|
||||
kill_process(proc);
|
||||
}
|
||||
}
|
||||
|
||||
static int add_list(const char *pkg, const char *proc) {
|
||||
if (proc[0] == '\0')
|
||||
proc = pkg;
|
||||
|
||||
@@ -105,15 +122,12 @@ static int add_list(const char *pkg, const char *proc = "") {
|
||||
char *err = db_exec(sql);
|
||||
db_err_cmd(err, return DAEMON_ERROR);
|
||||
|
||||
LOGI("hide_list add: [%s/%s]\n", pkg, proc);
|
||||
|
||||
// Critical region
|
||||
{
|
||||
// Critical region
|
||||
mutex_guard lock(monitor_lock);
|
||||
hide_set.emplace(pkg, proc);
|
||||
add_hide_set(pkg, proc);
|
||||
}
|
||||
|
||||
kill_process(proc);
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -123,27 +137,28 @@ int add_list(int client) {
|
||||
int ret = add_list(pkg, proc);
|
||||
free(pkg);
|
||||
free(proc);
|
||||
update_uid_map();
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_uid_map();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm_list(const char *pkg, const char *proc = "") {
|
||||
static int rm_list(const char *pkg, const char *proc) {
|
||||
bool remove = false;
|
||||
{
|
||||
// Critical region
|
||||
mutex_guard lock(monitor_lock);
|
||||
bool remove = false;
|
||||
for (auto it = hide_set.begin(); it != hide_set.end();) {
|
||||
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
|
||||
remove = true;
|
||||
LOGI("hide_list rm: [%s]\n", it->second.data());
|
||||
LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data());
|
||||
it = hide_set.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (!remove)
|
||||
return HIDE_ITEM_NOT_EXIST;
|
||||
}
|
||||
if (!remove)
|
||||
return HIDE_ITEM_NOT_EXIST;
|
||||
|
||||
char sql[4096];
|
||||
if (proc[0] == '\0')
|
||||
@@ -167,10 +182,11 @@ int rm_list(int client) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void init_list(const char *pkg, const char *proc) {
|
||||
LOGI("hide_list init: [%s/%s]\n", pkg, proc);
|
||||
hide_set.emplace(pkg, proc);
|
||||
kill_process(proc);
|
||||
static bool str_ends_safe(string_view s, string_view ss) {
|
||||
// Never kill webview zygote
|
||||
if (s == "webview_zygote")
|
||||
return false;
|
||||
return str_ends(s, ss);
|
||||
}
|
||||
|
||||
#define SNET_PROC "com.google.android.gms.unstable"
|
||||
@@ -181,25 +197,26 @@ static bool init_list() {
|
||||
LOGD("hide_list: initialize\n");
|
||||
|
||||
char *err = db_exec("SELECT * FROM hidelist", [](db_row &row) -> bool {
|
||||
init_list(row["package_name"].data(), row["process"].data());
|
||||
add_hide_set(row["package_name"].data(), row["process"].data());
|
||||
return true;
|
||||
});
|
||||
db_err_cmd(err, return false);
|
||||
|
||||
// If Android Q+, also kill blastula pool
|
||||
// If Android Q+, also kill blastula pool and all app zygotes
|
||||
if (SDK_INT >= 29) {
|
||||
kill_process("usap32", true);
|
||||
kill_process("usap64", true);
|
||||
kill_process("_zygote", true, proc_name_match<&str_ends_safe>);
|
||||
}
|
||||
|
||||
// Add SafetyNet by default
|
||||
init_list(GMS_PKG, SNET_PROC);
|
||||
init_list(MICROG_PKG, SNET_PROC);
|
||||
add_hide_set(GMS_PKG, SNET_PROC);
|
||||
add_hide_set(MICROG_PKG, SNET_PROC);
|
||||
|
||||
// We also need to hide the default GMS process if MAGISKTMP != /sbin
|
||||
// The snet process communicates with the main process and get additional info
|
||||
if (MAGISKTMP != "/sbin")
|
||||
init_list(GMS_PKG, GMS_PKG);
|
||||
add_hide_set(GMS_PKG, GMS_PKG);
|
||||
|
||||
update_uid_map();
|
||||
return true;
|
||||
@@ -251,7 +268,7 @@ int launch_magiskhide() {
|
||||
hide_late_sensitive_props();
|
||||
|
||||
// Start monitoring
|
||||
void *(*start)(void*) = [](void*) -> void* { proc_monitor(); return nullptr; };
|
||||
void *(*start)(void*) = [](void*) -> void* { proc_monitor(); };
|
||||
if (xpthread_create(&proc_monitor_thread, nullptr, start, nullptr))
|
||||
return DAEMON_ERROR;
|
||||
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <daemon.hpp>
|
||||
|
||||
#define SIGTERMTHRD SIGUSR1
|
||||
#define ISOLATED_MAGIC "isolated"
|
||||
|
||||
// CLI entries
|
||||
int launch_magiskhide();
|
||||
|
@@ -1,6 +1,3 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@@ -61,13 +58,13 @@ static int parse_ppid(int pid) {
|
||||
int ppid;
|
||||
|
||||
sprintf(path, "/proc/%d/stat", pid);
|
||||
FILE *stat = fopen(path, "re");
|
||||
if (stat == nullptr)
|
||||
|
||||
auto stat = open_file(path, "re");
|
||||
if (!stat)
|
||||
return -1;
|
||||
|
||||
// PID COMM STATE PPID .....
|
||||
fscanf(stat, "%*d %*s %*c %d", &ppid);
|
||||
fclose(stat);
|
||||
fscanf(stat.get(), "%*d %*s %*c %d", &ppid);
|
||||
|
||||
return ppid;
|
||||
}
|
||||
@@ -89,20 +86,27 @@ void update_uid_map() {
|
||||
string data_path(APP_DATA_DIR);
|
||||
size_t len = data_path.length();
|
||||
auto dir = open_dir(APP_DATA_DIR);
|
||||
bool firstIteration = true;
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
data_path.resize(len);
|
||||
data_path += '/';
|
||||
data_path += entry->d_name;
|
||||
data_path += entry->d_name; // multiuser user id
|
||||
data_path += '/';
|
||||
size_t user_len = data_path.length();
|
||||
struct stat st;
|
||||
for (auto &hide : hide_set) {
|
||||
if (hide.first == ISOLATED_MAGIC) {
|
||||
if (!firstIteration) continue;
|
||||
// Setup isolated processes
|
||||
uid_proc_map[-1].emplace_back(hide.second);
|
||||
}
|
||||
data_path.resize(user_len);
|
||||
data_path += hide.first;
|
||||
if (stat(data_path.data(), &st))
|
||||
continue;
|
||||
uid_proc_map[st.st_uid].emplace_back(hide.second);
|
||||
}
|
||||
firstIteration = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +228,7 @@ static bool check_pid(int pid) {
|
||||
|
||||
sprintf(path, "/proc/%d", pid);
|
||||
if (stat(path, &st)) {
|
||||
// Process killed unexpectedly, ignore
|
||||
// Process died unexpectedly, ignore
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
@@ -237,7 +241,7 @@ static bool check_pid(int pid) {
|
||||
if (auto f = open_file(path, "re")) {
|
||||
fgets(cmdline, sizeof(cmdline), f.get());
|
||||
} else {
|
||||
// Process killed unexpectedly, ignore
|
||||
// Process died unexpectedly, ignore
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
@@ -247,36 +251,59 @@ static bool check_pid(int pid) {
|
||||
return false;
|
||||
|
||||
int uid = st.st_uid;
|
||||
auto it = uid_proc_map.find(uid);
|
||||
if (it != uid_proc_map.end()) {
|
||||
for (auto &s : it->second) {
|
||||
if (s == cmdline) {
|
||||
// Double check whether ns is separated
|
||||
read_ns(pid, &st);
|
||||
bool mnt_ns = true;
|
||||
for (auto &zit : zygote_map) {
|
||||
if (zit.second.st_ino == st.st_ino &&
|
||||
zit.second.st_dev == st.st_dev) {
|
||||
mnt_ns = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// For some reason ns is not separated, abort
|
||||
if (!mnt_ns)
|
||||
break;
|
||||
auto it = uid_proc_map.end();
|
||||
|
||||
// Finally this is our target!
|
||||
// Detach from ptrace but should still remain stopped.
|
||||
// The hide daemon will resume the process.
|
||||
PTRACE_LOG("target found\n");
|
||||
LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
|
||||
detach_pid(pid, SIGSTOP);
|
||||
hide_daemon(pid);
|
||||
return true;
|
||||
if (uid % 100000 > 90000) {
|
||||
// Isolated process
|
||||
it = uid_proc_map.find(-1);
|
||||
if (it == uid_proc_map.end())
|
||||
goto not_target;
|
||||
for (auto &s : it->second) {
|
||||
if (str_starts(cmdline, s)) {
|
||||
LOGI("proc_monitor: (isolated) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
|
||||
goto inject_and_hide;
|
||||
}
|
||||
}
|
||||
}
|
||||
PTRACE_LOG("[%s] not our target\n", cmdline);
|
||||
|
||||
it = uid_proc_map.find(uid);
|
||||
if (it == uid_proc_map.end())
|
||||
goto not_target;
|
||||
for (auto &s : it->second) {
|
||||
if (s != cmdline)
|
||||
continue;
|
||||
|
||||
if (str_ends(s, "_zygote")) {
|
||||
LOGI("proc_monitor: (app zygote) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
|
||||
goto inject_and_hide;
|
||||
}
|
||||
|
||||
// Double check whether ns is separated
|
||||
read_ns(pid, &st);
|
||||
for (auto &zit : zygote_map) {
|
||||
if (zit.second.st_ino == st.st_ino &&
|
||||
zit.second.st_dev == st.st_dev) {
|
||||
// For some reason ns is not separated, abort
|
||||
goto not_target;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally this is our target!
|
||||
// Detach from ptrace but should still remain stopped.
|
||||
// The hide daemon will resume the process.
|
||||
LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
|
||||
detach_pid(pid, SIGSTOP);
|
||||
hide_daemon(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
not_target:
|
||||
PTRACE_LOG("[%s] is not our target\n", cmdline);
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
|
||||
inject_and_hide:
|
||||
// TODO: handle isolated processes and app zygotes
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
@@ -324,7 +351,7 @@ static void new_zygote(int pid) {
|
||||
}
|
||||
|
||||
#define WEVENT(s) (((s) & 0xffff0000) >> 16)
|
||||
#define DETACH_AND_CONT { detach = true; continue; }
|
||||
#define DETACH_AND_CONT { detach_pid(pid); continue; }
|
||||
|
||||
void proc_monitor() {
|
||||
// Unblock some signals
|
||||
@@ -354,9 +381,7 @@ void proc_monitor() {
|
||||
setitimer(ITIMER_REAL, &interval, nullptr);
|
||||
}
|
||||
|
||||
int status;
|
||||
|
||||
for (;;) {
|
||||
for (int status;;) {
|
||||
const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
|
||||
if (pid < 0) {
|
||||
if (errno == ECHILD) {
|
||||
@@ -370,38 +395,35 @@ void proc_monitor() {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bool detach = false;
|
||||
run_finally f([&] {
|
||||
if (detach)
|
||||
// Non of our business now
|
||||
detach_pid(pid);
|
||||
});
|
||||
|
||||
if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
|
||||
DETACH_AND_CONT;
|
||||
|
||||
if (WSTOPSIG(status) == SIGTRAP && WEVENT(status)) {
|
||||
int event = WEVENT(status);
|
||||
int signal = WSTOPSIG(status);
|
||||
|
||||
if (signal == SIGTRAP && event) {
|
||||
unsigned long msg;
|
||||
xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
|
||||
if (zygote_map.count(pid)) {
|
||||
// Zygote event
|
||||
switch (WEVENT(status)) {
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_FORK:
|
||||
case PTRACE_EVENT_VFORK:
|
||||
PTRACE_LOG("zygote forked: [%d]\n", msg);
|
||||
PTRACE_LOG("zygote forked: [%lu]\n", msg);
|
||||
attaches[msg] = true;
|
||||
break;
|
||||
case PTRACE_EVENT_EXIT:
|
||||
PTRACE_LOG("zygote exited with status: [%d]\n", msg);
|
||||
PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
|
||||
[[fallthrough]];
|
||||
default:
|
||||
zygote_map.erase(pid);
|
||||
DETACH_AND_CONT;
|
||||
}
|
||||
} else {
|
||||
switch (WEVENT(status)) {
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_CLONE:
|
||||
PTRACE_LOG("create new threads: [%d]\n", msg);
|
||||
PTRACE_LOG("create new threads: [%lu]\n", msg);
|
||||
if (attaches[pid] && check_pid(pid))
|
||||
continue;
|
||||
break;
|
||||
@@ -414,7 +436,7 @@ void proc_monitor() {
|
||||
}
|
||||
}
|
||||
xptrace(PTRACE_CONT, pid);
|
||||
} else if (WSTOPSIG(status) == SIGSTOP) {
|
||||
} else if (signal == SIGSTOP) {
|
||||
if (!attaches[pid]) {
|
||||
// Double check if this is actually a process
|
||||
attaches[pid] = is_process(pid);
|
||||
@@ -432,8 +454,8 @@ void proc_monitor() {
|
||||
}
|
||||
} else {
|
||||
// Not caused by us, resend signal
|
||||
xptrace(PTRACE_CONT, pid, nullptr, WSTOPSIG(status));
|
||||
PTRACE_LOG("signal [%d]\n", WSTOPSIG(status));
|
||||
xptrace(PTRACE_CONT, pid, nullptr, signal);
|
||||
PTRACE_LOG("signal [%d]\n", signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -58,12 +58,6 @@ int gen_rand_str(char *buf, int len, bool varlen) {
|
||||
return len - 1;
|
||||
}
|
||||
|
||||
int strend(const char *s1, const char *s2) {
|
||||
size_t l1 = strlen(s1);
|
||||
size_t l2 = strlen(s2);
|
||||
return strcmp(s1 + l1 - l2, s2);
|
||||
}
|
||||
|
||||
int exec_command(exec_t &exec) {
|
||||
int pipefd[] = {-1, -1};
|
||||
int outfd = -1;
|
||||
@@ -151,12 +145,6 @@ void set_nice_name(const char *name) {
|
||||
prctl(PR_SET_NAME, name);
|
||||
}
|
||||
|
||||
bool ends_with(const std::string_view &s1, const std::string_view &s2) {
|
||||
unsigned l1 = s1.length();
|
||||
unsigned l2 = s2.length();
|
||||
return l1 < l2 ? false : s1.compare(l1 - l2, l2, s2) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bionic's atoi runs through strtol().
|
||||
* Use our own implementation for faster conversion.
|
||||
|
@@ -8,9 +8,6 @@
|
||||
#define UID_ROOT 0
|
||||
#define UID_SHELL 2000
|
||||
|
||||
#define str_contains(s, ss) ((ss) != nullptr && (s).find(ss) != std::string::npos)
|
||||
#define str_starts(s, ss) ((ss) != nullptr && (s).compare(0, strlen(ss), ss) == 0)
|
||||
|
||||
class mutex_guard {
|
||||
public:
|
||||
explicit mutex_guard(pthread_mutex_t &m): mutex(&m) {
|
||||
@@ -65,10 +62,18 @@ using thread_entry = void *(*)(void *);
|
||||
int new_daemon_thread(thread_entry entry, void *arg = nullptr, const pthread_attr_t *attr = nullptr);
|
||||
int new_daemon_thread(std::function<void()> &&entry);
|
||||
|
||||
bool ends_with(const std::string_view &s1, const std::string_view &s2);
|
||||
static inline bool str_contains(std::string_view s, std::string_view ss) {
|
||||
return s.find(ss) != std::string::npos;
|
||||
}
|
||||
static inline bool str_starts(std::string_view s, std::string_view ss) {
|
||||
return s.rfind(ss, 0) == 0;
|
||||
}
|
||||
static inline bool str_ends(std::string_view s, std::string_view ss) {
|
||||
return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string::npos, ss) == 0;
|
||||
}
|
||||
|
||||
int fork_dont_care();
|
||||
int fork_no_orphan();
|
||||
int strend(const char *s1, const char *s2);
|
||||
void init_argv0(int argc, char **argv);
|
||||
void set_nice_name(const char *name);
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v);
|
||||
|
Reference in New Issue
Block a user