mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-02-21 09:18:30 +00:00
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:
parent
2b17c77195
commit
003fea52b1
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user