From 040643337dc469e0326ae054d6b8b36649148d0d Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sun, 12 Nov 2023 20:34:45 +0800 Subject: [PATCH] init monitor --- loader/src/CMakeLists.txt | 6 +- loader/src/injector/hook.cpp | 2 - loader/src/ptracer/main.cpp | 55 +---- loader/src/ptracer/main.hpp | 3 +- loader/src/ptracer/monitor.cpp | 385 +++++++++++++++++++++++++++------ loader/src/ptracer/ptracer.cpp | 40 +--- loader/src/ptracer/utils.cpp | 56 ++--- loader/src/ptracer/utils.hpp | 8 +- module/src/customize.sh | 18 +- module/src/post-fs-data.sh | 5 +- module/src/sepolicy.rule | 2 - module/src/service.sh | 11 +- zygiskd/src/main.rs | 4 +- zygiskd/src/watchdog.rs | 236 -------------------- 14 files changed, 389 insertions(+), 442 deletions(-) delete mode 100644 zygiskd/src/watchdog.rs diff --git a/loader/src/CMakeLists.txt b/loader/src/CMakeLists.txt index 00a0da1..b4f649b 100644 --- a/loader/src/CMakeLists.txt +++ b/loader/src/CMakeLists.txt @@ -14,8 +14,8 @@ target_include_directories(zygisk PRIVATE include) target_link_libraries(zygisk cxx::cxx log common lsplt_static phmap) aux_source_directory(ptracer PTRACER_SRC_LIST) -add_executable(libptracer.so ${PTRACER_SRC_LIST}) -target_include_directories(libptracer.so PRIVATE include) -target_link_libraries(libptracer.so cxx::cxx log common) +add_executable(libzygisk_ptrace.so ${PTRACER_SRC_LIST}) +target_include_directories(libzygisk_ptrace.so PRIVATE include) +target_link_libraries(libzygisk_ptrace.so cxx::cxx log common) add_subdirectory(external) diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index c3636e6..065ffd5 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -747,8 +747,6 @@ void hook_functions() { std::remove_if(plt_hook_list->begin(), plt_hook_list->end(), [](auto &t) { return *std::get<3>(t) == nullptr;}), plt_hook_list->end()); - - initialize_jni_hook(); } static void hook_unloader() { diff --git a/loader/src/ptracer/main.cpp b/loader/src/ptracer/main.cpp index 99bd1c3..4107932 100644 --- a/loader/src/ptracer/main.cpp +++ b/loader/src/ptracer/main.cpp @@ -13,50 +13,17 @@ using namespace std::string_view_literals; int main(int argc, char **argv) { - if (access("/system/lib" LP_SELECT("", "64"), R_OK) != 0) return 1; - auto lock_fd = open("/dev/zygisk/lock" LP_SELECT("32", "64"), O_CLOEXEC | O_CREAT, O_RDONLY); - if (lock_fd == -1) { - PLOGE("create lock file"); + if (argc >= 2 && argv[1] == "monitor"sv) { + init_monitor(); + return 0; + } else if (argc >= 3 && argv[1] == "trace"sv) { + auto pid = strtol(argv[2], 0, 0); + return !trace_zygote(pid); + } else if (argc >= 3 && argv[1] == "ctl"sv) { + // TODO + return 1; + } else { + LOGE("usage: %s monitor | trace | ctl ", argv[0]); return 1; } - struct flock lock{ - .l_type = F_RDLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0 - }; - if (fcntl(lock_fd, F_SETLK, &lock) == -1) { - PLOGE("set file lock"); - close(lock_fd); - return 1; - } - LOGI("zygote monitor started"); - struct timespec last_launch_time { .tv_sec = 0, .tv_nsec = 0 }, ts; - int launch_count = 0; - bool first = true; - while (true) { - auto pid = wait_for_zygote(); - if (pid == -1) break; - LOGI("inject zygote %d", pid); - if (first) first = false; - else { - LOGI("notify zygisk companion restart"); - zygiskd::ZygoteRestart(); - } - clock_gettime(CLOCK_MONOTONIC, &ts); - auto delta = ts.tv_sec - last_launch_time.tv_sec; - if (delta > 30) launch_count++; - else launch_count = 0; - if (launch_count >= 5) { - LOGE("zygote crash too much times, stop"); - break; - } - memcpy(&last_launch_time, &ts, sizeof(struct timespec)); - if (!trace_zygote(pid)) { - break; - } - } - __system_property_set("ctl.sigstop_off", "zygote"); - __system_property_set("ctl.sigstop_off", "zygote_secondary"); - return 1; } diff --git a/loader/src/ptracer/main.hpp b/loader/src/ptracer/main.hpp index fa3dd33..61bd36a 100644 --- a/loader/src/ptracer/main.hpp +++ b/loader/src/ptracer/main.hpp @@ -1,5 +1,4 @@ #pragma once -int wait_for_zygote(); +void init_monitor(); bool trace_zygote(int pid); -int find_zygote(); diff --git a/loader/src/ptracer/monitor.cpp b/loader/src/ptracer/monitor.cpp index 2fd28cd..0510d68 100644 --- a/loader/src/ptracer/monitor.cpp +++ b/loader/src/ptracer/monitor.cpp @@ -1,6 +1,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "main.hpp" #include "utils.hpp" @@ -9,78 +20,330 @@ using namespace std::string_view_literals; -int find_zygote() { - LOGD("find zygote"); - auto sockets = ScanUnixSockets(); - auto dir = xopen_dir("/proc"); - for (dirent *entry; (entry = readdir(dir.get()));) { - auto pid = parse_int(entry->d_name); - char comm[18]; - char state; - if (pid == -1 || pid == 1) continue; - auto stat_file = xopen_file((std::string("/proc/") + std::to_string(pid) + "/stat").c_str(), "r"); - if (stat_file == nullptr) continue; - if (fscanf(stat_file.get(), "%*d %17s %c", comm, &state) != 2 - || comm != "(init)"sv - || state != 'T') { - continue; + +#define STOPPED_WITH(sig, event) WIFSTOPPED(status) && (status >> 8 == ((sig) | (event << 8))) + +enum Command { + START = 1, + STOP, + EXIT +}; + +enum TracingState { + TRACING = 1, + STOPPING, + STOPPED +}; + +constexpr char SOCKET_NAME[] = "init_monitor"; + +struct EventLoop; + +struct EventHandler { + virtual int GetFd() = 0; + virtual void HandleEvent(EventLoop& loop, uint32_t event) = 0; +}; + +struct EventLoop { +private: + int epoll_fd_; + bool running = false; +public: + bool Init() { + epoll_fd_ = epoll_create(1); + if (epoll_fd_ == -1) { + PLOGE("failed to create"); + return false; } - LOGD("%d is stopped init", pid); - auto fd_dir = xopen_dir((std::string("/proc/") + std::to_string(pid) + "/fd").c_str()); - if (fd_dir == nullptr) continue; - for (dirent *fd_entry; (fd_entry = readdir(fd_dir.get()));) { - if (fd_entry->d_name == "."sv || fd_entry->d_name == ".."sv) continue; - struct stat st{}; - if (stat((std::string("/proc/") + std::to_string(pid) + "/fd/" + fd_entry->d_name).c_str(), &st) == -1) { - PLOGE("stat /proc/%d/fd/%s", pid, fd_entry->d_name); + return true; + } + + void Stop() { + 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) { + PLOGE("epoll_wait"); continue; } - if ((st.st_mode & S_IFSOCK) == 0) continue; - auto it = sockets.find(st.st_ino); - if (it != sockets.end() && it->second == LP_SELECT("/dev/socket/zygote_secondary", "/dev/socket/zygote")) { - LOGD("%d is zygote", pid); - return pid; + for (int i = 0; i < nfds; i++) { + reinterpret_cast(events[i].data.ptr)->HandleEvent(*this, + events[i].events); + if (!running) break; } } } - return -1; -} -int wait_for_zygote() { - auto name = "init.svc." LP_SELECT("zygote_secondary", "zygote"); - auto prop = __system_property_find(name); - if (prop == nullptr) { - __system_property_set(name, "stopped"); - prop = __system_property_find(name); - if (prop == nullptr) { - LOGE("failed to create prop"); - exit(1); + 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; + } + + bool UnregisterHandler(EventHandler &handler) { + if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), nullptr) == -1) { + PLOGE("failed to del event handler"); + return false; + } + return true; + } + + ~EventLoop() { + if (epoll_fd_ >= 0) close(epoll_fd_); + } +}; + +static TracingState tracing_state = TRACING; +static bool exit_requested = false; + +struct SocketHandler : public EventHandler { + int sock_fd_; + + bool Init() { + sock_fd_ = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (sock_fd_ == -1) { + PLOGE("socket create"); + return false; + } + struct sockaddr_un addr{ + .sun_family = AF_UNIX, + .sun_path={0}, + }; + strcpy(addr.sun_path + 1, SOCKET_NAME); + socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1; + if (bind(sock_fd_, (struct sockaddr *) &addr, socklen) == -1) { + PLOGE("bind socket"); + return false; + } + return true; + } + + int GetFd() override { + return sock_fd_; + } + + void HandleEvent(EventLoop &loop, uint32_t event) override { + Command cmd; + for (;;) { + auto nread = read(sock_fd_, &cmd, sizeof(cmd)); + if (nread == -1) { + if (errno == EAGAIN) { + break; + } + PLOGE("read socket"); + } + if (nread != sizeof(cmd)) { + LOGE("read %zu != %zu", nread, sizeof(cmd)); + continue; + } + switch (cmd) { + case START: + if (exit_requested) break; + if (tracing_state == STOPPING) { + tracing_state = TRACING; + } else if (tracing_state == STOPPED) { + ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); + LOGI("start tracing init"); + tracing_state = TRACING; + } + break; + case STOP: + if (exit_requested) break; + if (tracing_state == TRACING) { + LOGI("stop tracing requested"); + tracing_state = STOPPING; + ptrace(PTRACE_INTERRUPT, 1, 0, 0); + } + break; + case EXIT: + LOGI("prepare for exit ..."); + exit_requested = true; + if (tracing_state == TRACING) { + LOGI("stop tracing requested"); + tracing_state = STOPPING; + ptrace(PTRACE_INTERRUPT, 1, 0, 0); + } + break; + } } } - std::string last_state = "running"; - char val[PROP_VALUE_MAX]; - uint32_t new_serial = 0; - while (true) { - __system_property_wait(prop, new_serial, &new_serial, nullptr); - __system_property_get(name, val); - LOGD("%s(%u): %s\n", name, new_serial, val); - if (val != last_state && val == "running"sv) { - LOGI("zygote is running, find zygote"); - int pid = -1; - for (int i = 0; i < 5; i++) { - pid = find_zygote(); - if (pid != -1) break; - else { - LOGW("could not find zygote, wait 1s"); - sleep(1); + + ~SocketHandler() { + if (sock_fd_ >= 0) close(sock_fd_); + } +}; + +struct PtraceHandler : public EventHandler { +private: + int signal_fd_; + struct signalfd_siginfo fdsi; + int status; + std::set process; +public: + bool Init() { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) { + PLOGE("set sigprocmask"); + return false; + } + signal_fd_ = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (signal_fd_ == -1) { + PLOGE("create signalfd"); + return false; + } + ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); + return true; + } + + int GetFd() override { + return signal_fd_; + } + + void HandleEvent(EventLoop &loop, uint32_t event) override { + for (;;) { + ssize_t s = read(signal_fd_, &fdsi, sizeof(fdsi)); + if (s == -1) { + if (errno == EAGAIN) break; + PLOGE("read signalfd"); + continue; + } + if (s != sizeof(fdsi)) { + LOGW("read %zu != %zu", s, sizeof(fdsi)); + continue; + } + if (fdsi.ssi_signo != SIGCHLD) { + LOGW("no sigchld received"); + continue; + } + int pid; + while ((pid = waitpid(-1, &status, __WALL | WNOHANG)) != 0) { + if (pid == -1) { + if (tracing_state == STOPPED && errno == ECHILD) break; + err(EXIT_FAILURE, "waitpid"); + } + if (pid == 1) { + if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_FORK)) { + long child_pid; + ptrace(PTRACE_GETEVENTMSG, pid, 0, &child_pid); + LOGI("forked %ld", child_pid); + } else if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP) && + tracing_state == STOPPING) { + if (ptrace(PTRACE_DETACH, 1, 0, 0) == -1) + PLOGE("failed to detach init"); + tracing_state = STOPPED; + LOGI("stop tracing init"); + continue; + } + // suppress + // TODO: inject signal + if (WIFSTOPPED(status)) { + if (WPTEVENT(status) == 0) { + LOGW("suppressed signal sent to init: %s %d", + sigabbrev_np(WSTOPSIG(status)), WSTOPSIG(status)); + } + ptrace(PTRACE_CONT, pid, 0, 0); + } + continue; + } + auto state = process.find(pid); + if (state == process.end()) { + LOGI("new process %d attached", pid); + process.emplace(pid); + ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC); + ptrace(PTRACE_CONT, pid, 0, 0); + continue; + } else { + if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) { + auto program = get_program(pid); + LOGD("%d program %s", pid, program.c_str()); + const char* tracer = nullptr; + if (program == "/system/bin/app_process64") { + tracer = "./bin/zygisk-ptrace64"; + } else if (program == "/system/bin/app_process32") { + tracer = "./bin/zygisk-ptrace32"; + } + if (tracer != nullptr) { + LOGD("stopping %d", pid); + kill(pid, SIGSTOP); + ptrace(PTRACE_CONT, pid, 0, 0); + wait_pid(pid, &status, __WALL); + if (STOPPED_WITH(SIGSTOP, 0)) { + LOGD("detaching %d", pid); + ptrace(PTRACE_DETACH, pid, 0, SIGSTOP); + status = 0; + auto p = fork_dont_care(); + if (p == 0) { + execl(tracer, basename(tracer), "trace", std::to_string(pid).c_str(), nullptr); + PLOGE("failed to exec, kill"); + kill(pid, SIGKILL); + exit(1); + } else if (p == -1) { + PLOGE("failed to fork, kill"); + kill(pid, SIGKILL); + } + } + } + } else { + LOGD("process %d received unknown status %s", pid, + parse_status(status).c_str()); + } + process.erase(state); + if (WIFSTOPPED(status)) { + LOGI("detach process %d", pid); + ptrace(PTRACE_DETACH, pid, 0, 0); + } } } - if (pid == -1) { - LOGE("failed to find zygote"); - exit(1); - } - return pid; } - last_state = val; + LOGD("sigchld handle done"); + } + + ~PtraceHandler() { + if (signal_fd_ >= 0) close(signal_fd_); + } +}; + +void init_monitor() { + LOGI("init monitor started"); + SocketHandler socketHandler{}; + socketHandler.Init(); + PtraceHandler ptraceHandler{}; + ptraceHandler.Init(); + EventLoop looper; + looper.Init(); + looper.RegisterHandler(socketHandler, EPOLLIN | EPOLLET); + looper.RegisterHandler(ptraceHandler, EPOLLIN | EPOLLET); + looper.Loop(); + LOGI("exit"); +} + +void send_control_command(Command cmd) { + int sockfd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sockfd == -1) err(EXIT_FAILURE, "socket"); + struct sockaddr_un addr{ + .sun_family = AF_UNIX, + .sun_path={0}, + }; + strcpy(addr.sun_path + 1, SOCKET_NAME); + socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1; + auto nsend = sendto(sockfd, (void *) &cmd, sizeof(cmd), 0, (sockaddr *) &addr, socklen); + if (nsend == -1) { + err(EXIT_FAILURE, "send"); + } else if (nsend != sizeof(cmd)) { + printf("send %ld != %ld\n", nsend, sizeof(cmd)); + exit(1); } } diff --git a/loader/src/ptracer/ptracer.cpp b/loader/src/ptracer/ptracer.cpp index 4b74fd8..a6a8cce 100644 --- a/loader/src/ptracer/ptracer.cpp +++ b/loader/src/ptracer/ptracer.cpp @@ -145,7 +145,7 @@ bool inject_on_main(int pid, const char *lib_path) { LOGD("other reason %d", status); }*/ } else { - LOGE("stopped by other reason: %d", status); + LOGE("stopped by other reason: %s", parse_status(status).c_str()); } return false; } @@ -153,6 +153,7 @@ bool inject_on_main(int pid, const char *lib_path) { #define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event)) bool trace_zygote(int pid) { + LOGI("start tracing %d", pid); #define WAIT_OR_DIE if (wait_pid(pid, &status, __WALL) != pid) return false; #define CONT_OR_DIE \ if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) { \ @@ -161,20 +162,17 @@ bool trace_zygote(int pid) { } int status; LOGI("tracing %d (tracer %d)", pid, getpid()); - if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC) == -1) { + if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) { PLOGE("seize"); return false; } - struct finally { - int pid; - ~finally() { - ptrace(PTRACE_DETACH, pid, 0, 0); - } - } _{pid}; WAIT_OR_DIE if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) { - // if SIGSTOP is delivered before we seized it - LOGD("process is already stopped"); + if (!inject_on_main(pid, "/dev/zygisk/lib" LP_SELECT("", "64") "/libzygisk.so")) { + LOGE("failed to inject"); + return false; + } + LOGD("inject done, continue process"); if (kill(pid, SIGCONT)) { PLOGE("kill"); return false; @@ -186,30 +184,16 @@ bool trace_zygote(int pid) { WAIT_OR_DIE if (STOPPED_WITH(SIGCONT, 0)) { LOGD("received SIGCONT"); - ptrace(PTRACE_CONT, pid, 0, 0); + ptrace(PTRACE_DETACH, pid, 0, SIGCONT); } } else { LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", parse_status(status).c_str()); - return false; - } - } else if (STOPPED_WITH(SIGSTOP, 0)) { - // if SIGSTOP is delivered after we seized it - LOGD("process received SIGSTOP, suppress"); - CONT_OR_DIE - } else { - LOGE("unknown state %s, neither EVENT_STOP nor SIGSTOP", parse_status(status).c_str()); - return false; - } - WAIT_OR_DIE - // enter the app_process - if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) { - LOGI("app_process exec-ed"); - if (!inject_on_main(pid, "/dev/zygisk/lib" LP_SELECT("", "64") "/libzygisk.so")) { - LOGE("failed to inject"); + ptrace(PTRACE_DETACH, pid, 0, 0); return false; } } else { - LOGE("unknown status %s", parse_status(status).c_str()); + LOGE("unknown state %s, not SIGSTOP + EVENT_STOP", parse_status(status).c_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 b2f665f..899bd9d 100644 --- a/loader/src/ptracer/utils.cpp +++ b/loader/src/ptracer/utils.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "utils.hpp" #include "logging.h" @@ -62,37 +63,6 @@ std::vector MapInfo::Scan(const std::string& pid) { return info; } -// https://cs.android.com/android/platform/superproject/main/+/main:external/toybox/toys/net/netstat.c;l=200;drc=657f94698c7fc7d4f9838cbcf3b4b78e38939d5c -std::map ScanUnixSockets() { - constexpr static auto kSocketEntry = 1; - LOGD("scanning unix sockets"); - std::map info; - auto sockets = std::unique_ptr{fopen("/proc/net/unix", "r"), &fclose}; - char *line = nullptr; - size_t len = 0; - // skip header - getline(&line, &len, sockets.get()); - if (sockets) { - ssize_t read; - while ((read = getline(&line, &len, sockets.get())) > 0) { - line[read - 1] = '\0'; - ino_t ino; - char *path = nullptr; - // Num RefCount Protocol Flags Type St Inode Path - if (sscanf(line, "%*p: %*lx %*lx %*lx %*lx %*lx %lu%m[^\n]", &ino, &path) < kSocketEntry) { - continue; - } - if (path != nullptr) { - LOGD("%ld -> %s", ino, path + 1); - info.emplace(ino, path + 1); - free(path); - } - } - free(line); - } - return info; -} - ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len) { LOGD("write to remote addr %p size %zu", remote_addr, len); struct iovec local{ @@ -342,7 +312,7 @@ uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_add } return regs.REG_RET; } else { - LOGE("stopped by other reason %d", status); + LOGE("stopped by other reason %s", parse_status(status).c_str()); } return 0; } @@ -375,19 +345,33 @@ int wait_pid(int pid, int* status, int flags) { std::string parse_status(int status) { std::ostringstream os; - os << "status " << std::hex << status; - os << std::dec << " "; + os << "0x" << std::hex << status << std::dec << " "; if (WIFEXITED(status)) { os << "exited with " << WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { os << "signaled with " << sigabbrev_np(WTERMSIG(status)) << "(" << WTERMSIG(status) << ")"; } else if (WIFSTOPPED(status)) { - os << "stopped by"; + os << "stopped by "; auto stop_sig = WSTOPSIG(status); - os << "signal " << sigabbrev_np(stop_sig) << "(" << stop_sig << "),"; + os << "signal=" << sigabbrev_np(stop_sig) << "(" << stop_sig << "),"; os << "event=" << parse_ptrace_event(status); } else { os << "unknown"; } return os.str(); } + +std::string get_program(int pid) { + std::string path = "/proc/"; + path += std::to_string(pid); + path += "/exe"; + constexpr const auto SIZE = 256; + char buf[SIZE + 1]; + auto sz = readlink(path.c_str(), buf, SIZE); + if (sz == -1) { + PLOGE("readlink /proc/%d/exe", pid); + return ""; + } + buf[sz] = 0; + return buf; +} diff --git a/loader/src/ptracer/utils.hpp b/loader/src/ptracer/utils.hpp index dfe3df2..90aac2e 100644 --- a/loader/src/ptracer/utils.hpp +++ b/loader/src/ptracer/utils.hpp @@ -6,9 +6,9 @@ #include "daemon.h" #ifdef __LP64__ -#define LOG_TAG "zygisk-ptracer64" +#define LOG_TAG "zygisk-ptrace64" #else -#define LOG_TAG "zygisk-ptracer32" +#define LOG_TAG "zygisk-ptrace32" #endif #include "logging.h" @@ -91,6 +91,8 @@ int wait_pid(int pid, int* status, int flags); std::string parse_status(int status); +#define WPTEVENT(x) (x >> 16) + #define CASE_CONST_RETURN(x) case x: return #x; inline const char* parse_ptrace_event(int status) { @@ -114,5 +116,5 @@ inline const char* sigabbrev_np(int sig) { return "(unknown)"; } -std::map ScanUnixSockets(); +std::string get_program(int pid); diff --git a/module/src/customize.sh b/module/src/customize.sh index cd2bfc7..7bb30f6 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -114,18 +114,17 @@ if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" extract "$ZIPFILE" 'lib/x86/libzygisk.so' "$MODPATH/lib" true ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32" - extract "$ZIPFILE" 'lib/x86/libptracer.so' "$MODPATH/bin" true - mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer32" + extract "$ZIPFILE" 'lib/x86/libzygisk_ptrace.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libzygisk_ptrace.so" "$MODPATH/bin/zygisk-ptrace32" fi ui_print "- Extracting x64 libraries" extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64" extract "$ZIPFILE" 'lib/x86_64/libzygisk.so' "$MODPATH/lib64" true - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd" ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64" - extract "$ZIPFILE" 'lib/x86_64/libptracer.so' "$MODPATH/bin" true - mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer64" + extract "$ZIPFILE" 'lib/x86_64/libzygisk_ptrace.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libzygisk_ptrace.so" "$MODPATH/bin/zygisk-ptrace64" else if [ "$HAS32BIT" = true ]; then ui_print "- Extracting arm libraries" @@ -133,18 +132,17 @@ else mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk.so' "$MODPATH/lib" true ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32" - extract "$ZIPFILE" 'lib/armeabi-v7a/libptracer.so' "$MODPATH/bin" true - mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer32" + extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_ptrace.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libzygisk_ptrace.so" "$MODPATH/bin/zygisk-ptrace32" fi ui_print "- Extracting arm64 libraries" extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64" extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk.so' "$MODPATH/lib64" true - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd" ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64" - extract "$ZIPFILE" 'lib/arm64-v8a/libptracer.so' "$MODPATH/bin" true - mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer64" + extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_ptrace.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libzygisk_ptrace.so" "$MODPATH/bin/zygisk-ptrace64" fi ui_print "- Setting permissions" diff --git a/module/src/post-fs-data.sh b/module/src/post-fs-data.sh index 5a573d8..7e797fe 100644 --- a/module/src/post-fs-data.sh +++ b/module/src/post-fs-data.sh @@ -32,15 +32,12 @@ if [ -f $MODDIR/lib64/libzygisk.so ];then create_sys_perm /dev/zygisk/lib64 cp $MODDIR/lib64/libzygisk.so /dev/zygisk/lib64/libzygisk.so chcon u:object_r:system_lib_file:s0 /dev/zygisk/lib64/libzygisk.so - setprop ctl.sigstop_on zygote - unshare -m sh -c "./bin/zygisk-ptracer64 prop_monitor &" fi if [ -f $MODDIR/lib/libzygisk.so ];then create_sys_perm /dev/zygisk/lib cp $MODDIR/lib/libzygisk.so /dev/zygisk/lib/libzygisk.so chcon u:object_r:system_lib_file:s0 /dev/zygisk/lib/libzygisk.so - setprop ctl.sigstop_on zygote_secondary - unshare -m sh -c "./bin/zygisk-ptracer32 prop_monitor &" fi +unshare -m sh -c "./bin/zygisk-ptrace64 monitor &" diff --git a/module/src/sepolicy.rule b/module/src/sepolicy.rule index 6f16b88..2a9090e 100644 --- a/module/src/sepolicy.rule +++ b/module/src/sepolicy.rule @@ -1,5 +1,3 @@ -deny vold fusectlfs file write - allow * tmpfs * * allow zygote appdomain_tmpfs dir * allow zygote appdomain_tmpfs file * diff --git a/module/src/service.sh b/module/src/service.sh index 1adef00..47a1057 100644 --- a/module/src/service.sh +++ b/module/src/service.sh @@ -6,6 +6,7 @@ MODDIR=${0%/*} if [ "$ZYGISK_ENABLED" ]; then exit 0 fi + # temporary fix for AVD 30 if [ -f /dev/zygisk/wd ]; then log -p i -t "zygisk-sh" "prevent from instance duplicated" @@ -15,13 +16,6 @@ touch /dev/zygisk/wd cd "$MODDIR" -# temporary fix AVD 11 magisk -# if [ -f /dev/zygisk_service ];then -# log -p i -t "zygisk-sh" "service called twice"; -# exit; -# fi -# touch /dev/zygisk_service - if [ "$(which magisk)" ]; then for file in ../*; do if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then @@ -36,4 +30,5 @@ if [ "$(which magisk)" ]; then fi [ "$DEBUG" = true ] && export RUST_BACKTRACE=1 -unshare -m sh -c "bin/zygisk-wd &" +unshare -m sh -c "bin/zygisk-cp64 &" +unshare -m sh -c "bin/zygisk-cp32 &" diff --git a/zygiskd/src/main.rs b/zygiskd/src/main.rs index cb5a0f5..bc61a4a 100644 --- a/zygiskd/src/main.rs +++ b/zygiskd/src/main.rs @@ -2,7 +2,6 @@ mod constants; mod dl; mod root_impl; mod utils; -mod watchdog; mod zygiskd; use std::future::Future; @@ -25,9 +24,8 @@ fn start(name: &str) -> Result<()> { utils::switch_mount_namespace(1)?; root_impl::setup(); match name.trim_start_matches("zygisk-") { - "wd" => async_start(watchdog::main())?, lp_select!("cp32", "cp64") => zygiskd::main()?, - _ => println!("Available commands: wd, fuse, cp, ptrace"), + _ => println!("Available command: cp[32|64]"), } Ok(()) } diff --git a/zygiskd/src/watchdog.rs b/zygiskd/src/watchdog.rs deleted file mode 100644 index fa7ab8c..0000000 --- a/zygiskd/src/watchdog.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::{constants, root_impl, utils}; -use anyhow::{bail, Result}; -use std::fs; -use std::future::Future; -use std::io::{BufRead, BufReader, Write}; -use std::os::fd::AsRawFd; -use std::path::Path; -use std::pin::Pin; -use futures::stream::FuturesUnordered; -use futures::{FutureExt, pin_mut, select, StreamExt}; -use futures::future::Fuse; -use log::{debug, error, info}; -use rustix::mount::mount_bind; -use rustix::process::{getgid, getuid, kill_process, Pid, Signal}; -use tokio::process::{Child, Command}; -use tokio::task; -use tokio::task::JoinHandle; -use crate::utils::LateInit; - -static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new(); - -pub async fn main() -> Result<()> { - let result = run().await; - if result.is_err() { - set_prop_hint(constants::STATUS_CRASHED)?; - } - result -} - -async fn run() -> Result<()> { - info!("Start zygisk watchdog"); - check_permission()?; - mount_prop().await?; - if check_and_set_hint()? == false { - log::warn!("Requirements not met, exiting"); - return Ok(()); - } - spawn_daemon().await?; - Ok(()) -} - -fn check_permission() -> Result<()> { - info!("Check permission"); - let uid = getuid(); - if !uid.is_root() { - bail!("UID is not 0"); - } - - let gid = getgid(); - if !gid.is_root() { - bail!("GID is not 0"); - } - - let context = fs::read_to_string("/proc/self/attr/current")?; - let context = context.trim_end_matches('\0'); - if context != "u:r:su:s0" && context != "u:r:magisk:s0" { - bail!("SELinux context incorrect: {context}"); - } - - Ok(()) -} - -async fn mount_prop() -> Result<()> { - let module_prop_file = fs::File::open(constants::PATH_MODULE_PROP)?; - let mut section = 0; - let mut sections: [String; 2] = [String::new(), String::new()]; - let lines = BufReader::new(module_prop_file).lines(); - for line in lines { - let line = line?; - if line.starts_with("description=") { - sections[0].push_str("description="); - sections[1].push_str(line.trim_start_matches("description=")); - sections[1].push('\n'); - section = 1; - } else { - sections[section].push_str(&line); - sections[section].push('\n'); - } - } - PROP_SECTIONS.init(sections); - - info!("Mount {} -> {}", constants::PATH_PROP_OVERLAY, constants::PATH_MODULE_PROP); - fs::File::create(constants::PATH_PROP_OVERLAY)?; - mount_bind(constants::PATH_PROP_OVERLAY, constants::PATH_MODULE_PROP)?; - Ok(()) -} - -fn set_prop_hint(hint: &str) -> Result<()> { - let mut file = fs::File::create(constants::PATH_PROP_OVERLAY)?; - file.write_all(PROP_SECTIONS[0].as_bytes())?; - file.write_all(b"[")?; - file.write_all(hint.as_bytes())?; - file.write_all(b"] ")?; - file.write_all(PROP_SECTIONS[1].as_bytes())?; - Ok(()) -} - -fn check_and_set_hint() -> Result { - let root_impl = root_impl::get_impl(); - match root_impl { - root_impl::RootImpl::None => set_prop_hint(constants::STATUS_ROOT_IMPL_NONE)?, - root_impl::RootImpl::TooOld => set_prop_hint(constants::STATUS_ROOT_IMPL_TOO_OLD)?, - root_impl::RootImpl::Abnormal => set_prop_hint(constants::STATUS_ROOT_IMPL_ABNORMAL)?, - root_impl::RootImpl::Multiple => set_prop_hint(constants::STATUS_ROOT_IMPL_MULTIPLE)?, - _ => { - set_prop_hint(constants::STATUS_LOADED)?; - return Ok(true); - } - } - Ok(false) -} - -fn wait_for_ptrace(is_32bit: bool) -> Option>> { - let lock_path = if is_32bit { - if !Path::new(constants::PATH_PT_BIN32).is_file() { - return None - } - constants::PATH_PT_LOCK32 - } else { - if !Path::new(constants::PATH_PT_BIN64).is_file() { - return None - } - constants::PATH_PT_LOCK64 - }; - info!("wait for ptrace 32={}", is_32bit); - Some(task::spawn_blocking(move || -> Result<()> { - let file = match fs::OpenOptions::new().write(true).open(lock_path) { - Ok(f) => f, - Err(e) => { - bail!("failed to open lock: {}", e) - } - }; - unsafe { - let lock = libc::flock { - l_type: libc::F_WRLCK as libc::c_short, - l_whence: libc::SEEK_SET as libc::c_short, - l_start: 0, - l_len: 0, - l_pid: 0, - }; - loop { - if libc::fcntl(file.as_raw_fd(), libc::F_SETLKW, &lock) == 0 { - bail!("file lock obtained") - } else { - let errno = *libc::__errno(); - match errno { - libc::EINTR => continue, - _ => { - bail!("failed to wait on lock: {}", errno) - } - } - } - } - } - })) -} - -async fn spawn_daemon() -> Result<()> { - let mut lives = 5; - let lock32 = match wait_for_ptrace(true) { - Some(f) => f.fuse(), - None => Fuse::terminated() - }; - let lock64 = match wait_for_ptrace(false) { - Some(f) => f.fuse(), - None => Fuse::terminated() - }; - pin_mut!(lock32, lock64); - loop { - let mut futures = FuturesUnordered::>>>>::new(); - let mut child_ids = vec![]; - let daemon32 = Command::new(constants::PATH_CP_BIN32).arg("daemon").spawn(); - let daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn(); - async fn spawn_daemon(mut daemon: Child) -> Result<()> { - let id = daemon.id().unwrap(); - let result = daemon.wait().await?; - // FIXME: we must not get id here - log::error!("Daemon process {} died: {}", id, result); - Ok(()) - } - if let Ok(it) = daemon32 { - child_ids.push(it.id().unwrap()); - futures.push(Box::pin(spawn_daemon(it))); - } - if let Ok(it) = daemon64 { - child_ids.push(it.id().unwrap()); - futures.push(Box::pin(spawn_daemon(it))); - } - - let mut stop = false; - - select! { - l32 = lock32 => { - if let Ok(Err(it)) = l32 { - error!("wait on lock 32: {}", it); - } - error!("wait on lock 32"); - stop = true; - }, - l64 = lock64 => { - if let Ok(Err(it)) = l64 { - error!("wait on lock 64: {}", it); - } - error!("wait on lock 64"); - stop = true; - }, - res = futures.select_next_some() => { - if let Err(it) = res { - error!("wait on daemon: {}", it); - } - lives -= 1; - if lives == 0 { - error!("Too many crashes, abort"); - stop = true; - } - error!("wait on daemon"); - }, - complete => panic!("completed unexpectedly") - } - - for child in child_ids { - debug!("Killing child process {}", child); - let _ = kill_process(Pid::from_raw(child as i32).unwrap(), Signal::Kill); - } - - if stop { - utils::set_property(constants::PROP_CTL_SIGSTOP_OFF, "zygote")?; - utils::set_property(constants::PROP_CTL_SIGSTOP_OFF, "zygote_secondary")?; - } - error!("Restarting zygote..."); - utils::set_property(constants::PROP_CTL_RESTART, "zygote")?; - if stop { - bail!("Injecting failed or crashed too much times, Resetting ..."); - } - } -}