diff --git a/build.py b/build.py index 019e52529fc5c..ef691a24504ae 100755 --- a/build.py +++ b/build.py @@ -62,6 +62,7 @@ def vprint(str): cpu_count = multiprocessing.cpu_count() archs = ['armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'] +archs32map = {'x86_64': 'x86', 'arm64-v8a': 'armeabi-v7a'} triples = ['armv7a-linux-androideabi', 'i686-linux-android', 'aarch64-linux-android', 'x86_64-linux-android'] default_targets = ['magisk', 'magiskinit', 'magiskboot', 'magiskpolicy', 'busybox'] support_targets = default_targets + ['resetprop'] @@ -314,9 +315,17 @@ 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) + if archs32map.get(arch) is not None: + preload = op.join('native', 'out', arch, 'libzygisk-ld.so') + with open(preload, 'rb') as src: + text += binary_dump(src, 'zygisk64_ld', compressor=lambda x: x) + preload = op.join('native', 'out', archs32map[arch], 'libzygisk-ld.so') + with open(preload, 'rb') as src: + text += binary_dump(src, 'zygisk32_ld', compressor=lambda x: x) + else: + preload = op.join('native', 'out', arch, 'libzygisk-ld.so') + with open(preload, 'rb') as src: + text += binary_dump(src, 'zygisk32_ld', compressor=lambda x: x) write_if_diff(op.join(native_gen_path, f'{arch}_binaries.h'), text) diff --git a/native/src/Android.mk b/native/src/Android.mk index c68e1c0d9c6ee..35d89fdf76b7a 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -39,7 +39,6 @@ LOCAL_SRC_FILES := \ su/su_daemon.cpp \ zygisk/entry.cpp \ zygisk/main.cpp \ - zygisk/utils.cpp \ zygisk/hook.cpp \ zygisk/memory.cpp \ zygisk/deny/cli.cpp \ diff --git a/native/src/core/applets.cpp b/native/src/core/applets.cpp index 9ce77c35eb2ab..9375cb9df4af6 100644 --- a/native/src/core/applets.cpp +++ b/native/src/core/applets.cpp @@ -32,11 +32,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 cf30419746024..8f4e703578f85 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -29,6 +29,7 @@ enum : int { static int boot_state = FLAG_NONE; bool zygisk_enabled = false; +std::string native_bridge = ""; /********* * Setup * diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index e159933162cc4..5344909017b17 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -143,10 +143,11 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { su_daemon_handler(client, &cred); break; case MainRequest::ZYGOTE_RESTART: - close(client); LOGI("** zygote restarted\n"); pkg_xml_ino = 0; prune_su_access(); + remount_zygisk(); + close(client); break; case MainRequest::SQLITE_CMD: exec_sql(client); @@ -160,7 +161,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 5cdd4efd76a9e..0b15bddb0f6c4 100644 --- a/native/src/core/module.cpp +++ b/native/src/core/module.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -10,18 +11,40 @@ #include "core.hpp" #include "node.hpp" +#include "embed.hpp" using namespace std; #define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from) -static int bind_mount(const char *reason, const char *from, const char *to) { +namespace { +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) VLOGD(reason, from, to); return ret; } +std::atomic_uint zygote_start_count{1}; + +void remount_zygisk(bool is64bit, bool rollback) { + auto lib = "/system/lib"s + (is64bit ? "64" : "32") + native_bridge; + if (struct statfs st{}; statfs(lib.data(), &st) == 0) { + if (!rollback && st.f_type != TMPFS_MAGIC) { + // Our native bridge hasn't been mounted yet or has been unmounted before + xmount((MAGISKTMP + "/" ZYGISKBIN "/libzygisk_loader" + (is64bit ? "64" : "32") + + ".so").data(), lib.data(), nullptr, + MS_BIND, nullptr); + } else if (rollback && st.f_type == TMPFS_MAGIC && native_bridge != ZYGISKLDR) { + // We have mounted our native bridge which overrode the original one, + // and now we have been asked to revert our changes, unmount it + xumount2(lib.data(), MNT_DETACH); + } + } +} + +} + string node_entry::module_mnt; string node_entry::mirror_dir; @@ -179,30 +202,75 @@ void tmpfs_node::mount() { * Magisk Stuffs ****************/ -class magisk_node : public node_entry { -public: - explicit magisk_node(const char *name) : node_entry(name, DT_REG, this) {} +void magisk_node::mount() { + const string src = MAGISKTMP + "/" + name(); + if (access(src.data(), F_OK)) + return; - void mount() override { - const string src = MAGISKTMP + "/" + name(); - if (access(src.data(), F_OK)) - return; + const string &dir_name = parent()->node_path(); + if (name() == "magisk") { + for (int i = 0; applet_names[i]; ++i) { + string dest = dir_name + "/" + applet_names[i]; + VLOGD("create", "./magisk", dest.data()); + xsymlink("./magisk", dest.data()); + } + } else if (name() == "magiskpolicy") { + string dest = dir_name + "/supolicy"; + VLOGD("create", "./magiskpolicy", dest.data()); + xsymlink("./magiskpolicy", dest.data()); + } + create_and_mount("magisk", src); +} - const string &dir_name = parent()->node_path(); - if (name() == "magisk") { - for (int i = 0; applet_names[i]; ++i) { - string dest = dir_name + "/" + applet_names[i]; - VLOGD("create", "./magisk", dest.data()); - xsymlink("./magisk", dest.data()); - } - } else { - string dest = dir_name + "/supolicy"; - VLOGD("create", "./magiskpolicy", dest.data()); - xsymlink("./magiskpolicy", dest.data()); +void zygisk_node::mount() { + // prepare zygisk loader + auto zygisk_bin = MAGISKTMP + "/" ZYGISKBIN; + xmkdir(zygisk_bin.data(), 0); + string src = zygisk_bin + "/libzygisk_loader" + (is64bit ? "64" : "32") + ".so"; + int out = xopen(src.data(), O_WRONLY | O_CREAT | O_CLOEXEC, 0); + if (is64bit) { +#ifdef __LP64__ + xwrite(out, zygisk64_ld, sizeof(zygisk64_ld)); +#endif + } else { + xwrite(out, zygisk32_ld, sizeof(zygisk32_ld)); + } + close(out); + + + const string &dest = node_path(); + string mirror = mirror_path(); + file_attr attr{}; + if (getattr(mirror.data(), &attr) == 0) { + // file exists, mount upon it + if (attr.st.st_mode & S_IFLNK) { + // if it's a symlink, we need to resolve it + char buf[PATH_MAX] = {}; + xrealpath(mirror.data(), buf, sizeof(buf)); + mirror = mirror_dir + (buf[0] == '/' ? "" : "/") + buf; + // and get the real file attributes + getattr(mirror.data(), &attr); + } else if (!isa(parent())) { + // if not symlink and parent is not a tmpfs, we can just bind mount + bind_mount("zygisk", src.data(), dest.data()); + return; } - create_and_mount("magisk", src); + VLOGD("cp", mirror.data(), dest.data()); + // now, we need to create a copy of the original file + int s = xopen(mirror.data(), O_RDONLY | O_CLOEXEC, 0); + int d = xopen(dest.data(), O_WRONLY | O_CREAT | O_CLOEXEC, 0); + xsendfile(d, s, nullptr, attr.st.st_size); + close(s); + close(d); + // and set the attributes + setattr(dest.data(), &attr); + // finally, we can bind mount + bind_mount("zygisk", src.data(), dest.data()); + } else { + // file does not exist, create a new one and mount upon it + create_and_mount("zygisk", src); } -}; +} static void inject_magisk_bins(root_node *system) { auto bin = system->get_child("bin"); @@ -213,6 +281,10 @@ static void inject_magisk_bins(root_node *system) { // Insert binaries bin->insert(new magisk_node("magisk")); + if (access("/system/bin/app_process32", F_OK) == 0) + bin->insert(new magisk_node("magisk32")); + if (access("/system/bin/app_process64", F_OK) == 0) + bin->insert(new magisk_node("magisk64")); bin->insert(new magisk_node("magiskpolicy")); // Also delete all applets to make sure no modules can override it @@ -222,22 +294,6 @@ static void inject_magisk_bins(root_node *system) { } vector *module_list; -int app_process_32 = -1; -int app_process_64 = -1; - -#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 = MAGISKTMP + "/magisk" #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); \ -} void load_modules() { node_entry::mirror_dir = MAGISKTMP + "/" MIRRDIR; @@ -276,9 +332,42 @@ void load_modules() { system->collect_module_files(module, fd); close(fd); } - if (MAGISKTMP != "/sbin" || !str_contains(getenv("PATH") ?: "", "/sbin")) { - // Need to inject our binaries into /system/bin - inject_magisk_bins(system); + + inject_magisk_bins(system); + + if (zygisk_enabled) { + // If the system has built-in native bridge, we mount on top of it to avoid unnecessary property changes + // If not, we change the property to install our fake native bridge + + native_bridge = getprop(NBPROP); + if (native_bridge.empty() || native_bridge == "0") { + native_bridge = ZYGISKLDR; + setprop(NBPROP, ZYGISKLDR, false); + } + if (access("/system/lib", F_OK) == 0) { + auto lib = system->get_child("lib"); + if (!lib) { + lib = new inter_node("lib"); + system->insert(lib); + } + lib->insert(new zygisk_node(false, native_bridge.data())); + } + + if (access("/system/lib64", F_OK) == 0) { + auto lib64 = system->get_child("lib64"); + if (!lib64) { + lib64 = new inter_node("lib64"); + system->insert(lib64); + } + lib64->insert(new zygisk_node(true, native_bridge.data())); + } + + // If Huawei's Maple compiler is enabled, the system server will be created from + // a special Zygote which ignores native bridge and is out of our control + // To avoid it, we simply disable the "Maple compiler" + if (getprop("ro.maple.enable") == "1") { + setprop("ro.maple.enable", "0", false); + } } if (!system->is_empty()) { @@ -297,14 +386,6 @@ void load_modules() { root->prepare(); root->mount(); } - - // Mount on top of modules to enable zygisk - if (zygisk_enabled) { - string zygisk_bin = MAGISKTMP + "/" ZYGISKBIN; - mkdir(zygisk_bin.data(), 0); - mount_zygisk(32) - mount_zygisk(64) - } } /************************ @@ -482,3 +563,17 @@ void exec_module_scripts(const char *stage) { [](const module_info &info) -> string_view { return info.name; }); exec_module_scripts(stage, module_names); } + +// Used to remount zygisk after soft reboot +void remount_zygisk() { + bool rollback = zygote_start_count.fetch_add(1) >= 5; + if (rollback) { + LOGW("zygote crashes too many times, rolling-back\n"); + } + if (native_bridge == ZYGISKLDR) { + setprop(NBPROP, "0"); + } else { + remount_zygisk(false, rollback); + remount_zygisk(true, rollback); + } +} diff --git a/native/src/core/node.hpp b/native/src/core/node.hpp index aba9a44c2712a..2cd22909df939 100644 --- a/native/src/core/node.hpp +++ b/native/src/core/node.hpp @@ -300,6 +300,20 @@ class tmpfs_node : public dir_node { void mount() override; }; +class magisk_node : public node_entry { +public: + explicit magisk_node(const char *name) : node_entry(name, DT_REG, this) {} + void mount() override; +}; + +class zygisk_node : public magisk_node { +public: + explicit zygisk_node(bool is64bit, const char *name) : magisk_node(name), is64bit(is64bit) {} + void mount() override; +private: + bool is64bit; +}; + template static bool isa(node_entry *node) { return node && (node->_node_type & type_id()); diff --git a/native/src/core/restorecon.cpp b/native/src/core/restorecon.cpp index b18dbd91ee7b9..28d5ae146a1b5 100644 --- a/native/src/core/restorecon.cpp +++ b/native/src/core/restorecon.cpp @@ -95,7 +95,7 @@ void restore_tmpcon() { setfilecon_at(dfd, entry->d_name, SYSTEM_CON); if (SDK_INT >= 26) { - string magisk = MAGISKTMP + "/magisk"; - setfilecon(magisk.data(), EXEC_CON); + setfilecon((MAGISKTMP + "/magisk32").data(), EXEC_CON); + setfilecon((MAGISKTMP + "/magisk64").data(), EXEC_CON); } } diff --git a/native/src/include/daemon.hpp b/native/src/include/daemon.hpp index b2d812714204d..b5fd221f8f0dc 100644 --- a/native/src/include/daemon.hpp +++ b/native/src/include/daemon.hpp @@ -36,7 +36,6 @@ enum : int { SQLITE_CMD, REMOVE_MODULES, ZYGISK, - ZYGISK_PASSTHROUGH, _STAGE_BARRIER_, @@ -68,8 +67,7 @@ struct module_info { }; extern bool zygisk_enabled; -extern int app_process_32; -extern int app_process_64; +extern std::string native_bridge; extern std::vector *module_list; int connect_daemon(int req, bool create = false); @@ -102,7 +100,8 @@ std::vector get_app_no_list(); int get_manager(int user_id = 0, std::string *pkg = nullptr, bool install = false); void prune_su_access(); -// Denylist +// Zygisk +void remount_zygisk(); extern std::atomic_flag skip_pkg_rescan; void initialize_denylist(); int denylist_cli(int argc, char **argv); diff --git a/native/src/include/magisk.hpp b/native/src/include/magisk.hpp index 8de990fa98840..7103d12c13ea6 100644 --- a/native/src/include/magisk.hpp +++ b/native/src/include/magisk.hpp @@ -8,6 +8,8 @@ #define JAVA_PACKAGE_NAME "com.topjohnwu.magisk" #define LOGFILE "/cache/magisk.log" #define UNBLOCKFILE "/dev/.magisk_unblock" +#define ZYGISKLDR "libzygisk_loader.so" +#define NBPROP "ro.dalvik.vm.native.bridge" #define SECURE_DIR "/data/adb" #define MODULEROOT SECURE_DIR "/modules" #define MODULEUPGRADE SECURE_DIR "/modules_update" @@ -42,5 +44,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/init/rootdir.cpp b/native/src/init/rootdir.cpp index 4673b673811b1..c9e68839224b1 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -49,10 +49,14 @@ static bool hijack_initrc(const char *dir, const char *tmp_dir) { ssprintf(src, sizeof(src), "%sinit.rc", dir); ssprintf(dest, sizeof(dest), "%s/init.rc", INITRCMOCK); LOGD("Hijacking [%s]\n", src); + // Mock init.rc with our FIFO so we can change its content mkfifo(dest, 0644); auto src_file = xopen_file(src, "r"); xmount(dest, src, nullptr, MS_BIND, nullptr); + + // Create a new process waiting for init operations if (xfork()) { + // In parent process (init), return and continue boot process return true; } diff --git a/native/src/sepolicy/rules.cpp b/native/src/sepolicy/rules.cpp index 21974b027cbcb..6375c0c77718d 100644 --- a/native/src/sepolicy/rules.cpp +++ b/native/src/sepolicy/rules.cpp @@ -137,8 +137,6 @@ void sepolicy::magisk_rules() { // Let init run stuffs allow("kernel", SEPOL_PROC_DOMAIN, "fd", "use"); allow("init", SEPOL_PROC_DOMAIN, "process", ALL); - allow("init", "tmpfs", "file", "getattr"); - allow("init", "tmpfs", "file", "execute"); allow("init", "tmpfs", "fifo_file", "open"); allow("init", "tmpfs", "fifo_file", "read"); allow("init", "tmpfs", "fifo_file", "getattr"); @@ -192,6 +190,11 @@ void sepolicy::magisk_rules() { allow("zygote", "zygote", "capability", "sys_resource"); // prctl PR_SET_MM allow("zygote", "zygote", "process", "execmem"); allow("zygote", "fs_type", "filesystem", "unmount"); + allow("zygote", SEPOL_EXEC_TYPE, "file", "read"); + allow("zygote", SEPOL_EXEC_TYPE, "file", "open"); + allow("zygote", SEPOL_EXEC_TYPE, "file", "getattr"); + allow("zygote", SEPOL_EXEC_TYPE, "file", "map"); + allow("zygote", SEPOL_EXEC_TYPE, "file", "execute"); allow("system_server", "system_server", "process", "execmem"); // Shut llkd up diff --git a/native/src/su/connect.cpp b/native/src/su/connect.cpp index 37d358e6ef017..765a5967c743b 100644 --- a/native/src/su/connect.cpp +++ b/native/src/su/connect.cpp @@ -116,15 +116,7 @@ static void exec_cmd(const char *action, vector &data, 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)); - } + strscpy(exe, "/system/bin/app_process", sizeof(exe)); // First try content provider call method if (provider) { diff --git a/native/src/zygisk/entry.cpp b/native/src/zygisk/entry.cpp index 28b92cd752138..ce62ad29ff515 100644 --- a/native/src/zygisk/entry.cpp +++ b/native/src/zygisk/entry.cpp @@ -13,27 +13,12 @@ #include "zygisk.hpp" #include "module.hpp" #include "deny/deny.hpp" +#include "resetprop.hpp" 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); -} - [[gnu::destructor]] [[maybe_unused]] static void zygisk_cleanup_wait() { if (self_handle) { @@ -43,34 +28,25 @@ static void zygisk_cleanup_wait() { } } -static void *unload_first_stage(void *) { - // Wait 10us to make sure 1st stage is done - timespec ts = { .tv_sec = 0, .tv_nsec = 10000L }; - nanosleep(&ts, nullptr); - unmap_all(HIJACK_BIN); - xumount2(HIJACK_BIN, MNT_DETACH); - return nullptr; -} - extern "C" void zygisk_inject_entry(void *handle) { - zygisk_logging(); - ZLOGD("load success\n"); + self_handle = handle; + // we need to check if we are running in zygote process + // if not, we still need to load the original native bridge if exists + bool zygote = false; + if (auto fp = open_file("/proc/self/attr/current", "r")) { + char buf[16]; + fscanf(fp.get(), "%15s", buf); + zygote = (buf == "u:r:zygote:s0"sv && getppid() == 1 && getuid() == 0); + } - char *ld = getenv("LD_PRELOAD"); - if (char *c = strrchr(ld, ':')) { - *c = '\0'; - setenv("LD_PRELOAD", ld, 1); // Restore original LD_PRELOAD + if (zygote) { + zygisk_logging(); } else { - unsetenv("LD_PRELOAD"); + android_logging(); } + ZLOGD("load success\n"); - MAGISKTMP = getenv(MAGISKTMP_ENV); - self_handle = handle; - - unsetenv(MAGISKTMP_ENV); - sanitize_environ(); - hook_functions(); - new_daemon_thread(&unload_first_stage, nullptr); + hook_functions(zygote); } // The following code runs in zygote/app process @@ -128,10 +104,11 @@ static inline bool should_load_modules(uint32_t flags) { (flags & PROCESS_IS_MAGISK_APP) != PROCESS_IS_MAGISK_APP; } -int remote_get_info(int uid, const char *process, uint32_t *flags, vector &fds) { +int remote_get_info(int uid, const char *process, const char *app_data_dir, uint32_t *flags, vector &fds) { if (int fd = zygisk_request(ZygiskRequest::GET_INFO); fd >= 0) { write_int(fd, uid); write_string(fd, process); + write_string(fd, app_data_dir); xxread(fd, flags, sizeof(*flags)); if (should_load_modules(*flags)) { fds = recv_fds(fd); @@ -189,7 +166,7 @@ static void connect_companion(int client, bool is_64_bit) { socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); zygiskd_socket = fds[0]; if (fork_dont_care() == 0) { - string exe = MAGISKTMP + "/magisk" + (is_64_bit ? "64" : "32"); + string exe = "/system/bin/magisk"s + (is_64_bit ? "64" : "32"); // This fd has to survive exec fcntl(fds[1], F_SETFD, 0); char buf[16]; @@ -209,93 +186,11 @@ 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 = MAGISKTMP + "/" ZYGISKBIN "/loader64.so"; - app_fd = app_process_64; - } else { - hbin = HIJACK_BIN32; - mbin = MAGISKTMP + "/" 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(), "u:object_r:" SEPOL_FILE_TYPE ":s0"); - xmount(mbin.data(), hbin, nullptr, MS_BIND, nullptr); - - send_fd(client, app_fd); - write_string(client, MAGISKTMP); -} - -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); string process = read_string(client); + string app_data_dir = read_string(client); uint32_t flags = 0; @@ -306,8 +201,6 @@ static void get_process_info(int client, const sock_cred *cred) { int manager_app_id = get_manager(); if (to_app_id(uid) == manager_app_id) { flags |= PROCESS_IS_MAGISK_APP; - } else if (to_app_id(uid) == sys_ui_app_id) { - flags |= PROCESS_IS_SYS_UI; } if (denylist_enforced) { flags |= DENYLIST_ENFORCING; @@ -315,7 +208,11 @@ static void get_process_info(int client, const sock_cred *cred) { if (uid_granted_root(uid)) { flags |= PROCESS_GRANTED_ROOT; } - + // We assume all zygotes have launched when SystemUI is launching + // So it's safe to reset native bridge at this time + if (getprop(NBPROP) == ZYGISKLDR && app_data_dir.ends_with("/com.android.systemui")) { + setprop(NBPROP, "0"); + } xwrite(client, &flags, sizeof(flags)); if (should_load_modules(flags)) { @@ -377,12 +274,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 7c7560a69ca80..ae2bac45b3e1d 100644 --- a/native/src/zygisk/hook.cpp +++ b/native/src/zygisk/hook.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -11,10 +12,12 @@ #include #include +#include "magisk.hpp" #include "zygisk.hpp" #include "memory.hpp" #include "module.hpp" #include "deny/deny.hpp" +#include "resetprop.hpp" using namespace std; using jni_hook::hash_map; @@ -115,7 +118,7 @@ hash_map>> *jni_method_map; HookContext *g_ctx; const JNINativeInterface *old_functions = nullptr; JNINativeInterface *new_functions = nullptr; - +bool is_zygote = false; } // namespace #define HOOK_JNI(method) \ @@ -216,16 +219,9 @@ DCL_HOOK_FUNC(int, fork) { // Unmount stuffs in the process's private mount namespace DCL_HOOK_FUNC(int, unshare, int flags) { int res = old_unshare(flags); - if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 && - // For some unknown reason, unmounting app_process in SysUI can break. - // This is reproducible on the official AVD running API 26 and 27. - // Simply avoid doing any unmounts for SysUI to avoid potential issues. - (g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) { + if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 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; @@ -259,6 +255,52 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext, return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname); } +void ReloadNativeBridge(const std::string &nb) { +#if defined(__LP64__) + xumount2(("/system/lib64/" + nb).data(), MNT_DETACH); +#else + xumount2(("/system/lib/" + nb).data(), MNT_DETACH); +#endif + + // Use unwind to find the address of LoadNativeBridge + // and call it to load the real native bridge + void *load_native_bridge = nullptr; + _Unwind_Backtrace(+[](struct _Unwind_Context *ctx, void *arg) -> _Unwind_Reason_Code { + void *fp = reinterpret_cast(_Unwind_GetRegionStart(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); + // TODO: the return value is different on Android 5 + reinterpret_cast(load_native_bridge)(nb); +} + +// 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 = getprop(NBPROP); + if (nb != ZYGISKLDR) { + ReloadNativeBridge(nb); + } + } + if (!is_zygote) { + ZLOGI("Not in zygote, exiting\n"); + unhook_functions(); + close(logd_fd.exchange(-1)); + new_daemon_thread(reinterpret_cast(&dlclose), self_handle); + } + return old_dlclose(handle); +} + #undef DCL_HOOK_FUNC // ----------------------------------------------------------------- @@ -603,13 +645,20 @@ void HookContext::app_specialize_pre() { flags[APP_SPECIALIZE] = true; vector module_fds; - int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds); + const char *app_data_dir = ""; + if (args.app->app_data_dir) { + app_data_dir = env->GetStringUTFChars(args.app->app_data_dir, nullptr); + } + int fd = remote_get_info(args.app->uid, process, app_data_dir, &info_flags, module_fds); if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) { ZLOGI("[%s] is on the denylist\n", process); flags[DO_REVERT_UNMOUNT] = true; } else if (fd >= 0) { run_modules_pre(module_fds); } + if (args.app->app_data_dir) { + env->ReleaseStringUTFChars(args.app->app_data_dir, app_data_dir); + } close(fd); } @@ -678,7 +727,7 @@ void HookContext::nativeForkSystemServer_pre() { return; vector module_fds; - int fd = remote_get_info(1000, "system_server", &info_flags, module_fds); + int fd = remote_get_info(1000, "system_server", "", &info_flags, module_fds); if (fd >= 0) { if (module_fds.empty()) { write_int(fd, 0); @@ -721,7 +770,6 @@ void HookContext::nativeForkAndSpecialize_pre() { } else if (logd_fd >= 0) { exempted_fds.push_back(logd_fd); } - fork_pre(); if (pid == 0) { app_specialize_pre(); @@ -762,26 +810,35 @@ static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_ #define PLT_HOOK_REGISTER(DEV, INODE, NAME) \ PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME) -void hook_functions() { +void hook_functions(bool zygote) { + is_zygote = zygote; default_new(plt_hook_list); default_new(jni_hook_list); default_new(jni_method_map); 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")) { 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(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, selinux_android_setcontext); - PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc); - PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close); + PLT_HOOK_REGISTER(native_bridge_dev, native_bridge_inode, dlclose); + if (is_zygote) { + 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, selinux_android_setcontext); + PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc); + PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", + android_log_close); + } hook_commit(); // Remove unhooked methods plt_hook_list->erase( @@ -794,11 +851,12 @@ static bool unhook_functions() { bool success = true; // Restore JNIEnv - if (g_ctx->env->functions == new_functions) { + if (g_ctx && g_ctx->env->functions == new_functions) { g_ctx->env->functions = old_functions; - delete new_functions; } + delete new_functions; + // Unhook JNI methods for (const auto &[clz, methods] : *jni_hook_list) { if (!methods.empty() && g_ctx->env->RegisterNatives( diff --git a/native/src/zygisk/loader.c b/native/src/zygisk/loader.c index fc06d68509ab5..2a79771a5ac18 100644 --- a/native/src/zygisk/loader.c +++ b/native/src/zygisk/loader.c @@ -2,11 +2,9 @@ #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" +#define SECOND_STAGE_PATH "/system/bin/magisk64" #else -#define SECOND_STAGE_PATH "/system/bin/app_process32" +#define SECOND_STAGE_PATH "/system/bin/magisk32" #endif __attribute__((constructor)) diff --git a/native/src/zygisk/main.cpp b/native/src/zygisk/main.cpp index 58200df3f7400..9c1e794daaa07 100644 --- a/native/src/zygisk/main.cpp +++ b/native/src/zygisk/main.cpp @@ -13,101 +13,6 @@ 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 (!selinux_enabled()) { - for (int i = 0; i < argc; ++i) { - if (argv[i] == "--zygote"sv) { - zygote = true; - break; - } - } - } else 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; - - string tmp = read_string(socket); - 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); - } - setenv(MAGISKTMP_ENV, tmp.data(), 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); @@ -189,27 +94,6 @@ int zygisk_main(int argc, char *argv[]) { 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/module.hpp b/native/src/zygisk/module.hpp index fef9fc141d70a..a55b8f36cc53f 100644 --- a/native/src/zygisk/module.hpp +++ b/native/src/zygisk/module.hpp @@ -110,12 +110,11 @@ enum : uint32_t { PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT, PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST, - PROCESS_IS_SYS_UI = (1u << 29), DENYLIST_ENFORCING = (1u << 30), PROCESS_IS_MAGISK_APP = (1u << 31), UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING), - PRIVATE_MASK = (PROCESS_IS_SYS_UI | DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP) + PRIVATE_MASK = (DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP) }; struct api_abi_base { diff --git a/native/src/zygisk/ptrace.cpp b/native/src/zygisk/ptrace.cpp deleted file mode 100644 index ae5bb00edfe1f..0000000000000 --- a/native/src/zygisk/ptrace.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Original code: https://github.com/Chainfire/injectvm-binderjack/blob/master/app/src/main/jni/libinject/inject.cpp - * The code is heavily modified and sublicensed to GPLv3 for incorporating into Magisk. - * - * Copyright (c) 2015, Simone 'evilsocket' Margaritelli - * Copyright (c) 2015-2019, Jorrit 'Chainfire' Jongma - * Copyright (c) 2021, John 'topjohnwu' Wu - * - * See original LICENSE file from the original project for additional details: - * https://github.com/Chainfire/injectvm-binderjack/blob/master/LICENSE - */ - -/* - * NOTE: - * The code in this file was originally planned to be used for some features, - * but it turned out to be unsuitable for the task. However, this shall remain - * in our arsenal in case it may be used in the future. - */ - -#include -#include -#include -#include - -#include - -#include "zygisk.hpp" -#include "ptrace.hpp" - -using namespace std; - -#if defined(__arm__) -#define CPSR_T_MASK (1u << 5) -#define PARAMS_IN_REGS 4 -#elif defined(__aarch64__) -#define CPSR_T_MASK (1u << 5) -#define PARAMS_IN_REGS 8 -#define pt_regs user_pt_regs -#define uregs regs -#define ARM_pc pc -#define ARM_sp sp -#define ARM_cpsr pstate -#define ARM_lr regs[30] -#define ARM_r0 regs[0] -#endif - -bool _remote_read(int pid, uintptr_t addr, void *buf, size_t len) { - for (size_t i = 0; i < len; i += sizeof(long)) { - long data = xptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast(addr + i)); - if (data < 0) - return false; - memcpy(static_cast(buf) + i, &data, std::min(len - i, sizeof(data))); - } - return true; -} - -bool _remote_write(int pid, uintptr_t addr, const void *buf, size_t len) { - for (size_t i = 0; i < len; i += sizeof(long)) { - long data = 0; - memcpy(&data, static_cast(buf) + i, std::min(len - i, sizeof(data))); - if (xptrace(PTRACE_POKETEXT, pid, reinterpret_cast(addr + i), data) < 0) - return false; - } - return true; -} - -// Get remote registers -#define remote_getregs(regs) _remote_getregs(pid, regs) -static void _remote_getregs(int pid, pt_regs *regs) { -#if defined(__LP64__) - uintptr_t regset = NT_PRSTATUS; - iovec iov{}; - iov.iov_base = regs; - iov.iov_len = sizeof(*regs); - xptrace(PTRACE_GETREGSET, pid, reinterpret_cast(regset), &iov); -#else - xptrace(PTRACE_GETREGS, pid, nullptr, regs); -#endif -} - -// Set remote registers -#define remote_setregs(regs) _remote_setregs(pid, regs) -static void _remote_setregs(int pid, pt_regs *regs) { -#if defined(__LP64__) - uintptr_t regset = NT_PRSTATUS; - iovec iov{}; - iov.iov_base = regs; - iov.iov_len = sizeof(*regs); - xptrace(PTRACE_SETREGSET, pid, reinterpret_cast(regset), &iov); -#else - xptrace(PTRACE_SETREGS, pid, nullptr, regs); -#endif -} - -uintptr_t remote_call_abi(int pid, uintptr_t func_addr, int nargs, va_list va) { - pt_regs regs, regs_bak; - - // Get registers and save a backup - remote_getregs(®s); - memcpy(®s_bak, ®s, sizeof(regs)); - - // ABI dependent: Setup stack and registers to perform the call - -#if defined(__arm__) || defined(__aarch64__) - // Fill R0-Rx with the first 4 (32-bit) or 8 (64-bit) parameters - for (int i = 0; (i < nargs) && (i < PARAMS_IN_REGS); ++i) { - regs.uregs[i] = va_arg(va, uintptr_t); - } - - // Push remaining parameters onto stack - if (nargs > PARAMS_IN_REGS) { - regs.ARM_sp -= sizeof(uintptr_t) * (nargs - PARAMS_IN_REGS); - uintptr_t stack = regs.ARM_sp; - for (int i = PARAMS_IN_REGS; i < nargs; ++i) { - uintptr_t arg = va_arg(va, uintptr_t); - remote_write(stack, &arg, sizeof(uintptr_t)); - stack += sizeof(uintptr_t); - } - } - - // Set return address - regs.ARM_lr = 0; - - // Set function address to call - regs.ARM_pc = func_addr; - - // Setup the current processor status register - if (regs.ARM_pc & 1u) { - // thumb - regs.ARM_pc &= (~1u); - regs.ARM_cpsr |= CPSR_T_MASK; - } else { - // arm - regs.ARM_cpsr &= ~CPSR_T_MASK; - } -#elif defined(__i386__) - // Push all params onto stack - regs.esp -= sizeof(uintptr_t) * nargs; - uintptr_t stack = regs.esp; - for (int i = 0; i < nargs; ++i) { - uintptr_t arg = va_arg(va, uintptr_t); - remote_write(stack, &arg, sizeof(uintptr_t)); - stack += sizeof(uintptr_t); - } - - // Push return address onto stack - uintptr_t ret_addr = 0; - regs.esp -= sizeof(uintptr_t); - remote_write(regs.esp, &ret_addr, sizeof(uintptr_t)); - - // Set function address to call - regs.eip = func_addr; -#elif defined(__x86_64__) - // Align, rsp - 8 must be a multiple of 16 at function entry point - uintptr_t space = sizeof(uintptr_t); - if (nargs > 6) - space += sizeof(uintptr_t) * (nargs - 6); - while (((regs.rsp - space - 8) & 0xF) != 0) - regs.rsp--; - - // Fill [RDI, RSI, RDX, RCX, R8, R9] with the first 6 parameters - for (int i = 0; (i < nargs) && (i < 6); ++i) { - uintptr_t arg = va_arg(va, uintptr_t); - switch (i) { - case 0: regs.rdi = arg; break; - case 1: regs.rsi = arg; break; - case 2: regs.rdx = arg; break; - case 3: regs.rcx = arg; break; - case 4: regs.r8 = arg; break; - case 5: regs.r9 = arg; break; - } - } - - // Push remaining parameters onto stack - if (nargs > 6) { - regs.rsp -= sizeof(uintptr_t) * (nargs - 6); - uintptr_t stack = regs.rsp; - for(int i = 6; i < nargs; ++i) { - uintptr_t arg = va_arg(va, uintptr_t); - remote_write(stack, &arg, sizeof(uintptr_t)); - stack += sizeof(uintptr_t); - } - } - - // Push return address onto stack - uintptr_t ret_addr = 0; - regs.rsp -= sizeof(uintptr_t); - remote_write(regs.rsp, &ret_addr, sizeof(uintptr_t)); - - // Set function address to call - regs.rip = func_addr; - - // may be needed - regs.rax = 0; - regs.orig_rax = 0; -#else -#error Unsupported ABI -#endif - - // Resume process to do the call - remote_setregs(®s); - xptrace(PTRACE_CONT, pid); - - // Catch SIGSEGV caused by the 0 return address - int status; - while (waitpid(pid, &status, __WALL | __WNOTHREAD) == pid) { - if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSEGV)) - break; - xptrace(PTRACE_CONT, pid); - } - - // Get registers again for return value - remote_getregs(®s); - - // Restore registers - remote_setregs(®s_bak); - -#if defined(__arm__) || defined(__aarch64__) - return regs.ARM_r0; -#elif defined(__i386__) - return regs.eax; -#elif defined(__x86_64__) - return regs.rax; -#endif -} - -uintptr_t remote_call_vararg(int pid, uintptr_t addr, int nargs, ...) { - char lib_name[4096]; - auto off = get_function_off(getpid(), addr, lib_name); - if (off == 0) - return 0; - auto remote = get_function_addr(pid, lib_name, off); - if (remote == 0) - return 0; - va_list va; - va_start(va, nargs); - auto result = remote_call_abi(pid, remote, nargs, va); - va_end(va); - return result; -} diff --git a/native/src/zygisk/ptrace.hpp b/native/src/zygisk/ptrace.hpp deleted file mode 100644 index b4951a62502ff..0000000000000 --- a/native/src/zygisk/ptrace.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -// Write bytes to the remote process at addr -bool _remote_write(int pid, uintptr_t addr, const void *buf, size_t len); -#define remote_write(...) _remote_write(pid, __VA_ARGS__) - -// Read bytes from the remote process at addr -bool _remote_read(int pid, uintptr_t addr, void *buf, size_t len); -#define remote_read(...) _remote_read(pid, __VA_ARGS__) - -// Call a remote function -// Arguments are expected to be only integer-like or pointer types -// as other more complex C ABIs are not implemented. -uintptr_t remote_call_abi(int pid, uintptr_t func_addr, int nargs, va_list va); - -// Find remote offset and invoke function -uintptr_t remote_call_vararg(int pid, uintptr_t addr, int nargs, ...); - -// C++ wrapper for auto argument counting and casting function pointers -template -static uintptr_t _remote_call(int pid, FuncPtr sym, Args && ...args) { - auto addr = reinterpret_cast(sym); - return remote_call_vararg(pid, addr, sizeof...(args), std::forward(args)...); -} -#define remote_call(...) _remote_call(pid, __VA_ARGS__) diff --git a/native/src/zygisk/utils.cpp b/native/src/zygisk/utils.cpp deleted file mode 100644 index 97cc5e2a389f5..0000000000000 --- a/native/src/zygisk/utils.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -#include "zygisk.hpp" -#include - -using namespace std; -static vector find_maps(const char *name) { - auto maps = lsplt::MapInfo::Scan(); - for (auto iter = maps.begin(); iter != maps.end();) { - if (iter->path != name) { - iter = maps.erase(iter); - } else { - ++iter; - } - } - return maps; -} - -void unmap_all(const char *name) { - auto maps = find_maps(name); - for (auto &info : maps) { - void *addr = reinterpret_cast(info.start); - size_t size = info.end - info.start; - if (info.perms & PROT_READ) { - // Make sure readable pages are still readable - void *dummy = xmmap(nullptr, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - mremap(dummy, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, addr); - } else { - munmap(addr, size); - } - } -} - -void remap_all(const char *name) { - auto maps = find_maps(name); - for (auto &info : maps) { - void *addr = reinterpret_cast(info.start); - size_t size = info.end - info.start; - void *copy = xmmap(nullptr, size, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if ((info.perms & PROT_READ) == 0) { - mprotect(addr, size, PROT_READ); - } - memcpy(copy, addr, size); - mremap(copy, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, addr); - mprotect(addr, size, info.perms); - } -} - -uintptr_t get_function_off(int pid, uintptr_t addr, char *lib) { - for (auto &info : lsplt::MapInfo::Scan()) { - if (addr >= info.start && addr < info.end) { - if (lib) - strcpy(lib, info.path.data()); - return addr - info.start + info.offset; - } - } - return 0; -} - -uintptr_t get_function_addr(int pid, const char *lib, uintptr_t off) { - for (auto &info : lsplt::MapInfo::Scan()) { - if (info.path == lib && (info.perms & PROT_EXEC)) { - return info.start - info.offset + off; - } - } - return 0; -} diff --git a/native/src/zygisk/zygisk.hpp b/native/src/zygisk/zygisk.hpp index 418fc8c6c12e6..2af05e6da7a15 100644 --- a/native/src/zygisk/zygisk.hpp +++ b/native/src/zygisk/zygisk.hpp @@ -5,19 +5,12 @@ #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, GET_LOG_PIPE, CONNECT_COMPANION, GET_MODDIR, - PASSTHROUGH, END }; } @@ -26,30 +19,17 @@ enum : int { #define ZLOGD(...) LOGD("zygisk64: " __VA_ARGS__) #define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__) #define ZLOGI(...) LOGI("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 HIJACK_BIN HIJACK_BIN32 #endif -// Unmap all pages matching the name -void unmap_all(const char *name); - -// Remap all matching pages with anonymous pages -void remap_all(const char *name); - -// Get library name + offset (from start of ELF), given function address -uintptr_t get_function_off(int pid, uintptr_t addr, char *lib); - -// Get function address, given library name + offset -uintptr_t get_function_addr(int pid, const char *lib, uintptr_t off); - extern void *self_handle; -void hook_functions(); -int remote_get_info(int uid, const char *process, uint32_t *flags, std::vector &fds); +void hook_functions(bool zygote); +int remote_get_info(int uid, const char *process, const char *app_data_dir, uint32_t *flags, + std::vector &fds); inline int zygisk_request(int req) { int fd = connect_daemon(MainRequest::ZYGISK);