diff --git a/loader/src/CMakeLists.txt b/loader/src/CMakeLists.txt index 7bf53e1..00a0da1 100644 --- a/loader/src/CMakeLists.txt +++ b/loader/src/CMakeLists.txt @@ -13,4 +13,9 @@ add_library(zygisk SHARED ${INJECTOR_SRC_LIST}) 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_subdirectory(external) diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 382dbf9..d158827 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -284,6 +284,14 @@ DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) { return res; } + DCL_HOOK_FUNC(char *, strdup, const char *s) { + if (s == "com.android.internal.os.ZygoteInit"sv) { + LOGD("strdup %s\n", s); + replace_jni_methods(); + } + return old_strdup(s); + } + #undef DCL_HOOK_FUNC // ----------------------------------------------------------------- @@ -762,6 +770,7 @@ void hook_functions() { PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare); + PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, strdup); PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close); hook_commit(); @@ -770,8 +779,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()); - - replace_jni_methods(); } static void hook_unloader() { diff --git a/loader/src/ptracer/main.cpp b/loader/src/ptracer/main.cpp new file mode 100644 index 0000000..4cad52c --- /dev/null +++ b/loader/src/ptracer/main.cpp @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +#include "main.hpp" +#include "utils.hpp" + +using namespace std::string_view_literals; + +int main(int argc, char **argv) { + if (argc >= 2 && argv[1] == "prop_monitor"sv) { + if (access("/system/lib" LP_SELECT("", "64"), R_OK) == 0) prop_monitor_main(); + } else if (argc >= 3 && argv[1] == "trace-zygote"sv) { + auto pid = strtol(argv[2], nullptr, 0); + trace_zygote_main(pid); + } else { + if (argc >= 2) LOGE("unknown command %s", argv[1]); + else LOGE("no command specified"); + } + return 0; +} diff --git a/loader/src/ptracer/main.hpp b/loader/src/ptracer/main.hpp new file mode 100644 index 0000000..087d29c --- /dev/null +++ b/loader/src/ptracer/main.hpp @@ -0,0 +1,4 @@ +#pragma once + +void prop_monitor_main(); +void trace_zygote_main(int pid); diff --git a/loader/src/ptracer/monitor.cpp b/loader/src/ptracer/monitor.cpp new file mode 100644 index 0000000..ad19109 --- /dev/null +++ b/loader/src/ptracer/monitor.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include "main.hpp" +#include "utils.hpp" + +void prop_monitor_main() { + LOGI("prop monitor started"); + // if service is not running, pid = "" + auto name = "init.svc_debug_pid." LP_SELECT("zygote_secondary", "zygote"); // argv[1]; + LOGI("start monitoring %s", name); + auto prop = __system_property_find(name); + if (prop == nullptr) { + __system_property_set(name, ""); + prop = __system_property_find(name); + if (prop == nullptr) { + LOGE("failed to create prop"); + exit(1); + } + } + 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); + auto pid = strtol(val, nullptr, 0); + if (pid != 0) { + LOGD("start ptrace %ld", pid); + if (fork_dont_care() == 0) { + execl("/proc/self/exe", "zygisk-ptracer", "trace-zygote", val, nullptr); + PLOGE("failed to exec"); + exit(1); + } + } + } +} diff --git a/loader/src/ptracer/ptracer.cpp b/loader/src/ptracer/ptracer.cpp new file mode 100644 index 0000000..3692099 --- /dev/null +++ b/loader/src/ptracer/ptracer.cpp @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.hpp" + +bool inject_on_main(int pid, const char *lib_path) { + // 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 = reinterpret_cast(regs.REG_SP); + LOGD("kernel argument %p %s", arg, get_addr_mem_region(map, arg).c_str()); + int argc; + auto argv = reinterpret_cast(reinterpret_cast(arg) + 1); + LOGD("argv %p", argv); + read_proc(pid, arg, &argc, sizeof(argc)); + LOGD("argc %d", argc); + auto envp = argv + argc + 1; + LOGD("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; + } + ++p; + auto auxv = reinterpret_cast(p); + LOGD("auxv %p %s", auxv, get_addr_mem_region(map, auxv).c_str()); + auto v = auxv; + void *entry_addr = nullptr; + void *addr_of_entry_addr = nullptr; + while (true) { + ElfW(auxv_t) buf; + read_proc(pid, (uintptr_t *) v, &buf, sizeof(buf)); + if (buf.a_type == AT_ENTRY) { + entry_addr = reinterpret_cast(buf.a_un.a_val); + addr_of_entry_addr = reinterpret_cast(v) + offsetof(ElfW(auxv_t), a_un); + LOGD("entry address %p %s (v=%p, entry_addr=%p)", entry_addr, + get_addr_mem_region(map, entry_addr).c_str(), v, addr_of_entry_addr); + break; + } + if (buf.a_type == AT_NULL) break; + v++; + } + if (entry_addr == nullptr) { + 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; + if (waitpid(pid, &status, __WALL) == -1) { + PLOGE("wait"); + return false; + } + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) { + if (!get_regs(pid, regs)) return false; + if (regs.REG_IP != break_addr) { + 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_base = find_module_base(map, "libc.so"); + + // 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_base, args); + LOGD("remote handle %p", (void *) remote_handle); + if (remote_handle == 0) { + LOGE("handle is null"); + 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_base, args); + LOGD("injector entry %p", (void*) injector_entry); + if (injector_entry == 0) { + LOGE("injector entry is null"); + return false; + } + + // call injector entry(handle) + args.clear(); + args.push_back(remote_handle); + remote_call(pid, regs, injector_entry, (uintptr_t) libc_base, 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; + + /* + ptrace(PTRACE_CONT, pid, 0, 0); + waitpid(pid, &status, __WALL); + if (WIFSTOPPED(status)) { + siginfo_t siginfo; + ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo); + LOGD("process stopped by signal %d %s si_code=%d si_addr=%p", WSTOPSIG(status), + strsignal(WSTOPSIG(status)), siginfo.si_code, siginfo.si_addr); + pause(); + } else { + LOGD("other reason %d", status); + }*/ + } else { + LOGE("stopped by other reason: %d", status); + } + return false; +} + +#define STOPPED_WITH(sig, event) (WIFSTOPPED(status) && WSTOPSIG(status) == (sig) && (status >> 16) == (event)) + +void trace_zygote_main(int pid) { + int status; + LOGI("tracing %d (tracer %d)", pid, getpid()); + if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXEC) == -1) { + PLOGE("seize"); + return; + } + wait_pid(pid, &status, __WALL); + if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) { + // if SIGSTOP is delivered before we seized it + LOGD("process is already stopped"); + kill(pid, SIGCONT); + ptrace(PTRACE_CONT, pid, 0, 0); + wait_pid(pid, &status, __WALL); + if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP)) { + ptrace(PTRACE_CONT, pid, 0, 0); + wait_pid(pid, &status, __WALL); + if (STOPPED_WITH(SIGCONT, 0)) { + LOGD("received SIGCONT"); + ptrace(PTRACE_CONT, pid, 0, 0); + } + } else { + LOGE("unknown state %s, not SIGTRAP + EVENT_STOP", parse_status(status).c_str()); + } + } else if (STOPPED_WITH(SIGSTOP, 0)) { + // if SIGSTOP is delivered after we seized it + LOGD("process received SIGSTOP, suppress"); + ptrace(PTRACE_CONT, pid, 0, 0); + } else { + LOGE("unknown state %s, neither EVENT_STOP nor SIGSTOP", parse_status(status).c_str()); + exit(1); + } + wait_pid(pid, &status, __WALL); + // enter the app_process + if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) { + LOGD("app_process exec-ed"); + if (!inject_on_main(pid, "/dev/zygisk/lib" LP_SELECT("", "64") "/libzygisk.so")) { + LOGE("failed to inject"); + exit(1); + } + } else { + LOGE("unknown status %d", status); + exit(1); + } + ptrace(PTRACE_DETACH, pid, 0, 0); +} diff --git a/loader/src/ptracer/utils.cpp b/loader/src/ptracer/utils.cpp new file mode 100644 index 0000000..4d7912c --- /dev/null +++ b/loader/src/ptracer/utils.cpp @@ -0,0 +1,362 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" +#include "logging.h" + +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); + } + 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{ + .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 (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 (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 (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) { + PLOGE("getregs"); + return false; + } +#elif defined(__aarch64__) || defined(__arm__) + struct iovec iov = { + .iov_base = ®s, + .iov_len = sizeof(struct user_regs_struct), + }; + if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) { + PLOGE("getregs"); + return false; + } +#endif + return true; +} + +bool set_regs(int pid, struct user_regs_struct ®s) { +#if defined(__x86_64__) || defined(__i386__) + if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) { + PLOGE("setregs"); + return false; + } +#elif defined(__aarch64__) || defined(__arm__) + struct iovec iov = { + .iov_base = ®s, + .iov_len = sizeof(struct user_regs_struct), + }; + if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) { + PLOGE("setregs"); + return false; + } +#endif + return true; +} + +std::string get_addr_mem_region(std::vector &info, void *addr) { + for (auto &map: info) { + if (map.start <= (uintptr_t) addr && map.end > (uintptr_t) 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; + } + } + return ""; +} + +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; +} + +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); + 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; +} + +void align_stack(struct user_regs_struct ®s, long preserve) { + regs.REG_SP = (regs.REG_SP - preserve) & ~0xf; +} + +void *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 = reinterpret_cast(regs.REG_SP); + if (!write_proc(pid, addr, str, len)) { + LOGE("failed to write string %s", str); + } + LOGD("pushed string %p", 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); + LOGD("call %d args", args.size()); + for (auto &a: args) { + LOGD("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"); + } + } + 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"); + } + regs.REG_IP = func_addr; +#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"); + } + } + 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"); + } + regs.REG_IP = func_addr; +#elif defined(__aarch64__) + for (int i = 0; i < args.size() && i < 8; 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); + } + regs.regs[30] = return_addr; + regs.REG_IP = func_addr; +#elif defined(__arm__) + for (int i = 0; i < args.size() && i < 4; 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); + } + 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; + } else { + regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK; + } +#endif + if (!set_regs(pid, regs)) { + LOGE("failed to set regs"); + return 0; + } + ptrace(PTRACE_CONT, pid, 0, 0); + int status; + if (waitpid(pid, &status, __WALL) == -1) { + PLOGE("wait"); + } + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) { + if (!get_regs(pid, regs)) { + LOGE("failed to get regs after call"); + return 0; + } + if (regs.REG_IP != return_addr) { + LOGE("wrong return addr %p", (void *) regs.REG_IP); + return 0; + } + return regs.REG_RET; + } else { + LOGE("stopped by other reason %d", status); + } + 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; +} + +int wait_pid(int pid, int* status, int flags) { + while (true) { + auto result = waitpid(pid, status, flags); + if (result == -1 && errno == EINTR) continue; + return result; + } +} + +std::string parse_status(int status) { + std::ostringstream os; + os << "status " << std::hex << status; + os << 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"; + auto stop_sig = WSTOPSIG(status); + os << "signal " << sigabbrev_np(stop_sig) << "(" << stop_sig << "),"; + os << "event=" << parse_ptrace_event(status); + } else { + os << "unknown"; + } + return os.str(); +} diff --git a/loader/src/ptracer/utils.hpp b/loader/src/ptracer/utils.hpp new file mode 100644 index 0000000..09a4c45 --- /dev/null +++ b/loader/src/ptracer/utils.hpp @@ -0,0 +1,114 @@ +#pragma once +#include +#include + +#include "daemon.h" + +#ifdef __LP64__ +#define LOG_TAG "zygisk-ptracer64" +#else +#define LOG_TAG "zygisk-ptracer32" +#endif +#include "logging.h" + +struct MapInfo { + /// \brief The start address of the memory region. + uintptr_t start; + /// \brief The end address of the memory region. + uintptr_t end; + /// \brief The permissions of the memory region. This is a bit mask of the following values: + /// - PROT_READ + /// - PROT_WRITE + /// - PROT_EXEC + uint8_t perms; + /// \brief Whether the memory region is private. + bool is_private; + /// \brief The offset of the memory region. + uintptr_t offset; + /// \brief The device number of the memory region. + /// Major can be obtained by #major() + /// Minor can be obtained by #minor() + dev_t dev; + /// \brief The inode number of the memory region. + ino_t inode; + /// \brief The path of the memory region. + std::string path; + + /// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries. + /// This is useful to find out the inode of the library to hook. + /// \return A list of \ref MapInfo entries. + [[maybe_unused, gnu::visibility("default")]] static std::vector Scan(const std::string& pid = "self"); +}; + +#if defined(__x86_64__) +#define REG_SP rsp +#define REG_IP rip +#define REG_RET rax +#elif defined(__i386__) +#define REG_SP esp +#define REG_IP eip +#define REG_RET eax +#elif defined(__aarch64__) +#define REG_SP sp +#define REG_IP pc +#define REG_RET regs[0] +#elif defined(__arm__) +#define REG_SP uregs[13] +#define REG_IP uregs[15] +#define REG_RET uregs[0] +#define user_regs_struct user_regs +#endif + +ssize_t write_proc(int pid, uintptr_t *remote_addr, const void *buf, size_t len); + +ssize_t read_proc(int pid, uintptr_t *remote_addr, void *buf, size_t len); + +bool get_regs(int pid, struct user_regs_struct ®s); + +bool set_regs(int pid, struct user_regs_struct ®s); + +std::string get_addr_mem_region(std::vector &info, void *addr); + +void *find_module_base(std::vector &info, std::string_view suffix); + +void *find_func_addr( + std::vector &local_info, + std::vector &remote_info, + std::string_view module, + std::string_view func); + +void align_stack(struct user_regs_struct ®s, long preserve = 0); + +void *push_string(int pid, struct user_regs_struct ®s, const char *str); + +uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr, + std::vector &args); + +int fork_dont_care(); + +int wait_pid(int pid, int* status, int flags); + +std::string parse_status(int status); + +#define CASE_CONST_RETURN(x) case x: return #x; + +inline const char* parse_ptrace_event(int status) { + status = status >> 16; + switch (status) { + CASE_CONST_RETURN(PTRACE_EVENT_FORK) + CASE_CONST_RETURN(PTRACE_EVENT_VFORK) + CASE_CONST_RETURN(PTRACE_EVENT_CLONE) + CASE_CONST_RETURN(PTRACE_EVENT_EXEC) + CASE_CONST_RETURN(PTRACE_EVENT_VFORK_DONE) + CASE_CONST_RETURN(PTRACE_EVENT_EXIT) + CASE_CONST_RETURN(PTRACE_EVENT_SECCOMP) + CASE_CONST_RETURN(PTRACE_EVENT_STOP) + default: + return "(no event)"; + } +} + +inline const char* sigabbrev_np(int sig) { + if (sig > 0 && sig < NSIG) return sys_signame[sig]; + return "(unknown)"; +} diff --git a/module/src/customize.sh b/module/src/customize.sh index fb341ed..1816dbc 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -53,9 +53,9 @@ VERSION=$(grep_prop version "${TMPDIR}/module.prop") ui_print "- Installing Zygisk Next $VERSION" # check android -if [ "$API" -lt 29 ]; then +if [ "$API" -lt 30 ]; then ui_print "! Unsupported sdk: $API" - abort "! Minimal supported sdk is 29 (Android 10)" + abort "! Minimal supported sdk is 30 (Android 11)" else ui_print "- Device sdk: $API" fi @@ -104,52 +104,53 @@ mv "$TMPDIR/sepolicy.rule" "$MODPATH" HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true mkdir "$MODPATH/bin" -mkdir "$MODPATH/system" -mkdir "$MODPATH/system/lib64" -[ "$HAS32BIT" = true ] && mkdir "$MODPATH/system/lib" +mkdir "$MODPATH/lib" +mkdir "$MODPATH/lib64" if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then if [ "$HAS32BIT" = true ]; then ui_print "- Extracting x86 libraries" extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" - extract "$ZIPFILE" 'lib/x86/libzygisk.so' "$MODPATH/system/lib" true + extract "$ZIPFILE" 'lib/x86/libzygisk.so' "$MODPATH/lib" true ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32" - ln -sf "zygiskd32" "$MODPATH/bin/zygisk-ptrace32" + extract "$ZIPFILE" 'lib/x86/libptracer.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer32" 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/system/lib64" true + extract "$ZIPFILE" 'lib/x86_64/libzygisk.so' "$MODPATH/lib64" true ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd" - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-fuse" ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64" - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-ptrace64" + extract "$ZIPFILE" 'lib/x86_64/libptracer.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer64" else if [ "$HAS32BIT" = true ]; then ui_print "- Extracting arm libraries" extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" - extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk.so' "$MODPATH/system/lib" true + extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk.so' "$MODPATH/lib" true ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32" - ln -sf "zygiskd32" "$MODPATH/bin/zygisk-ptrace32" + extract "$ZIPFILE" 'lib/armeabi-v7a/libptracer.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer32" 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/system/lib64" true + extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk.so' "$MODPATH/lib64" true ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd" - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-fuse" ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64" - ln -sf "zygiskd64" "$MODPATH/bin/zygisk-ptrace64" + extract "$ZIPFILE" 'lib/arm64-v8a/libptracer.so' "$MODPATH/bin" true + mv "$MODPATH/bin/libptracer.so" "$MODPATH/bin/zygisk-ptracer64" fi ui_print "- Setting permissions" set_perm_recursive "$MODPATH/bin" 0 0 0755 0755 -set_perm_recursive "$MODPATH/system/lib" 0 0 0755 0644 u:object_r:system_lib_file:s0 -set_perm_recursive "$MODPATH/system/lib64" 0 0 0755 0644 u:object_r:system_lib_file:s0 +set_perm_recursive "$MODPATH/lib" 0 0 0755 0644 u:object_r:system_lib_file:s0 +set_perm_recursive "$MODPATH/lib64" 0 0 0755 0644 u:object_r:system_lib_file:s0 # If Huawei's Maple is enabled, system_server is created with a special way which is out of Zygisk's control HUAWEI_MAPLE_ENABLED=$(grep_prop ro.maple.enable) diff --git a/module/src/post-fs-data.sh b/module/src/post-fs-data.sh index 1981d28..5a573d8 100644 --- a/module/src/post-fs-data.sh +++ b/module/src/post-fs-data.sh @@ -20,5 +20,27 @@ if [ "$(which magisk)" ]; then done fi -[ "$DEBUG" = true ] && export RUST_BACKTRACE=1 -unshare -m sh -c "bin/zygisk-fuse &" +create_sys_perm() { + mkdir -p $1 + chmod 555 $1 + chcon u:object_r:system_file:s0 $1 +} + +create_sys_perm /dev/zygisk + +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 + diff --git a/zygiskd/Cargo.toml b/zygiskd/Cargo.toml index 8d7877d..98003e9 100644 --- a/zygiskd/Cargo.toml +++ b/zygiskd/Cargo.toml @@ -23,10 +23,6 @@ proc-maps = "0.3" rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] } tokio = { version = "1.28", features = ["full"] } -binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" } -fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false } -ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" } - [profile.release] strip = true opt-level = "z" diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index f367e90..43e4536 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -22,8 +22,6 @@ pub const PATH_ZYGISK_LIB: &str = concatcp!(lp_select!("/system/lib", "/system/l pub const PATH_WORK_DIR: &str = "/dev/zygisk"; // TODO: Replace with /debug_ramdisk/zygisk pub const PATH_PROP_OVERLAY: &str = concatcp!(PATH_WORK_DIR, "/module.prop"); pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, lp_select!("/cp32.sock", "/cp64.sock")); -pub const PATH_FUSE_DIR: &str = concatcp!(PATH_WORK_DIR, "/fuse"); -pub const PATH_FUSE_PCL: &str = concatcp!(PATH_FUSE_DIR, "/preloaded-classes"); pub const PATH_MODULES_DIR: &str = ".."; pub const PATH_MODULE_PROP: &str = "module.prop"; diff --git a/zygiskd/src/fuse.rs b/zygiskd/src/fuse.rs deleted file mode 100644 index 5862a83..0000000 --- a/zygiskd/src/fuse.rs +++ /dev/null @@ -1,205 +0,0 @@ -use std::cmp::min; -use anyhow::{bail, Result}; -use std::ffi::OsStr; -use std::{fs, thread}; -use std::io::Read; -use std::process::{Command, Stdio}; -use std::sync::{mpsc, Mutex}; -use std::time::{Duration, SystemTime}; -use fuser::{FileAttr, Filesystem, FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, ReplyOpen, Request}; -use libc::ENOENT; -use log::{error, info}; -use rustix::fs::UnmountFlags; -use rustix::mount::{mount_bind, unmount}; -use rustix::path::Arg; -use crate::constants; -use crate::utils::LateInit; - -pub struct DelegateFilesystem; - -const fn attr(inode: u64, size: u64, kind: FileType) -> FileAttr { - FileAttr { - ino: inode, - size, - blocks: 0, - atime: SystemTime::UNIX_EPOCH, - mtime: SystemTime::UNIX_EPOCH, - ctime: SystemTime::UNIX_EPOCH, - crtime: SystemTime::UNIX_EPOCH, - kind, - perm: 0o644, - nlink: 0, - uid: 0, - gid: 0, - rdev: 0, - blksize: 0, - flags: 0, - } -} - -const INO_DIR: u64 = 1; -const INO_PCL: u64 = 2; - -static ATTR_DIR: FileAttr = attr(INO_DIR, 0, FileType::Directory); -static ATTR_PCL: LateInit = LateInit::new(); - -static PCL_CONTENT: LateInit> = LateInit::new(); - -const ENTRIES: &[(u64, FileType, &str)] = &[ - (INO_DIR, FileType::Directory, "."), - (INO_DIR, FileType::Directory, ".."), - (INO_PCL, FileType::RegularFile, "preloaded-classes"), -]; - -const TTL: Duration = Duration::from_secs(1); - -fn ptrace_zygote64(pid: u32) -> Result<()> { - static LAST: Mutex = Mutex::new(0); - - let mut last = LAST.lock().unwrap(); - if *last == pid { - return Ok(()); - } - *last = pid; - let (sender, receiver) = mpsc::channel::<()>(); - - let worker = move || -> Result<()> { - let mut child = Command::new(constants::PATH_PTRACE_BIN64).stdout(Stdio::piped()).arg(format!("{}", pid)).spawn()?; - child.stdout.as_mut().unwrap().read_exact(&mut [0u8; 1])?; - info!("child attached"); - sender.send(())?; - let result = child.wait()?; - info!("ptrace64 process status {}", result); - Ok(()) - }; - - thread::spawn(move || { - if let Err(e) = worker() { - error!("Crashed: {:?}", e); - } - }); - - receiver.recv()?; - Ok(()) -} - -fn ptrace_zygote32(pid: u32) -> Result<()> { - static LAST: Mutex = Mutex::new(0); - - let mut last = LAST.lock().unwrap(); - if *last == pid { - return Ok(()); - } - *last = pid; - let (sender, receiver) = mpsc::channel::<()>(); - - let worker = move || -> Result<()> { - let mut child = Command::new(constants::PATH_PTRACE_BIN32).stdout(Stdio::piped()).arg(format!("{}", pid)).spawn()?; - child.stdout.as_mut().unwrap().read_exact(&mut [0u8; 1])?; - info!("child attached"); - sender.send(())?; - let result = child.wait()?; - info!("ptrace32 process status {}", result); - Ok(()) - }; - - thread::spawn(move || { - if let Err(e) = worker() { - error!("Crashed: {:?}", e); - } - }); - - receiver.recv()?; - Ok(()) -} - -impl Filesystem for DelegateFilesystem { - fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { - if parent != INO_DIR { - reply.error(ENOENT); - return; - } - match name.as_str().unwrap() { - "preloaded-classes" => reply.entry(&TTL, &ATTR_PCL, 0), - _ => reply.error(ENOENT), - } - } - - fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { - match ino { - INO_DIR => reply.attr(&TTL, &ATTR_DIR), - INO_PCL => reply.attr(&TTL, &ATTR_PCL), - _ => reply.error(ENOENT), - } - } - - fn open(&mut self, req: &Request<'_>, ino: u64, _flags: i32, reply: ReplyOpen) { - if ino == INO_PCL { - let pid = req.pid(); - let process = format!("/proc/{}/cmdline", pid); - let process = fs::read_to_string(process).unwrap(); - let process = &process[..process.find('\0').unwrap()]; - info!("Process {} is reading preloaded-classes", process); - match process { - "zygote64" => ptrace_zygote64(pid).unwrap(), - "zygote" => ptrace_zygote32(pid).unwrap(), - _ => (), - } - } - reply.opened(0, 0); - } - - fn read(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, size: u32, _flags: i32, _lock_owner: Option, reply: ReplyData) { - let offset = offset as usize; - let size = size as usize; - if ino == INO_PCL { - let len = PCL_CONTENT.len(); - if offset >= len { - reply.data(&[]); - } else { - let end = min(offset + size, len); - reply.data(&PCL_CONTENT[offset..end]); - } - } else { - reply.error(ENOENT); - } - } - - fn readdir(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) { - if ino != INO_DIR { - reply.error(ENOENT); - return; - } - for (i, entry) in ENTRIES.iter().enumerate().skip(offset as usize) { - if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) { - break; - } - } - reply.ok(); - } -} - -pub fn main() -> Result<()> { - info!("Start zygisk fuse"); - fs::create_dir(constants::PATH_WORK_DIR)?; - fs::create_dir(constants::PATH_FUSE_DIR)?; - PCL_CONTENT.init(fs::read(constants::PATH_PCL)?); - ATTR_PCL.init(attr(INO_PCL, PCL_CONTENT.len() as u64, FileType::RegularFile)); - let options = [ - fuser::MountOption::FSName(String::from("zygisk")), - fuser::MountOption::AllowOther, - fuser::MountOption::RO, - ]; - let session = fuser::spawn_mount2( - DelegateFilesystem, - constants::PATH_FUSE_DIR, - &options, - )?; - mount_bind(constants::PATH_FUSE_PCL, constants::PATH_PCL)?; - let crash = session.guard.join(); - unmount(constants::PATH_PCL, UnmountFlags::DETACH)?; - match crash { - Err(e) => bail!("Fuse mount crashed: {:?}", e), - _ => bail!("Fuse mount exited unexpectedly"), - } -} diff --git a/zygiskd/src/main.rs b/zygiskd/src/main.rs index 4bd032d..cb5a0f5 100644 --- a/zygiskd/src/main.rs +++ b/zygiskd/src/main.rs @@ -1,7 +1,5 @@ mod constants; mod dl; -mod fuse; -mod ptrace; mod root_impl; mod utils; mod watchdog; @@ -28,9 +26,7 @@ fn start(name: &str) -> Result<()> { root_impl::setup(); match name.trim_start_matches("zygisk-") { "wd" => async_start(watchdog::main())?, - "fuse" => fuse::main()?, lp_select!("cp32", "cp64") => zygiskd::main()?, - lp_select!("ptrace32", "ptrace64") => ptrace::main()?, _ => println!("Available commands: wd, fuse, cp, ptrace"), } Ok(()) diff --git a/zygiskd/src/ptrace.rs b/zygiskd/src/ptrace.rs deleted file mode 100644 index d91fb9f..0000000 --- a/zygiskd/src/ptrace.rs +++ /dev/null @@ -1,233 +0,0 @@ -use log::{debug, info}; -use std::ffi::CString; -use std::env; -use std::io::Write; -use rustix::path::Arg; -use proc_maps::{get_process_maps, MapRange, Pid}; -use ptrace_do::{RawProcess, TracedProcess}; -use rustix::process::getpid; -use crate::{constants, lp_select}; -use anyhow::{bail, Result}; - -const ANDROID_LIBC: &str = "bionic/libc.so"; -const ANDROID_LIBDL: &str = "bionic/libdl.so"; - -fn find_module_for_pid(pid: Pid, library: &str) -> Result { - let maps = get_process_maps(pid)?; - for map in maps.into_iter() { - if let Some(p) = map.filename() { - if p.as_str()?.contains(library) { - return Ok(map); - } - } - } - bail!("Cannot find module {library} for pid {pid}"); -} - -fn find_remote_procedure( - pid: Pid, - library: &str, - local_addr: usize, -) -> Result { - let local_module = find_module_for_pid(getpid().as_raw_nonzero().get(), library)?; - debug!( - "Identifed local range {library} ({:?}) at {:x}", - local_module.filename(), - local_module.start() - ); - - let remote_module = find_module_for_pid(pid, library)?; - debug!( - "Identifed remote range {library} ({:?}) at {:x}", - remote_module.filename(), - remote_module.start() - ); - - Ok(local_addr - local_module.start() + remote_module.start()) -} - -fn ptrace_zygote(pid: u32) -> Result<()> { - info!("Injecting into pid {}", pid); - let zygisk_lib = CString::new(constants::PATH_ZYGISK_LIB)?; - let libc_base = find_module_for_pid(pid as i32, ANDROID_LIBC)?.start(); - let mmap_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBC, - libc::mmap as usize, - )?; - let munmap_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBC, - libc::munmap as usize, - )?; - let dlopen_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBDL, - libc::dlopen as usize, - )?; - let dlsym_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBDL, - libc::dlsym as usize, - )?; - let errno_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBC, - libc::__errno as usize, - )?; - let dlerror_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBDL, - libc::dlerror as usize, - )?; - let strlen_remote = find_remote_procedure( - pid as i32, - ANDROID_LIBC, - libc::strlen as usize, - )?; - - let tracer = TracedProcess::attach(RawProcess::new(pid as i32))?; - std::io::stdout().write(b"1")?; - info!("attached process {}", pid); - std::io::stdout().flush()?; - let frame = tracer.next_frame()?; - debug!("Waited for a frame"); - - // Map a buffer in the remote process - debug!("remote mmap addr {:x}", mmap_remote); - let mmap_params: [usize; 6] = [ - 0, - 0x1000, - (libc::PROT_READ | libc::PROT_WRITE) as usize, - (libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as usize, - 0, - 0, - ]; - let mut arr: Vec = Vec::new(); - for p in mmap_params { - arr.extend_from_slice(&p.to_le_bytes()); - } - arr.as_slice(); - let (regs, mut frame) = frame.invoke_remote( - mmap_remote, - libc_base, - &mmap_params, - )?; - let buf_addr = regs.return_value(); - debug!("remote stopped at addr {:x}", regs.program_counter()); - if regs.program_counter() != libc_base { - let data = std::mem::MaybeUninit::::uninit(); - let siginfo = unsafe { - libc::ptrace(libc::PTRACE_GETSIGINFO, pid, 0, &data); - data.assume_init() - }; - bail!( - "stopped at unexpected addr {:x} signo {} si_code {} si_addr {:?}", - regs.program_counter(), - siginfo.si_signo, - siginfo.si_code, - unsafe { siginfo.si_addr() }, - ); - } - if buf_addr == usize::MAX { - debug!("errno remote {:x}", errno_remote); - let (regs, frame) = frame.invoke_remote( - errno_remote, - libc_base, - &[], - )?; - debug!("errno called"); - if regs.program_counter() != libc_base { - bail!("stopped at unexpected addr {:x} when getting errno", regs.program_counter()); - } - let err_addr = regs.return_value(); - let mut buf = [0u8; 4]; - frame.read_memory_mut(err_addr, &mut buf)?; - let err = i32::from_le_bytes(buf); - bail!("remote failed with {}", err); - } - debug!("Buffer addr: {:x}", buf_addr); - - // Load zygisk into remote process - frame.write_memory(buf_addr, zygisk_lib.as_bytes_with_nul())?; - let (regs, mut frame) = frame.invoke_remote( - dlopen_remote, - libc_base, - &[buf_addr, libc::RTLD_NOW as usize], - )?; - let handle = regs.return_value(); - debug!("Load zygisk into remote process: {:x}", handle); - if regs.program_counter() != libc_base { - let data = std::mem::MaybeUninit::::uninit(); - let siginfo = unsafe { - libc::ptrace(libc::PTRACE_GETSIGINFO, pid, 0, &data); - data.assume_init() - }; - bail!( - "stopped at unexpected addr {:x} signo {} si_code {} si_addr {:?}", - regs.program_counter(), - siginfo.si_signo, - siginfo.si_code, - unsafe { siginfo.si_addr() }, - ); - } - if handle == 0 { - debug!("got handle 0"); - let (regs, frame) = frame.invoke_remote( - dlerror_remote, - libc_base, - &[], - )?; - let err_addr = regs.return_value(); - if err_addr == 0 { - bail!("dlerror err addr 0"); - } - debug!("err addr {:x}", err_addr); - let (regs, frame) = frame.invoke_remote( - strlen_remote, - libc_base, - &[err_addr], - )?; - let len = regs.return_value(); - if len == 0 { - bail!("dlerror len 0"); - } - debug!("err len {}", len); - let mut buf = vec![0u8; len]; - frame.read_memory_mut(err_addr, buf.as_mut_slice())?; - bail!("err {:?}", buf); - } - - let entry = CString::new("entry")?; - frame.write_memory(buf_addr, entry.as_bytes_with_nul())?; - let (regs, frame) = frame.invoke_remote( - dlsym_remote, - libc_base, - &[handle, buf_addr], - )?; - let entry = regs.return_value(); - debug!("Call zygisk entry: {:x}", entry); - let (_, frame) = frame.invoke_remote( - entry, - libc_base, - &[handle], - )?; - - // Cleanup - let _ = frame.invoke_remote( - munmap_remote, - libc_base, - &[buf_addr], - )?; - debug!("Cleaned up"); - Ok(()) -} - -pub fn main() -> Result<()> { - info!("Start zygisk ptrace"); - let args: Vec = env::args().collect(); - let pid = args[1].parse::().unwrap(); - info!("ptracing {} pid {}", lp_select!("zygote32", "zygote64"), pid); - ptrace_zygote(pid)?; - Ok(()) -} diff --git a/zygiskd/src/watchdog.rs b/zygiskd/src/watchdog.rs index cd2f38e..2dabfbb 100644 --- a/zygiskd/src/watchdog.rs +++ b/zygiskd/src/watchdog.rs @@ -4,8 +4,6 @@ use std::fs; use std::future::Future; use std::io::{BufRead, BufReader, Write}; use std::pin::Pin; -use std::time::Duration; -use binder::IBinder; use futures::stream::FuturesUnordered; use futures::StreamExt; use log::{debug, error, info}; @@ -128,27 +126,6 @@ async fn spawn_daemon() -> Result<()> { futures.push(Box::pin(spawn_daemon(it))); } - async fn binder_listener() -> Result<()> { - let mut binder = loop { - match binder::get_service("activity") { - Some(binder) => break binder, - None => { - log::trace!("System server not ready, wait for 1s..."); - tokio::time::sleep(Duration::from_secs(1)).await; - } - }; - }; - - info!("System server ready"); - - loop { - if binder.ping_binder().is_err() { break; } - tokio::time::sleep(Duration::from_secs(1)).await; - } - bail!("System server died"); - } - futures.push(Box::pin(binder_listener())); - if let Err(e) = futures.next().await.unwrap() { error!("{}", e); }