From 678d886343d14231b9d940c0b77866df4b917b60 Mon Sep 17 00:00:00 2001 From: "Pedro.js" Date: Thu, 20 Jun 2024 22:53:13 -0300 Subject: [PATCH] improve: CLI and code (#3) This commit improves the CLI of ReZygisk, allowing the use of important information like PID of the daemons. Also improves the code of the loaders ptracer. --- loader/src/common/daemon.cpp | 33 +- loader/src/include/daemon.h | 15 + loader/src/ptracer/main.cpp | 122 +++- loader/src/ptracer/monitor.cpp | 486 +++++++++------- loader/src/ptracer/{main.h => monitor.h} | 3 +- loader/src/ptracer/ptracer.cpp | 462 ++++++++------- loader/src/ptracer/utils.cpp | 679 +++++++++++++---------- loader/src/ptracer/utils.hpp | 1 + module/build.gradle.kts | 10 +- module/src/customize.sh | 2 - module/src/zygisk-ctl.sh | 6 - zygiskd/src/constants.rs | 1 + zygiskd/src/zygiskd.rs | 14 + 13 files changed, 1109 insertions(+), 725 deletions(-) rename loader/src/ptracer/{main.h => monitor.h} (89%) delete mode 100644 module/src/zygisk-ctl.sh diff --git a/loader/src/common/daemon.cpp b/loader/src/common/daemon.cpp index b8ab7eaa..bdb8d1f1 100644 --- a/loader/src/common/daemon.cpp +++ b/loader/src/common/daemon.cpp @@ -18,10 +18,11 @@ namespace zygiskd { int Connect(uint8_t retry) { int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - struct sockaddr_un addr{ - .sun_family = AF_UNIX, - .sun_path={0}, + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + .sun_path = { 0 }, }; + auto socket_path = TMP_PATH + kCPSocketName; strcpy(addr.sun_path, socket_path.c_str()); socklen_t socklen = sizeof(addr); @@ -67,6 +68,7 @@ namespace zygiskd { } socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags); socket_utils::write_u32(fd, uid); + return socket_utils::read_u32(fd); } @@ -139,4 +141,29 @@ namespace zygiskd { } } } + + void GetInfo(struct zygote_info *info) { + /* TODO: Optimize and avoid re-connect twice here */ + int fd = Connect(1); + + if (fd != -1) { + info->running = true; + + socket_utils::write_u8(fd, (uint8_t) SocketAction::GetInfo); + + int flags = socket_utils::read_u32(fd); + + if (flags & (1 << 29)) { + info->root_impl = ZYGOTE_ROOT_IMPL_KERNELSU; + } else if (flags & (1 << 30)) { + info->root_impl = ZYGOTE_ROOT_IMPL_MAGISK; + } else { + info->root_impl = ZYGOTE_ROOT_IMPL_NONE; + } + + info->pid = socket_utils::read_u32(fd); + + close(fd); + } else info->running = false; + } } diff --git a/loader/src/include/daemon.h b/loader/src/include/daemon.h index 839d30db..94b2d52f 100644 --- a/loader/src/include/daemon.h +++ b/loader/src/include/daemon.h @@ -42,6 +42,18 @@ class UniqueFd { Fd fd_ = -1; }; +enum zygote_root_impl { + ZYGOTE_ROOT_IMPL_NONE, + ZYGOTE_ROOT_IMPL_KERNELSU, + ZYGOTE_ROOT_IMPL_MAGISK +}; + +struct zygote_info { + enum zygote_root_impl root_impl; + pid_t pid; + bool running; +}; + namespace zygiskd { struct Module { @@ -55,6 +67,7 @@ namespace zygiskd { PingHeartBeat, RequestLogcatFd, GetProcessFlags, + GetInfo, ReadModules, RequestCompanionSocket, GetModuleDir, @@ -81,4 +94,6 @@ namespace zygiskd { void ZygoteRestart(); void SystemServerStarted(); + + void GetInfo(struct zygote_info *info); } diff --git a/loader/src/ptracer/main.cpp b/loader/src/ptracer/main.cpp index 2b10c54b..81db5684 100644 --- a/loader/src/ptracer/main.cpp +++ b/loader/src/ptracer/main.cpp @@ -1,12 +1,60 @@ -#include "main.h" +#include + +#include "monitor.h" #include "utils.hpp" #include "daemon.h" +#define CUSTOM_TMP_PATH 0 +#define SBIN_AS_TMP_PATH 1 +#define DEBUG_RAMDISK_AS_TMP_PATH 2 + int main(int argc, char **argv) { - zygiskd::Init(getenv("TMP_PATH")); + int tmp_path_type = CUSTOM_TMP_PATH; + + if (getenv("TMP_PATH") == NULL) { + tmp_path_type = SBIN_AS_TMP_PATH; + + FILE *fp = fopen("/sbin", "r"); + if (fp == NULL) { + tmp_path_type = DEBUG_RAMDISK_AS_TMP_PATH; + + fp = fopen("/debug_ramdisk", "r"); + + if (fp == NULL) { + printf("Cannot find TMP_PATH. You should make an issue about that.\n"); + + return 1; + } else fclose(fp); + } else fclose(fp); + } else { + tmp_path_type = CUSTOM_TMP_PATH; + } + + switch (tmp_path_type) { + case CUSTOM_TMP_PATH: { + zygiskd::Init(getenv("TMP_PATH")); + + break; + } + case SBIN_AS_TMP_PATH: { + zygiskd::Init("/sbin"); + + break; + } + case DEBUG_RAMDISK_AS_TMP_PATH: { + zygiskd::Init("/debug_ramdisk"); + + break; + } + } + + printf("The ReZygisk Tracer %s\n\n", ZKSU_VERSION); + if (argc >= 2 && strcmp(argv[1], "monitor") == 0) { init_monitor(); + printf("[ReZygisk]: Started monitoring...\n"); + return 0; } else if (argc >= 3 && strcmp(argv[1], "trace") == 0) { if (argc >= 4 && strcmp(argv[3], "--restart") == 0) zygiskd::ZygoteRestart(); @@ -18,35 +66,75 @@ int main(int argc, char **argv) { return 1; } + printf("[ReZygisk]: Tracing %ld...\n", pid); + return 0; } else if (argc >= 2 && strcmp(argv[1], "ctl") == 0) { - if (argc == 3) { - if (strcmp(argv[2], "start") == 0) { - send_control_command(START); + enum Command command; - return 0; - } else if (strcmp(argv[2], "stop") == 0) { - send_control_command(STOP); + if (strcmp(argv[2], "start") == 0) command = START; + else if (strcmp(argv[2], "stop") == 0) command = STOP; + else if (strcmp(argv[2], "exit") == 0) command = EXIT; + else { + printf("[ReZygisk]: Usage: %s ctl \n", argv[0]); - return 0; - } else if (strcmp(argv[2], "exit") == 0) { - send_control_command(EXIT); + return 1; + } - return 0; - } + if (send_control_command(command) == -1) { + printf("[ReZygisk]: Failed to send the command, is the daemon running?\n"); + + return 1; } - printf("ReZygisk Tracer %s\n", ZKSU_VERSION); - printf("Usage: %s ctl start|stop|exit\n", argv[0]); + printf("[ReZygisk]: command sent\n"); - return 1; + return 0; } else if (argc >= 2 && strcmp(argv[1], "version") == 0) { printf("ReZygisk Tracer %s\n", ZKSU_VERSION); + return 0; + } else if (argc >= 2 && strcmp(argv[1], "info") == 0) { + printf("ReZygisk Tracer %s\n", ZKSU_VERSION); + + struct zygote_info info; + zygiskd::GetInfo(&info); + + printf("Daemon process PID: %d\n", info.pid); + + switch (info.root_impl) { + case ZYGOTE_ROOT_IMPL_NONE: { + printf("Root implementation: none\n"); + + break; + } + case ZYGOTE_ROOT_IMPL_KERNELSU: { + printf("Root implementation: KernelSU\n"); + + break; + } + case ZYGOTE_ROOT_IMPL_MAGISK: { + printf("Root implementation: Magisk\n"); + + break; + } + } + + printf("Is the daemon running: %s\n", info.running ? "yes" : "no"); + return 0; } else { printf("ReZygisk Tracer %s\n", ZKSU_VERSION); - printf("usage: %s monitor | trace | ctl | version\n", argv[0]); + printf( + "Available commands:\n" + " - monitor\n" + " - trace [--restart]\n" + " - ctl \n" + " - version: Shows the version of ReZygisk.\n" + " - info: Shows information about the created daemon/injection.\n" + "\n" + "<...>: Obligatory\n" + "[...]: Optional\n"); return 1; } diff --git a/loader/src/ptracer/monitor.cpp b/loader/src/ptracer/monitor.cpp index 8e91b23a..327a5e0b 100644 --- a/loader/src/ptracer/monitor.cpp +++ b/loader/src/ptracer/monitor.cpp @@ -13,7 +13,7 @@ #include #include -#include "main.h" +#include "monitor.h" #include "utils.hpp" #include "files.hpp" #include "misc.hpp" @@ -41,81 +41,103 @@ struct EventHandler { }; struct EventLoop { -private: + private: int epoll_fd_; bool running = false; -public: + public: bool Init() { - epoll_fd_ = epoll_create(1); - if (epoll_fd_ == -1) { - PLOGE("failed to create"); - return false; - } - return true; + epoll_fd_ = epoll_create(1); + if (epoll_fd_ == -1) { + PLOGE("failed to create"); + + return false; + } + + return true; } void Stop() { - running = false; + running = false; } void Loop() { - running = true; - constexpr auto MAX_EVENTS = 2; - struct epoll_event events[MAX_EVENTS]; - while (running) { - int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1); - if (nfds == -1) { - if (errno != EINTR) - PLOGE("epoll_wait"); - continue; - } - for (int i = 0; i < nfds; i++) { - reinterpret_cast(events[i].data.ptr)->HandleEvent(*this, - events[i].events); - if (!running) break; - } + running = true; + + constexpr auto MAX_EVENTS = 2; + struct epoll_event events[MAX_EVENTS]; + + while (running) { + int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1); + if (nfds == -1) { + if (errno != EINTR) PLOGE("epoll_wait"); + + continue; } + + for (int i = 0; i < nfds; i++) { + reinterpret_cast(events[i].data.ptr)->HandleEvent(*this, + events[i].events); + if (!running) break; + } + } } bool RegisterHandler(EventHandler &handler, uint32_t events) { - struct epoll_event ev{}; - ev.events = events; - ev.data.ptr = &handler; - if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) { - PLOGE("failed to add event handler"); - return false; - } - return true; + struct epoll_event ev{}; + ev.events = events; + ev.data.ptr = &handler; + if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) { + PLOGE("failed to add event handler"); + + return false; + } + + return true; } - [[maybe_unused]] bool UnregisterHandler(EventHandler &handler) { - if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) { - PLOGE("failed to del event handler"); - return false; - } - return true; + bool UnregisterHandler(EventHandler &handler) { + if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) { + PLOGE("failed to del event handler"); + + return false; + } + + return true; } ~EventLoop() { - if (epoll_fd_ >= 0) close(epoll_fd_); + if (epoll_fd_ >= 0) close(epoll_fd_); } }; static TracingState tracing_state = TRACING; static char prop_path[PATH_MAX]; - struct Status { - bool supported = false; - bool zygote_injected = false; - bool daemon_running = false; - pid_t daemon_pid = -1; - char *daemon_info; - char *daemon_error_info; + bool supported = false; + bool zygote_injected = false; + bool daemon_running = false; + pid_t daemon_pid = -1; + char *daemon_info; + char *daemon_error_info; }; -static Status status64; -static Status status32; +static Status status64 = { + .supported = false, + .zygote_injected = false, + .daemon_running = false, + .daemon_pid = -1, + .daemon_info = NULL, + .daemon_error_info = NULL +}; +static Status status32 = { + .supported = false, + .zygote_injected = false, + .daemon_running = false, + .daemon_pid = -1, + .daemon_info = NULL, + .daemon_error_info = NULL +}; struct SocketHandler : public EventHandler { int sock_fd_; @@ -157,155 +179,157 @@ struct SocketHandler : public EventHandler { }; while (1) { - std::vector buf; - buf.resize(sizeof(MsgHead), 0); + std::vector buf; + buf.resize(sizeof(MsgHead), 0); - MsgHead &msg = *reinterpret_cast(buf.data()); + MsgHead &msg = *reinterpret_cast(buf.data()); - ssize_t real_size; - ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK); - if (nread == -1) { - if (errno == EAGAIN) break; + ssize_t real_size; + ssize_t nread = recv(sock_fd_, &msg, sizeof(msg), MSG_PEEK); + if (nread == -1) { + if (errno == EAGAIN) break; - PLOGE("read socket"); - } + PLOGE("read socket"); + } + + if (static_cast(nread) < sizeof(Command)) { + LOGE("read %zu < %zu", nread, sizeof(Command)); + continue; + } + + if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) { + if (nread != sizeof(msg)) { + LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(MsgHead)); - if (static_cast(nread) < sizeof(Command)) { - LOGE("read %zu < %zu", nread, sizeof(Command)); continue; } - if (msg.cmd >= Command::DAEMON64_SET_INFO && msg.cmd != Command::SYSTEM_SERVER_STARTED) { - if (nread != sizeof(msg)) { - LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(MsgHead)); + real_size = sizeof(MsgHead) + msg.length; + } else { + if (nread != sizeof(Command)) { + LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(Command)); - continue; - } + continue; + } - real_size = sizeof(MsgHead) + msg.length; - } else { - if (nread != sizeof(Command)) { - LOGE("cmd %d size %zu != %zu", msg.cmd, nread, sizeof(Command)); + real_size = sizeof(Command); + } - continue; - } + buf.resize(real_size); + nread = recv(sock_fd_, &msg, real_size, 0); - real_size = sizeof(Command); - } + if (nread == -1) { + if (errno == EAGAIN) break; - buf.resize(real_size); - nread = recv(sock_fd_, &msg, real_size, 0); + PLOGE("recv"); + continue; + } - if (nread == -1) { - if (errno == EAGAIN) break; + if (nread != real_size) { + LOGE("real size %zu != %zu", real_size, nread); - PLOGE("recv"); - continue; - } + continue; + } - if (nread != real_size) { - LOGE("real size %zu != %zu", real_size, nread); + switch (msg.cmd) { + case START: { + if (tracing_state == STOPPING) tracing_state = TRACING; + else if (tracing_state == STOPPED) { + ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); - continue; - } + LOGI("start tracing init"); - switch (msg.cmd) { - case START: { - if (tracing_state == STOPPING) tracing_state = TRACING; - else if (tracing_state == STOPPED) { - ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); + tracing_state = TRACING; + } - LOGI("start tracing init"); + updateStatus(); - tracing_state = TRACING; - } + break; + } + case STOP: { + if (tracing_state == TRACING) { + LOGI("stop tracing requested"); - updateStatus(); + tracing_state = STOPPING; + memcpy(monitor_stop_reason, "user requested", sizeof("user requested")); - break; + ptrace(PTRACE_INTERRUPT, 1, 0, 0); + updateStatus(); } - case STOP: { - if (tracing_state == TRACING) { - LOGI("stop tracing requested"); - tracing_state = STOPPING; - memcpy(monitor_stop_reason, "user requested", sizeof("user requested")); - - ptrace(PTRACE_INTERRUPT, 1, 0, 0); - updateStatus(); - } + break; + } + case EXIT: { + LOGI("prepare for exit ..."); - break; - } - case EXIT: { - LOGI("prepare for exit ..."); + tracing_state = EXITING; + memcpy(monitor_stop_reason, "user requested", sizeof("user requested")); - tracing_state = EXITING; - memcpy(monitor_stop_reason, "user requested", sizeof("user requested")); + updateStatus(); + loop.Stop(); - updateStatus(); - loop.Stop(); + break; + } + case ZYGOTE64_INJECTED: { + status64.zygote_injected = true; - break; - } - case ZYGOTE64_INJECTED: { - status64.zygote_injected = true; + updateStatus(); - updateStatus(); + break; + } + case ZYGOTE32_INJECTED: { + status32.zygote_injected = true; - break; - } - case ZYGOTE32_INJECTED: { - status32.zygote_injected = true; + updateStatus(); - updateStatus(); + break; + } + case DAEMON64_SET_INFO: { + LOGD("received daemon64 info %s", msg.data); - break; - } - case DAEMON64_SET_INFO: { - LOGD("received daemon64 info %s", msg.data); + status64.daemon_info = msg.data; + updateStatus(); - status64.daemon_info = msg.data; - updateStatus(); + break; + } + case DAEMON32_SET_INFO: { + LOGD("received daemon32 info %s", msg.data); - break; - } - case DAEMON32_SET_INFO: { - LOGD("received daemon32 info %s", msg.data); + status32.daemon_info = msg.data; + updateStatus(); - status32.daemon_info = msg.data; - updateStatus(); + break; + } + case DAEMON64_SET_ERROR_INFO: { + LOGD("received daemon64 error info %s", msg.data); - break; - } - case DAEMON64_SET_ERROR_INFO: { - LOGD("received daemon64 error info %s", msg.data); + status64.daemon_running = false; + status64.daemon_error_info = msg.data; + updateStatus(); - status64.daemon_running = false; - status64.daemon_error_info = msg.data; - updateStatus(); + break; + } + case DAEMON32_SET_ERROR_INFO: { + LOGD("received daemon32 error info %s", msg.data); - break; - } - case DAEMON32_SET_ERROR_INFO: { - LOGD("received daemon32 error info %s", msg.data); + status32.daemon_running = false; + status32.daemon_error_info = msg.data; + updateStatus(); - status32.daemon_running = false; - status32.daemon_error_info = msg.data; - updateStatus(); + break; + } + case SYSTEM_SERVER_STARTED: { + LOGD("system server started, mounting prop"); - break; + if (mount(prop_path, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) { + PLOGE("failed to mount prop"); } - case SYSTEM_SERVER_STARTED: { - LOGD("system server started, mounting prop"); - if (mount(prop_path, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) { - PLOGE("failed to mount prop"); - } + umount2(prop_path, MNT_DETACH); - break; - } + break; } + } } } @@ -316,20 +340,25 @@ struct SocketHandler : public EventHandler { constexpr int MAX_RETRY_COUNT = 5; -#define CREATE_ZYGOTE_START_COUNTER(abi) \ -struct timespec last_zygote##abi{.tv_sec = 0, .tv_nsec = 0}; \ -int count_zygote##abi = 0; \ -bool should_stop_inject##abi() { \ - struct timespec now{}; \ - clock_gettime(CLOCK_MONOTONIC, &now); \ - if (now.tv_sec - last_zygote##abi.tv_sec < 30) { \ - count_zygote##abi++; \ - } else { \ - count_zygote##abi = 0; \ - } \ - last_zygote##abi = now; \ - return count_zygote##abi >= MAX_RETRY_COUNT; \ -} +#define CREATE_ZYGOTE_START_COUNTER(abi) \ + struct timespec last_zygote##abi = { \ + .tv_sec = 0, \ + .tv_nsec = 0 \ + }; \ + \ + int count_zygote ## abi = 0; \ + bool should_stop_inject ## abi() { \ + struct timespec now = {}; \ + clock_gettime(CLOCK_MONOTONIC, &now); \ + if (now.tv_sec - last_zygote ## abi.tv_sec < 30) \ + count_zygote ## abi++; \ + else \ + count_zygote ## abi = 0; \ + \ + last_zygote##abi = now; \ + \ + return count_zygote##abi >= MAX_RETRY_COUNT; \ + } CREATE_ZYGOTE_START_COUNTER(64) CREATE_ZYGOTE_START_COUNTER(32) @@ -360,6 +389,8 @@ static bool ensure_daemon_created(bool is_64bit) { exit(1); } else { + LOGI("daemon%s started with pid %d", is_64bit ? "64" : "32", pid); + status.supported = true; status.daemon_pid = pid; status.daemon_running = true; @@ -367,23 +398,25 @@ static bool ensure_daemon_created(bool is_64bit) { return true; } } else { + LOGI("daemon%s already started with pid %d", is_64bit ? "64" : "32", status.daemon_pid); + return status.daemon_running; } } -#define CHECK_DAEMON_EXIT(abi) \ - if (status##abi.supported && pid == status64.daemon_pid) { \ - char status_str[64]; \ - parse_status(status, status_str, sizeof(status_str)); \ - \ - LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \ - status##abi.daemon_running = false; \ - \ - if (status##abi.daemon_error_info[0] == '\0') \ - memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \ - \ - updateStatus(); \ - continue; \ +#define CHECK_DAEMON_EXIT(abi) \ + if (status##abi.supported && pid == status64.daemon_pid) { \ + char status_str[64]; \ + parse_status(status, status_str, sizeof(status_str)); \ + \ + LOGW("daemon" #abi "pid %d exited: %s", pid, status_str); \ + status##abi.daemon_running = false; \ + \ + if (status##abi.daemon_error_info[0] == '\0') \ + memcpy(status##abi.daemon_error_info, status_str, strlen(status_str)); \ + \ + updateStatus(); \ + continue; \ } #define PRE_INJECT(abi, is_64) \ @@ -605,7 +638,9 @@ struct SigChldHandler : public EventHandler { }; static char pre_section[1024]; +static int pre_section_len = 0; static char post_section[1024]; +static int post_section_len = 0; #define WRITE_STATUS_ABI(suffix) \ if (status ## suffix.supported) { \ @@ -616,7 +651,7 @@ static char post_section[1024]; else strcat(status_text, "❌ not injected,"); \ \ strcat(status_text, " daemon" #suffix ":"); \ - if (status ## suffix .daemon_running) { \ + if (status ## suffix.daemon_running) { \ strcat(status_text, "😋 running"); \ \ if (status ## suffix.daemon_info[0] != '\0') { \ @@ -637,7 +672,7 @@ static char post_section[1024]; static void updateStatus() { FILE *prop = fopen(prop_path, "w"); - char status_text[64] = "monitor: "; + char status_text[256] = "monitor: "; switch (tracing_state) { case TRACING: { @@ -671,6 +706,8 @@ static void updateStatus() { WRITE_STATUS_ABI(32) fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section); + + fclose(prop); } static bool prepare_environment() { @@ -686,19 +723,65 @@ static bool prepare_environment() { return false; } - char line[1024]; - ssize_t len = fread(line, 1, sizeof(line), orig_prop); - /* TODO: Check if this part translation is correct. - Old approach may be a callback approach that gets called - every new line - */ - if ((unsigned long)len > (sizeof("description") - 1) && strncmp(line, "description=", sizeof("description") - 1) == 0) { - strcat(post_section, "description="); - strcat(post_section, line + sizeof("description")); - } else { - strcat(pre_section, line); + const char field_name[] = "description="; + + /* TODO: improve this code */ + int i = 1; + while (1) { + int int_char = fgetc(orig_prop); + if (int_char == EOF) break; + + pre_section[pre_section_len] = (char)int_char; + pre_section[pre_section_len + 1] = '\0'; + pre_section_len++; + + if ((char)int_char != field_name[0]) continue; + + while (1) { + int int_char2 = fgetc(orig_prop); + if (int_char2 == EOF) break; + + if ((char)int_char2 == field_name[i]) { + i++; + + if (i == (int)(sizeof(field_name) - 1)) { + pre_section[pre_section_len] = (char)int_char2; + pre_section[pre_section_len + 1] = '\0'; + pre_section_len++; + + while (1) { + int int_char3 = fgetc(orig_prop); + if (int_char3 == EOF) break; + + post_section[post_section_len] = (char)int_char3; + post_section[post_section_len + 1] = '\0'; + post_section_len++; + + i++; + } + + break; + } else { + pre_section[pre_section_len] = (char)int_char2; + pre_section[pre_section_len + 1] = '\0'; + pre_section_len++; + + continue; + } + } else { + pre_section[pre_section_len] = (char)int_char2; + pre_section[pre_section_len + 1] = '\0'; + pre_section_len++; + + i = 1; + + break; + } + } } + fclose(orig_prop); + updateStatus(); return true; @@ -724,9 +807,9 @@ void init_monitor() { LOGI("exit"); } -void send_control_command(enum Command cmd) { +int send_control_command(enum Command cmd) { int sockfd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (sockfd == -1) err(EXIT_FAILURE, "socket"); + if (sockfd == -1) return -1; struct sockaddr_un addr = { .sun_family = AF_UNIX, @@ -737,12 +820,9 @@ void send_control_command(enum Command cmd) { socklen_t socklen = sizeof(sa_family_t) + sun_path_len; ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (sockaddr *)&addr, socklen); - if (nsend == -1) err(EXIT_FAILURE, "send"); - else if (nsend != sizeof(cmd)) { - printf("[ReZygisk]: Failed to send data. Tried to send %zu bytes but only %zu were sent.\n", sizeof(cmd), nsend); - exit(1); - } + /* TODO: Should we close even when it fails? */ + close(sockfd); - printf("[ReZygisk]: command sent\n"); + return nsend != sizeof(cmd) ? -1 : 0; } diff --git a/loader/src/ptracer/main.h b/loader/src/ptracer/monitor.h similarity index 89% rename from loader/src/ptracer/main.h rename to loader/src/ptracer/monitor.h index 3306cf69..c7db0f3c 100644 --- a/loader/src/ptracer/main.h +++ b/loader/src/ptracer/monitor.h @@ -4,6 +4,7 @@ #include void init_monitor(); + bool trace_zygote(int pid); enum Command { @@ -21,6 +22,6 @@ enum Command { SYSTEM_SERVER_STARTED = 10 }; -void send_control_command(enum Command cmd); +int send_control_command(enum Command cmd); #endif /* MAIN_HPP */ \ No newline at end of file diff --git a/loader/src/ptracer/ptracer.cpp b/loader/src/ptracer/ptracer.cpp index 6ab61b02..fa3b7914 100644 --- a/loader/src/ptracer/ptracer.cpp +++ b/loader/src/ptracer/ptracer.cpp @@ -19,209 +19,297 @@ #include "utils.hpp" bool inject_on_main(int pid, const char *lib_path) { - LOGI("injecting %s to zygote %d", lib_path, pid); - // parsing KernelArgumentBlock - // https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8 - struct user_regs_struct regs{}, backup{}; - auto map = MapInfo::Scan(std::to_string(pid)); - if (!get_regs(pid, regs)) return false; - auto arg = static_cast(regs.REG_SP); - LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str()); - int argc; - auto argv = reinterpret_cast(reinterpret_cast(arg) + 1); - LOGV("argv %p", argv); - read_proc(pid, arg, &argc, sizeof(argc)); - LOGV("argc %d", argc); - auto envp = argv + argc + 1; - LOGV("envp %p", envp); - auto p = envp; - while (true) { - uintptr_t *buf; - read_proc(pid, (uintptr_t) p, &buf, sizeof(buf)); - if (buf != nullptr) ++p; - else break; + LOGI("injecting %s to zygote %d", lib_path, pid); + + /* + parsing KernelArgumentBlock + + https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8 + */ + + struct user_regs_struct regs {}, + backup {}; + + /* WARNING: C++ keyword */ + std::vector map = MapInfo::Scan(std::to_string(pid)); + if (!get_regs(pid, regs)) return false; + + uintptr_t arg = (uintptr_t)regs.REG_SP; + + LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str()); + + int argc; + char **argv = (char **)((uintptr_t *)arg + 1); + LOGV("argv %p", (void *)argv); + + read_proc(pid, arg, &argc, sizeof(argc)); + LOGV("argc %d", argc); + + /* WARNING: C++ keyword */ + auto envp = argv + argc + 1; + LOGV("envp %p", (void *)envp); + + /* WARNING: C++ keyword */ + auto p = envp; + while (1) { + uintptr_t *buf; + read_proc(pid, (uintptr_t)p, &buf, sizeof(buf)); + + if (buf == NULL) break; + + /* TODO: Why ++p? */ + p++; + } + + /* TODO: Why ++p? */ + p++; + + ElfW(auxv_t) *auxv = (ElfW(auxv_t) *)p; + LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str()); + + ElfW(auxv_t) *v = auxv; + uintptr_t entry_addr = 0; + uintptr_t addr_of_entry_addr = 0; + + while (1) { + ElfW(auxv_t) buf; + + read_proc(pid, (uintptr_t)v, &buf, sizeof(buf)); + + if (buf.a_type == AT_ENTRY) { + entry_addr = (uintptr_t)buf.a_un.a_val; + addr_of_entry_addr = (uintptr_t)v + offsetof(ElfW(auxv_t), a_un); + + LOGV("entry address %" PRIxPTR " %s (entry=%" PRIxPTR ", entry_addr=%" PRIxPTR ")", entry_addr, + get_addr_mem_region(map, entry_addr).c_str(), (uintptr_t)v, addr_of_entry_addr); + + break; } - ++p; - auto auxv = reinterpret_cast(p); - LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str()); - auto v = auxv; - uintptr_t entry_addr = 0; - uintptr_t addr_of_entry_addr = 0; - while (true) { - ElfW(auxv_t) buf; - read_proc(pid, (uintptr_t) v, &buf, sizeof(buf)); - if (buf.a_type == AT_ENTRY) { - entry_addr = (uintptr_t) buf.a_un.a_val; - addr_of_entry_addr = (uintptr_t) v + offsetof(ElfW(auxv_t), a_un); - LOGV("entry address %" PRIxPTR " %s (entry=%" PRIxPTR ", entry_addr=%" PRIxPTR ")", entry_addr, - get_addr_mem_region(map, entry_addr).c_str(), (uintptr_t) v, addr_of_entry_addr); - break; - } - if (buf.a_type == AT_NULL) break; - v++; + + if (buf.a_type == AT_NULL) break; + + v++; + } + + if (entry_addr == 0) { + LOGE("failed to get entry"); + + return false; + } + + /* + Replace the program entry with an invalid address + For arm32 compatibility, we set the last bit to the same as the entry address + */ + + uintptr_t break_addr = (-0x05ec1cff & ~1) | ((uintptr_t)entry_addr & 1); + if (!write_proc(pid, (uintptr_t)addr_of_entry_addr, &break_addr, sizeof(break_addr))) return false; + + ptrace(PTRACE_CONT, pid, 0, 0); + + int status; + wait_for_trace(pid, &status, __WALL); + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) { + if (!get_regs(pid, regs)) return false; + + if (static_cast(regs.REG_IP & ~1) != (break_addr & ~1)) { + LOGE("stopped at unknown addr %p", (void *) regs.REG_IP); + + return false; } - if (entry_addr == 0) { - LOGE("failed to get entry"); + + /* The linker has been initialized now, we can do dlopen */ + LOGD("stopped at entry"); + + /* restore entry address */ + if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false; + + /* backup registers */ + memcpy(&backup, ®s, sizeof(regs)); + + /* WARNING: C++ keyword */ + map = MapInfo::Scan(std::to_string(pid)); + + /* WARNING: C++ keyword */ + std::vector local_map = MapInfo::Scan(); + void *libc_return_addr = find_module_return_addr(map, "libc.so"); + LOGD("libc return addr %p", libc_return_addr); + + /* call dlopen */ + void *dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen"); + if (dlopen_addr == NULL) return false; + + /* WARNING: C++ keyword */ + std::vector args; + + /* WARNING: C++ keyword */ + uintptr_t str = push_string(pid, regs, lib_path); + + args.clear(); + args.push_back((long) str); + args.push_back((long) RTLD_NOW); + + uintptr_t remote_handle = remote_call(pid, regs, (uintptr_t)dlopen_addr, (uintptr_t)libc_return_addr, args); + LOGD("remote handle %p", (void *)remote_handle); + if (remote_handle == 0) { + LOGE("handle is null"); + + /* call dlerror */ + void *dlerror_addr = find_func_addr(local_map, map, "libdl.so", "dlerror"); + if (dlerror_addr == NULL) { + LOGE("find dlerror"); + return false; + } + + args.clear(); + + uintptr_t dlerror_str_addr = remote_call(pid, regs, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args); + LOGD("dlerror str %p", (void*) dlerror_str_addr); + if (dlerror_str_addr == 0) return false; + + void *strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen"); + if (strlen_addr == NULL) { + LOGE("find strlen"); + + return false; + } + + args.clear(); + args.push_back(dlerror_str_addr); + + uintptr_t dlerror_len = remote_call(pid, regs, (uintptr_t)strlen_addr, (uintptr_t)libc_return_addr, args); + if (dlerror_len <= 0) { + LOGE("dlerror len <= 0"); + + return false; + } + + /* NOTICE: C++ -> C */ + char *err = (char *)malloc(dlerror_len + 1); + read_proc(pid, (uintptr_t) dlerror_str_addr, err, dlerror_len); + + LOGE("dlerror info %s", err); + + return false; } - // Replace the program entry with an invalid address - // For arm32 compatibility, we set the last bit to the same as the entry address - uintptr_t break_addr = (-0x05ec1cff & ~1) | ((uintptr_t) entry_addr & 1); - if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &break_addr, sizeof(break_addr))) return false; - ptrace(PTRACE_CONT, pid, 0, 0); - int status; - wait_for_trace(pid, &status, __WALL); - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) { - if (!get_regs(pid, regs)) return false; - if (static_cast(regs.REG_IP & ~1) != (break_addr & ~1)) { - LOGE("stopped at unknown addr %p", (void *) regs.REG_IP); - return false; - } - // The linker has been initialized now, we can do dlopen - LOGD("stopped at entry"); - - // restore entry address - if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false; - - // backup registers - memcpy(&backup, ®s, sizeof(regs)); - map = MapInfo::Scan(std::to_string(pid)); - auto local_map = MapInfo::Scan(); - auto libc_return_addr = find_module_return_addr(map, "libc.so"); - LOGD("libc return addr %p", libc_return_addr); - - // call dlopen - auto dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen"); - if (dlopen_addr == nullptr) return false; - std::vector args; - auto str = push_string(pid, regs, lib_path); - args.clear(); - args.push_back((long) str); - args.push_back((long) RTLD_NOW); - auto remote_handle = remote_call(pid, regs, (uintptr_t) dlopen_addr, (uintptr_t) libc_return_addr, args); - LOGD("remote handle %p", (void *) remote_handle); - if (remote_handle == 0) { - LOGE("handle is null"); - // call dlerror - auto dlerror_addr = find_func_addr(local_map, map, "libdl.so", "dlerror"); - if (dlerror_addr == nullptr) { - LOGE("find dlerror"); - return false; - } - args.clear(); - auto dlerror_str_addr = remote_call(pid, regs, (uintptr_t) dlerror_addr, (uintptr_t) libc_return_addr, args); - LOGD("dlerror str %p", (void*) dlerror_str_addr); - if (dlerror_str_addr == 0) return false; - auto strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen"); - if (strlen_addr == nullptr) { - LOGE("find strlen"); - return false; - } - args.clear(); - args.push_back(dlerror_str_addr); - auto dlerror_len = remote_call(pid, regs, (uintptr_t) strlen_addr, (uintptr_t) libc_return_addr, args); - if (dlerror_len <= 0) { - LOGE("dlerror len <= 0"); - return false; - } - std::string err; - err.resize(dlerror_len + 1, 0); - read_proc(pid, (uintptr_t) dlerror_str_addr, err.data(), dlerror_len); - LOGE("dlerror info %s", err.c_str()); - return false; - } - - // call dlsym(handle, "entry") - auto dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym"); - if (dlsym_addr == nullptr) return false; - args.clear(); - str = push_string(pid, regs, "entry"); - args.push_back(remote_handle); - args.push_back((long) str); - auto injector_entry = remote_call(pid, regs, (uintptr_t) dlsym_addr, (uintptr_t) libc_return_addr, args); - LOGD("injector entry %p", (void*) injector_entry); - if (injector_entry == 0) { - LOGE("injector entry is null"); - return false; - } - - // call injector entry(handle, path) - args.clear(); - args.push_back(remote_handle); - str = push_string(pid, regs, zygiskd::GetTmpPath().c_str()); - args.push_back((long) str); - remote_call(pid, regs, injector_entry, (uintptr_t) libc_return_addr, args); - - // reset pc to entry - backup.REG_IP = (long) entry_addr; - LOGD("invoke entry"); - // restore registers - if (!set_regs(pid, backup)) return false; - - return true; - } else { - char status_str[64]; - parse_status(status, status_str, sizeof(status_str)); + /* call dlsym(handle, "entry") */ + void *dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym"); + if (dlsym_addr == NULL) return false; - LOGE("stopped by other reason: %s", status_str); + args.clear(); + str = push_string(pid, regs, "entry"); + args.push_back(remote_handle); + args.push_back((long) str); + + uintptr_t injector_entry = remote_call(pid, regs, (uintptr_t)dlsym_addr, (uintptr_t)libc_return_addr, args); + LOGD("injector entry %p", (void *)injector_entry); + if (injector_entry == 0) { + LOGE("injector entry is null"); + + return false; } - return false; + + /* call injector entry(handle, path) */ + + args.clear(); + args.push_back(remote_handle); + str = push_string(pid, regs, zygiskd::GetTmpPath().c_str()); + args.push_back((long) str); + + remote_call(pid, regs, injector_entry, (uintptr_t)libc_return_addr, args); + + /* reset pc to entry */ + backup.REG_IP = (long) entry_addr; + LOGD("invoke entry"); + + /* restore registers */ + if (!set_regs(pid, backup)) return false; + + return true; + } else { + char status_str[64]; + parse_status(status, status_str, sizeof(status_str)); + + LOGE("stopped by other reason: %s", status_str); + } + + return false; } #define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event)) +#define WAIT_OR_DIE wait_for_trace(pid, &status, __WALL); +#define CONT_OR_DIE \ + if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \ + PLOGE("cont"); \ + \ + return false; \ + } bool trace_zygote(int pid) { - LOGI("start tracing %d", pid); -#define WAIT_OR_DIE wait_for_trace(pid, &status, __WALL); -#define CONT_OR_DIE \ - if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \ - PLOGE("cont"); \ - return false; \ + LOGI("start tracing %d (tracer %d)", pid, getpid()); + + int status; + + if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) { + PLOGE("seize"); + + return false; + } + + WAIT_OR_DIE + + if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) { + /* WARNING: C++ keyword */ + std::string lib_path = zygiskd::GetTmpPath(); + lib_path += "/lib" LP_SELECT("", "64") "/libzygisk.so"; + + if (!inject_on_main(pid, lib_path.c_str())) { + LOGE("failed to inject"); + + return false; } - int status; - LOGI("tracing %d (tracer %d)", pid, getpid()); - if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) { - PLOGE("seize"); - return false; + + LOGD("inject done, continue process"); + if (kill(pid, SIGCONT)) { + PLOGE("kill"); + + return false; } + + CONT_OR_DIE WAIT_OR_DIE - if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) { - std::string lib_path = zygiskd::GetTmpPath(); - lib_path += "/lib" LP_SELECT("", "64") "/libzygisk.so"; - if (!inject_on_main(pid, lib_path.c_str())) { - LOGE("failed to inject"); - return false; - } - LOGD("inject done, continue process"); - if (kill(pid, SIGCONT)) { - PLOGE("kill"); - return false; - } - CONT_OR_DIE - WAIT_OR_DIE - if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) { - CONT_OR_DIE - WAIT_OR_DIE - if (STOPPED_WITH(SIGCONT, 0)) { - LOGD("received SIGCONT"); - ptrace(PTRACE_DETACH, pid, 0, SIGCONT); - } - } else { - char status_str[64]; - parse_status(status, status_str, sizeof(status_str)); - - LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", status_str); - ptrace(PTRACE_DETACH, pid, 0, 0); - return false; - } + + if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) { + CONT_OR_DIE + WAIT_OR_DIE + + if (STOPPED_WITH(SIGCONT, 0)) { + LOGD("received SIGCONT"); + + ptrace(PTRACE_DETACH, pid, 0, SIGCONT); + } } else { - char status_str[64]; - parse_status(status, status_str, sizeof(status_str)); + char status_str[64]; + parse_status(status, status_str, sizeof(status_str)); - LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", status_str); - ptrace(PTRACE_DETACH, pid, 0, 0); - return false; + LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", status_str); + + ptrace(PTRACE_DETACH, pid, 0, 0); + + return false; } - return true; + } else { + char status_str[64]; + parse_status(status, status_str, sizeof(status_str)); + + LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", status_str); + + ptrace(PTRACE_DETACH, pid, 0, 0); + + return false; + } + + return true; } diff --git a/loader/src/ptracer/utils.cpp b/loader/src/ptracer/utils.cpp index 072fb8d3..70d327d0 100644 --- a/loader/src/ptracer/utils.cpp +++ b/loader/src/ptracer/utils.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -22,400 +24,481 @@ #include #include -#include "utils.hpp" -#include "logging.h" #include #include +#include "utils.hpp" +#include "logging.h" + bool switch_mnt_ns(int pid, int *fd) { - int nsfd, old_nsfd = -1; - std::string path; - if (pid == 0) { - if (fd != nullptr) { - nsfd = *fd; - *fd = -1; - } else return false; - path = "/proc/self/fd/"; - path += std::to_string(nsfd); - } else { - if (fd != nullptr) { - old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC); - if (old_nsfd == -1) { - PLOGE("get old nsfd"); - return false; - } - *fd = old_nsfd; - } - path = std::string("/proc/") + std::to_string(pid) + "/ns/mnt"; - nsfd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (nsfd == -1) { - PLOGE("open nsfd %s", path.c_str()); - close(old_nsfd); - return false; - } - } - if (setns(nsfd, CLONE_NEWNS) == -1) { - PLOGE("set ns to %s", path.c_str()); - close(nsfd); - close(old_nsfd); + int nsfd, old_nsfd = -1; + + /* WARNING: C++ keyword */ + char path[PATH_MAX]; + if (pid == 0) { + if (fd != NULL) { + nsfd = *fd; + *fd = -1; + } else return false; + + snprintf(path, sizeof(path), "/proc/self/fd/%d", nsfd); + } else { + if (fd != NULL) { + old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC); + if (old_nsfd == -1) { + PLOGE("get old nsfd"); + return false; + } + + *fd = old_nsfd; + } + + snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid); + + nsfd = open(path, O_RDONLY | O_CLOEXEC); + if (nsfd == -1) { + PLOGE("open nsfd %s", path); + + close(old_nsfd); + + return false; } + } + + if (setns(nsfd, CLONE_NEWNS) == -1) { + PLOGE("set ns to %s", path); + close(nsfd); - return true; + close(old_nsfd); + + return false; + } + + close(nsfd); + + return true; } -std::vector MapInfo::Scan(const std::string& pid) { - constexpr static auto kPermLength = 5; - constexpr static auto kMapEntry = 7; - std::vector info; - std::string file_name = std::string("/proc/") + pid + "/maps"; - auto maps = std::unique_ptr{fopen(file_name.c_str(), "r"), &fclose}; - if (maps) { - char *line = nullptr; - size_t len = 0; - ssize_t read; - while ((read = getline(&line, &len, maps.get())) > 0) { - line[read - 1] = '\0'; - uintptr_t start = 0; - uintptr_t end = 0; - uintptr_t off = 0; - ino_t inode = 0; - unsigned int dev_major = 0; - unsigned int dev_minor = 0; - std::array perm{'\0'}; - int path_off; - if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start, - &end, perm.data(), &off, &dev_major, &dev_minor, &inode, - &path_off) != kMapEntry) { - continue; - } - while (path_off < read && isspace(line[path_off])) path_off++; - auto &ref = info.emplace_back(MapInfo{start, end, 0, perm[3] == 'p', off, - static_cast(makedev(dev_major, dev_minor)), - inode, line + path_off}); - if (perm[0] == 'r') ref.perms |= PROT_READ; - if (perm[1] == 'w') ref.perms |= PROT_WRITE; - if (perm[2] == 'x') ref.perms |= PROT_EXEC; - } - free(line); +/* WARNING: C++ keyword */ +std::vector MapInfo::Scan(const std::string &pid) { + constexpr static auto kPermLength = 5; + constexpr static auto kMapEntry = 7; + + /* WARNING: C++ keyword */ + std::vector info; + char file_name[NAME_MAX]; + snprintf(file_name, sizeof(file_name), "/proc/%s/maps", pid.c_str()); + + /* WARNING: C++ keyword */ + auto maps = std::unique_ptr{fopen(file_name, "r"), &fclose}; + if (maps) { + char *line = NULL; + size_t len = 0; + ssize_t read; + + /* WARNING: C++ keyword */ + while ((read = getline(&line, &len, maps.get())) > 0) { + line[read - 1] = '\0'; + + uintptr_t start = 0; + uintptr_t end = 0; + uintptr_t off = 0; + ino_t inode = 0; + unsigned int dev_major = 0; + unsigned int dev_minor = 0; + + /* WARNING: C++ keyword */ + std::array perm {'\0'}; + + int path_off; + if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start, + &end, perm.data(), &off, &dev_major, &dev_minor, &inode, + &path_off) != kMapEntry) continue; + + while (path_off < read && isspace(line[path_off])) path_off++; + + /* WARNING: C++ keyword */ + MapInfo &ref = info.emplace_back(MapInfo{ + start, + end, + 0, + perm[3] == 'p', + off, + static_cast(makedev(dev_major, dev_minor)), + inode, + line + path_off + }); + + if (perm[0] == 'r') ref.perms |= PROT_READ; + if (perm[1] == 'w') ref.perms |= PROT_WRITE; + if (perm[2] == 'x') ref.perms |= PROT_EXEC; } - return info; + + free(line); + } + + return info; } ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len) { - LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len); - struct iovec local{ - .iov_base = (void *) buf, - .iov_len = len - }; - struct iovec remote{ - .iov_base = (void *) remote_addr, - .iov_len = len - }; - auto l = process_vm_writev(pid, &local, 1, &remote, 1, 0); - if (l == -1) { - PLOGE("process_vm_writev"); - } else if (static_cast(l) != len) { - LOGW("not fully written: %zu, excepted %zu", l, len); - } - return l; + LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len); + + struct iovec local = { + .iov_base = (void *)buf, + .iov_len = len + }; + + struct iovec remote = { + .iov_base = (void *)remote_addr, + .iov_len = len + }; + + ssize_t l = process_vm_writev(pid, &local, 1, &remote, 1, 0); + if (l == -1) PLOGE("process_vm_writev"); + else if ((size_t)l != len) LOGW("not fully written: %zu, excepted %zu", l, len); + + return l; } ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len) { - struct iovec local{ - .iov_base = (void *) buf, - .iov_len = len - }; - struct iovec remote{ - .iov_base = (void *) remote_addr, - .iov_len = len - }; - auto l = process_vm_readv(pid, &local, 1, &remote, 1, 0); - if (l == -1) { - PLOGE("process_vm_readv"); - } else if (static_cast(l) != len) { - LOGW("not fully read: %zu, excepted %zu", l, len); - } - return l; + struct iovec local = { + .iov_base = (void *)buf, + .iov_len = len + }; + + struct iovec remote = { + .iov_base = (void *)remote_addr, + .iov_len = len + }; + + ssize_t l = process_vm_readv(pid, &local, 1, &remote, 1, 0); + if (l == -1) PLOGE("process_vm_readv"); + else if ((size_t)l != len) LOGW("not fully read: %zu, excepted %zu", l, len); + + return l; } bool get_regs(int pid, struct user_regs_struct ®s) { -#if defined(__x86_64__) || defined(__i386__) + #if defined(__x86_64__) || defined(__i386__) if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) { - PLOGE("getregs"); - return false; + PLOGE("getregs"); + + return false; } -#elif defined(__aarch64__) || defined(__arm__) + #elif defined(__aarch64__) || defined(__arm__) struct iovec iov = { - .iov_base = ®s, - .iov_len = sizeof(struct user_regs_struct), + .iov_base = ®s, + .iov_len = sizeof(struct user_regs_struct), }; + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) { - PLOGE("getregs"); - return false; + PLOGE("getregs"); + + return false; } -#endif - return true; + #endif + + return true; } bool set_regs(int pid, struct user_regs_struct ®s) { -#if defined(__x86_64__) || defined(__i386__) + #if defined(__x86_64__) || defined(__i386__) if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) { - PLOGE("setregs"); - return false; + PLOGE("setregs"); + + return false; } -#elif defined(__aarch64__) || defined(__arm__) + #elif defined(__aarch64__) || defined(__arm__) struct iovec iov = { - .iov_base = ®s, - .iov_len = sizeof(struct user_regs_struct), + .iov_base = ®s, + .iov_len = sizeof(struct user_regs_struct), }; + if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) { - PLOGE("setregs"); - return false; + PLOGE("setregs"); + + return false; } -#endif - return true; + #endif + + return true; } +/* WARNING: C++ keyword */ std::string get_addr_mem_region(std::vector &info, uintptr_t addr) { - for (auto &map: info) { - if (map.start <= addr && map.end > addr) { - auto s = std::string(map.path); - s += ' '; - s += map.perms & PROT_READ ? 'r' : '-'; - s += map.perms & PROT_WRITE ? 'w' : '-'; - s += map.perms & PROT_EXEC ? 'x' : '-'; - return s; - } + /* WARNING: C++ keyword */ + for (auto &map: info) { + if (map.start <= addr && map.end > addr) { + /* WARNING: C++ keyword */ + auto s = std::string(map.path); + + s += ' '; + s += map.perms & PROT_READ ? 'r' : '-'; + s += map.perms & PROT_WRITE ? 'w' : '-'; + s += map.perms & PROT_EXEC ? 'x' : '-'; + + return s; } - return ""; -} + } + return ""; +} +/* WARNING: C++ keyword */ void *find_module_return_addr(std::vector &info, std::string_view suffix) { - for (auto &map: info) { - if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) { - return (void *) map.start; - } - } - return nullptr; + /* WARNING: C++ keyword */ + for (auto &map: info) { + /* WARNING: C++ keyword */ + if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) return (void *)map.start; + } + + return NULL; } +/* WARNING: C++ keyword */ void *find_module_base(std::vector &info, std::string_view suffix) { - for (auto &map: info) { - if (map.offset == 0 && map.path.ends_with(suffix)) { - return (void *) map.start; - } - } - return nullptr; + /* WARNING: C++ keyword */ + for (auto &map: info) { + /* WARNING: C++ keyword */ + if (map.offset == 0 && map.path.ends_with(suffix)) return (void *)map.start; + } + + return NULL; } -void *find_func_addr( - std::vector &local_info, - std::vector &remote_info, - std::string_view module, - std::string_view func) { - auto lib = dlopen(module.data(), RTLD_NOW); - if (lib == nullptr) { - LOGE("failed to open lib %s: %s", module.data(), dlerror()); - return nullptr; - } - auto sym = reinterpret_cast(dlsym(lib, func.data())); - if (sym == nullptr) { - LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror()); - dlclose(lib); - return nullptr; - } - LOGD("sym %s: %p", func.data(), sym); +/* WARNING: C++ keyword */ +void *find_func_addr(std::vector &local_info, std::vector &remote_info, std::string_view module, std::string_view func) { + void *lib = dlopen(module.data(), RTLD_NOW); + if (lib == NULL) { + LOGE("failed to open lib %s: %s", module.data(), dlerror()); + + return NULL; + } + + uint8_t *sym = (uint8_t *)dlsym(lib, func.data()); + if (sym == NULL) { + LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror()); + dlclose(lib); - auto local_base = reinterpret_cast(find_module_base(local_info, module)); - if (local_base == nullptr) { - LOGE("failed to find local base for module %s", module.data()); - return nullptr; - } - auto remote_base = reinterpret_cast(find_module_base(remote_info, module)); - if (remote_base == nullptr) { - LOGE("failed to find remote base for module %s", module.data()); - return nullptr; - } - LOGD("found local base %p remote base %p", local_base, remote_base); - auto addr = (sym - local_base) + remote_base; - LOGD("addr %p", addr); - return addr; + + return NULL; + } + + LOGD("sym %s: %p", func.data(), sym); + + dlclose(lib); + + uint8_t *local_base = (uint8_t *)find_module_base(local_info, module); + if (local_base == NULL) { + LOGE("failed to find local base for module %s", module.data()); + + return NULL; + } + + uint8_t *remote_base = (uint8_t *)find_module_base(remote_info, module); + if (remote_base == NULL) { + LOGE("failed to find remote base for module %s", module.data()); + + return NULL; + } + + LOGD("found local base %p remote base %p", local_base, remote_base); + + uint8_t *addr = (sym - local_base) + remote_base; + LOGD("addr %p", addr); + + return addr; } +/* WARNING: C++ keyword */ void align_stack(struct user_regs_struct ®s, long preserve) { - regs.REG_SP = (regs.REG_SP - preserve) & ~0xf; + regs.REG_SP = (regs.REG_SP - preserve) & ~0xf; } +/* WARNING: C++ keyword */ uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str) { - auto len = strlen(str) + 1; - regs.REG_SP -= len; - align_stack(regs); - auto addr = static_cast(regs.REG_SP); - if (!write_proc(pid, addr, str, len)) { - LOGE("failed to write string %s", str); - } - LOGD("pushed string %" PRIxPTR, addr); - return addr; + size_t len = strlen(str) + 1; + + regs.REG_SP -= len; + + align_stack(regs); + + uintptr_t addr = (uintptr_t)regs.REG_SP; + if (!write_proc(pid, addr, str, len)) LOGE("failed to write string %s", str); + + LOGD("pushed string %" PRIxPTR, addr); + + return addr; } -uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr, - std::vector &args) { - align_stack(regs); - LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size()); - for (auto &a: args) { - LOGV("arg %p", (void *) a); - } -#if defined(__x86_64__) - if (args.size() >= 1) { - regs.rdi = args[0]; - } - if (args.size() >= 2) { - regs.rsi = args[1]; - } - if (args.size() >= 3) { - regs.rdx = args[2]; - } - if (args.size() >= 4) { - regs.rcx = args[3]; - } - if (args.size() >= 5) { - regs.r8 = args[4]; - } - if (args.size() >= 6) { - regs.r9 = args[5]; - } +/* WARNING: C++ keyword */ +uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr, std::vector &args) { + align_stack(regs); + + /* WARNING: C++ keyword */ + LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size()); + + /* WARNING: C++ keyword */ + for (auto &a: args) { + LOGV("arg %p", (void *) a); + } + + #if defined(__x86_64__) + if (args.size() >= 1) regs.rdi = args[0]; + if (args.size() >= 2) regs.rsi = args[1]; + if (args.size() >= 3) regs.rdx = args[2]; + if (args.size() >= 4) regs.rcx = args[3]; + if (args.size() >= 5) regs.r8 = args[4]; + if (args.size() >= 6) regs.r9 = args[5]; if (args.size() > 6) { - auto remain = (args.size() - 6) * sizeof(long); - align_stack(regs, remain); - if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) { - LOGE("failed to push arguments"); - } + long remain = (args.size() - 6L) * sizeof(long); + align_stack(regs, remain); + + if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments"); } + regs.REG_SP -= sizeof(long); - if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) { - LOGE("failed to write return addr"); - } + + if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr"); + regs.REG_IP = func_addr; -#elif defined(__i386__) + #elif defined(__i386__) if (args.size() > 0) { - auto remain = (args.size()) * sizeof(long); - align_stack(regs, remain); - if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) { - LOGE("failed to push arguments"); - } + long remain = (args.size()) * sizeof(long); + align_stack(regs, remain); + + if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments"); } + regs.REG_SP -= sizeof(long); - if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) { - LOGE("failed to write return addr"); - } + + if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr"); + regs.REG_IP = func_addr; -#elif defined(__aarch64__) + #elif defined(__aarch64__) for (size_t i = 0; i < args.size() && i < 8; i++) { - regs.regs[i] = args[i]; + regs.regs[i] = args[i]; } + if (args.size() > 8) { - auto remain = (args.size() - 8) * sizeof(long); - align_stack(regs, remain); - write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain); + long remain = (args.size() - 8) * sizeof(long); + align_stack(regs, remain); + + write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain); } + regs.regs[30] = return_addr; regs.REG_IP = func_addr; -#elif defined(__arm__) + #elif defined(__arm__) for (size_t i = 0; i < args.size() && i < 4; i++) { - regs.uregs[i] = args[i]; + regs.uregs[i] = args[i]; } + if (args.size() > 4) { - auto remain = (args.size() - 4) * sizeof(long); - align_stack(regs, remain); - write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain); + long remain = (args.size() - 4) * sizeof(long); + align_stack(regs, remain); + + write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain); } + regs.uregs[14] = return_addr; regs.REG_IP = func_addr; + constexpr auto CPSR_T_MASK = 1lu << 5; + if ((regs.REG_IP & 1) != 0) { - regs.REG_IP = regs.REG_IP & ~1; - regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK; + regs.REG_IP = regs.REG_IP & ~1; + regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK; } else { - regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK; - } -#endif - if (!set_regs(pid, regs)) { - LOGE("failed to set regs"); - return 0; + regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK; } - ptrace(PTRACE_CONT, pid, 0, 0); - int status; - wait_for_trace(pid, &status, __WALL); - if (!get_regs(pid, regs)) { - LOGE("failed to get regs after call"); - return 0; - } - if (WSTOPSIG(status) == SIGSEGV) { - if (static_cast(regs.REG_IP) != return_addr) { - LOGE("wrong return addr %p", (void *) regs.REG_IP); - return 0; - } - return regs.REG_RET; - } else { - char status_str[64]; - parse_status(status, status_str, sizeof(status_str)); + #endif + + if (!set_regs(pid, regs)) { + LOGE("failed to set regs"); - LOGE("stopped by other reason %s at addr %p", status_str, (void*) regs.REG_IP); - } return 0; + } + + ptrace(PTRACE_CONT, pid, 0, 0); + + int status; + wait_for_trace(pid, &status, __WALL); + if (!get_regs(pid, regs)) { + LOGE("failed to get regs after call"); + + return 0; + } + + if (WSTOPSIG(status) == SIGSEGV) { + if ((uintptr_t)regs.REG_IP != return_addr) { + LOGE("wrong return addr %p", (void *) regs.REG_IP); + + return 0; + } + + return regs.REG_RET; + } else { + char status_str[64]; + parse_status(status, status_str, sizeof(status_str)); + + LOGE("stopped by other reason %s at addr %p", status_str, (void *)regs.REG_IP); + } + + return 0; } int fork_dont_care() { - auto pid = fork(); - if (pid < 0) { - PLOGE("fork 1"); - } else if (pid == 0) { - pid = fork(); - if (pid < 0) { - PLOGE("fork 2"); - } else if (pid > 0) { - exit(0); - } - } else { - int status; - waitpid(pid, &status, __WALL); - } - return pid; + pid_t pid = fork(); + + if (pid < 0) PLOGE("fork 1"); + else if (pid == 0) { + pid = fork(); + if (pid < 0) PLOGE("fork 2"); + else if (pid > 0) exit(0); + } else { + int status; + waitpid(pid, &status, __WALL); + } + + return pid; } -void wait_for_trace(int pid, int* status, int flags) { - while (true) { - auto result = waitpid(pid, status, flags); - if (result == -1) { - if (errno == EINTR) { - continue; - } else { - PLOGE("wait %d failed", pid); - exit(1); - } - } - if (!WIFSTOPPED(*status)) { - char status_str[64]; - parse_status(*status, status_str, sizeof(status_str)); - - LOGE("process %d not stopped for trace: %s, exit", pid, status_str); - exit(1); - } - return; +void wait_for_trace(int pid, int *status, int flags) { + while (1) { + pid_t result = waitpid(pid, status, flags); + if (result == -1) { + if (errno == EINTR) continue; + + PLOGE("wait %d failed", pid); + exit(1); + } + + if (!WIFSTOPPED(*status)) { + char status_str[64]; + parse_status(*status, status_str, sizeof(status_str)); + + LOGE("process %d not stopped for trace: %s, exit", pid, status_str); + + exit(1); } + + return; + } } void parse_status(int status, char *buf, size_t len) { snprintf(buf, len, "0x%x ", status); + if (WIFEXITED(status)) { snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status)); } else if (WIFSTOPPED(status)) { snprintf(buf + strlen(buf), len - strlen(buf), "stopped by "); - auto stop_sig = WSTOPSIG(status); + + int stop_sig = WSTOPSIG(status); snprintf(buf + strlen(buf), len - strlen(buf), "signal=%s(%d),", sigabbrev_np(stop_sig), stop_sig); snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status)); } else { @@ -424,7 +507,7 @@ void parse_status(int status, char *buf, size_t len) { } int get_program(int pid, char *buf, size_t size) { - char path[64]; + char path[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/exe", pid); ssize_t sz = readlink(path, buf, size); diff --git a/loader/src/ptracer/utils.hpp b/loader/src/ptracer/utils.hpp index 79652f4d..83858b39 100644 --- a/loader/src/ptracer/utils.hpp +++ b/loader/src/ptracer/utils.hpp @@ -10,6 +10,7 @@ #else #define LOG_TAG "zygisk-ptrace32" #endif + #include "logging.h" struct MapInfo { diff --git a/module/build.gradle.kts b/module/build.gradle.kts index ea172a34..e9d788d1 100644 --- a/module/build.gradle.kts +++ b/module/build.gradle.kts @@ -52,7 +52,7 @@ androidComponents.onVariants { variant -> into(moduleDir) from("${rootProject.projectDir}/README.md") from("$projectDir/src") { - exclude("module.prop", "customize.sh", "post-fs-data.sh", "service.sh", "zygisk-ctl.sh", "mazoku") + exclude("module.prop", "customize.sh", "post-fs-data.sh", "service.sh", "mazoku") filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) } from("$projectDir/src") { @@ -66,7 +66,7 @@ androidComponents.onVariants { variant -> } from("$projectDir/src/mazoku") from("$projectDir/src") { - include("customize.sh", "post-fs-data.sh", "service.sh", "zygisk-ctl.sh") + include("customize.sh", "post-fs-data.sh", "service.sh") val tokens = mapOf( "DEBUG" to if (buildTypeLowered == "debug") "true" else "false", "MIN_KSU_VERSION" to "$minKsuVersion", @@ -158,12 +158,6 @@ androidComponents.onVariants { variant -> root.file("bin/$abi64/zygiskd").asFile ) ) - set.add( - Pair( - root.file("bin/zygisk-ctl").asFile, - root.file("zygisk-ctl.sh").asFile - ) - ) sig.initSign(privKey) set.forEach { it.first.sha(it.second) } val signFile = root.file(name).asFile diff --git a/module/src/customize.sh b/module/src/customize.sh index cd7348aa..6b8ddffb 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -94,14 +94,12 @@ ui_print "- Extracting module files" extract "$ZIPFILE" 'module.prop' "$MODPATH" extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH" extract "$ZIPFILE" 'service.sh' "$MODPATH" -extract "$ZIPFILE" 'zygisk-ctl.sh' "$MODPATH" extract "$ZIPFILE" 'mazoku' "$MODPATH" mv "$TMPDIR/sepolicy.rule" "$MODPATH" mkdir "$MODPATH/bin" mkdir "$MODPATH/lib" mkdir "$MODPATH/lib64" -mv "$MODPATH/zygisk-ctl.sh" "$MODPATH/bin/zygisk-ctl" if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then ui_print "- Extracting x86 libraries" diff --git a/module/src/zygisk-ctl.sh b/module/src/zygisk-ctl.sh deleted file mode 100644 index 861adce0..00000000 --- a/module/src/zygisk-ctl.sh +++ /dev/null @@ -1,6 +0,0 @@ -MODDIR=${0%/*}/.. - -export TMP_PATH=/sbin -[ -d /sbin ] || export TMP_PATH=/debug_ramdisk - -exec $MODDIR/bin/zygisk-ptrace64 ctl $* diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index 747584a9..b5d209d3 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -27,6 +27,7 @@ pub enum DaemonSocketAction { PingHeartbeat, RequestLogcatFd, GetProcessFlags, + GetInfo, ReadModules, RequestCompanionSocket, GetModuleDir, diff --git a/zygiskd/src/zygiskd.rs b/zygiskd/src/zygiskd.rs index 61db9e80..96cf2c0a 100644 --- a/zygiskd/src/zygiskd.rs +++ b/zygiskd/src/zygiskd.rs @@ -268,6 +268,20 @@ fn handle_daemon_action( ); stream.write_u32(flags.bits())?; } + DaemonSocketAction::GetInfo => { + let mut flags = ProcessFlags::empty(); + + match root_impl::get_impl() { + root_impl::RootImpl::KernelSU => flags |= ProcessFlags::PROCESS_ROOT_IS_KSU, + root_impl::RootImpl::Magisk => flags |= ProcessFlags::PROCESS_ROOT_IS_MAGISK, + _ => panic!("wrong root impl: {:?}", root_impl::get_impl()), + } + + stream.write_u32(flags.bits())?; + + let pid = unsafe { libc::getpid() }; + stream.write_u32(pid as u32)?; + } DaemonSocketAction::ReadModules => { stream.write_usize(context.modules.len())?; for module in context.modules.iter() {