diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index c6610a8e2..549d3ab3d 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -68,6 +68,9 @@ static void handle_request_async(int client, int code, ucred cred) { close(client); reboot(); break; + case ZYGISK_REQUEST: + zygisk_handler(client, &cred); + break; default: close(client); break; @@ -116,6 +119,7 @@ static void handle_request(int client) { case BOOT_COMPLETE: case SQLITE_CMD: case GET_PATH: + case MAGISKHIDE: if (!is_root) { write_int(client, ROOT_REQUIRED); goto done; @@ -127,9 +131,9 @@ static void handle_request(int client) { goto done; } break; - case MAGISKHIDE: // accept hide request from zygote - if (!is_root && !is_zygote) { - write_int(client, ROOT_REQUIRED); + case ZYGISK_REQUEST: + if (!is_zygote) { + write_int(client, DAEMON_ERROR); goto done; } break; @@ -260,7 +264,7 @@ int connect_daemon(bool create) { if (connect(fd, (sockaddr*) &sun, len)) { if (!create || getuid() != UID_ROOT) { LOGE("No daemon is currently running!\n"); - exit(1); + return 1; } if (fork_dont_care() == 0) { diff --git a/native/jni/core/logging.cpp b/native/jni/core/logging.cpp index 6ec5a8fba..436840a4e 100644 --- a/native/jni/core/logging.cpp +++ b/native/jni/core/logging.cpp @@ -158,6 +158,16 @@ void magisk_logging() { log_cb.ex = nop_ex; } +#define alog(prio) [](auto fmt, auto ap){ \ +return __android_log_vprint(ANDROID_LOG_##prio, "Magisk", fmt, ap); } +void android_logging() { + log_cb.d = alog(DEBUG); + log_cb.i = alog(INFO); + log_cb.w = alog(WARN); + log_cb.e = alog(ERROR); + log_cb.ex = nop_ex; +} + void start_log_daemon() { int fds[2]; if (pipe2(fds, O_CLOEXEC) == 0) { diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index 635d3a846..50b4b9446 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -25,6 +25,7 @@ enum : int { MAGISKHIDE, SQLITE_CMD, REMOVE_MODULES, + ZYGISK_REQUEST, DAEMON_CODE_END, }; @@ -47,12 +48,15 @@ enum : int { int connect_daemon(bool create = false); +void android_logging(); + // Daemon handlers void post_fs_data(int client); void late_start(int client); void boot_complete(int client); void magiskhide_handler(int client, ucred *cred); void su_daemon_handler(int client, ucred *credential); +void zygisk_handler(int client, ucred *cred); // MagiskHide void check_enable_hide(); diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index acea4b51d..db72f8a59 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -3,9 +3,10 @@ #include #include #include -#include #include +#include +#include #include "inject.hpp" @@ -14,16 +15,6 @@ using namespace std; static void *self_handle = nullptr; static atomic active_threads = -1; -#define alog(prio) [](auto fmt, auto ap){ \ -return __android_log_vprint(ANDROID_LOG_##prio, "Magisk", fmt, ap); } -static void inject_logging() { - log_cb.d = alog(DEBUG); - log_cb.i = alog(INFO); - log_cb.w = alog(WARN); - log_cb.e = alog(ERROR); - log_cb.ex = nop_ex; -} - void self_unload() { LOGD("hook: Request to self unload\n"); // If unhook failed, do not unload or else it will cause SIGSEGV @@ -33,7 +24,7 @@ void self_unload() { active_threads--; } -static void *unload_first_stage(void *) { +static void *unload_first_stage(void *v) { // Setup 1ms timespec ts = { .tv_sec = 0, .tv_nsec = 1000000L }; @@ -43,7 +34,8 @@ static void *unload_first_stage(void *) { // Wait another 1ms to make sure all threads left our code nanosleep(&ts, nullptr); - unmap_all(INJECT_LIB_1); + char *path = static_cast(v); + unmap_all(path); active_threads--; return nullptr; } @@ -87,66 +79,108 @@ static void inject_cleanup_wait() { __attribute__((constructor)) static void inject_init() { - if (getenv(INJECT_ENV_2)) { - inject_logging(); + if (char *env = getenv(INJECT_ENV_2)) { + android_logging(); LOGD("zygote: inject 2nd stage\n"); active_threads = 1; unsetenv(INJECT_ENV_2); // Get our own handle - self_handle = dlopen(INJECT_LIB_2, RTLD_LAZY); + self_handle = dlopen(env, RTLD_LAZY); dlclose(self_handle); - unlink(INJECT_LIB_2); hook_functions(); + // Update path to 1st stage lib + *(strrchr(env, '.') - 1) = '1'; + // Some cleanup sanitize_environ(); active_threads++; - new_daemon_thread(&unload_first_stage); - } else if (char *env = getenv(INJECT_ENV_1)) { - inject_logging(); + new_daemon_thread(&unload_first_stage, env); + } else if (getenv(INJECT_ENV_1)) { + android_logging(); LOGD("zygote: inject 1st stage\n"); - if (env[0] == '1') + char *ld = getenv("LD_PRELOAD"); + char *path; + if (char *c = strrchr(ld, ':')) { + *c = '\0'; + setenv("LD_PRELOAD", ld, 1); // Restore original LD_PRELOAD + path = c + 1; + } else { unsetenv("LD_PRELOAD"); - else - setenv("LD_PRELOAD", env, 1); // Restore original LD_PRELOAD + path = ld; + } + + // Update path to 2nd stage lib + *(strrchr(path, '.') - 1) = '2'; // Setup second stage - setenv(INJECT_ENV_2, "1", 1); - cp_afc(INJECT_LIB_1, INJECT_LIB_2); - dlopen(INJECT_LIB_2, RTLD_LAZY); + setenv(INJECT_ENV_2, path, 1); + dlopen(path, RTLD_LAZY); - unlink(INJECT_LIB_1); unsetenv(INJECT_ENV_1); } } int app_process_main(int argc, char *argv[]) { - inject_logging(); - char buf[4096]; - if (realpath("/proc/self/exe", buf) == nullptr) - return 1; + android_logging(); - int in = xopen(buf, O_RDONLY); - int out = xopen(INJECT_LIB_1, O_WRONLY | O_CREAT, 0644); - sendfile(out, in, nullptr, INT_MAX); - close(in); - close(out); + if (int fd = connect_daemon(); fd >= 0) { + write_int(fd, ZYGISK_REQUEST); + write_int(fd, ZYGISK_SETUP); - if (char *ld = getenv("LD_PRELOAD")) { - char env[128]; - sprintf(env, "%s:" INJECT_LIB_1, ld); - setenv("LD_PRELOAD", env, 1); - setenv(INJECT_ENV_1, ld, 1); // Backup original LD_PRELOAD - } else { - setenv("LD_PRELOAD", INJECT_LIB_1, 1); - setenv(INJECT_ENV_1, "1", 1); + if (read_int(fd) == 0) { + string path = read_string(fd); + string lib = path + ".1.so"; + if (char *ld = getenv("LD_PRELOAD")) { + char env[256]; + sprintf(env, "%s:%s", ld, lib.data()); + setenv("LD_PRELOAD", env, 1); + } else { + setenv("LD_PRELOAD", lib.data(), 1); + } + setenv(INJECT_ENV_1, "1", 1); + } + close(fd); } // Execute real app_process - xumount2(buf, MNT_DETACH); + char buf[256]; + xreadlink("/proc/self/exe", buf, sizeof(buf)); + xumount2("/proc/self/exe", MNT_DETACH); execve(buf, argv, environ); return 1; } + +// The following code runs in magiskd + +static void setup_files(int client, ucred *cred) { + LOGD("zygisk: setup files\n"); + + char buf[PATH_MAX]; + sprintf(buf, "/proc/%d/exe", cred->pid); + if (realpath(buf, buf) == nullptr) { + write_int(client, 1); + return; + } + + write_int(client, 0); + + string path = MAGISKTMP + "/zygisk." + basename(buf); + cp_afc(buf, (path + ".1.so").data()); + cp_afc(buf, (path + ".2.so").data()); + + write_string(client, path); +} + +void zygisk_handler(int client, ucred *cred) { + int code = read_int(client); + switch (code) { + case ZYGISK_SETUP: + setup_files(client, cred); + break; + } + close(client); +} diff --git a/native/jni/zygisk/inject.hpp b/native/jni/zygisk/inject.hpp index 01663e648..82b923fc3 100644 --- a/native/jni/zygisk/inject.hpp +++ b/native/jni/zygisk/inject.hpp @@ -3,11 +3,13 @@ #include #include -#define INJECT_LIB_1 "/dev/tmp/magisk.1.so" -#define INJECT_LIB_2 "/dev/tmp/magisk.2.so" #define INJECT_ENV_1 "MAGISK_INJ_1" #define INJECT_ENV_2 "MAGISK_INJ_2" +enum : int { + ZYGISK_SETUP, +}; + // Unmap all pages matching the name void unmap_all(const char *name); diff --git a/scripts/emulator.sh b/scripts/emulator.sh index dabb97090..d88b75871 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -23,7 +23,7 @@ abort() { mount_sbin() { mount -t tmpfs -o 'mode=0755' tmpfs /sbin - $SELINUX && chcon u:object_r:rootfs:s0 /sbin + chcon u:object_r:rootfs:s0 /sbin } if [ ! -f /system/build.prop ]; then @@ -56,14 +56,14 @@ fi pgrep magiskd >/dev/null && pkill -9 magiskd [ -f /sbin/magisk ] && umount -l /sbin [ -f /system/bin/magisk ] && umount -l /system/bin +if [ -d /dev/magisk ]; then + umount -l /dev/magisk 2>/dev/null + rm -rf /dev/magisk +fi # SELinux stuffs -SELINUX=false -[ -e /sys/fs/selinux ] && SELINUX=true -if $SELINUX; then - ln -sf ./magiskinit magiskpolicy - ./magiskpolicy --live --magisk -fi +ln -sf ./magiskinit magiskpolicy +./magiskpolicy --live --magisk BINDIR=/sbin @@ -99,12 +99,14 @@ elif [ -e /sbin ]; then done else # Android Q+ without sbin, use overlayfs - BINDIR=/system/bin - rm -rf /dev/magisk - mkdir -p /dev/magisk/upper + BINDIR=/dev/magisk/upper + mkdir /dev/magisk + mount -t tmpfs -o 'mode=0755' tmpfs /dev/magisk + chcon u:object_r:system_file:s0 /dev/magisk + mkdir /dev/magisk/upper mkdir /dev/magisk/work ./magisk --clone-attr /system/bin /dev/magisk/upper - mount -t overlay overlay -olowerdir=/system/bin,upperdir=/dev/magisk/upper,workdir=/dev/magisk/work /system/bin + mount -t overlay overlay -o lowerdir=/system/bin,upperdir=/dev/magisk/upper,workdir=/dev/magisk/work /system/bin fi # Magisk stuffs