diff --git a/native/jni/core/core.hpp b/native/jni/core/core.hpp index 68049197d..6a9cc95d9 100644 --- a/native/jni/core/core.hpp +++ b/native/jni/core/core.hpp @@ -34,3 +34,6 @@ void exec_common_scripts(const char *stage); void exec_module_scripts(const char *stage, const std::vector &modules); void install_apk(const char *apk); [[noreturn]] void install_module(const char *file); + +// Zygisk companion entrypoint +[[noreturn]] void zygiskd(int socket); diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index b27af63fe..6b7c482aa 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -39,6 +39,7 @@ Advanced Options (Internal APIs): --sqlite SQL exec SQL commands to Magisk database --path print Magisk tmpfs mount path --denylist ARGS denylist config CLI + --companion FD start zygisk root companion Available applets: )EOF"); @@ -127,6 +128,8 @@ int magisk_main(int argc, char *argv[]) { return 0; } else if (argc >= 3 && argv[1] == "--install-module"sv) { install_module(argv[2]); + } else if (argc >= 3 && argv[1] == "--companion"sv) { + zygiskd(parse_int(argv[2])); } #if 0 /* Entry point for testing stuffs */ diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index 2e3305778..4911b2d74 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -138,10 +138,10 @@ void set_nice_name(const char *name) { * Bionic's atoi runs through strtol(). * Use our own implementation for faster conversion. */ -int parse_int(const char *s) { +int parse_int(string_view s) { int val = 0; - char c; - while ((c = *(s++))) { + for (char c : s) { + if (!c) break; if (c > '9' || c < '0') return -1; val = val * 10 + c - '0'; diff --git a/native/jni/utils/misc.hpp b/native/jni/utils/misc.hpp index 5946cb940..e00542bbd 100644 --- a/native/jni/utils/misc.hpp +++ b/native/jni/utils/misc.hpp @@ -79,9 +79,7 @@ public: bool operator!=(const stateless_allocator&) { return false; } }; -int parse_int(const char *s); -static inline int parse_int(const std::string &s) { return parse_int(s.data()); } -static inline int parse_int(std::string_view s) { return parse_int(s.data()); } +int parse_int(std::string_view s); using thread_entry = void *(*)(void *); int new_daemon_thread(thread_entry entry, void *arg = nullptr); diff --git a/native/jni/zygisk/api.hpp b/native/jni/zygisk/api.hpp index dbd11d2ff..233d3ea37 100644 --- a/native/jni/zygisk/api.hpp +++ b/native/jni/zygisk/api.hpp @@ -164,6 +164,9 @@ struct Api { // Another good use case for a companion process is that if you want to share some resources // across multiple processes, hold the resources in the companion process and pass it over. // + // The root companion process is ABI aware; that is, when calling this function from a 32-bit + // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit. + // // Returns a file descriptor to a socket that is connected to the socket passed to your // module's companion request handler. Returns -1 if the connection attempt failed. int connectCompanion(); diff --git a/native/jni/zygisk/companion.cpp b/native/jni/zygisk/companion.cpp index a85a968da..129b9b0e1 100644 --- a/native/jni/zygisk/companion.cpp +++ b/native/jni/zygisk/companion.cpp @@ -4,26 +4,30 @@ #include #include #include +#include #include "zygisk.hpp" using namespace std; -static int zygiskd_socket = -1; +void zygiskd(int socket) { + if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) + exit(-1); + android_logging(); -[[noreturn]] static void zygiskd(int socket) { - set_nice_name("zygiskd"); - LOGI("* Launching zygiskd\n"); +#if defined(__LP64__) + set_nice_name("zygiskd64"); + LOGI("* Launching zygiskd64\n"); +#else + set_nice_name("zygiskd32"); + LOGI("* Launching zygiskd32\n"); +#endif // Load modules using comp_entry = void(*)(int); vector modules; { -#if defined(__LP64__) - vector module_fds = zygisk_module_fds(true); -#else - vector module_fds = zygisk_module_fds(false); -#endif + vector module_fds = recv_fds(socket); char buf[256]; for (int fd : module_fds) { snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); @@ -35,6 +39,9 @@ static int zygiskd_socket = -1; } } + // ack + write_int(socket, 0); + // Start accepting requests pollfd pfd = { socket, POLLIN, 0 }; for (;;) { @@ -68,7 +75,10 @@ static int zygiskd_socket = -1; } } -void start_companion(int client) { +static int zygiskd_sockets[] = { -1, -1 }; +#define zygiskd_socket zygiskd_sockets[is_64_bit] + +void connect_companion(int client, bool is_64_bit) { if (zygiskd_socket >= 0) { // Make sure the socket is still valid pollfd pfd = { zygiskd_socket, 0, 0 }; @@ -84,7 +94,20 @@ void start_companion(int client) { socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); zygiskd_socket = fds[0]; if (fork_dont_care() == 0) { - zygiskd(fds[1]); + string exe = MAGISKTMP + "/magisk" + (is_64_bit ? "64" : "32"); + // This fd has to survive exec + fcntl(fds[1], F_SETFD, 0); + char buf[16]; + snprintf(buf, sizeof(buf), "%d", fds[1]); + execlp(exe.data(), "magisk", "--companion", buf, (char *) nullptr); + exit(-1); + } + close(fds[1]); + vector module_fds = zygisk_module_fds(is_64_bit); + send_fds(zygiskd_socket, module_fds.data(), module_fds.size()); + // Wait for ack + if (read_int(zygiskd_socket) != 0) { + return; } } send_fd(zygiskd_socket, client); diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index 894268c72..51a84641c 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -243,12 +243,16 @@ int remote_request_unmount() { // The following code runs in magiskd +static bool get_exe(int pid, char *buf, size_t sz) { + snprintf(buf, sz, "/proc/%d/exe", pid); + return xreadlink(buf, buf, sz) > 0; +} + static void setup_files(int client, const sock_cred *cred) { LOGD("zygisk: setup files for pid=[%d]\n", cred->pid); char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/exe", cred->pid); - if (xreadlink(buf, buf, sizeof(buf)) < 0) { + if (!get_exe(cred->pid, buf, sizeof(buf))) { write_int(client, 1); return; } @@ -308,8 +312,7 @@ static void get_process_info(int client, const sock_cred *cred) { if (!info.on_denylist) { char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/exe", cred->pid); - xreadlink(buf, buf, sizeof(buf)); + get_exe(cred->pid, buf, sizeof(buf)); vector fds = zygisk_module_fds(str_ends(buf, "64")); send_fds(client, fds.data(), fds.size()); } @@ -336,6 +339,7 @@ static void send_log_pipe(int fd) { void zygisk_handler(int client, const sock_cred *cred) { int code = read_int(client); + char buf[256]; switch (code) { case ZYGISK_SETUP: setup_files(client, cred); @@ -349,8 +353,9 @@ void zygisk_handler(int client, const sock_cred *cred) { case ZYGISK_GET_LOG_PIPE: send_log_pipe(client); break; - case ZYGISK_START_COMPANION: - start_companion(client); + case ZYGISK_CONNECT_COMPANION: + get_exe(cred->pid, buf, sizeof(buf)); + connect_companion(client, str_ends(buf, "64")); break; } close(client); diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index e3915357b..36a1dae8c 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -290,7 +290,7 @@ bool ZygiskModule::registerModule(ApiTable *table, long *module) { int ZygiskModule::connectCompanion() const { if (int fd = connect_daemon(); fd >= 0) { write_int(fd, ZYGISK_REQUEST); - write_int(fd, ZYGISK_START_COMPANION); + write_int(fd, ZYGISK_CONNECT_COMPANION); write_int(fd, id); return fd; } diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index c6be4a6c5..56a4ba3a0 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -12,7 +12,7 @@ enum : int { ZYGISK_GET_INFO, ZYGISK_UNMOUNT, ZYGISK_GET_LOG_PIPE, - ZYGISK_START_COMPANION, + ZYGISK_CONNECT_COMPANION, }; #if defined(__LP64__) @@ -47,4 +47,4 @@ void hook_functions(); bool unhook_functions(); std::vector remote_get_info(int uid, const char *process, AppInfo *info); int remote_request_unmount(); -void start_companion(int client); +void connect_companion(int client, bool is_64_bit);