From 144ff5e716f367e55b0259b03507ad8c2f14be32 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 9 Apr 2017 07:25:10 +0800 Subject: [PATCH] Integrate MagiskHide into Magisk Daemon --- jni/Android.mk | 2 +- jni/daemon/daemon.c | 44 +++++++++++++-- jni/daemon/daemon.h | 17 ++++++ jni/magisk.h | 23 ++++---- jni/magiskhide/hide_daemon.c | 38 ++++++++++--- jni/magiskhide/magiskhide.c | 101 +++++++++++++++++++++++++++++----- jni/magiskhide/magiskhide.h | 7 ++- jni/magiskhide/proc_monitor.c | 53 +++++++++++++----- jni/main.c | 20 ++++++- jni/utils/log_monitor.c | 4 +- jni/utils/utils.h | 6 +- jni/utils/xwrap.c | 57 +++++++++++++------ 12 files changed, 297 insertions(+), 75 deletions(-) diff --git a/jni/Android.mk b/jni/Android.mk index 5cccb94fd..942280aec 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -37,7 +37,7 @@ LOCAL_SRC_FILES := \ # su/utils.c \ # su/pts.c -LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch +LOCAL_CFLAGS := -Wno-implicit-exception-spec-mismatch -DDEBUG LOCAL_LDLIBS := -llog include $(BUILD_EXECUTABLE) diff --git a/jni/daemon/daemon.c b/jni/daemon/daemon.c index 9aa7c0f01..4dbf555a8 100644 --- a/jni/daemon/daemon.c +++ b/jni/daemon/daemon.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,12 +19,33 @@ #include "daemon.h" static void request_handler(int client) { - /* TODO: Put all function entrypoints here - it'll currently just log the input string */ - char *s = read_string(client); + client_request req = read_int(client); + char *s; + int pid, status, code; + switch (req) { + case LAUNCH_MAGISKHIDE: + launch_magiskhide(client); + break; + case STOP_MAGISKHIDE: + stop_magiskhide(client); + break; + case ADD_HIDELIST: + // TODO: Add hidelist + break; + case RM_HIDELIST: + // TODO: Remove hidelist + break; + case SUPERUSER: + // TODO: Run su + break; + case TEST: + s = read_string(client); + LOGI("%s\n", s); + free(s); + write_int(client, 0); + break; + } close(client); - LOGI("%s\n", s); - free(s); } /* Setup the address and return socket fd */ @@ -39,10 +61,14 @@ static int setup_socket(struct sockaddr_un *sun) { return fd; } + +static void do_nothing() {} + void start_daemon() { // Launch the daemon, create new session, set proper context if (getuid() != AID_ROOT || getgid() != AID_ROOT) { - PLOGE("daemon requires root. uid/gid not root"); + fprintf(stderr, "Starting daemon requires root: %s\n", strerror(errno)); + PLOGE("start daemon"); } switch (fork()) { case -1: @@ -63,6 +89,12 @@ void start_daemon() { // Change process name strcpy(argv0, "magisk_daemon"); + // The root daemon should not do anything if an error occurs + // It should stay intact in any circumstances + err_handler = do_nothing; + + // Start log monitor + monitor_logs(); // Loop forever to listen to requests while(1) { diff --git a/jni/daemon/daemon.h b/jni/daemon/daemon.h index 91e0e52d1..8648277ed 100644 --- a/jni/daemon/daemon.h +++ b/jni/daemon/daemon.h @@ -1,7 +1,24 @@ /* daemon.h - Utility functions for daemon-client communication */ + +// Commands require connecting to daemon +typedef enum { + LAUNCH_MAGISKHIDE, + STOP_MAGISKHIDE, + ADD_HIDELIST, + RM_HIDELIST, + SUPERUSER, + TEST +} client_request; + +// daemon.c + +void start_daemon(); +int connect_daemon(); + // socket_trans.c + int recv_fd(int sockfd); void send_fd(int sockfd, int fd); int read_int(int fd); diff --git a/jni/magisk.h b/jni/magisk.h index bd661906a..540446800 100644 --- a/jni/magisk.h +++ b/jni/magisk.h @@ -9,6 +9,7 @@ #include #include +#include #include #define AID_SHELL (get_shell_uid()) @@ -21,6 +22,13 @@ #define LOG_TAG "Magisk" +// Global handler for PLOGE +extern __thread void (*err_handler)(void); + +// Two common error handlers +void exit_proc(); +void exit_thread(); + #ifdef DEBUG #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #else @@ -30,14 +38,14 @@ #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); exit(1) -#define PLOGEV(fmt, err, args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err)); exit(1) +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler() void stub(const char *fmt, ...); // Global buffer (only for main thread!!) #define BUF_SIZE 4096 extern char magiskbuf[BUF_SIZE]; + extern char *argv0; /* For changing process name */ // Multi-call entrypoints @@ -53,19 +61,12 @@ int resetprop_main(int argc, char *argv[]); } #endif -/***************** - * Magisk Daemon * - *****************/ - -void start_daemon(); -int connect_daemon(); - /************** * MagiskHide * **************/ -void launch_magiskhide(); -void stop_magiskhide(); +void launch_magiskhide(int client); +void stop_magiskhide(int client); #endif diff --git a/jni/magiskhide/hide_daemon.c b/jni/magiskhide/hide_daemon.c index 9fcce2796..b58475055 100644 --- a/jni/magiskhide/hide_daemon.c +++ b/jni/magiskhide/hide_daemon.c @@ -18,7 +18,7 @@ #include "utils.h" #include "magiskhide.h" -static int isMocked = 0; +static int isMocked = 0, pid; static void manage_selinux() { if (isMocked) return; @@ -43,23 +43,36 @@ static void lazy_unmount(const char* mountpoint) { LOGI("hide_daemon: Unmount Failed (%s)\n", mountpoint); } -void hide_daemon() { +static void hide_daemon_err() { + LOGD("hide_daemon: error occured, stopping magiskhide services\n"); + // Resume process if possible + kill(pid, SIGCONT); + int err = 1; + write(sv[1], &err, sizeof(err)); + _exit(-1); +} + +int hide_daemon() { // Fork to a new process - switch(fork()) { + hide_pid = fork(); + switch(hide_pid) { case -1: PLOGE("fork"); + return 1; case 0: break; default: - return; + return 0; } - close(pipefd[1]); + close(sv[0]); // Set the process name strcpy(argv0, "magiskhide_daemon"); + // When an error occurs, report its failure to main process + err_handler = hide_daemon_err; - int pid, fd; + int fd; FILE *fp; char cache_block[256], *line; struct vector mount_list; @@ -67,9 +80,12 @@ void hide_daemon() { cache_block[0] = '\0'; while(1) { - xxread(pipefd[0], &pid, sizeof(pid)); + xxread(sv[1], &pid, sizeof(pid)); // Termination called - if(pid == -1) exit(0); + if(pid == -1) { + LOGD("hide_daemon: received termination request\n"); + _exit(0); + } snprintf(magiskbuf, BUF_SIZE, "/proc/%d/ns/mnt", pid); if(access(magiskbuf, F_OK) == -1) continue; // Maybe process died.. @@ -95,7 +111,7 @@ void hide_daemon() { } } } - + // First unmount the dummy skeletons, cache mounts, and /sbin links vec_for_each_r(&mount_list, line) { if (strstr(line, "tmpfs /system") || strstr(line, "tmpfs /vendor") || strstr(line, "tmpfs /sbin") @@ -125,6 +141,10 @@ void hide_daemon() { // All done, send resume signal kill(pid, SIGCONT); + + // Tell main process all is well + pid = 0; + xwrite(sv[1], &pid, sizeof(pid)); } // Should never go here diff --git a/jni/magiskhide/magiskhide.c b/jni/magiskhide/magiskhide.c index e15f8cb10..0d5daf3d2 100644 --- a/jni/magiskhide/magiskhide.c +++ b/jni/magiskhide/magiskhide.c @@ -8,38 +8,67 @@ #include #include #include +#include +#include #include #include "magisk.h" #include "utils.h" #include "magiskhide.h" +#include "daemon.h" -int pipefd[2]; +int sv[2], hide_pid; struct vector *hide_list, *new_list; +int isEnabled = 0; static pthread_t proc_monitor_thread; -static void kill_proc(int pid) { +void kill_proc(int pid) { kill(pid, SIGTERM); } -void launch_magiskhide() { +static void usage(char *arg0) { + fprintf(stderr, + "%s [--options [arguments...] ]\n\n" + "Options:\n" + " --enable: Start the magiskhide daemon\n" + " --disable: Stop the magiskhide daemon\n" + " --add : Add to the list\n" + " --rm : Remove from the list\n" + " --ls: Print out the current hide list\n" + , arg0); + exit(1); +} + +void launch_magiskhide(int client) { + if (isEnabled) { + write_int(client, 0); + return; + } /* * The setns system call do not support multithread processes * We have to fork a new process, and communicate with pipe */ - xpipe(pipefd); + LOGI("* Starting MagiskHide\n"); + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) == -1) + goto error; // Launch the hide daemon - hide_daemon(); + if (hide_daemon()) + goto error; - close(pipefd[0]); + close(sv[1]); // Initialize the hide list - hide_list = new_list = malloc(sizeof(*hide_list)); + hide_list = new_list = xmalloc(sizeof(*hide_list)); + if (hide_list == NULL) + goto error; vec_init(hide_list); FILE *fp = xfopen(HIDELIST, "r"); + if (fp == NULL) + goto error; file_to_vector(hide_list, fp); fclose(fp); char *line; @@ -49,20 +78,66 @@ void launch_magiskhide() { } // Start a new thread to monitor processes - pthread_create(&proc_monitor_thread, NULL, proc_monitor, NULL); - pthread_join(proc_monitor_thread, NULL); + if (xpthread_create(&proc_monitor_thread, NULL, proc_monitor, NULL)) + goto error; + + isEnabled = 1; + write_int(client, 0); + return; +error: + write_int(client, 1); + return; } -void stop_magiskhide() { +void stop_magiskhide(int client) { + if (!isEnabled) + return; + + LOGI("* Stopping MagiskHide\n"); + int kill = -1; // Terminate hide daemon - xwrite(pipefd[1], &kill, sizeof(kill)); + write(sv[0], &kill, sizeof(kill)); + close(sv[0]); + waitpid(hide_pid, NULL, 0); // Stop process monitor pthread_kill(proc_monitor_thread, SIGUSR1); pthread_join(proc_monitor_thread, NULL); + + isEnabled = 0; + write_int(client, 0); } int magiskhide_main(int argc, char *argv[]) { - launch_magiskhide(); - return 0; + if (argc < 2) { + usage(argv[0]); + } + client_request req; + if (strcmp(argv[1], "--enable") == 0) { + req = LAUNCH_MAGISKHIDE; + } else if (strcmp(argv[1], "--disable") == 0) { + req = STOP_MAGISKHIDE; + } else if (strcmp(argv[1], "--add") == 0 && argc > 2) { + req = ADD_HIDELIST; + } else if (strcmp(argv[1], "--rm") == 0 && argc > 2) { + req = RM_HIDELIST; + } else if (strcmp(argv[1], "--ls") == 0) { + FILE *fp = xfopen(HIDELIST, "r"); + while (fgets(magiskbuf, BUF_SIZE, fp)) { + printf("%s", magiskbuf); + } + fclose(fp); + return 0; + } + int fd = connect_daemon(); + write_int(fd, req); + if (req == ADD_HIDELIST || req == RM_HIDELIST) { + write_string(fd, argv[2]); + } + int code = read_int(fd); + close(fd); + if (code) { + fprintf(stderr, "Error occured in MagiskHide daemon\n"); + } + return code; } diff --git a/jni/magiskhide/magiskhide.h b/jni/magiskhide/magiskhide.h index 434108c0c..740d28ee7 100644 --- a/jni/magiskhide/magiskhide.h +++ b/jni/magiskhide/magiskhide.h @@ -6,13 +6,16 @@ #define ENFORCE_FILE "/sys/fs/selinux/enforce" #define POLICY_FILE "/sys/fs/selinux/policy" +// Kill process +void kill_proc(int pid); + // Hide daemon -void hide_daemon(); +int hide_daemon(); // Process monitor void *proc_monitor(void *args); -extern int pipefd[2]; +extern int sv[2], hide_pid, isEnabled; extern struct vector *hide_list, *new_list; #endif diff --git a/jni/magiskhide/proc_monitor.c b/jni/magiskhide/proc_monitor.c index 0659c93a8..5d65095e6 100644 --- a/jni/magiskhide/proc_monitor.c +++ b/jni/magiskhide/proc_monitor.c @@ -11,15 +11,12 @@ #include #include #include +#include #include "magisk.h" #include "utils.h" #include "magiskhide.h" -/* WTF... the macro in the NDK pthread.h is broken.... */ -#define pthread_cleanup_push_fix(routine, arg) \ -pthread_cleanup_push(routine, arg) } while(0); - static int zygote_num = 0; static char init_ns[32], zygote_ns[2][32]; @@ -32,15 +29,21 @@ static void read_namespace(const int pid, char* target, const size_t size) { // Workaround for the lack of pthread_cancel static void quit_pthread(int sig) { - pthread_exit(NULL); -} - -static void cleanup_handler(void *arg) { + LOGD("proc_monitor: running cleanup\n"); + char *line; + vec_for_each(hide_list, line) { + ps_filter_proc_name(line, kill_proc); + } vec_deep_destroy(hide_list); - vec_deep_destroy(new_list); free(hide_list); - free(new_list); + if (new_list != hide_list) { + vec_deep_destroy(new_list); + free(new_list); + } hide_list = new_list = NULL; + isEnabled = 0; + LOGD("proc_monitor: terminating...\n"); + pthread_exit(NULL); } static void store_zygote_ns(int pid) { @@ -51,10 +54,21 @@ static void store_zygote_ns(int pid) { ++zygote_num; } +static void proc_monitor_err() { + LOGD("proc_monitor: error occured, stopping magiskhide services\n"); + int kill = -1; + // If process monitor dies, kill hide daemon too + write(sv[0], &kill, sizeof(kill)); + close(sv[0]); + waitpid(hide_pid, NULL, 0); + quit_pthread(SIGUSR1); +} + void *proc_monitor(void *args) { // Register the cancel signal signal(SIGUSR1, quit_pthread); - pthread_cleanup_push_fix(cleanup_handler, NULL); + // The error handler should only exit the thread, not the whole process + err_handler = proc_monitor_err; int pid; char buffer[512]; @@ -106,6 +120,8 @@ void *proc_monitor(void *args) { hide_list = new_list; } + ret = 0; + vec_for_each(hide_list, line) { if (strcmp(processName, line) == 0) { read_namespace(pid, buffer, 32); @@ -121,13 +137,19 @@ void *proc_monitor(void *args) { if (ret) break; } + ret = 0; + // Send pause signal ASAP if (kill(pid, SIGSTOP) == -1) continue; LOGI("proc_monitor: %s(PID=%d ns=%s)\n", processName, pid, buffer); // Unmount start - xwrite(pipefd[1], &pid, sizeof(pid)); + xwrite(sv[0], &pid, sizeof(pid)); + + // Get the hide daemon return code + xxread(sv[0], &ret, sizeof(ret)); + LOGD("proc_monitor: hide daemon response code: %d\n", ret); break; } } @@ -135,10 +157,15 @@ void *proc_monitor(void *args) { vec_deep_destroy(temp); free(temp); } + if (ret) { + // Wait hide process to kill itself + waitpid(hide_pid, NULL, 0); + quit_pthread(SIGUSR1); + } } // Should never be here pclose(p); - quit_pthread(SIGUSR1); + pthread_exit(NULL); return NULL; } diff --git a/jni/main.c b/jni/main.c index f286d5907..620ddf950 100644 --- a/jni/main.c +++ b/jni/main.c @@ -1,6 +1,9 @@ /* main.c - The entry point, should be multi-call */ +#include +#include + #include "utils.h" #include "magisk.h" #include "daemon.h" @@ -10,8 +13,22 @@ char *argv0; void stub(const char *fmt, ...) {} +// Global error hander function +// Should be changed each thread/process +__thread void (*err_handler)(void); + +void exit_proc() { + exit(-1); +} + +void exit_thread() { + pthread_exit(NULL); +} + int main(int argc, char *argv[]) { argv0 = argv[0]; + // Exit the whole app if error occurs by default + err_handler = exit_proc; char * arg = strrchr(argv[0], '/'); if (arg) ++arg; if (strcmp(arg, "magisk") == 0) { @@ -27,8 +44,9 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[1], "--test") == 0) { // Temporary testing entry int fd = connect_daemon(); + write_int(fd, TEST); write_string(fd, argv[2]); - return 0; + return read_int(fd); } else { // It's calling applets --argc; diff --git a/jni/utils/log_monitor.c b/jni/utils/log_monitor.c index 8c09ddd51..c21399ac1 100644 --- a/jni/utils/log_monitor.c +++ b/jni/utils/log_monitor.c @@ -12,8 +12,8 @@ #include "utils.h" static void *logger_thread(void *args) { - rename("/cache/magisk.log", "/cache/last_magisk.log"); - FILE *logfile = xfopen("/cache/magisk.log", "w"); + // rename("/cache/magisk.log", "/cache/last_magisk.log"); + FILE *logfile = xfopen("/cache/magisk_test.log", "w"); // Disable buffering setbuf(logfile, NULL); // Start logcat diff --git a/jni/utils/utils.h b/jni/utils/utils.h index 60beadb3e..26dd54ce2 100644 --- a/jni/utils/utils.h +++ b/jni/utils/utils.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -34,6 +35,9 @@ void *xcalloc(size_t nmemb, size_t size); void *xrealloc(void *ptr, size_t size); ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags); +int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); +int xsocketpair(int domain, int type, int protocol, int sv[2]); // log_monitor.c @@ -48,4 +52,4 @@ ssize_t fdreadline(int fd, char *buf, size_t size); void ps(void (*func)(int)); void ps_filter_proc_name(const char *filter, void (*func)(int)); -#endif \ No newline at end of file +#endif diff --git a/jni/utils/xwrap.c b/jni/utils/xwrap.c index ddb9c28b0..7810e1f09 100644 --- a/jni/utils/xwrap.c +++ b/jni/utils/xwrap.c @@ -39,10 +39,11 @@ int xopen(const char *pathname, int flags) { } ssize_t xwrite(int fd, const void *buf, size_t count) { - if (count != write(fd, buf, count)) { + int ret = write(fd, buf, count); + if (count != ret) { PLOGE("write"); } - return count; + return ret; } // Read error other than EOF @@ -56,24 +57,27 @@ ssize_t xread(int fd, void *buf, size_t count) { // Read exact same size as count ssize_t xxread(int fd, void *buf, size_t count) { - if (count != read(fd, buf, count)) { + int ret = read(fd, buf, count); + if (count != ret) { PLOGE("read"); } - return count; + return ret; } int xpipe(int pipefd[2]) { - if (pipe(pipefd) == -1) { + int ret = pipe(pipefd); + if (ret == -1) { PLOGE("pipe"); } - return 0; + return ret; } int xsetns(int fd, int nstype) { - if (setns(fd, nstype) == -1) { + int ret = setns(fd, nstype); + if (ret == -1) { PLOGE("setns"); } - return 0; + return ret; } DIR *xopendir(const char *name) { @@ -102,10 +106,11 @@ pid_t xsetsid() { } int xsetcon(char *context) { - if (setcon(context) == -1) { + int ret = setcon(context); + if (ret == -1) { PLOGE("setcon: %s", context); } - return 0; + return ret; } int xsocket(int domain, int type, int protocol) { @@ -117,24 +122,27 @@ int xsocket(int domain, int type, int protocol) { } int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - if (bind(sockfd, addr, addrlen) == -1) { + int ret = bind(sockfd, addr, addrlen); + if (ret == -1) { PLOGE("bind"); } - return 0; + return ret; } int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - if (connect(sockfd, addr, addrlen) == -1) { + int ret = connect(sockfd, addr, addrlen); + if (ret == -1) { PLOGE("bind"); } - return 0; + return ret; } int xlisten(int sockfd, int backlog) { - if (listen(sockfd, backlog) == -1) { + int ret = listen(sockfd, backlog); + if (ret == -1) { PLOGE("listen"); } - return 0; + return ret; } int xaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { @@ -185,4 +193,21 @@ ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { return rec; } +int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { + errno = pthread_create(thread, attr, start_routine, arg); + if (errno) { + PLOGE("pthread_create"); + } + return errno; +} + +int xsocketpair(int domain, int type, int protocol, int sv[2]) { + int ret = socketpair(domain, type, protocol, sv); + if (ret == -1) { + PLOGE("socketpair"); + } + return ret; +} +