From 993b18752bff18a9fab8fa1183494754c89c5e97 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Mon, 11 Dec 2023 23:38:43 +0800 Subject: [PATCH] daemon launched by monitor & show daemon status in module.prop --- loader/src/ptracer/main.cpp | 1 + loader/src/ptracer/monitor.cpp | 243 +++++++++++++++++++++++++++------ loader/src/ptracer/utils.cpp | 39 ++++++ loader/src/ptracer/utils.hpp | 3 + module/src/service.sh | 4 - 5 files changed, 243 insertions(+), 47 deletions(-) diff --git a/loader/src/ptracer/main.cpp b/loader/src/ptracer/main.cpp index 44312b9..bbb5185 100644 --- a/loader/src/ptracer/main.cpp +++ b/loader/src/ptracer/main.cpp @@ -9,6 +9,7 @@ #include "main.hpp" #include "utils.hpp" #include "daemon.h" +#include using namespace std::string_view_literals; diff --git a/loader/src/ptracer/monitor.cpp b/loader/src/ptracer/monitor.cpp index 72f90b5..b9e72c4 100644 --- a/loader/src/ptracer/monitor.cpp +++ b/loader/src/ptracer/monitor.cpp @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include "main.hpp" #include "utils.hpp" @@ -24,13 +26,17 @@ using namespace std::string_view_literals; #define STOPPED_WITH(sig, event) WIFSTOPPED(status) && (status >> 8 == ((sig) | (event << 8))) +static void updateStatus(); enum TracingState { TRACING = 1, STOPPING, - STOPPED + STOPPED, + EXITING }; +std::string monitor_stop_reason; + constexpr char SOCKET_NAME[] = "init_monitor"; std::string GetControlSocketName() { @@ -158,16 +164,22 @@ struct SocketHandler : public EventHandler { LOGI("start tracing init"); tracing_state = TRACING; } + updateStatus(); break; case STOP: if (tracing_state == TRACING) { LOGI("stop tracing requested"); tracing_state = STOPPING; + monitor_stop_reason = "user requested"; ptrace(PTRACE_INTERRUPT, 1, 0, 0); + updateStatus(); } break; case EXIT: LOGI("prepare for exit ..."); + tracing_state = EXITING; + monitor_stop_reason = "user requested"; + updateStatus(); loop.Stop(); break; } @@ -181,35 +193,61 @@ struct SocketHandler : public EventHandler { constexpr auto MAX_RETRY_COUNT = 5; -struct timespec last_zygote64{.tv_sec = 0, .tv_nsec = 0}; -int count_zygote64 = 0; -bool should_stop_inject64() { - struct timespec now{}; - clock_gettime(CLOCK_MONOTONIC, &now); - if (now.tv_sec - last_zygote64.tv_sec < 30) { - count_zygote64++; - } else { - count_zygote64 = 0; - } - last_zygote64 = now; - return count_zygote64 >= 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; \ } -struct timespec last_zygote32{.tv_sec = 0, .tv_nsec = 0}; -int count_zygote32 = 0; -bool should_stop_inject32() { - struct timespec now{}; - clock_gettime(CLOCK_MONOTONIC, &now); - if (now.tv_sec - last_zygote32.tv_sec < 30) { - count_zygote32++; +CREATE_ZYGOTE_START_COUNTER(64) +CREATE_ZYGOTE_START_COUNTER(32) + +struct Status { + bool supported = false; + bool zygote_injected = false; + bool daemon_running = false; + pid_t daemon_pid = -1; + std::string daemon_crash_reason; +}; + +static Status status64; +static Status status32; + +static bool ensure_daemon_created(bool is_64bit) { + auto &status = is_64bit ? status64 : status32; + if (status.daemon_pid == -1) { + auto pid = fork(); + if (pid < 0) { + PLOGE("create daemon (64=%s)", is_64bit ? "true" : "false"); + return false; + } else if (pid == 0) { + std::string daemon_name = "./bin/zygisk-cp"; + daemon_name += is_64bit ? "64" : "32"; + execl(daemon_name.c_str(), basename(daemon_name.c_str()), nullptr); + PLOGE("exec daemon %s failed", daemon_name.c_str()); + exit(1); + } else { + status.supported = true; + status.daemon_pid = pid; + status.daemon_running = true; + updateStatus(); + return true; + } } else { - count_zygote32 = 0; + return status.daemon_running; } - last_zygote32 = now; - return count_zygote32 >= MAX_RETRY_COUNT; } -struct PtraceHandler : public EventHandler { +struct SigChldHandler : public EventHandler { private: int signal_fd_; struct signalfd_siginfo fdsi; @@ -288,6 +326,19 @@ public: } continue; } +#define CHECK_DAEMON_EXIT(abi) \ + if (status##abi.supported && pid == status64.daemon_pid) { \ + auto status_str = parse_status(status); \ + LOGW("daemon" #abi "pid %d exited: %s", pid, status_str.c_str()); \ + status##abi.daemon_running = false; \ + if (status##abi.daemon_crash_reason.empty()) { \ + status##abi.daemon_crash_reason = status_str; \ + } \ + updateStatus(); \ + continue; \ + } + CHECK_DAEMON_EXIT(64) + CHECK_DAEMON_EXIT(32) auto state = process.find(pid); if (state == process.end()) { LOGV("new process %d attached", pid); @@ -305,23 +356,28 @@ public: LOGW("stop injecting %d because not tracing", pid); break; } - if (program == "/system/bin/app_process64") { - tracer = "./bin/zygisk-ptrace64"; - if (should_stop_inject64()) { - LOGW("zygote64 restart too much times, stop injecting"); - tracing_state = STOPPING; - ptrace(PTRACE_INTERRUPT, 1, 0, 0); - break; - } - } else if (program == "/system/bin/app_process32") { - tracer = "./bin/zygisk-ptrace32"; - if (should_stop_inject32()) { - LOGW("zygote32 restart too much times, stop injecting"); - tracing_state = STOPPING; - ptrace(PTRACE_INTERRUPT, 1, 0, 0); - break; - } +#define PRE_INJECT(abi, is_64) \ + if (program == "/system/bin/app_process"#abi) { \ + tracer = "./bin/zygisk-ptrace"#abi; \ + if (should_stop_inject##abi()) { \ + LOGW("zygote" #abi " restart too much times, stop injecting"); \ + tracing_state = STOPPING; \ + monitor_stop_reason = "zygote crashed"; \ + updateStatus(); \ + ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ + break; \ + } \ + if (!ensure_daemon_created(is_64)) { \ + LOGW("daemon" #abi " not running, stop injecting"); \ + tracing_state = STOPPING; \ + monitor_stop_reason = "daemon not running"; \ + updateStatus(); \ + ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ + break; \ + } \ } + PRE_INJECT(64, true) + PRE_INJECT(32, false) if (tracer != nullptr) { LOGD("stopping %d", pid); kill(pid, SIGSTOP); @@ -345,7 +401,6 @@ public: } } } while (false); - } else { LOGE("process %d received unknown status %s", pid, parse_status(status).c_str()); @@ -360,17 +415,119 @@ public: } } - ~PtraceHandler() { + ~SigChldHandler() { if (signal_fd_ >= 0) close(signal_fd_); } }; +static std::string prop_path; +static std::string pre_section; +static std::string post_section; + +static void updateStatus() { + auto prop = xopen_file(prop_path.c_str(), "w"); + std::string status_text = "monitor:"; + switch (tracing_state) { + case TRACING: + status_text += "tracing"; + break; + case STOPPING: + [[fallthrough]]; + case STOPPED: + status_text += "stopped"; + break; + case EXITING: + status_text += "exited"; + break; + } + if (tracing_state != TRACING && !monitor_stop_reason.empty()) { + status_text += "("; + status_text += monitor_stop_reason; + status_text += ")"; + } + status_text += ", "; +#define WRITE_STATUS_ABI(suffix) \ + if (status##suffix.supported) { \ + status_text += " zygote" #suffix ":"; \ + if (status##suffix.zygote_injected) status_text += "injected,"; \ + else status_text += "not injected,"; \ + status_text += " daemon" #suffix ":"; \ + if (status##suffix.daemon_running) status_text += "running"; \ + else { \ + status_text += "crashed"; \ + if (!status##suffix.daemon_crash_reason.empty()) { \ + status_text += "("; \ + status_text += status##suffix.daemon_crash_reason; \ + status_text += ")"; \ + } \ + } \ + } + WRITE_STATUS_ABI(64) + WRITE_STATUS_ABI(32) + fprintf(prop.get(), "%s[%s]%s", pre_section.c_str(), status_text.c_str(), post_section.c_str()); +} + +static bool prepare_environment() { + auto path = getenv(MAGIC_PATH_ENV); + if (path == nullptr) { + LOGE("path is null, is MAGIC_PATH_ENV specified?"); + return false; + } + prop_path = std::string(path) + "/module.prop"; + close(open(prop_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); + auto orig_prop = xopen_file("./module.prop", "r"); + if (orig_prop == nullptr) { + PLOGE("failed to open orig prop"); + return false; + } + bool post = false; + file_readline(false, orig_prop.get(), [&](std::string_view line) -> bool { + if (line.starts_with("description=")) { + post = true; + pre_section += "description="; + post_section += line.substr(sizeof("description")); + } else { + if (post) { + post_section += line; + } else { + pre_section += line; + } + } + return true; + }); + int old_ns; + char wd[128]; + if (getcwd(wd, sizeof(wd)) == nullptr) { + PLOGE("get cwd"); + return false; + } + if (!switch_mnt_ns(1, &old_ns)) return false; + if (chdir(wd) == -1) { + PLOGE("chdir %s", wd); + return false; + } + if (mount(prop_path.c_str(), "/data/adb/modules/zygisksu/module.prop", nullptr, MS_BIND, nullptr) == -1) { + PLOGE("failed to mount prop"); + return false; + } + if (!switch_mnt_ns(0, &old_ns)) return false; + if (chdir(wd) == -1) { + PLOGE("chdir %s", wd); + return false; + } + updateStatus(); + return true; +} + void init_monitor() { LOGI("Zygisk Next %s", ZKSU_VERSION); LOGI("init monitor started"); + if (!prepare_environment()) { + exit(1); + } SocketHandler socketHandler{}; socketHandler.Init(); - PtraceHandler ptraceHandler{}; + SigChldHandler ptraceHandler{}; ptraceHandler.Init(); EventLoop looper; looper.Init(); diff --git a/loader/src/ptracer/utils.cpp b/loader/src/ptracer/utils.cpp index 7988b37..fdbc2ea 100644 --- a/loader/src/ptracer/utils.cpp +++ b/loader/src/ptracer/utils.cpp @@ -24,6 +24,45 @@ #include "utils.hpp" #include "logging.h" +#include +#include + +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); + return false; + } + close(nsfd); + return true; +} std::vector MapInfo::Scan(const std::string& pid) { constexpr static auto kPermLength = 5; diff --git a/loader/src/ptracer/utils.hpp b/loader/src/ptracer/utils.hpp index d8fc59f..14f9109 100644 --- a/loader/src/ptracer/utils.hpp +++ b/loader/src/ptracer/utils.hpp @@ -119,3 +119,6 @@ inline const char* sigabbrev_np(int sig) { std::string get_program(int pid); void *find_module_return_addr(std::vector &info, std::string_view suffix); +// pid = 0, fd != nullptr -> set to fd +// pid != 0, fd != nullptr -> set to pid ns, give orig ns in fd +bool switch_mnt_ns(int pid, int *fd); diff --git a/module/src/service.sh b/module/src/service.sh index 3dfabdb..12107c2 100644 --- a/module/src/service.sh +++ b/module/src/service.sh @@ -24,7 +24,3 @@ if [ "$(which magisk)" ]; then fi done fi - -[ "$DEBUG" = true ] && export RUST_BACKTRACE=1 -unshare -m sh -c "bin/zygisk-cp64 &" -unshare -m sh -c "bin/zygisk-cp32 &"