Several MagiskHide improvements

- Directly get UID instead of traversing /data/data everytime
- Use /data/user_de/0 instead of /data/data on Android 7.0+
- Update hide_uid set incrementally when adding/initializing targets
- Guard hide_uid set with the same lock as hide_list vector
- Do not add GMS package into database; only add to in-memory list
This commit is contained in:
topjohnwu 2019-02-13 06:16:26 -05:00
parent 24da3485bd
commit c66cabd80f
4 changed files with 87 additions and 116 deletions

View File

@ -17,8 +17,13 @@
using namespace std;
vector<string> hide_list;
// Protect access to both hide_list and hide_uid
pthread_mutex_t list_lock;
vector<string> hide_list;
set<uid_t> hide_uid;
// Treat GMS separately as we're only interested in one component
int gms_uid = -1;
static pthread_t proc_monitor_thread;
@ -157,6 +162,24 @@ static void kill_process(const char *name) {
*slash = '/';
}
static int add_pkg_uid(const char *proc) {
char path[4096];
struct stat st;
const char *data = SDK_INT >= 24 ? "/data/user_de/0" : "/data/data";
sprintf(path, "%s/%s", data, proc);
if (xstat(path, &st) == 0) {
hide_uid.insert(st.st_uid);
return st.st_uid;
}
return -1;
}
void refresh_uid() {
hide_uid.clear();
for (auto &s : hide_list)
add_pkg_uid(s.c_str());
}
void clean_magisk_props() {
LOGD("hide_utils: Cleaning magisk props\n");
getprop([](const char *name, auto, auto) -> void {
@ -167,25 +190,25 @@ void clean_magisk_props() {
int add_list(const char *proc) {
for (auto &s : hide_list) {
// They should be unique
if (s == proc)
return HIDE_ITEM_EXIST;
}
LOGI("hide_list add: [%s]\n", proc);
// Add to database
char sql[4096];
snprintf(sql, sizeof(sql), "INSERT INTO hidelist (process) VALUES('%s')", proc);
char *err = db_exec(sql);
db_err_cmd(err, return DAEMON_ERROR);
LOGI("hide_list add: [%s]\n", proc);
// Critical region
pthread_mutex_lock(&list_lock);
hide_list.emplace_back(proc);
kill_process(proc);
add_pkg_uid(proc);
pthread_mutex_unlock(&list_lock);
kill_process(proc);
return DAEMON_SUCCESS;
}
@ -193,29 +216,27 @@ int add_list(int client) {
char *proc = read_string(client);
int ret = add_list(proc);
free(proc);
// Update inotify list
update_apk_list();
update_inotify_mask();
return ret;
}
static int rm_list(const char *proc) {
// Update list in critical region
bool do_rm = false;
// Critical region
bool remove = false;
pthread_mutex_lock(&list_lock);
for (auto it = hide_list.begin(); it != hide_list.end(); ++it) {
if (*it == proc) {
do_rm = true;
remove = true;
LOGI("hide_list rm: [%s]\n", proc);
hide_list.erase(it);
kill_process(proc);
break;
}
}
if (remove)
refresh_uid();
pthread_mutex_unlock(&list_lock);
if (do_rm) {
if (remove) {
char sql[4096];
snprintf(sql, sizeof(sql), "DELETE FROM hidelist WHERE process='%s'", proc);
char *err = db_exec(sql);
@ -230,26 +251,30 @@ int rm_list(int client) {
char *proc = read_string(client);
int ret = rm_list(proc);
free(proc);
// Update inotify list
update_apk_list();
update_inotify_mask();
return ret;
}
int init_list(void *, int, char **data, char**) {
LOGI("hide_list init: [%s]\n", *data);
hide_list.emplace_back(*data);
kill_process(*data);
int uid = add_pkg_uid(*data);
if (strcmp(*data, SAFETYNET_PKG) == 0)
gms_uid = uid;
return 0;
}
void init_list(const char *proc) {
init_list(nullptr, 0, (char **) &proc, nullptr);
}
#define LEGACY_LIST MODULEROOT "/.core/hidelist"
static int collect_list(void *, int, char **data, char**) {
LOGI("hide_list: [%s]\n", data[0]);
hide_list.push_back(data[0]);
kill_process(data[0]);
return 0;
}
bool init_list() {
LOGD("hide_list: initialize\n");
char *err = db_exec("SELECT process FROM hidelist", collect_list);
char *err = db_exec("SELECT process FROM hidelist", init_list);
db_err_cmd(err, return false);
// Migrate old hide list into database
@ -260,6 +285,12 @@ bool init_list() {
unlink(LEGACY_LIST);
}
// Add SafetyNet by default
rm_list(SAFETYNET_PROCESS);
rm_list(SAFETYNET_COMPONENT);
init_list(SAFETYNET_PKG);
update_inotify_mask();
return true;
}
@ -300,11 +331,6 @@ int launch_magiskhide(int client) {
if (!init_list())
goto error;
// Add SafetyNet by default
rm_list(SAFETYNET_PROCESS);
rm_list(SAFETYNET_COMPONENT);
add_list(SAFETYNET_PKG);
// Get thread reference
proc_monitor_thread = pthread_self();
if (client >= 0) {

View File

@ -4,6 +4,7 @@
#include <pthread.h>
#include <vector>
#include <string>
#include <set>
#include "daemon.h"
@ -21,7 +22,7 @@ int rm_list(int client);
void ls_list(int client);
// Update APK list for inotify
void update_apk_list();
void update_inotify_mask();
// Process monitor
void proc_monitor();
@ -30,14 +31,13 @@ void proc_monitor();
void manage_selinux();
void hide_sensitive_props();
void clean_magisk_props();
// List managements
int add_list(const char *proc);
bool init_list();
void refresh_uid();
extern bool hide_enabled;
extern pthread_mutex_t list_lock;
extern std::vector<std::string> hide_list;
extern std::set<uid_t> hide_uid;
extern int gms_uid;
enum {
LAUNCH_MAGISKHIDE,

View File

@ -22,7 +22,6 @@
#include <vector>
#include <string>
#include <unordered_map>
#include <set>
#include <magisk.h>
#include <utils.h>
@ -33,15 +32,21 @@ using namespace std;
extern char *system_block, *vendor_block, *data_block;
static int inotify_fd = -1;
#define EVENT_SIZE sizeof(struct inotify_event)
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
#define __ALIGN_EVENT __attribute__ ((aligned(__alignof__(struct inotify_event))))
// Workaround for the lack of pthread_cancel
static void term_thread(int) {
LOGD("proc_monitor: running cleanup\n");
hide_list.clear();
hide_uid.clear();
hide_enabled = false;
pthread_mutex_destroy(&list_lock);
close(inotify_fd);
inotify_fd = -1;
LOGD("proc_monitor: terminating\n");
pthread_exit(nullptr);
}
@ -164,21 +169,16 @@ static inline int fast_atoi(const char *str) {
static DIR *dfd;
// Use unordered map with pid and namespace inode number to avoid time-consuming GC
static unordered_map<int, uint64_t> pid_ns_map;
// Use set for slow insertion but fast searching(which we'd encounter a lot more)
static set<uid_t> hide_uid;
// Treat GMS separately as we're only interested in one component
static int gms_uid = -1;
static void detect_new_processes() {
struct dirent *dp;
struct stat ns, pns;
int pid, ppid;
bool hide;
uid_t uid;
unordered_map<int, uint64_t>::const_iterator pos;
// Iterate through /proc and get a process that reads the target APK
rewinddir(dfd);
pthread_mutex_lock(&list_lock);
while ((dp = readdir(dfd))) {
if (!isdigit(dp->d_name[0]))
continue;
@ -191,23 +191,16 @@ static void detect_new_processes() {
continue;
uid = get_uid(pid) % 100000; // Handle multiuser
if (hide_uid.find(uid) != hide_uid.end()) {
bool is_target = hide_uid.count(uid) != 0;
if (is_target) {
// Make sure our target is alive
if ((ppid = parse_ppid(pid)) < 0 || read_ns(ppid, &pns) || read_ns(pid, &ns))
continue;
// Check if it's a process we haven't already hijacked
hide = false;
pos = pid_ns_map.find(pid);
if (pos == pid_ns_map.end()) {
hide = true;
pid_ns_map.insert(pair<int, uint64_t>(pid, ns.st_ino));
} else if (pos->second != ns.st_ino) {
hide = true;
pid_ns_map[pos->first] = ns.st_ino;
}
if (hide) {
auto pos = pid_ns_map.find(pid);
if (pos == pid_ns_map.end() || pos->second != ns.st_ino) {
pid_ns_map[pid] = ns.st_ino;
if (uid == gms_uid) {
// Check /proc/uid/cmdline to see if it's SAFETYNET_PROCESS
if (!is_pid_safetynet_process(pid))
@ -225,15 +218,15 @@ static void detect_new_processes() {
* We have to fork a new process, setns, then do the unmounts
*/
LOGI("proc_monitor: UID=[%ju] PID=[%d] ns=[%llu]\n",
(uintmax_t)uid, pid, ns.st_ino);
(uintmax_t)uid, pid, ns.st_ino);
if (fork_dont_care() == 0)
hide_daemon(pid);
}
}
}
pthread_mutex_unlock(&list_lock);
}
static int inotify_fd = 0;
static void listdir_apk(const char *name) {
DIR *dir;
struct dirent *entry;
@ -260,9 +253,9 @@ static void listdir_apk(const char *name) {
// Compare with (path + 10) to trim "/data/app/"
if (strncmp(path + 10, s.c_str(), s.length()) == 0) {
if (inotify_add_watch(inotify_fd, path, IN_OPEN | IN_DELETE) > 0) {
LOGI("proc_monitor: Monitoring %s\n", path, inotify_fd);
LOGI("proc_monitor: Monitoring %s\n", path);
} else {
LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno));
LOGE("proc_monitor: Failed to monitor %s: %s\n", path, strerror(errno));
}
break;
}
@ -275,58 +268,12 @@ static void listdir_apk(const char *name) {
closedir(dir);
}
static void update_pkg_list() {
DIR *dir;
struct dirent *entry;
struct stat st;
char path[4096];
const char* target;
const char data_path[] = "/data/data";
if (!(dir = opendir(data_path)))
return;
pthread_mutex_lock(&list_lock);
for (auto &s : hide_list)
LOGD("proc_monitor: hide_list: %s\n", s.c_str());
pthread_mutex_unlock(&list_lock);
hide_uid.clear();
while ((entry = readdir(dir)) != NULL) {
snprintf(path, sizeof(path), "%s/%s", data_path,
entry->d_name);
if (entry->d_type == DT_DIR) {
pthread_mutex_lock(&list_lock);
for (auto &s : hide_list) {
target = s.c_str();
if (strcmp(entry->d_name, target) == 0) {
if (stat(path, &st) == -1)
continue;
LOGI("proc_monitor: %s UID is %d\n", target, st.st_uid);
hide_uid.insert(st.st_uid);
if (strcmp(entry->d_name, SAFETYNET_PKG) == 0) {
LOGI("proc_monitor: Got GMS: %d\n", st.st_uid);
gms_uid = st.st_uid;
}
}
}
pthread_mutex_unlock(&list_lock);
}
}
closedir(dir);
}
// Iterate through /data/app and search all .apk files
void update_apk_list() {
void update_inotify_mask() {
// Setup inotify
const char data_app[] = "/data/app";
if (inotify_fd)
if (inotify_fd >= 0)
close(inotify_fd);
inotify_fd = inotify_init();
@ -344,9 +291,6 @@ void update_apk_list() {
} else {
LOGE("proc_monitor: Failed to monitor %s: %s\n", strerror(errno));
}
// Update pkg_uid_map by reading from /data/data
update_pkg_list();
}
void proc_monitor() {
@ -366,8 +310,6 @@ void proc_monitor() {
term_thread(TERM_THREAD);
}
update_apk_list();
if ((dfd = opendir("/proc")) == NULL) {
LOGE("proc_monitor: Unable to open /proc\n");
term_thread(TERM_THREAD);
@ -380,11 +322,11 @@ void proc_monitor() {
struct inotify_event *event;
ssize_t len;
char *p;
char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
char buffer[EVENT_BUF_LEN] __ALIGN_EVENT;
for (;;) {
len = read(inotify_fd, buffer, EVENT_BUF_LEN);
if (len == -1) {
LOGE("proc_monitor: failed to read from inotify: %s\n", strerror(errno));
PLOGE("proc_monitor: read inotify");
sleep(1);
continue;
}
@ -399,7 +341,10 @@ void proc_monitor() {
detect_new_processes();
} else {
LOGI("proc_monitor: inotify: /data/app change detected\n");
update_apk_list();
pthread_mutex_lock(&list_lock);
refresh_uid();
pthread_mutex_unlock(&list_lock);
update_inotify_mask();
break;
}

View File

@ -385,7 +385,7 @@ void write_zero(int fd, size_t size) {
}
vector<string> file_to_vector(const char *filename) {
auto arr = vector<string>();
vector<string> arr;
if (access(filename, R_OK) != 0)
return arr;
char *line = nullptr;