mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-02-19 19:38:48 +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;
|
||||
// Disable all modules and magiskhide so next boot will be clean
|
||||
disable_modules();
|
||||
stop_magiskhide();
|
||||
disable_hide();
|
||||
} else {
|
||||
exec_common_scripts("post-fs-data");
|
||||
auto_start_magiskhide(false);
|
||||
check_enable_hide();
|
||||
handle_modules();
|
||||
}
|
||||
|
||||
@ -350,7 +350,7 @@ void boot_complete(int client) {
|
||||
if (access(SECURE_DIR, F_OK) != 0)
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
|
||||
auto_start_magiskhide(true);
|
||||
check_enable_hide();
|
||||
|
||||
if (!check_manager()) {
|
||||
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);
|
||||
|
||||
// MagiskHide
|
||||
void auto_start_magiskhide(bool late_props);
|
||||
int stop_magiskhide();
|
||||
void check_enable_hide();
|
||||
int disable_hide();
|
||||
|
@ -9,69 +9,6 @@
|
||||
|
||||
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) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("hide: Unmounted (%s)\n", mountpoint);
|
||||
@ -86,8 +23,7 @@ void hide_daemon(int pid) {
|
||||
}
|
||||
}
|
||||
|
||||
#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && \
|
||||
strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0)
|
||||
#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && str_starts(mentry->mnt_dir, "/" #dir))
|
||||
|
||||
void hide_unmount(int pid) {
|
||||
if (pid > 0 && switch_mnt_ns(pid))
|
||||
@ -95,16 +31,6 @@ void hide_unmount(int 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;
|
||||
|
||||
// Unmount dummy skeletons and /sbin links
|
||||
@ -121,7 +47,7 @@ void hide_unmount(int pid) {
|
||||
|
||||
// Unmount all Magisk created mounts
|
||||
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);
|
||||
return true;
|
||||
});
|
||||
@ -129,4 +55,3 @@ void hide_unmount(int pid) {
|
||||
for (auto &s : reversed(targets))
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
|
||||
|
@ -13,16 +13,16 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool hide_state = false;
|
||||
static set<pair<string, string>> hide_set; /* set of <pkg, process> pair */
|
||||
map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
|
||||
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 */
|
||||
|
||||
// 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() {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
uid_proc_map.clear();
|
||||
static atomic<bool> hide_state = false;
|
||||
|
||||
static void rebuild_uid_map() {
|
||||
uid_proc_map->clear();
|
||||
string data_path(APP_DATA_DIR);
|
||||
size_t len = data_path.length();
|
||||
auto dir = open_dir(APP_DATA_DIR);
|
||||
@ -34,17 +34,17 @@ void update_uid_map() {
|
||||
data_path += '/';
|
||||
size_t user_len = data_path.length();
|
||||
struct stat st;
|
||||
for (auto &hide : hide_set) {
|
||||
for (const auto &hide : *hide_set) {
|
||||
if (hide.first == ISOLATED_MAGIC) {
|
||||
if (!first_iter) continue;
|
||||
// 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 += hide.first;
|
||||
if (stat(data_path.data(), &st))
|
||||
continue;
|
||||
uid_proc_map[st.st_uid].emplace_back(hide.second);
|
||||
(*uid_proc_map)[st.st_uid].emplace_back(hide.second);
|
||||
}
|
||||
first_iter = false;
|
||||
}
|
||||
@ -52,26 +52,19 @@ void update_uid_map() {
|
||||
|
||||
// Leave /proc fd opened as we're going to read from it repeatedly
|
||||
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) {
|
||||
struct dirent *dp;
|
||||
template<class F>
|
||||
static void crawl_procfs(F &&fn) {
|
||||
rewinddir(procfp);
|
||||
dirent *dp;
|
||||
int pid;
|
||||
while ((dp = readdir(dir))) {
|
||||
while ((dp = readdir(procfp))) {
|
||||
pid = parse_int(dp->d_name);
|
||||
if (pid > 0 && !fn(pid))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool hide_enabled() {
|
||||
mutex_guard g(hide_state_lock);
|
||||
return hide_state;
|
||||
}
|
||||
|
||||
template <bool str_op(string_view, string_view)>
|
||||
static bool proc_name_match(int pid, const char *name) {
|
||||
char buf[4019];
|
||||
@ -86,12 +79,12 @@ static bool proc_name_match(int pid, const char *name) {
|
||||
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,
|
||||
bool (*filter)(int, const char *) = proc_name_match<&str_eql>) {
|
||||
template<bool str_op(string_view, string_view) = &str_eql>
|
||||
static void kill_process(const char *name, bool multi = false) {
|
||||
crawl_procfs([=](int pid) -> bool {
|
||||
if (filter(pid, name)) {
|
||||
if (proc_name_match<str_op>(pid, name)) {
|
||||
kill(pid, SIGKILL);
|
||||
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) {
|
||||
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) {
|
||||
// Kill all matching isolated processes
|
||||
kill_process(proc, true, proc_name_match<&str_starts>);
|
||||
kill_process<&str_starts>(proc, true);
|
||||
} else {
|
||||
kill_process(proc);
|
||||
}
|
||||
@ -153,9 +146,15 @@ static int add_list(const char *pkg, const char *proc) {
|
||||
if (!validate(pkg, proc))
|
||||
return HIDE_INVALID_PKG;
|
||||
|
||||
for (auto &hide : hide_set)
|
||||
if (hide.first == pkg && hide.second == proc)
|
||||
return HIDE_ITEM_EXIST;
|
||||
{
|
||||
// Critical region
|
||||
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
|
||||
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);
|
||||
char *err = db_exec(sql);
|
||||
db_err_cmd(err, return DAEMON_ERROR);
|
||||
|
||||
{
|
||||
// Critical region
|
||||
mutex_guard lock(hide_state_lock);
|
||||
add_hide_set(pkg, proc);
|
||||
}
|
||||
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
int add_list(int client) {
|
||||
string pkg = read_string(client);
|
||||
string proc = read_string(client);
|
||||
int ret = add_list(pkg.data(), proc.data());
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_uid_map();
|
||||
return ret;
|
||||
return add_list(pkg.data(), proc.data());
|
||||
}
|
||||
|
||||
static int rm_list(const char *pkg, const char *proc) {
|
||||
bool remove = false;
|
||||
{
|
||||
// Critical region
|
||||
mutex_guard lock(hide_state_lock);
|
||||
for (auto it = hide_set.begin(); it != hide_set.end();) {
|
||||
mutex_guard lock(hide_data_lock);
|
||||
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/%s]\n", it->first.data(), it->second.data());
|
||||
it = hide_set.erase(it);
|
||||
it = hide_set->erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (!remove)
|
||||
return HIDE_ITEM_NOT_EXIST;
|
||||
rebuild_uid_map();
|
||||
}
|
||||
if (!remove)
|
||||
return HIDE_ITEM_NOT_EXIST;
|
||||
|
||||
char sql[4096];
|
||||
if (proc[0] == '\0')
|
||||
@ -207,17 +197,14 @@ static int rm_list(const char *pkg, const char *proc) {
|
||||
snprintf(sql, sizeof(sql),
|
||||
"DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc);
|
||||
char *err = db_exec(sql);
|
||||
db_err(err);
|
||||
db_err_cmd(err, return DAEMON_ERROR);
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
int rm_list(int client) {
|
||||
string pkg = read_string(client);
|
||||
string proc = read_string(client);
|
||||
int ret = rm_list(pkg.data(), proc.data());
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_uid_map();
|
||||
return ret;
|
||||
return rm_list(pkg.data(), proc.data());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define SNET_PROC "com.google.android.gms.unstable"
|
||||
#define GMS_PKG "com.google.android.gms"
|
||||
|
||||
static bool init_list() {
|
||||
LOGD("hide: initialize\n");
|
||||
|
||||
@ -243,27 +227,22 @@ static bool init_list() {
|
||||
if (SDK_INT >= 29) {
|
||||
kill_process("usap32", 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;
|
||||
}
|
||||
|
||||
void ls_list(int client) {
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
for (auto &hide : hide_set) {
|
||||
write_int(client, hide.first.size() + hide.second.size() + 1);
|
||||
xwrite(client, hide.first.data(), hide.first.size());
|
||||
xwrite(client, "|", 1);
|
||||
xwrite(client, hide.second.data(), hide.second.size());
|
||||
{
|
||||
mutex_guard lock(hide_data_lock);
|
||||
for (const auto &hide : *hide_set) {
|
||||
write_int(client, hide.first.size() + hide.second.size() + 1);
|
||||
xwrite(client, hide.first.data(), hide.first.size());
|
||||
xwrite(client, "|", 1);
|
||||
xwrite(client, hide.second.data(), hide.second.size());
|
||||
}
|
||||
}
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
@ -272,14 +251,12 @@ void ls_list(int client) {
|
||||
static void update_hide_config() {
|
||||
char sql[64];
|
||||
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);
|
||||
db_err(err);
|
||||
}
|
||||
|
||||
int launch_magiskhide(bool late_props) {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
|
||||
int enable_hide() {
|
||||
if (hide_state)
|
||||
return HIDE_IS_ENABLED;
|
||||
|
||||
@ -291,31 +268,30 @@ int launch_magiskhide(bool late_props) {
|
||||
|
||||
LOGI("* Enable MagiskHide\n");
|
||||
|
||||
mutex_guard lock(hide_data_lock);
|
||||
default_new(hide_set);
|
||||
default_new(uid_proc_map);
|
||||
|
||||
// Initialize the hide list
|
||||
if (!init_list())
|
||||
return DAEMON_ERROR;
|
||||
|
||||
hide_sensitive_props();
|
||||
if (late_props)
|
||||
hide_late_sensitive_props();
|
||||
|
||||
hide_state = true;
|
||||
update_hide_config();
|
||||
|
||||
// Unlock here or else we'll be stuck in deadlock
|
||||
lock.unlock();
|
||||
|
||||
update_uid_map();
|
||||
rebuild_uid_map();
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
int stop_magiskhide() {
|
||||
mutex_guard g(hide_state_lock);
|
||||
int disable_hide() {
|
||||
mutex_guard lock(hide_data_lock);
|
||||
|
||||
if (hide_state) {
|
||||
LOGI("* Disable MagiskHide\n");
|
||||
uid_proc_map.clear();
|
||||
hide_set.clear();
|
||||
delete uid_proc_map;
|
||||
delete hide_set;
|
||||
uid_proc_map = nullptr;
|
||||
hide_set = nullptr;
|
||||
}
|
||||
|
||||
hide_state = false;
|
||||
@ -323,43 +299,41 @@ int stop_magiskhide() {
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
void auto_start_magiskhide(bool late_props) {
|
||||
if (hide_enabled()) {
|
||||
hide_late_sensitive_props();
|
||||
} else {
|
||||
void check_enable_hide() {
|
||||
if (!hide_state) {
|
||||
db_settings dbs;
|
||||
get_db_settings(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) {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
bool is_hide_target(int uid, string_view process) {
|
||||
mutex_guard lock(hide_data_lock);
|
||||
|
||||
if (uid % 100000 >= 90000) {
|
||||
// Isolated processes
|
||||
auto it = uid_proc_map.find(-1);
|
||||
if (it == uid_proc_map.end())
|
||||
auto it = uid_proc_map->find(-1);
|
||||
if (it == uid_proc_map->end())
|
||||
return false;
|
||||
|
||||
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))
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
auto it = uid_proc_map.find(uid);
|
||||
if (it == uid_proc_map.end())
|
||||
auto it = uid_proc_map->find(uid);
|
||||
if (it == uid_proc_map->end())
|
||||
return false;
|
||||
|
||||
for (auto &s : it->second) {
|
||||
if (s.length() > max_len && process.length() > max_len && str_starts(s, process))
|
||||
return true;
|
||||
if (s == process)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hide_enabled() {
|
||||
return hide_state;
|
||||
}
|
||||
|
@ -20,9 +20,6 @@ using namespace std;
|
||||
" ls Print the current hide list\n"
|
||||
" exec CMDs... Execute commands in isolated mount\n"
|
||||
" namespace and do all hide unmounts\n"
|
||||
#ifdef MAGISK_DEBUG
|
||||
" test Run process monitor test\n"
|
||||
#endif
|
||||
, arg0);
|
||||
exit(1);
|
||||
}
|
||||
@ -32,10 +29,10 @@ void magiskhide_handler(int client, ucred *cred) {
|
||||
int res = DAEMON_ERROR;
|
||||
|
||||
switch (req) {
|
||||
case STOP_MAGISKHIDE:
|
||||
case ADD_HIDELIST:
|
||||
case RM_HIDELIST:
|
||||
case LS_HIDELIST:
|
||||
case DISABLE_HIDE:
|
||||
case ADD_LIST:
|
||||
case RM_LIST:
|
||||
case LS_LIST:
|
||||
if (!hide_enabled()) {
|
||||
write_int(client, HIDE_NOT_ENABLED);
|
||||
close(client);
|
||||
@ -44,19 +41,19 @@ void magiskhide_handler(int client, ucred *cred) {
|
||||
}
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
res = launch_magiskhide(true);
|
||||
case ENABLE_HIDE:
|
||||
res = enable_hide();
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
res = stop_magiskhide();
|
||||
case DISABLE_HIDE:
|
||||
res = disable_hide();
|
||||
break;
|
||||
case ADD_HIDELIST:
|
||||
case ADD_LIST:
|
||||
res = add_list(client);
|
||||
break;
|
||||
case RM_HIDELIST:
|
||||
case RM_LIST:
|
||||
res = rm_list(client);
|
||||
break;
|
||||
case LS_HIDELIST:
|
||||
case LS_LIST:
|
||||
ls_list(client);
|
||||
return;
|
||||
case HIDE_STATUS:
|
||||
@ -79,15 +76,15 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
|
||||
int req;
|
||||
if (opt == "enable"sv)
|
||||
req = LAUNCH_MAGISKHIDE;
|
||||
req = ENABLE_HIDE;
|
||||
else if (opt == "disable"sv)
|
||||
req = STOP_MAGISKHIDE;
|
||||
req = DISABLE_HIDE;
|
||||
else if (opt == "add"sv)
|
||||
req = ADD_HIDELIST;
|
||||
req = ADD_LIST;
|
||||
else if (opt == "rm"sv)
|
||||
req = RM_HIDELIST;
|
||||
req = RM_LIST;
|
||||
else if (opt == "ls"sv)
|
||||
req = LS_HIDELIST;
|
||||
req = LS_LIST;
|
||||
else if (opt == "status"sv)
|
||||
req = HIDE_STATUS;
|
||||
else if (opt == "exec"sv && argc > 2) {
|
||||
@ -104,7 +101,7 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, MAGISKHIDE);
|
||||
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[3] ? argv[3] : "");
|
||||
}
|
||||
@ -141,7 +138,7 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
return DAEMON_ERROR;
|
||||
}
|
||||
|
||||
if (req == LS_HIDELIST) {
|
||||
if (req == LS_LIST) {
|
||||
string res;
|
||||
for (;;) {
|
||||
read_string(fd, res);
|
||||
|
@ -1,48 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include <daemon.hpp>
|
||||
|
||||
#define SIGTERMTHRD SIGUSR1
|
||||
#define ISOLATED_MAGIC "isolated"
|
||||
|
||||
// CLI entries
|
||||
int launch_magiskhide(bool late_props);
|
||||
int stop_magiskhide();
|
||||
int enable_hide();
|
||||
int disable_hide();
|
||||
int add_list(int client);
|
||||
int rm_list(int client);
|
||||
void ls_list(int client);
|
||||
|
||||
// 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();
|
||||
void update_uid_map();
|
||||
bool is_hide_target(int uid, std::string_view process, int max_len = 1024);
|
||||
bool is_hide_target(int uid, std::string_view process);
|
||||
|
||||
// Hide policies
|
||||
void hide_daemon(int pid);
|
||||
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 {
|
||||
LAUNCH_MAGISKHIDE,
|
||||
STOP_MAGISKHIDE,
|
||||
ADD_HIDELIST,
|
||||
RM_HIDELIST,
|
||||
LS_HIDELIST,
|
||||
ENABLE_HIDE,
|
||||
DISABLE_HIDE,
|
||||
ADD_LIST,
|
||||
RM_LIST,
|
||||
LS_LIST,
|
||||
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);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<typename T, typename Impl>
|
||||
class stateless_allocator {
|
||||
public:
|
||||
|
@ -326,9 +326,6 @@ int hook_register(const char *path, const char *symbol, void *new_func, void **o
|
||||
|
||||
} // namespace
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(T *&p) { p = new T(); }
|
||||
|
||||
#define XHOOK_REGISTER_SYM(PATH_REGEX, SYM, NAME) \
|
||||
hook_register(PATH_REGEX, SYM, (void*) new_##NAME, (void **) &old_##NAME)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user