From d0fc372ecd374e2c6492104e8d71bcf2eab8e706 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 17 Oct 2021 04:36:18 -0700 Subject: [PATCH] Implement Zygisk companion process --- native/jni/Android.mk | 1 + native/jni/utils/misc.cpp | 2 +- native/jni/zygisk/api.hpp | 8 +-- native/jni/zygisk/companion.cpp | 91 +++++++++++++++++++++++++++++++++ native/jni/zygisk/entry.cpp | 3 ++ native/jni/zygisk/hook.cpp | 10 ++-- native/jni/zygisk/module.hpp | 2 +- native/jni/zygisk/zygisk.hpp | 2 + 8 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 native/jni/zygisk/companion.cpp diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 0446a1589..afa006c6e 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -32,6 +32,7 @@ LOCAL_SRC_FILES := \ zygisk/utils.cpp \ zygisk/hook.cpp \ zygisk/memory.cpp \ + zygisk/companion.cpp \ zygisk/deny/cli.cpp \ zygisk/deny/utils.cpp \ zygisk/deny/revert.cpp diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index 68bf99b65..2e3305778 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -27,7 +27,7 @@ int fork_no_orphan() { int pid = xfork(); if (pid) return pid; - prctl(PR_SET_PDEATHSIG, SIGTERM); + prctl(PR_SET_PDEATHSIG, SIGKILL); if (getppid() == 1) exit(1); return 0; diff --git a/native/jni/zygisk/api.hpp b/native/jni/zygisk/api.hpp index bdf6b6cb7..48e9ff2aa 100644 --- a/native/jni/zygisk/api.hpp +++ b/native/jni/zygisk/api.hpp @@ -141,12 +141,8 @@ 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. // - // When this function is called, in the companion process, a socket pair will be created, - // your module's onCompanionRequest(int) callback will receive one socket, and the other - // socket will be returned. - // - // Returns a file descriptor to a socket that is connected to the socket passed to - // your module's onCompanionRequest(int). Returns -1 if the connection attempt failed. + // 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(); // Force Magisk's denylist unmount routines to run on this process. diff --git a/native/jni/zygisk/companion.cpp b/native/jni/zygisk/companion.cpp new file mode 100644 index 000000000..a85a968da --- /dev/null +++ b/native/jni/zygisk/companion.cpp @@ -0,0 +1,91 @@ +#include +#include + +#include +#include +#include + +#include "zygisk.hpp" + +using namespace std; + +static int zygiskd_socket = -1; + +[[noreturn]] static void zygiskd(int socket) { + set_nice_name("zygiskd"); + LOGI("* Launching zygiskd\n"); + + // 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 + char buf[256]; + for (int fd : module_fds) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); + comp_entry entry = nullptr; + if (void *h = dlopen(buf, RTLD_LAZY)) { + *(void **) &entry = dlsym(h, "zygisk_companion_entry"); + } + modules.push_back(entry); + } + } + + // Start accepting requests + pollfd pfd = { socket, POLLIN, 0 }; + for (;;) { + poll(&pfd, 1, -1); + if (!(pfd.revents & POLLIN)) { + // Something bad happened in magiskd, terminate zygiskd + exit(0); + } + int client = recv_fd(socket); + int module_id = read_int(client); + if (module_id < modules.size() && modules[module_id]) { + exec_task([=, entry = modules[module_id]] { + int dup = fcntl(client, F_DUPFD_CLOEXEC); + entry(client); + // Only close client if it is the same as dup so we don't + // accidentally close a re-used file descriptor. + // This check is required because the module companion + // handler could've closed the file descriptor already. + if (struct stat s1; fstat(client, &s1) == 0) { + struct stat s2{}; + fstat(dup, &s2); + if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { + close(client); + } + } + close(dup); + }); + } else { + close(client); + } + } +} + +void start_companion(int client) { + if (zygiskd_socket >= 0) { + // Make sure the socket is still valid + pollfd pfd = { zygiskd_socket, 0, 0 }; + poll(&pfd, 1, 0); + if (pfd.revents) { + // Any revent means error + close(zygiskd_socket); + zygiskd_socket = -1; + } + } + if (zygiskd_socket < 0) { + int fds[2]; + socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); + zygiskd_socket = fds[0]; + if (fork_dont_care() == 0) { + zygiskd(fds[1]); + } + } + send_fd(zygiskd_socket, client); +} diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index 30f52b44f..469430b5d 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -349,6 +349,9 @@ void zygisk_handler(int client, ucred *cred) { case ZYGISK_GET_LOG_PIPE: send_log_pipe(client); break; + case ZYGISK_START_COMPANION: + start_companion(client); + break; } close(client); } diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index 59c3b5f16..4925add99 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -284,9 +284,13 @@ bool ZygiskModule::registerModule(ApiTable *table, long *module) { return true; } -int ZygiskModule::connectCompanion() { - // TODO - (void) id; +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, id); + return fd; + } return -1; } diff --git a/native/jni/zygisk/module.hpp b/native/jni/zygisk/module.hpp index e7d729359..ff7f4ccb7 100644 --- a/native/jni/zygisk/module.hpp +++ b/native/jni/zygisk/module.hpp @@ -81,7 +81,7 @@ struct ZygiskModule { v1->postServerSpecialize(v1->_this, force_cast(args)); } - int connectCompanion(); + int connectCompanion() const; static void forceDenyListUnmount(); static bool registerModule(ApiTable *table, long *module); diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index 6155a59d3..c6be4a6c5 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -12,6 +12,7 @@ enum : int { ZYGISK_GET_INFO, ZYGISK_UNMOUNT, ZYGISK_GET_LOG_PIPE, + ZYGISK_START_COMPANION, }; #if defined(__LP64__) @@ -46,3 +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);