Remove all non-Magisk hiding code

Magisk no longer interferes with any signals/info that were not created
or caused by Magisk itself.
This commit is contained in:
topjohnwu 2021-08-18 02:01:54 -07:00
parent 2b17c77195
commit 003fea52b1
9 changed files with 110 additions and 636 deletions

View File

@ -298,10 +298,10 @@ void post_fs_data(int client) {
safe_mode = true; safe_mode = true;
// Disable all modules and magiskhide so next boot will be clean // Disable all modules and magiskhide so next boot will be clean
disable_modules(); disable_modules();
stop_magiskhide(); disable_hide();
} else { } else {
exec_common_scripts("post-fs-data"); exec_common_scripts("post-fs-data");
auto_start_magiskhide(false); check_enable_hide();
handle_modules(); handle_modules();
} }
@ -350,7 +350,7 @@ void boot_complete(int client) {
if (access(SECURE_DIR, F_OK) != 0) if (access(SECURE_DIR, F_OK) != 0)
xmkdir(SECURE_DIR, 0700); xmkdir(SECURE_DIR, 0700);
auto_start_magiskhide(true); check_enable_hide();
if (!check_manager()) { if (!check_manager()) {
if (access(MANAGERAPK, F_OK) == 0) { if (access(MANAGERAPK, F_OK) == 0) {

View File

@ -55,5 +55,5 @@ void magiskhide_handler(int client, ucred *cred);
void su_daemon_handler(int client, ucred *credential); void su_daemon_handler(int client, ucred *credential);
// MagiskHide // MagiskHide
void auto_start_magiskhide(bool late_props); void check_enable_hide();
int stop_magiskhide(); int disable_hide();

View File

@ -9,69 +9,6 @@
using namespace std; using namespace std;
static const char *prop_key[] =
{ "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked",
"ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags",
"ro.vendor.boot.warranty_bit", "ro.vendor.warranty_bit",
"vendor.boot.vbmeta.device_state", nullptr };
static const char *prop_val[] =
{ "locked", "green", "1",
"enforcing", "0", "0",
"0", "1", "user", "release-keys",
"0", "0",
"locked", nullptr };
static const char *late_prop_key[] =
{ "vendor.boot.verifiedbootstate", nullptr };
static const char *late_prop_val[] =
{ "green", nullptr };
void hide_sensitive_props() {
LOGI("hide: Hiding sensitive props\n");
for (int i = 0; prop_key[i]; ++i) {
auto value = getprop(prop_key[i]);
if (!value.empty() && value != prop_val[i])
setprop(prop_key[i], prop_val[i], false);
}
// Hide that we booted from recovery when magisk is in recovery mode
auto bootmode = getprop("ro.bootmode");
if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("ro.bootmode", "unknown", false);
bootmode = getprop("ro.boot.mode");
if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("ro.boot.mode", "unknown", false);
bootmode = getprop("vendor.boot.mode");
if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("vendor.boot.mode", "unknown", false);
// Xiaomi cross region flash
auto hwc = getprop("ro.boot.hwc");
if (!hwc.empty() && str_contains(hwc, "CN"))
setprop("ro.boot.hwc", "GLOBAL", false);
auto hwcountry = getprop("ro.boot.hwcountry");
if (!hwcountry.empty() && str_contains(hwcountry, "China"))
setprop("ro.boot.hwcountry", "GLOBAL", false);
auto selinux = getprop("ro.build.selinux");
if (!selinux.empty())
delprop("ro.build.selinux");
}
void hide_late_sensitive_props() {
LOGI("hide: Hiding sensitive props (late)\n");
for (int i = 0; late_prop_key[i]; ++i) {
auto value = getprop(late_prop_key[i]);
if (!value.empty() && value != late_prop_val[i])
setprop(late_prop_key[i], late_prop_val[i], false);
}
}
static void lazy_unmount(const char* mountpoint) { static void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1) if (umount2(mountpoint, MNT_DETACH) != -1)
LOGD("hide: Unmounted (%s)\n", mountpoint); LOGD("hide: Unmounted (%s)\n", mountpoint);
@ -86,8 +23,7 @@ void hide_daemon(int pid) {
} }
} }
#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && \ #define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && str_starts(mentry->mnt_dir, "/" #dir))
strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0)
void hide_unmount(int pid) { void hide_unmount(int pid) {
if (pid > 0 && switch_mnt_ns(pid)) if (pid > 0 && switch_mnt_ns(pid))
@ -95,16 +31,6 @@ void hide_unmount(int pid) {
LOGD("hide: handling PID=[%d]\n", pid); LOGD("hide: handling PID=[%d]\n", pid);
char val;
int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
xxread(fd, &val, sizeof(val));
close(fd);
// Permissive
if (val == '0') {
chmod(SELINUX_ENFORCE, 0640);
chmod(SELINUX_POLICY, 0440);
}
vector<string> targets; vector<string> targets;
// Unmount dummy skeletons and /sbin links // Unmount dummy skeletons and /sbin links
@ -121,7 +47,7 @@ void hide_unmount(int pid) {
// Unmount all Magisk created mounts // Unmount all Magisk created mounts
parse_mnt("/proc/self/mounts", [&](mntent *mentry) { parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
if (strstr(mentry->mnt_fsname, BLOCKDIR)) if (str_contains(mentry->mnt_fsname, BLOCKDIR))
targets.emplace_back(mentry->mnt_dir); targets.emplace_back(mentry->mnt_dir);
return true; return true;
}); });
@ -129,4 +55,3 @@ void hide_unmount(int pid) {
for (auto &s : reversed(targets)) for (auto &s : reversed(targets))
lazy_unmount(s.data()); lazy_unmount(s.data());
} }

View File

@ -13,16 +13,16 @@
using namespace std; using namespace std;
static bool hide_state = false; static set<pair<string, string>> *hide_set; /* set of <pkg, process> pair */
static set<pair<string, string>> hide_set; /* set of <pkg, process> pair */ static map<int, vector<string_view>> *uid_proc_map; /* uid -> list of process */
map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
// Locks the variables above // Locks the variables above
pthread_mutex_t hide_state_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t hide_data_lock = PTHREAD_MUTEX_INITIALIZER;
void update_uid_map() { static atomic<bool> hide_state = false;
mutex_guard lock(hide_state_lock);
uid_proc_map.clear(); static void rebuild_uid_map() {
uid_proc_map->clear();
string data_path(APP_DATA_DIR); string data_path(APP_DATA_DIR);
size_t len = data_path.length(); size_t len = data_path.length();
auto dir = open_dir(APP_DATA_DIR); auto dir = open_dir(APP_DATA_DIR);
@ -34,17 +34,17 @@ void update_uid_map() {
data_path += '/'; data_path += '/';
size_t user_len = data_path.length(); size_t user_len = data_path.length();
struct stat st; struct stat st;
for (auto &hide : hide_set) { for (const auto &hide : *hide_set) {
if (hide.first == ISOLATED_MAGIC) { if (hide.first == ISOLATED_MAGIC) {
if (!first_iter) continue; if (!first_iter) continue;
// Setup isolated processes // Setup isolated processes
uid_proc_map[-1].emplace_back(hide.second); (*uid_proc_map)[-1].emplace_back(hide.second);
} }
data_path.resize(user_len); data_path.resize(user_len);
data_path += hide.first; data_path += hide.first;
if (stat(data_path.data(), &st)) if (stat(data_path.data(), &st))
continue; continue;
uid_proc_map[st.st_uid].emplace_back(hide.second); (*uid_proc_map)[st.st_uid].emplace_back(hide.second);
} }
first_iter = false; first_iter = false;
} }
@ -52,26 +52,19 @@ void update_uid_map() {
// Leave /proc fd opened as we're going to read from it repeatedly // Leave /proc fd opened as we're going to read from it repeatedly
static DIR *procfp; static DIR *procfp;
void crawl_procfs(const function<bool(int)> &fn) {
rewinddir(procfp);
crawl_procfs(procfp, fn);
}
void crawl_procfs(DIR *dir, const function<bool(int)> &fn) { template<class F>
struct dirent *dp; static void crawl_procfs(F &&fn) {
rewinddir(procfp);
dirent *dp;
int pid; int pid;
while ((dp = readdir(dir))) { while ((dp = readdir(procfp))) {
pid = parse_int(dp->d_name); pid = parse_int(dp->d_name);
if (pid > 0 && !fn(pid)) if (pid > 0 && !fn(pid))
break; break;
} }
} }
bool hide_enabled() {
mutex_guard g(hide_state_lock);
return hide_state;
}
template <bool str_op(string_view, string_view)> template <bool str_op(string_view, string_view)>
static bool proc_name_match(int pid, const char *name) { static bool proc_name_match(int pid, const char *name) {
char buf[4019]; char buf[4019];
@ -86,12 +79,12 @@ static bool proc_name_match(int pid, const char *name) {
return false; return false;
} }
static inline bool str_eql(string_view s, string_view ss) { return s == ss; } static inline bool str_eql(string_view a, string_view b) { return a == b; }
static void kill_process(const char *name, bool multi = false, template<bool str_op(string_view, string_view) = &str_eql>
bool (*filter)(int, const char *) = proc_name_match<&str_eql>) { static void kill_process(const char *name, bool multi = false) {
crawl_procfs([=](int pid) -> bool { crawl_procfs([=](int pid) -> bool {
if (filter(pid, name)) { if (proc_name_match<str_op>(pid, name)) {
kill(pid, SIGKILL); kill(pid, SIGKILL);
return multi; return multi;
} }
@ -137,10 +130,10 @@ static bool validate(const char *pkg, const char *proc) {
static void add_hide_set(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); LOGI("hide_list add: [%s/%s]\n", pkg, proc);
hide_set.emplace(pkg, proc); hide_set->emplace(pkg, proc);
if (strcmp(pkg, ISOLATED_MAGIC) == 0) { if (strcmp(pkg, ISOLATED_MAGIC) == 0) {
// Kill all matching isolated processes // Kill all matching isolated processes
kill_process(proc, true, proc_name_match<&str_starts>); kill_process<&str_starts>(proc, true);
} else { } else {
kill_process(proc); kill_process(proc);
} }
@ -153,9 +146,15 @@ static int add_list(const char *pkg, const char *proc) {
if (!validate(pkg, proc)) if (!validate(pkg, proc))
return HIDE_INVALID_PKG; return HIDE_INVALID_PKG;
for (auto &hide : hide_set) {
if (hide.first == pkg && hide.second == proc) // Critical region
return HIDE_ITEM_EXIST; mutex_guard lock(hide_data_lock);
for (const auto &hide : *hide_set)
if (hide.first == pkg && hide.second == proc)
return HIDE_ITEM_EXIST;
add_hide_set(pkg, proc);
rebuild_uid_map();
}
// Add to database // Add to database
char sql[4096]; char sql[4096];
@ -163,42 +162,33 @@ static int add_list(const char *pkg, const char *proc) {
"INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc); "INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc);
char *err = db_exec(sql); char *err = db_exec(sql);
db_err_cmd(err, return DAEMON_ERROR); db_err_cmd(err, return DAEMON_ERROR);
{
// Critical region
mutex_guard lock(hide_state_lock);
add_hide_set(pkg, proc);
}
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int add_list(int client) { int add_list(int client) {
string pkg = read_string(client); string pkg = read_string(client);
string proc = read_string(client); string proc = read_string(client);
int ret = add_list(pkg.data(), proc.data()); return add_list(pkg.data(), proc.data());
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; bool remove = false;
{ {
// Critical region // Critical region
mutex_guard lock(hide_state_lock); mutex_guard lock(hide_data_lock);
for (auto it = hide_set.begin(); it != hide_set.end();) { for (auto it = hide_set->begin(); it != hide_set->end();) {
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) { if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
remove = true; remove = true;
LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data()); LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data());
it = hide_set.erase(it); it = hide_set->erase(it);
} else { } else {
++it; ++it;
} }
} }
if (!remove)
return HIDE_ITEM_NOT_EXIST;
rebuild_uid_map();
} }
if (!remove)
return HIDE_ITEM_NOT_EXIST;
char sql[4096]; char sql[4096];
if (proc[0] == '\0') if (proc[0] == '\0')
@ -207,17 +197,14 @@ static int rm_list(const char *pkg, const char *proc) {
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc); "DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc);
char *err = db_exec(sql); char *err = db_exec(sql);
db_err(err); db_err_cmd(err, return DAEMON_ERROR);
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int rm_list(int client) { int rm_list(int client) {
string pkg = read_string(client); string pkg = read_string(client);
string proc = read_string(client); string proc = read_string(client);
int ret = rm_list(pkg.data(), proc.data()); return rm_list(pkg.data(), proc.data());
if (ret == DAEMON_SUCCESS)
update_uid_map();
return ret;
} }
static bool str_ends_safe(string_view s, string_view ss) { static bool str_ends_safe(string_view s, string_view ss) {
@ -227,9 +214,6 @@ static bool str_ends_safe(string_view s, string_view ss) {
return str_ends(s, ss); return str_ends(s, ss);
} }
#define SNET_PROC "com.google.android.gms.unstable"
#define GMS_PKG "com.google.android.gms"
static bool init_list() { static bool init_list() {
LOGD("hide: initialize\n"); LOGD("hide: initialize\n");
@ -243,27 +227,22 @@ static bool init_list() {
if (SDK_INT >= 29) { if (SDK_INT >= 29) {
kill_process("usap32", true); kill_process("usap32", true);
kill_process("usap64", true); kill_process("usap64", true);
kill_process("_zygote", true, proc_name_match<&str_ends_safe>); kill_process<&str_ends_safe>("_zygote", true);
} }
// Add SafetyNet by default
add_hide_set(GMS_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")
add_hide_set(GMS_PKG, GMS_PKG);
return true; return true;
} }
void ls_list(int client) { void ls_list(int client) {
write_int(client, DAEMON_SUCCESS); write_int(client, DAEMON_SUCCESS);
for (auto &hide : hide_set) { {
write_int(client, hide.first.size() + hide.second.size() + 1); mutex_guard lock(hide_data_lock);
xwrite(client, hide.first.data(), hide.first.size()); for (const auto &hide : *hide_set) {
xwrite(client, "|", 1); write_int(client, hide.first.size() + hide.second.size() + 1);
xwrite(client, hide.second.data(), hide.second.size()); xwrite(client, hide.first.data(), hide.first.size());
xwrite(client, "|", 1);
xwrite(client, hide.second.data(), hide.second.size());
}
} }
write_int(client, 0); write_int(client, 0);
close(client); close(client);
@ -272,14 +251,12 @@ void ls_list(int client) {
static void update_hide_config() { static void update_hide_config() {
char sql[64]; char sql[64];
sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)", sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)",
DB_SETTING_KEYS[HIDE_CONFIG], hide_state); DB_SETTING_KEYS[HIDE_CONFIG], hide_state.load());
char *err = db_exec(sql); char *err = db_exec(sql);
db_err(err); db_err(err);
} }
int launch_magiskhide(bool late_props) { int enable_hide() {
mutex_guard lock(hide_state_lock);
if (hide_state) if (hide_state)
return HIDE_IS_ENABLED; return HIDE_IS_ENABLED;
@ -291,31 +268,30 @@ int launch_magiskhide(bool late_props) {
LOGI("* Enable MagiskHide\n"); LOGI("* Enable MagiskHide\n");
mutex_guard lock(hide_data_lock);
default_new(hide_set);
default_new(uid_proc_map);
// Initialize the hide list // Initialize the hide list
if (!init_list()) if (!init_list())
return DAEMON_ERROR; return DAEMON_ERROR;
hide_sensitive_props();
if (late_props)
hide_late_sensitive_props();
hide_state = true; hide_state = true;
update_hide_config(); update_hide_config();
// Unlock here or else we'll be stuck in deadlock rebuild_uid_map();
lock.unlock();
update_uid_map();
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int stop_magiskhide() { int disable_hide() {
mutex_guard g(hide_state_lock); mutex_guard lock(hide_data_lock);
if (hide_state) { if (hide_state) {
LOGI("* Disable MagiskHide\n"); LOGI("* Disable MagiskHide\n");
uid_proc_map.clear(); delete uid_proc_map;
hide_set.clear(); delete hide_set;
uid_proc_map = nullptr;
hide_set = nullptr;
} }
hide_state = false; hide_state = false;
@ -323,43 +299,41 @@ int stop_magiskhide() {
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
void auto_start_magiskhide(bool late_props) { void check_enable_hide() {
if (hide_enabled()) { if (!hide_state) {
hide_late_sensitive_props();
} else {
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])
launch_magiskhide(late_props); enable_hide();
} }
} }
bool is_hide_target(int uid, string_view process, int max_len) { bool is_hide_target(int uid, string_view process) {
mutex_guard lock(hide_state_lock); mutex_guard lock(hide_data_lock);
if (uid % 100000 >= 90000) { if (uid % 100000 >= 90000) {
// Isolated processes // Isolated processes
auto it = uid_proc_map.find(-1); auto it = uid_proc_map->find(-1);
if (it == uid_proc_map.end()) if (it == uid_proc_map->end())
return false; return false;
for (auto &s : it->second) { for (auto &s : it->second) {
if (s.length() > max_len && process.length() > max_len && str_starts(s, process))
return true;
if (str_starts(process, s)) if (str_starts(process, s))
return true; return true;
} }
} else { } else {
auto it = uid_proc_map.find(uid); auto it = uid_proc_map->find(uid);
if (it == uid_proc_map.end()) if (it == uid_proc_map->end())
return false; return false;
for (auto &s : it->second) { for (auto &s : it->second) {
if (s.length() > max_len && process.length() > max_len && str_starts(s, process))
return true;
if (s == process) if (s == process)
return true; return true;
} }
} }
return false; return false;
} }
bool hide_enabled() {
return hide_state;
}

View File

@ -20,9 +20,6 @@ using namespace std;
" ls Print the current hide list\n" " ls Print the current hide list\n"
" exec CMDs... Execute commands in isolated mount\n" " exec CMDs... Execute commands in isolated mount\n"
" namespace and do all hide unmounts\n" " namespace and do all hide unmounts\n"
#ifdef MAGISK_DEBUG
" test Run process monitor test\n"
#endif
, arg0); , arg0);
exit(1); exit(1);
} }
@ -32,10 +29,10 @@ void magiskhide_handler(int client, ucred *cred) {
int res = DAEMON_ERROR; int res = DAEMON_ERROR;
switch (req) { switch (req) {
case STOP_MAGISKHIDE: case DISABLE_HIDE:
case ADD_HIDELIST: case ADD_LIST:
case RM_HIDELIST: case RM_LIST:
case LS_HIDELIST: case LS_LIST:
if (!hide_enabled()) { if (!hide_enabled()) {
write_int(client, HIDE_NOT_ENABLED); write_int(client, HIDE_NOT_ENABLED);
close(client); close(client);
@ -44,19 +41,19 @@ void magiskhide_handler(int client, ucred *cred) {
} }
switch (req) { switch (req) {
case LAUNCH_MAGISKHIDE: case ENABLE_HIDE:
res = launch_magiskhide(true); res = enable_hide();
break; break;
case STOP_MAGISKHIDE: case DISABLE_HIDE:
res = stop_magiskhide(); res = disable_hide();
break; break;
case ADD_HIDELIST: case ADD_LIST:
res = add_list(client); res = add_list(client);
break; break;
case RM_HIDELIST: case RM_LIST:
res = rm_list(client); res = rm_list(client);
break; break;
case LS_HIDELIST: case LS_LIST:
ls_list(client); ls_list(client);
return; return;
case HIDE_STATUS: case HIDE_STATUS:
@ -79,15 +76,15 @@ int magiskhide_main(int argc, char *argv[]) {
int req; int req;
if (opt == "enable"sv) if (opt == "enable"sv)
req = LAUNCH_MAGISKHIDE; req = ENABLE_HIDE;
else if (opt == "disable"sv) else if (opt == "disable"sv)
req = STOP_MAGISKHIDE; req = DISABLE_HIDE;
else if (opt == "add"sv) else if (opt == "add"sv)
req = ADD_HIDELIST; req = ADD_LIST;
else if (opt == "rm"sv) else if (opt == "rm"sv)
req = RM_HIDELIST; req = RM_LIST;
else if (opt == "ls"sv) else if (opt == "ls"sv)
req = LS_HIDELIST; req = LS_LIST;
else if (opt == "status"sv) else if (opt == "status"sv)
req = HIDE_STATUS; req = HIDE_STATUS;
else if (opt == "exec"sv && argc > 2) { else if (opt == "exec"sv && argc > 2) {
@ -104,7 +101,7 @@ int magiskhide_main(int argc, char *argv[]) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, MAGISKHIDE); write_int(fd, MAGISKHIDE);
write_int(fd, req); write_int(fd, req);
if (req == ADD_HIDELIST || req == RM_HIDELIST) { if (req == ADD_LIST || req == RM_LIST) {
write_string(fd, argv[2]); write_string(fd, argv[2]);
write_string(fd, argv[3] ? argv[3] : ""); write_string(fd, argv[3] ? argv[3] : "");
} }
@ -141,7 +138,7 @@ int magiskhide_main(int argc, char *argv[]) {
return DAEMON_ERROR; return DAEMON_ERROR;
} }
if (req == LS_HIDELIST) { if (req == LS_LIST) {
string res; string res;
for (;;) { for (;;) {
read_string(fd, res); read_string(fd, res);

View File

@ -1,48 +1,35 @@
#pragma once #pragma once
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h> #include <pthread.h>
#include <unistd.h>
#include <dirent.h>
#include <string_view> #include <string_view>
#include <functional> #include <functional>
#include <map> #include <map>
#include <daemon.hpp> #include <daemon.hpp>
#define SIGTERMTHRD SIGUSR1
#define ISOLATED_MAGIC "isolated" #define ISOLATED_MAGIC "isolated"
// CLI entries // CLI entries
int launch_magiskhide(bool late_props); int enable_hide();
int stop_magiskhide(); int disable_hide();
int add_list(int client); 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);
// Utility functions // Utility functions
void crawl_procfs(const std::function<bool (int)> &fn);
void crawl_procfs(DIR *dir, const std::function<bool (int)> &fn);
bool hide_enabled(); bool hide_enabled();
void update_uid_map(); bool is_hide_target(int uid, std::string_view process);
bool is_hide_target(int uid, std::string_view process, int max_len = 1024);
// Hide policies // Hide policies
void hide_daemon(int pid); void hide_daemon(int pid);
void hide_unmount(int pid = -1); void hide_unmount(int pid = -1);
void hide_sensitive_props();
void hide_late_sensitive_props();
extern pthread_mutex_t hide_state_lock;
extern std::map<int, std::vector<std::string_view>> uid_proc_map;
enum { enum {
LAUNCH_MAGISKHIDE, ENABLE_HIDE,
STOP_MAGISKHIDE, DISABLE_HIDE,
ADD_HIDELIST, ADD_LIST,
RM_HIDELIST, RM_LIST,
LS_HIDELIST, LS_LIST,
HIDE_STATUS, HIDE_STATUS,
}; };

View File

@ -1,409 +0,0 @@
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <sys/ptrace.h>
#include <sys/inotify.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <vector>
#include <bitset>
#include <utils.hpp>
#include "magiskhide.hpp"
using namespace std;
static int inotify_fd = -1;
static void new_zygote(int pid);
pthread_t monitor_thread;
/******************
* Data structures
******************/
#define PID_MAX 32768
struct pid_set {
bitset<PID_MAX>::const_reference operator[](size_t pos) const { return set[pos - 1]; }
bitset<PID_MAX>::reference operator[](size_t pos) { return set[pos - 1]; }
void reset() { set.reset(); }
private:
bitset<PID_MAX> set;
};
// true if pid is monitored
static pid_set attaches;
// zygote pid -> mnt ns
static map<int, struct stat> zygote_map;
/********
* Utils
********/
static inline int read_ns(const int pid, struct stat *st) {
char path[32];
sprintf(path, "/proc/%d/ns/mnt", pid);
return stat(path, st);
}
static int parse_ppid(int pid) {
char path[32];
int ppid;
sprintf(path, "/proc/%d/stat", pid);
auto stat = open_file(path, "re");
if (!stat)
return -1;
// PID COMM STATE PPID .....
fscanf(stat.get(), "%*d %*s %*c %d", &ppid);
return ppid;
}
static bool is_zygote_done() {
#ifdef __LP64__
return zygote_map.size() >= 2;
#else
return zygote_map.size() >= 1;
#endif
}
static void check_zygote() {
crawl_procfs([](int pid) -> bool {
char buf[512];
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
if (FILE *f = fopen(buf, "re")) {
fgets(buf, sizeof(buf), f);
if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1)
new_zygote(pid);
fclose(f);
}
return true;
});
if (is_zygote_done()) {
// Stop periodic scanning
timeval val { .tv_sec = 0, .tv_usec = 0 };
itimerval interval { .it_interval = val, .it_value = val };
setitimer(ITIMER_REAL, &interval, nullptr);
}
}
#define APP_PROC "/system/bin/app_process"
static void setup_inotify() {
inotify_fd = xinotify_init1(IN_CLOEXEC);
if (inotify_fd < 0)
return;
// Setup inotify asynchronous I/O
fcntl(inotify_fd, F_SETFL, O_ASYNC);
struct f_owner_ex ex = {
.type = F_OWNER_TID,
.pid = gettid()
};
fcntl(inotify_fd, F_SETOWN_EX, &ex);
// Monitor packages.xml
inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE);
// Monitor app_process
if (access(APP_PROC "32", F_OK) == 0) {
inotify_add_watch(inotify_fd, APP_PROC "32", IN_ACCESS);
if (access(APP_PROC "64", F_OK) == 0)
inotify_add_watch(inotify_fd, APP_PROC "64", IN_ACCESS);
} else {
inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS);
}
}
/************************
* Async signal handlers
************************/
static void inotify_event(int) {
// Make sure we can actually read stuffs
// or else the whole thread will be blocked.
struct pollfd pfd = {
.fd = inotify_fd,
.events = POLLIN,
.revents = 0
};
if (poll(&pfd, 1, 0) <= 0)
return; // Nothing to read
char buf[512];
auto event = reinterpret_cast<struct inotify_event *>(buf);
read(inotify_fd, buf, sizeof(buf));
if ((event->mask & IN_CLOSE_WRITE) && event->name == "packages.xml"sv)
update_uid_map();
check_zygote();
}
static void term_thread(int) {
LOGD("proc_monitor: cleaning up\n");
zygote_map.clear();
attaches.reset();
close(inotify_fd);
inotify_fd = -1;
// Restore all signal handlers that was set
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, nullptr);
struct sigaction act{};
act.sa_handler = SIG_DFL;
sigaction(SIGTERMTHRD, &act, nullptr);
sigaction(SIGIO, &act, nullptr);
sigaction(SIGALRM, &act, nullptr);
LOGD("proc_monitor: terminate\n");
pthread_exit(nullptr);
}
/******************
* Ptrace Madness
******************/
// Ptrace is super tricky, preserve all excessive logging in code
// but disable when actually building for usage (you won't want
// your logcat spammed with new thread events from all apps)
//#define PTRACE_LOG(fmt, args...) LOGD("PID=[%d] " fmt, pid, ##args)
#define PTRACE_LOG(...)
static void detach_pid(int pid, int signal = 0) {
attaches[pid] = false;
ptrace(PTRACE_DETACH, pid, 0, signal);
PTRACE_LOG("detach\n");
}
static bool check_pid(int pid) {
char path[128];
char cmdline[1024];
struct stat st;
sprintf(path, "/proc/%d", pid);
if (stat(path, &st)) {
// Process died unexpectedly, ignore
detach_pid(pid);
return true;
}
int uid = st.st_uid;
// UID hasn't changed
if (uid == 0)
return false;
sprintf(path, "/proc/%d/cmdline", pid);
if (auto f = open_file(path, "re")) {
fgets(cmdline, sizeof(cmdline), f.get());
} else {
// Process died unexpectedly, ignore
detach_pid(pid);
return true;
}
if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv ||
cmdline == "usap32"sv || cmdline == "usap64"sv)
return false;
if (!is_hide_target(uid, cmdline, 95))
goto not_target;
// Ensure 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) {
// ns not separated, abort
LOGW("proc_monitor: skip [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
goto not_target;
}
}
// Detach but the process should still remain stopped
// The hide daemon will resume the process after hiding it
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;
}
static bool is_process(int pid) {
char buf[128];
char key[32];
int tgid;
sprintf(buf, "/proc/%d/status", pid);
auto fp = open_file(buf, "re");
// PID is dead
if (!fp)
return false;
while (fgets(buf, sizeof(buf), fp.get())) {
sscanf(buf, "%s", key);
if (key == "Tgid:"sv) {
sscanf(buf, "%*s %d", &tgid);
return tgid == pid;
}
}
return false;
}
static void new_zygote(int pid) {
struct stat st;
if (read_ns(pid, &st))
return;
auto it = zygote_map.find(pid);
if (it != zygote_map.end()) {
// Update namespace info
it->second = st;
return;
}
LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid);
zygote_map[pid] = st;
xptrace(PTRACE_ATTACH, pid);
waitpid(pid, nullptr, __WALL | __WNOTHREAD);
xptrace(PTRACE_SETOPTIONS, pid, nullptr,
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXIT);
xptrace(PTRACE_CONT, pid);
}
#define DETACH_AND_CONT { detach_pid(pid); continue; }
void proc_monitor() {
monitor_thread = pthread_self();
// Backup original mask
sigset_t orig_mask;
pthread_sigmask(SIG_SETMASK, nullptr, &orig_mask);
sigset_t unblock_set;
sigemptyset(&unblock_set);
sigaddset(&unblock_set, SIGTERMTHRD);
sigaddset(&unblock_set, SIGIO);
sigaddset(&unblock_set, SIGALRM);
struct sigaction act{};
sigfillset(&act.sa_mask);
act.sa_handler = SIG_IGN;
sigaction(SIGTERMTHRD, &act, nullptr);
sigaction(SIGIO, &act, nullptr);
sigaction(SIGALRM, &act, nullptr);
// Temporary unblock to clear pending signals
pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);
pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);
act.sa_handler = term_thread;
sigaction(SIGTERMTHRD, &act, nullptr);
act.sa_handler = inotify_event;
sigaction(SIGIO, &act, nullptr);
act.sa_handler = [](int){ check_zygote(); };
sigaction(SIGALRM, &act, nullptr);
setup_inotify();
// First try find existing zygotes
check_zygote();
if (!is_zygote_done()) {
// Periodic scan every 250ms
timeval val { .tv_sec = 0, .tv_usec = 250000 };
itimerval interval { .it_interval = val, .it_value = val };
setitimer(ITIMER_REAL, &interval, nullptr);
}
for (int status;;) {
pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);
const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
if (pid < 0) {
if (errno == ECHILD) {
// Nothing to wait yet, sleep and wait till signal interruption
LOGD("proc_monitor: nothing to monitor, wait for signal\n");
struct timespec ts = {
.tv_sec = INT_MAX,
.tv_nsec = 0
};
nanosleep(&ts, nullptr);
}
continue;
}
pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);
if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
DETACH_AND_CONT;
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 (event) {
case PTRACE_EVENT_FORK:
case PTRACE_EVENT_VFORK:
PTRACE_LOG("zygote forked: [%lu]\n", msg);
attaches[msg] = true;
break;
case PTRACE_EVENT_EXIT:
PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
[[fallthrough]];
default:
zygote_map.erase(pid);
DETACH_AND_CONT;
}
} else {
switch (event) {
case PTRACE_EVENT_CLONE:
PTRACE_LOG("create new threads: [%lu]\n", msg);
if (attaches[pid] && check_pid(pid))
continue;
break;
case PTRACE_EVENT_EXEC:
case PTRACE_EVENT_EXIT:
PTRACE_LOG("exit or execve\n");
[[fallthrough]];
default:
DETACH_AND_CONT;
}
}
xptrace(PTRACE_CONT, pid);
} else if (signal == SIGSTOP) {
if (!attaches[pid]) {
// Double check if this is actually a process
attaches[pid] = is_process(pid);
}
if (attaches[pid]) {
// This is a process, continue monitoring
PTRACE_LOG("SIGSTOP from child\n");
xptrace(PTRACE_SETOPTIONS, pid, nullptr,
PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT);
xptrace(PTRACE_CONT, pid);
} else {
// This is a thread, do NOT monitor
PTRACE_LOG("SIGSTOP from thread\n");
DETACH_AND_CONT;
}
} else {
// Not caused by us, resend signal
xptrace(PTRACE_CONT, pid, nullptr, signal);
PTRACE_LOG("signal [%d]\n", signal);
}
}
}

View File

@ -58,6 +58,9 @@ reversed_container<T> reversed(T &base) {
return reversed_container<T>(base); return reversed_container<T>(base);
} }
template<class T>
static inline void default_new(T *&p) { p = new T(); }
template<typename T, typename Impl> template<typename T, typename Impl>
class stateless_allocator { class stateless_allocator {
public: public:

View File

@ -326,9 +326,6 @@ int hook_register(const char *path, const char *symbol, void *new_func, void **o
} // namespace } // namespace
template<class T>
static inline void default_new(T *&p) { p = new T(); }
#define XHOOK_REGISTER_SYM(PATH_REGEX, SYM, NAME) \ #define XHOOK_REGISTER_SYM(PATH_REGEX, SYM, NAME) \
hook_register(PATH_REGEX, SYM, (void*) new_##NAME, (void **) &old_##NAME) hook_register(PATH_REGEX, SYM, (void*) new_##NAME, (void **) &old_##NAME)