diff --git a/build.py b/build.py index e34ec6667..55af084e0 100755 --- a/build.py +++ b/build.py @@ -246,7 +246,7 @@ def run_ndk_build(flags): error("Build binary failed!") os.chdir("..") for arch in archs: - for tgt in support_targets + ["libinit-ld.so", "libzygisk-ld.so"]: + for tgt in support_targets + ["libinit-ld.so"]: source = op.join("native", "libs", arch, tgt) target = op.join("native", "out", arch, tgt) mv(source, target) @@ -342,9 +342,6 @@ def dump_bin_header(args): preload = op.join("native", "out", arch, "libinit-ld.so") with open(preload, "rb") as src: text = binary_dump(src, "init_ld_xz") - preload = op.join("native", "out", arch, "libzygisk-ld.so") - with open(preload, "rb") as src: - text += binary_dump(src, "zygisk_ld", compressor=lambda x: x) write_if_diff(op.join(native_gen_path, f"{arch}_binaries.h"), text) @@ -395,8 +392,9 @@ def build_binary(args): flag = "" clean = False - if "magisk" in args.target or "magiskinit" in args.target: - flag += " B_PRELOAD=1" + if "magisk" in args.target: + flag += " B_MAGISK=1" + clean = True if "magiskpolicy" in args.target: flag += " B_POLICY=1" @@ -417,14 +415,10 @@ def build_binary(args): if flag: run_ndk_build(flag) - # magiskinit and magisk embeds preload.so + # magiskinit embeds preload.so flag = "" - if "magisk" in args.target: - flag += " B_MAGISK=1" - clean = True - if "magiskinit" in args.target: flag += " B_INIT=1" diff --git a/native/src/Android.mk b/native/src/Android.mk index 1bb0c2d99..b972b372f 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -38,6 +38,7 @@ LOCAL_SRC_FILES := \ zygisk/utils.cpp \ zygisk/hook.cpp \ zygisk/memory.cpp \ + zygisk/native_bridge.cpp \ zygisk/deny/cli.cpp \ zygisk/deny/utils.cpp \ zygisk/deny/revert.cpp @@ -57,12 +58,6 @@ LOCAL_SRC_FILES := init/preload.c LOCAL_STRIP_MODE := --strip-all include $(BUILD_SHARED_LIBRARY) -include $(CLEAR_VARS) -LOCAL_MODULE := zygisk-ld -LOCAL_SRC_FILES := zygisk/loader.c -LOCAL_STRIP_MODE := --strip-all -include $(BUILD_SHARED_LIBRARY) - endif ifdef B_INIT diff --git a/native/src/core/applets.cpp b/native/src/core/applets.cpp index a81712be0..ee1578812 100644 --- a/native/src/core/applets.cpp +++ b/native/src/core/applets.cpp @@ -31,11 +31,6 @@ int main(int argc, char *argv[]) { string_view argv0 = basename(argv[0]); - // app_process is actually not an applet - if (argv0.starts_with("app_process")) { - return app_process_main(argc, argv); - } - umask(0); if (argv[0][0] == '\0') { diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index 949fb2284..88d9edff1 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -416,6 +416,8 @@ static void boot_complete() { // Ensure manager exists check_pkg_refresh(); get_manager(0, nullptr, true); + + reset_zygisk(true); } void boot_stage_handler(int client, int code) { diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index 28d6c19ff..27a4c85b9 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -144,6 +144,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { LOGI("** zygote restarted\n"); pkg_xml_ino = 0; prune_su_access(); + reset_zygisk(false); close(client); break; case MainRequest::SQLITE_CMD: @@ -158,7 +159,6 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { break; } case MainRequest::ZYGISK: - case MainRequest::ZYGISK_PASSTHROUGH: zygisk_handler(client, &cred); break; default: diff --git a/native/src/core/module.cpp b/native/src/core/module.cpp index b70044df1..13a0f3ebd 100644 --- a/native/src/core/module.cpp +++ b/native/src/core/module.cpp @@ -15,6 +15,8 @@ using namespace std; #define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from) +static string native_bridge = "0"; + static int bind_mount(const char *reason, const char *from, const char *to) { int ret = xmount(from, to, nullptr, MS_BIND | MS_REC, nullptr); if (ret == 0) @@ -22,9 +24,6 @@ static int bind_mount(const char *reason, const char *from, const char *to) { return ret; } -string node_entry::module_mnt; -string node_entry::mirror_dir; - /************************* * Node Tree Construction *************************/ @@ -211,6 +210,21 @@ public: } }; +class zygisk_node : public node_entry { +public: + explicit zygisk_node(const char *name, bool is64bit) : node_entry(name, DT_REG, this), + is64bit(is64bit) {} + + void mount() override { + const string src = get_magisk_tmp() + "/magisk"s + (is64bit ? "64" : "32"); + create_and_mount("zygisk", src); + xmount(nullptr, node_path().data(), nullptr, MS_REMOUNT | MS_BIND | MS_RDONLY, nullptr); + } + +private: + bool is64bit; +}; + static void inject_magisk_bins(root_node *system) { auto bin = system->get_child("bin"); if (!bin) { @@ -228,24 +242,28 @@ static void inject_magisk_bins(root_node *system) { delete bin->extract("supolicy"); } -vector *module_list; -int app_process_32 = -1; -int app_process_64 = -1; +static void inject_zygisk_libs(root_node *system) { + if (access("/system/bin/linker", F_OK) == 0) { + auto lib = system->get_child("lib"); + if (!lib) { + lib = new inter_node("lib"); + system->insert(lib); + } + lib->insert(new zygisk_node(native_bridge.data(), false)); + } -#define mount_zygisk(bit) \ -if (access("/system/bin/app_process" #bit, F_OK) == 0) { \ - app_process_##bit = xopen("/system/bin/app_process" #bit, O_RDONLY | O_CLOEXEC); \ - string zbin = zygisk_bin + "/app_process" #bit; \ - string mbin = get_magisk_tmp() + "/magisk"s #bit; \ - int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \ - int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \ - xsendfile(out, src, nullptr, INT_MAX); \ - close(out); \ - close(src); \ - clone_attr("/system/bin/app_process" #bit, zbin.data()); \ - bind_mount("zygisk", zbin.data(), "/system/bin/app_process" #bit); \ + if (access("/system/bin/linker64", F_OK) == 0) { + auto lib64 = system->get_child("lib64"); + if (!lib64) { + lib64 = new inter_node("lib64"); + system->insert(lib64); + } + lib64->insert(new zygisk_node(native_bridge.data(), true)); + } } +vector *module_list; + void load_modules() { node_entry::mirror_dir = get_magisk_tmp() + "/"s MIRRDIR; node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/"; @@ -289,6 +307,22 @@ void load_modules() { inject_magisk_bins(system); } + if (zygisk_enabled) { + string native_bridge_orig = get_prop(NBPROP); + if (native_bridge_orig.empty()) { + native_bridge_orig = "0"; + } + native_bridge = native_bridge_orig != "0" ? ZYGISKLDR + native_bridge_orig : ZYGISKLDR; + set_prop(NBPROP, native_bridge.data(), true); + // Weather Huawei's Maple compiler is enabled. + // If so, system server will be created by a special Zygote which ignores the native bridge + // and make system server out of our control. Avoid it by disabling. + if (get_prop("ro.maple.enable") == "1") { + set_prop("ro.maple.enable", "0", true); + } + inject_zygisk_libs(system); + } + if (!system->is_empty()) { // Handle special read-only partitions for (const char *part : { "/vendor", "/product", "/system_ext" }) { @@ -304,14 +338,6 @@ void load_modules() { root->mount(); } - // Mount on top of modules to enable zygisk - if (zygisk_enabled) { - string zygisk_bin = get_magisk_tmp() + "/"s ZYGISKBIN; - mkdir(zygisk_bin.data(), 0); - mount_zygisk(32) - mount_zygisk(64) - } - ssprintf(buf, sizeof(buf), "%s/" WORKERDIR, get_magisk_tmp()); xmount(nullptr, buf, nullptr, MS_REMOUNT | MS_RDONLY, nullptr); } @@ -491,3 +517,23 @@ void exec_module_scripts(const char *stage) { [](const module_info &info) -> string_view { return info.name; }); exec_module_scripts(stage, module_names); } + +void reset_zygisk(bool restore) { + if (!zygisk_enabled) return; + static atomic_uint zygote_start_count{1}; + if (restore) { + zygote_start_count = 1; + } else if (zygote_start_count.fetch_add(1) > 3) { + LOGW("zygote crashes too many times, rolling-back\n"); + restore = true; + } + if (restore) { + string native_bridge_orig = "0"; + if (native_bridge.length() > strlen(ZYGISKLDR)) { + native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR)); + } + set_prop(NBPROP, native_bridge_orig.data(), true); + } else { + set_prop(NBPROP, native_bridge.data(), true); + } +} diff --git a/native/src/core/node.hpp b/native/src/core/node.hpp index aba9a44c2..a75d290ed 100644 --- a/native/src/core/node.hpp +++ b/native/src/core/node.hpp @@ -49,8 +49,8 @@ public: virtual void mount() = 0; - static string module_mnt; - static string mirror_dir; + inline static string module_mnt; + inline static string mirror_dir; protected: template diff --git a/native/src/core/su/connect.cpp b/native/src/core/su/connect.cpp index 94efcc26a..2db72abb0 100644 --- a/native/src/core/su/connect.cpp +++ b/native/src/core/su/connect.cpp @@ -10,11 +10,11 @@ using namespace std; #define CALL_PROVIDER \ -exe, "/system/bin", "com.android.commands.content.Content", \ +"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \ "call", "--uri", target, "--user", user, "--method", action #define START_ACTIVITY \ -exe, "/system/bin", "com.android.commands.am.Am", \ +"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ "start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \ "-f", "0x58800020", "--es", "action", action @@ -130,21 +130,10 @@ static bool check_no_error(int fd) { static void exec_cmd(const char *action, vector &data, const shared_ptr &info, bool provider = true) { - char exe[128]; char target[128]; char user[4]; ssprintf(user, sizeof(user), "%d", to_user_id(info->eval_uid)); - if (zygisk_enabled) { -#if defined(__LP64__) - ssprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_64); -#else - ssprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_32); -#endif - } else { - strscpy(exe, "/system/bin/app_process", sizeof(exe)); - } - // First try content provider call method if (provider) { ssprintf(target, sizeof(target), "content://%s.provider", info->mgr_pkg.data()); diff --git a/native/src/exported_sym.txt b/native/src/exported_sym.txt index 80dc508b4..802ae57bc 100644 --- a/native/src/exported_sym.txt +++ b/native/src/exported_sym.txt @@ -1,4 +1,4 @@ { zygisk_inject_entry; - unload_first_stage; + NativeBridgeItf; }; diff --git a/native/src/include/daemon.hpp b/native/src/include/daemon.hpp index 9f8a4983c..e9d063db7 100644 --- a/native/src/include/daemon.hpp +++ b/native/src/include/daemon.hpp @@ -35,7 +35,6 @@ enum : int { SQLITE_CMD, REMOVE_MODULES, ZYGISK, - ZYGISK_PASSTHROUGH, _STAGE_BARRIER_, @@ -67,9 +66,8 @@ struct module_info { }; extern bool zygisk_enabled; -extern int app_process_32; -extern int app_process_64; extern std::vector *module_list; +void reset_zygisk(bool restore); extern "C" const char *get_magisk_tmp(); int connect_daemon(int req, bool create = false); diff --git a/native/src/include/magisk.hpp b/native/src/include/magisk.hpp index fd4230c2e..1142a29cc 100644 --- a/native/src/include/magisk.hpp +++ b/native/src/include/magisk.hpp @@ -3,6 +3,8 @@ #include #define JAVA_PACKAGE_NAME "com.topjohnwu.magisk" +#define ZYGISKLDR "libzygisk.so" +#define NBPROP "ro.dalvik.vm.native.bridge" #define SECURE_DIR "/data/adb" #define MODULEROOT SECURE_DIR "/modules" #define MODULEUPGRADE SECURE_DIR "/modules_update" @@ -49,5 +51,4 @@ extern int SDK_INT; int magisk_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]); -int app_process_main(int argc, char *argv[]); int zygisk_main(int argc, char *argv[]); diff --git a/native/src/zygisk/entry.cpp b/native/src/zygisk/entry.cpp index 257211673..b143c4ff3 100644 --- a/native/src/zygisk/entry.cpp +++ b/native/src/zygisk/entry.cpp @@ -6,9 +6,7 @@ #include #include -#include #include -#include #include "zygisk.hpp" #include "module.hpp" @@ -18,43 +16,11 @@ using namespace std; void *self_handle = nullptr; -// Make sure /proc/self/environ is sanitized -// Filter env and reset MM_ENV_END -static void sanitize_environ() { - char *cur = environ[0]; - - for (int i = 0; environ[i]; ++i) { - // Copy all env onto the original stack - size_t len = strlen(environ[i]); - memmove(cur, environ[i], len + 1); - environ[i] = cur; - cur += len + 1; - } - - prctl(PR_SET_MM, PR_SET_MM_ENV_END, cur, 0, 0); -} - -extern "C" void unload_first_stage() { - ZLOGD("unloading first stage\n"); - unmap_all(HIJACK_BIN); - xumount2(HIJACK_BIN, MNT_DETACH); -} - -extern "C" void zygisk_inject_entry(void *handle) { - zygisk_logging(); - ZLOGD("load success\n"); - - char *ld = getenv("LD_PRELOAD"); - if (char *c = strrchr(ld, ':')) { - *c = '\0'; - setenv("LD_PRELOAD", ld, 1); // Restore original LD_PRELOAD - } else { - unsetenv("LD_PRELOAD"); - } - +extern "C" [[maybe_unused]] void zygisk_inject_entry(void *handle) { self_handle = handle; - sanitize_environ(); + zygisk_logging(); hook_functions(); + ZLOGD("load success\n"); } // The following code runs in zygote/app process @@ -146,88 +112,6 @@ static void connect_companion(int client, bool is_64_bit) { send_fd(zygiskd_socket, client); } -static timespec last_zygote_start; -static int zygote_start_counts[] = { 0, 0 }; -#define zygote_start_count zygote_start_counts[is_64_bit] -#define zygote_started (zygote_start_counts[0] + zygote_start_counts[1]) -#define zygote_start_reset(val) { zygote_start_counts[0] = val; zygote_start_counts[1] = val; } - -static void setup_files(int client, const sock_cred *cred) { - LOGD("zygisk: setup files for pid=[%d]\n", cred->pid); - - char buf[4096]; - if (!get_exe(cred->pid, buf, sizeof(buf))) { - write_int(client, 1); - return; - } - - // Hijack some binary in /system/bin to host loader - const char *hbin; - string mbin; - int app_fd; - bool is_64_bit = str_ends(buf, "64"); - if (is_64_bit) { - hbin = HIJACK_BIN64; - mbin = get_magisk_tmp() + "/"s ZYGISKBIN "/loader64.so"; - app_fd = app_process_64; - } else { - hbin = HIJACK_BIN32; - mbin = get_magisk_tmp() + "/"s ZYGISKBIN "/loader32.so"; - app_fd = app_process_32; - } - - if (!zygote_started) { - // First zygote launch, record time - clock_gettime(CLOCK_MONOTONIC, &last_zygote_start); - } - - if (zygote_start_count) { - // This zygote ABI had started before, kill existing zygiskd - close(zygiskd_sockets[0]); - close(zygiskd_sockets[1]); - zygiskd_sockets[0] = -1; - zygiskd_sockets[1] = -1; - xumount2(hbin, MNT_DETACH); - } - ++zygote_start_count; - - if (zygote_start_count >= 5) { - // Bootloop prevention - timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - if (ts.tv_sec - last_zygote_start.tv_sec > 60) { - // This is very likely manual soft reboot - memcpy(&last_zygote_start, &ts, sizeof(ts)); - zygote_start_reset(1); - } else { - // If any zygote relaunched more than 5 times within a minute, - // don't do any setups further to prevent bootloop. - zygote_start_reset(999); - write_int(client, 1); - return; - } - } - - // Ack - write_int(client, 0); - - // Receive and bind mount loader - int ld_fd = xopen(mbin.data(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0755); - string ld_data = read_string(client); - xwrite(ld_fd, ld_data.data(), ld_data.size()); - close(ld_fd); - setfilecon(mbin.data(), MAGISK_FILE_CON); - xmount(mbin.data(), hbin, nullptr, MS_BIND, nullptr); - - send_fd(client, app_fd); -} - -static void magiskd_passthrough(int client) { - bool is_64_bit = read_int(client); - write_int(client, 0); - send_fd(client, is_64_bit ? app_process_64 : app_process_32); -} - extern bool uid_granted_root(int uid); static void get_process_info(int client, const sock_cred *cred) { int uid = read_int(client); @@ -301,12 +185,6 @@ void zygisk_handler(int client, const sock_cred *cred) { int code = read_int(client); char buf[256]; switch (code) { - case ZygiskRequest::SETUP: - setup_files(client, cred); - break; - case ZygiskRequest::PASSTHROUGH: - magiskd_passthrough(client); - break; case ZygiskRequest::GET_INFO: get_process_info(client, cred); break; diff --git a/native/src/zygisk/hook.cpp b/native/src/zygisk/hook.cpp index 4568888a9..52ade36c1 100644 --- a/native/src/zygisk/hook.cpp +++ b/native/src/zygisk/hook.cpp @@ -2,14 +2,14 @@ #include #include #include +#include #include #include #include #include -#include -#include +#include #include "zygisk.hpp" #include "memory.hpp" @@ -29,6 +29,7 @@ static void hook_unloader(); static void unhook_functions(); static void hook_jni_env(); static void restore_jni_env(JNIEnv *env); +static void ReloadNativeBridge(const string &nb); namespace { @@ -157,9 +158,6 @@ DCL_HOOK_FUNC(int, unshare, int flags) { (g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) { if (g_ctx->flags[DO_REVERT_UNMOUNT]) { revert_unmount(); - } else { - umount2("/system/bin/app_process64", MNT_DETACH); - umount2("/system/bin/app_process32", MNT_DETACH); } // Restore errno back to 0 errno = 0; @@ -209,6 +207,21 @@ DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) { return res; } +// it should be safe to assume all dlclose's in libnativebridge are for zygisk_loader +DCL_HOOK_FUNC(int, dlclose, void *handle) { + static bool kDone = false; + if (!kDone) { + ZLOGV("dlclose zygisk_loader\n"); + kDone = true; + string nb = get_prop(NBPROP); + if (nb != ZYGISKLDR) { + auto orig_nb_path = nb.substr(sizeof(ZYGISKLDR) - 1); + ReloadNativeBridge(orig_nb_path); + } + } + [[clang::musttail]] return old_dlclose(handle); +} + #undef DCL_HOOK_FUNC // ----------------------------------------------------------------- @@ -714,6 +727,37 @@ void HookContext::nativeForkAndSpecialize_post() { // ----------------------------------------------------------------- +inline void *unwind_get_region_start(_Unwind_Context *ctx) { + auto fp = _Unwind_GetRegionStart(ctx); +#if defined(__arm__) + auto pc = _Unwind_GetGR(ctx, 15); // r15 is pc + if (pc & 1) { + // Thumb mode + fp |= 1; + } +#endif + return reinterpret_cast(fp); +} + +static void ReloadNativeBridge(const string &nb) { + // Use unwind to find the address of LoadNativeBridge + // and call it to get NativeBridgeRuntimeCallbacks + void *load_native_bridge = nullptr; + _Unwind_Backtrace(+[](struct _Unwind_Context *ctx, void *arg) -> _Unwind_Reason_Code { + void *fp = unwind_get_region_start(ctx); + Dl_info info{}; + dladdr(fp, &info); + ZLOGV("backtrace: %p %s\n", fp, info.dli_fname ? info.dli_fname : "???"); + if (info.dli_fname && std::string_view(info.dli_fname).ends_with("/libart.so")) { + ZLOGV("LoadNativeBridge: %p\n", fp); + *reinterpret_cast(arg) = fp; + return _URC_END_OF_STACK; + } + return _URC_NO_REASON; + }, &load_native_bridge); + reinterpret_cast(load_native_bridge)(nb); +} + static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func) { if (!lsplt::RegisterHook(dev, inode, symbol, new_func, old_func)) { ZLOGE("Failed to register plt_hook \"%s\"\n", symbol); @@ -741,15 +785,20 @@ void hook_functions() { ino_t android_runtime_inode = 0; dev_t android_runtime_dev = 0; + ino_t native_bridge_inode = 0; + dev_t native_bridge_dev = 0; for (auto &map : lsplt::MapInfo::Scan()) { - if (map.path.ends_with("libandroid_runtime.so")) { + if (map.path.ends_with("/libandroid_runtime.so")) { android_runtime_inode = map.inode; android_runtime_dev = map.dev; - break; + } else if (map.path.ends_with("/libnativebridge.so")) { + native_bridge_inode = map.inode; + native_bridge_dev = map.dev; } } + PLT_HOOK_REGISTER(native_bridge_dev, native_bridge_inode, dlclose); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc); diff --git a/native/src/zygisk/loader.c b/native/src/zygisk/loader.c deleted file mode 100644 index 363d4c70d..000000000 --- a/native/src/zygisk/loader.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -#if defined(__LP64__) -// Use symlink to workaround linker bug on old broken Android -// https://issuetracker.google.com/issues/36914295 -#define SECOND_STAGE_PATH "/system/bin/app_process" -#else -#define SECOND_STAGE_PATH "/system/bin/app_process32" -#endif - -__attribute__((constructor)) -static void zygisk_loader(void) { - android_dlextinfo info = { - .flags = ANDROID_DLEXT_FORCE_LOAD - }; - void *handle = android_dlopen_ext(SECOND_STAGE_PATH, RTLD_LAZY, &info); - if (handle) { - void(*entry)(void*) = dlsym(handle, "zygisk_inject_entry"); - if (entry) { - entry(handle); - } - void (*unload)(void) = dlsym(handle, "unload_first_stage"); - if (unload) { - __attribute__((musttail)) return unload(); - } - } -} diff --git a/native/src/zygisk/main.cpp b/native/src/zygisk/main.cpp index 36e608497..66ded0858 100644 --- a/native/src/zygisk/main.cpp +++ b/native/src/zygisk/main.cpp @@ -7,98 +7,11 @@ #include #include #include -#include #include "zygisk.hpp" using namespace std; -// Entrypoint for app_process overlay -int app_process_main(int argc, char *argv[]) { - android_logging(); - char buf[PATH_MAX]; - - bool zygote = false; - if (auto fp = open_file("/proc/self/attr/current", "r")) { - fscanf(fp.get(), "%s", buf); - zygote = (buf == "u:r:zygote:s0"sv); - } - - if (!zygote) { - // For the non zygote case, we need to get real app_process via passthrough - // We have to connect magiskd via exec-ing magisk due to SELinux restrictions - - // This is actually only relevant for calling app_process via ADB shell - // because zygisk shall already have the app_process overlays unmounted - // during app process specialization within its private mount namespace. - - int fds[2]; - socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); - if (fork_dont_care() == 0) { - // This fd has to survive exec - fcntl(fds[1], F_SETFD, 0); - ssprintf(buf, sizeof(buf), "%d", fds[1]); -#if defined(__LP64__) - execlp("magisk", "", "zygisk", "passthrough", buf, "1", (char *) nullptr); -#else - execlp("magisk", "", "zygisk", "passthrough", buf, "0", (char *) nullptr); -#endif - exit(-1); - } - - close(fds[1]); - if (read_int(fds[0]) != 0) { - fprintf(stderr, "Failed to connect magiskd, try umount %s or reboot.\n", argv[0]); - return 1; - } - int app_proc_fd = recv_fd(fds[0]); - if (app_proc_fd < 0) - return 1; - close(fds[0]); - - fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC); - fexecve(app_proc_fd, argv, environ); - return 1; - } - - if (int socket = zygisk_request(ZygiskRequest::SETUP); socket >= 0) { - do { - if (read_int(socket) != 0) - break; - - // Send over zygisk loader - write_int(socket, sizeof(zygisk_ld)); - xwrite(socket, zygisk_ld, sizeof(zygisk_ld)); - - int app_proc_fd = recv_fd(socket); - if (app_proc_fd < 0) - break; - - if (char *ld = getenv("LD_PRELOAD")) { - string env = ld; - env += ':'; - env += HIJACK_BIN; - setenv("LD_PRELOAD", env.data(), 1); - } else { - setenv("LD_PRELOAD", HIJACK_BIN, 1); - } - - close(socket); - - fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC); - fexecve(app_proc_fd, argv, environ); - } while (false); - - close(socket); - } - - // If encountering any errors, unmount and execute the original app_process - xreadlink("/proc/self/exe", buf, sizeof(buf)); - xumount2("/proc/self/exe", MNT_DETACH); - execve(buf, argv, environ); - return 1; -} - static void zygiskd(int socket) { if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) exit(-1); @@ -177,30 +90,8 @@ static void zygiskd(int socket) { // This should only ever be called internally int zygisk_main(int argc, char *argv[]) { android_logging(); - if (argc == 3 && argv[1] == "companion"sv) { zygiskd(parse_int(argv[2])); - } else if (argc == 4 && argv[1] == "passthrough"sv) { - int client = parse_int(argv[2]); - int is_64_bit = parse_int(argv[3]); - if (fcntl(client, F_GETFD) < 0) - return 1; - if (int magiskd = connect_daemon(MainRequest::ZYGISK_PASSTHROUGH); magiskd >= 0) { - write_int(magiskd, ZygiskRequest::PASSTHROUGH); - write_int(magiskd, is_64_bit); - - if (read_int(magiskd) != 0) { - write_int(client, 1); - return 0; - } - - write_int(client, 0); - int real_app_fd = recv_fd(magiskd); - send_fd(client, real_app_fd); - } else { - write_int(client, 1); - return 0; - } } return 0; } diff --git a/native/src/zygisk/native_bridge.cpp b/native/src/zygisk/native_bridge.cpp new file mode 100644 index 000000000..2ab5989d7 --- /dev/null +++ b/native/src/zygisk/native_bridge.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include +#include "../core/core.hpp" + +// The reference layout of this struct +// https://cs.android.com/android/platform/superproject/main/+/main:art/libnativebridge/include/nativebridge/native_bridge.h +struct NativeBridgeCallbacks { + uint32_t version; + void *padding[5]; + bool (*isCompatibleWith)(uint32_t); +}; + +static bool is_compatible_with(uint32_t) { + auto name = get_prop(NBPROP); + android_dlextinfo info = { + .flags = ANDROID_DLEXT_FORCE_LOAD + }; + void *handle = android_dlopen_ext(name.data(), RTLD_LAZY, &info); + if (handle) { + auto entry = reinterpret_cast(dlsym(handle, "zygisk_inject_entry")); + if (entry) { + entry(handle); + } + } + return false; +} + +extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf{ + .version = 2, + .padding = {}, + .isCompatibleWith = &is_compatible_with, +}; diff --git a/native/src/zygisk/zygisk.hpp b/native/src/zygisk/zygisk.hpp index dbc65f892..d32a8e99b 100644 --- a/native/src/zygisk/zygisk.hpp +++ b/native/src/zygisk/zygisk.hpp @@ -6,18 +6,11 @@ #include #include -#define MAGISKTMP_ENV "MAGISKTMP" - -#define HIJACK_BIN64 "/system/bin/appwidget" -#define HIJACK_BIN32 "/system/bin/bu" - namespace ZygiskRequest { enum : int { - SETUP, GET_INFO, CONNECT_COMPANION, GET_MODDIR, - PASSTHROUGH, END }; } @@ -27,13 +20,11 @@ enum : int { #define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__) #define ZLOGI(...) LOGI("zygisk64: " __VA_ARGS__) #define ZLOGW(...) LOGW("zygisk64: " __VA_ARGS__) -#define HIJACK_BIN HIJACK_BIN64 #else #define ZLOGD(...) LOGD("zygisk32: " __VA_ARGS__) #define ZLOGE(...) LOGE("zygisk32: " __VA_ARGS__) #define ZLOGI(...) LOGI("zygisk32: " __VA_ARGS__) #define ZLOGW(...) LOGW("zygisk32: " __VA_ARGS__) -#define HIJACK_BIN HIJACK_BIN32 #endif // Unmap all pages matching the name