From 515f346dcc21209de414032d8390b189fd893a40 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 22 Apr 2019 16:36:23 -0400 Subject: [PATCH 1/3] Monitor app_process Some stupid Samsung ROMs will spawn multiple zygote daemons. Since we switched to ptrace based process monitoring, we have to know all zygote processes to trace. This is an attempt to fix this issue. Close #1272 --- native/jni/magiskhide/hide_utils.cpp | 2 +- native/jni/magiskhide/magiskhide.h | 5 +- native/jni/magiskhide/proc_monitor.cpp | 75 ++++++++++++++++---------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index 4166847c8..b95c40953 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -71,7 +71,7 @@ void crawl_procfs(DIR *dir, const function &fn) { } } -bool proc_name_match(int pid, const char *name) { +static bool proc_name_match(int pid, const char *name) { char buf[4019]; FILE *f; #if 0 diff --git a/native/jni/magiskhide/magiskhide.h b/native/jni/magiskhide/magiskhide.h index 7e0a81e0e..e60ba8a51 100644 --- a/native/jni/magiskhide/magiskhide.h +++ b/native/jni/magiskhide/magiskhide.h @@ -19,8 +19,6 @@ #define SAFETYNET_PKG "com.google.android.gms" #define MICROG_SAFETYNET "org.microg.gms.droidguard" -#define WEVENT(s) (((s) & 0xffff0000) >> 16) - // CLI entries void launch_magiskhide(int client); int stop_magiskhide(); @@ -29,15 +27,14 @@ int rm_list(int client); void ls_list(int client); // Process monitoring -void *update_uid_map(void * p = nullptr); void proc_monitor(); +void update_uid_map(); // Utility functions void manage_selinux(); void clean_magisk_props(); void crawl_procfs(const std::function &fn); void crawl_procfs(DIR *dir, const std::function &fn); -bool proc_name_match(int pid, const char *name); extern bool hide_enabled; extern pthread_mutex_t monitor_lock; diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp index be6b47a63..0881112c8 100644 --- a/native/jni/magiskhide/proc_monitor.cpp +++ b/native/jni/magiskhide/proc_monitor.cpp @@ -134,18 +134,21 @@ static bool parse_packages_xml(string_view s) { return true; } +void update_uid_map() { + MutexGuard lock(monitor_lock); + uid_proc_map.clear(); + file_readline("/data/system/packages.xml", parse_packages_xml, true); +} + static void check_zygote() { int min_zyg = 1; if (access("/system/bin/app_process64", R_OK) == 0) min_zyg = 2; - for (bool first = true; zygote_map.size() < min_zyg; first = false) { - if (!first) - usleep(10000); + for (;;) { crawl_procfs([](int pid) -> bool { char buf[512]; snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); - FILE *f = fopen(buf, "re"); - if (f) { + if (FILE *f = fopen(buf, "re"); f) { fgets(buf, sizeof(buf), f); if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1) new_zygote(pid); @@ -153,14 +156,41 @@ static void check_zygote() { } return true; }); + if (zygote_map.size() >= min_zyg) + break; + usleep(10000); } } -void *update_uid_map(void*) { - MutexGuard lock(monitor_lock); - uid_proc_map.clear(); - file_readline("/data/system/packages.xml", parse_packages_xml, true); - return nullptr; +#define APP_PROC "/system/bin/app_process" + +static void setup_inotify() { + inotify_fd = xinotify_init1(IN_CLOEXEC); + if (inotify_fd < 0) + term_thread(); + + // 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); + } + + // First find existing zygotes + check_zygote(); } /************************* @@ -233,7 +263,10 @@ static void inotify_event(int) { read(inotify_fd, buf, sizeof(buf)); if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) { LOGD("proc_monitor: /data/system/packages.xml updated\n"); - new_daemon_thread(update_uid_map); + uid_proc_map.clear(); + file_readline("/data/system/packages.xml", parse_packages_xml, true); + } else if (event->mask & IN_ACCESS) { + check_zygote(); } } @@ -362,12 +395,10 @@ static void new_zygote(int pid) { xptrace(PTRACE_CONT, pid); } +#define WEVENT(s) (((s) & 0xffff0000) >> 16) #define DETACH_AND_CONT { detach = true; continue; } -void proc_monitor() { - inotify_fd = xinotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - term_thread(); +void proc_monitor() { // Unblock some signals sigset_t block_set; sigemptyset(&block_set); @@ -381,19 +412,7 @@ void proc_monitor() { act.sa_handler = inotify_event; sigaction(SIGIO, &act, nullptr); - // 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); - - // Start monitoring packages.xml - inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE); - - // First find existing zygotes - check_zygote(); + setup_inotify(); int status; From 003e44fb84dd1142bf987b0fa8c0b1e5ddd5b4a0 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 24 Apr 2019 00:13:48 -0400 Subject: [PATCH 2/3] Remove requirement to use early-init daemon We used to construct /sbin tmpfs overlay in early-init stage after SELinux is properly initialized. However the way it is implemented (forking daemon from magiskinit with complicated file waiting triggers) is extremely complicated and error prone. This commit moves the construction of the sbin overlay to pre-init stage. The catch is that since SELinux is not present at that point, proper selabel has to be reconstructed afterwards. Some additional SEPolicy rules are added to make sure init can access magisk binaries, and the secontext relabeling task is assigned to the main Magisk daemon. --- native/jni/core/daemon.cpp | 2 + native/jni/core/init.cpp | 182 ++++++++++++----------------- native/jni/core/magisk.cpp | 1 + native/jni/core/magiskrc.h | 8 +- native/jni/include/magisk.h | 2 - native/jni/magiskpolicy/rules.cpp | 2 + native/jni/utils/include/selinux.h | 1 + native/jni/utils/selinux.cpp | 29 ++++- 8 files changed, 110 insertions(+), 117 deletions(-) diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index f284ccb56..719bec4a9 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -101,6 +101,8 @@ static void main_daemon() { android_logging(); setsid(); setcon("u:r:" SEPOL_PROC_DOMAIN ":s0"); + restore_rootcon(); + int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC); xdup2(fd, STDOUT_FILENO); xdup2(fd, STDERR_FILENO); diff --git a/native/jni/core/init.cpp b/native/jni/core/init.cpp index 43da07002..7814ec1d3 100644 --- a/native/jni/core/init.cpp +++ b/native/jni/core/init.cpp @@ -46,7 +46,8 @@ static int vprintk(const char *fmt, va_list ap) { static void setup_klog() { mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); - kmsg = xfopen("/kmsg", "ae"); + int fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); + kmsg = fdopen(fd, "w"); setbuf(kmsg, nullptr); unlink("/kmsg"); log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; @@ -156,11 +157,10 @@ private: bool read_dt_fstab(const char *name, char *partname, char *partfs); bool patch_sepolicy(); void cleanup(); + void re_exec_init(); public: explicit MagiskInit(char *argv[]) : argv(argv) {} - void setup_overlay(); - void re_exec_init(); void start(); void test(); }; @@ -460,6 +460,27 @@ void MagiskInit::early_mount() { mount_root(odm); } +static void patch_socket_name(const char *path) { + uint8_t *buf; + char name[sizeof(MAIN_SOCKET)]; + size_t size; + mmap_rw(path, buf, size); + for (int i = 0; i < size; ++i) { + if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) { + gen_rand_str(name, sizeof(name)); + memcpy(buf + i, name, sizeof(name)); + i += sizeof(name); + } + } + munmap(buf, size); +} + +constexpr const char wrapper[] = +"#!/system/bin/sh\n" +"export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:/apex/com.android.runtime/" LIBNAME "\"\n" +"exec /sbin/magisk.bin \"$0\" \"$@\"\n" +; + void MagiskInit::setup_rootfs() { bool patch_init = patch_sepolicy(); @@ -531,6 +552,7 @@ void MagiskInit::setup_rootfs() { gen_rand_str(pfd_svc + 1, sizeof(pfd_svc) - 1); gen_rand_str(ls_svc + 1, sizeof(ls_svc) - 1); gen_rand_str(bc_svc + 1, sizeof(bc_svc) - 1); + LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc); fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc, bc_svc, bc_svc); fclose(rc); clone_attr("/init.rc", "/init.p.rc"); @@ -545,6 +567,56 @@ void MagiskInit::setup_rootfs() { int rootdir = xopen("/root", O_RDONLY | O_CLOEXEC); int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); link_dir(sbin, rootdir); + close(sbin); + + LOGD("Mount /sbin tmpfs overlay\n"); + xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755"); + sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); + + char path[64]; + + // Create symlinks pointing back to /root + DIR *dir = xfdopendir(rootdir); + struct dirent *entry; + while((entry = xreaddir(dir))) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + sprintf(path, "/root/%s", entry->d_name); + xsymlinkat(path, sbin, entry->d_name); + } + + // Dump binaries + mkdir(MAGISKTMP, 0755); + fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000); + write(fd, config.buf, config.sz); + close(fd); + fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755); + write(fd, self.buf, self.sz); + close(fd); + if (access("/system/apex", F_OK) == 0) { + LOGD("APEX detected, use wrapper\n"); + dump_magisk("/sbin/magisk.bin", 0755); + patch_socket_name("/sbin/magisk.bin"); + fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755); + write(fd, wrapper, sizeof(wrapper) - 1); + close(fd); + } else { + dump_magisk("/sbin/magisk", 0755); + patch_socket_name("/sbin/magisk"); + } + + // Create applet symlinks + for (int i = 0; applet_names[i]; ++i) { + sprintf(path, "/sbin/%s", applet_names[i]); + xsymlink("/sbin/magisk", path); + } + for (int i = 0; init_applet[i]; ++i) { + sprintf(path, "/sbin/%s", init_applet[i]); + xsymlink("/sbin/magiskinit", path); + } + + close(rootdir); + close(sbin); } bool MagiskInit::patch_sepolicy() { @@ -603,99 +675,8 @@ void MagiskInit::cleanup() { umount_root(odm); } -static inline void patch_socket_name(const char *path) { - uint8_t *buf; - char name[sizeof(MAIN_SOCKET)]; - size_t size; - mmap_rw(path, buf, size); - for (int i = 0; i < size; ++i) { - if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) { - gen_rand_str(name, sizeof(name)); - memcpy(buf + i, name, sizeof(name)); - i += sizeof(name); - } - } - munmap(buf, size); -} - -static const char wrapper[] = -"#!/system/bin/sh\n" -"export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:/apex/com.android.runtime/" LIBNAME "\"\n" -"exec /sbin/magisk.bin \"$0\" \"$@\"\n"; - -void MagiskInit::setup_overlay() { - char path[128]; - int fd; - - // Wait for early-init start - while (access(EARLYINIT, F_OK) != 0) - usleep(10); - setcon("u:r:" SEPOL_PROC_DOMAIN ":s0"); - unlink(EARLYINIT); - -#ifdef MAGISK_DEBUG - kmsg = xfopen("/dev/kmsg", "ae"); - setbuf(kmsg, nullptr); -#endif - - LOGD("Setting up overlay\n"); - - // Mount the /sbin tmpfs overlay - xmount("tmpfs", "/sbin", "tmpfs", 0, nullptr); - chmod("/sbin", 0755); - setfilecon("/sbin", "u:object_r:rootfs:s0"); - - // Dump binaries - mkdir(MAGISKTMP, 0755); - fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000); - write(fd, config.buf, config.sz); - close(fd); - fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755); - write(fd, self.buf, self.sz); - close(fd); - if (access("/system/apex", F_OK) == 0) { - LOGD("APEX detected, use wrapper\n"); - dump_magisk("/sbin/magisk.bin", 0755); - patch_socket_name("/sbin/magisk.bin"); - setfilecon("/sbin/magisk.bin", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); - fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755); - write(fd, wrapper, sizeof(wrapper) - 1); - close(fd); - } else { - dump_magisk("/sbin/magisk", 0755); - patch_socket_name("/sbin/magisk"); - } - setfilecon("/sbin/magisk", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); - setfilecon("/sbin/magiskinit", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); - - // Create applet symlinks - for (int i = 0; applet_names[i]; ++i) { - sprintf(path, "/sbin/%s", applet_names[i]); - xsymlink("/sbin/magisk", path); - } - for (int i = 0; init_applet[i]; ++i) { - sprintf(path, "/sbin/%s", init_applet[i]); - xsymlink("/sbin/magiskinit", path); - } - - // Create symlinks pointing back to /root - DIR *dir = xopendir("/root"); - struct dirent *entry; - fd = xopen("/sbin", O_RDONLY); - while((entry = xreaddir(dir))) { - if (entry->d_name == "."sv || entry->d_name == ".."sv) - continue; - sprintf(path, "/root/%s", entry->d_name); - xsymlinkat(path, fd, entry->d_name); - } - closedir(dir); - close(fd); - - close(xopen(EARLYINITDONE, O_RDONLY | O_CREAT, 0)); - exit(0); -} - void MagiskInit::re_exec_init() { + LOGD("Re-exec /init\n"); cleanup(); execv("/init", argv); exit(1); @@ -722,6 +703,7 @@ void MagiskInit::start() { preset(); early_mount(); setup_rootfs(); + re_exec_init(); } void MagiskInit::test() { @@ -767,14 +749,4 @@ int main(int argc, char *argv[]) { // Run the main routine init.start(); - - // Close all file descriptors - for (int i = 0; i < 30; ++i) - close(i); - - // Launch daemon to setup overlay - if (fork_dont_care() == 0) - init.setup_overlay(); - - init.re_exec_init(); } diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index b2543f6f3..3468728cd 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -71,6 +71,7 @@ int magisk_main(int argc, char *argv[]) { unlock_blocks(); return 0; } else if (strcmp(argv[1], "--restorecon") == 0) { + restore_rootcon(); restorecon(); return 0; } else if (strcmp(argv[1], "--clone-attr") == 0) { diff --git a/native/jni/core/magiskrc.h b/native/jni/core/magiskrc.h index 40c0028c2..f0d7a7554 100644 --- a/native/jni/core/magiskrc.h +++ b/native/jni/core/magiskrc.h @@ -1,15 +1,9 @@ #include #include -static const char magiskrc[] = +constexpr const char magiskrc[] = "\n\n" -"on early-init\n" -" write " EARLYINIT " 1\n" -" wait " EARLYINITDONE "\n" -" rm " EARLYINITDONE "\n" -"\n" - "on post-fs-data\n" " start logd\n" " load_persist_props\n" diff --git a/native/jni/include/magisk.h b/native/jni/include/magisk.h index fc9bca31b..4ea5557ce 100644 --- a/native/jni/include/magisk.h +++ b/native/jni/include/magisk.h @@ -6,8 +6,6 @@ #define JAVA_PACKAGE_NAME "com.topjohnwu.magisk" #define LOGFILE "/cache/magisk.log" #define UNBLOCKFILE "/dev/.magisk_unblock" -#define EARLYINIT "/dev/.magisk_early_init" -#define EARLYINITDONE "/dev/.magisk_early_init_done" #define DISABLEFILE "/cache/.disable_magisk" #define MAGISKTMP "/sbin/.magisk" #define MIRRDIR MAGISKTMP "/mirror" diff --git a/native/jni/magiskpolicy/rules.cpp b/native/jni/magiskpolicy/rules.cpp index e9907a104..ee9d96a46 100644 --- a/native/jni/magiskpolicy/rules.cpp +++ b/native/jni/magiskpolicy/rules.cpp @@ -54,6 +54,8 @@ void sepol_magisk_rules() { // Let init run stuffs sepol_allow("kernel", SEPOL_PROC_DOMAIN, "fd", "use"); sepol_allow("init", SEPOL_PROC_DOMAIN, "process", ALL); + sepol_allow("init", "tmpfs", "file", "getattr"); + sepol_allow("init", "tmpfs", "file", "execute"); // Shell, properties, logs if (sepol_exists("default_prop")) diff --git a/native/jni/utils/include/selinux.h b/native/jni/utils/include/selinux.h index 11801d813..21b0a4096 100644 --- a/native/jni/utils/include/selinux.h +++ b/native/jni/utils/include/selinux.h @@ -36,6 +36,7 @@ void setfilecon_at(int dirfd, const char *name, const char *con); void selinux_builtin_impl(); void dload_selinux(); void restorecon(); +void restore_rootcon(); #ifdef __cplusplus } diff --git a/native/jni/utils/selinux.cpp b/native/jni/utils/selinux.cpp index 3ac5484cc..f19600e62 100644 --- a/native/jni/utils/selinux.cpp +++ b/native/jni/utils/selinux.cpp @@ -1,17 +1,21 @@ +#include #include #include #include #include #include -#include +#include #include #include #include +using namespace std::literals; + #define UNLABEL_CON "u:object_r:unlabeled:s0" #define SYSTEM_CON "u:object_r:system_file:s0" #define ADB_CON "u:object_r:adb_data_file:s0" +#define ROOT_CON "u:object_r:rootfs:s0" #define MAGISK_CON "u:object_r:" SEPOL_FILE_DOMAIN ":s0" // Stub implementation @@ -164,7 +168,7 @@ static void restore_syscon(int dirfd) { dir = xfdopendir(dirfd); while ((entry = xreaddir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); if (entry->d_type == DT_DIR) { @@ -193,7 +197,7 @@ static void restore_magiskcon(int dirfd) { dir = xfdopendir(dirfd); while ((entry = xreaddir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); if (entry->d_type == DT_DIR) { @@ -220,3 +224,22 @@ void restorecon() { restore_magiskcon(fd); close(fd); } + +void restore_rootcon() { + setfilecon("/sbin", ROOT_CON); + struct dirent *entry; + DIR *dir = xopendir("/sbin"); + int dfd = dirfd(dir); + + while ((entry = xreaddir(dir))) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + setfilecon_at(dfd, entry->d_name, ROOT_CON); + } + + setfilecon("/sbin/magisk.bin", MAGISK_CON); + setfilecon("/sbin/magisk", MAGISK_CON); + setfilecon("/sbin/magiskinit", MAGISK_CON); + + closedir(dir); +} From 54d1207f92f5a33f47c2ddc0fbd7d4a05e7ebba3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 24 Apr 2019 01:59:47 -0400 Subject: [PATCH 3/3] Auto remove post_ota.sh --- app/src/main/res/raw/utils.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/raw/utils.sh b/app/src/main/res/raw/utils.sh index 5dda5e38b..66d2cafe1 100644 --- a/app/src/main/res/raw/utils.sh +++ b/app/src/main/res/raw/utils.sh @@ -77,6 +77,7 @@ post_ota() { cat << EOF > post-fs-data.d/post_ota.sh ${1}/bootctl mark-boot-successful rm -f ${1}/bootctl +rm -f ${1}/post-fs-data.d/post_ota.sh EOF chmod 755 post-fs-data.d/post_ota.sh cd /