From 9810eb3974286203b8c01ffc34b20a82a328c21d Mon Sep 17 00:00:00 2001 From: nampud Date: Sun, 29 Jun 2025 08:41:42 +0200 Subject: [PATCH] improve: `ptrace_message` reset with `seccomp` (#191) This commit improves how "ptrace_message" is cleaned by utilizing seccomp (Secure Computation Mode) to clear its value. "ptrace_message" not being cleared is a Linux kernel vulnerability/bug that impacts all versions below 6.1, as the fix only came in 6.1, and because of that, we need to find ways to "0" it. This is the second fix for that, being the GKI2 fix the first: https://github.com/PerformanC/ReZygisk/commit/70697be9a5230b3e254a9df68b8cba599dbbab90 --- loader/src/injector/clear.c | 97 ++++++++++++++++++++++++++++++++++ loader/src/injector/entry.cpp | 1 + loader/src/injector/zygisk.hpp | 2 + loader/src/ptracer/ptracer.c | 2 +- loader/src/ptracer/utils.c | 34 +++++++++++- loader/src/ptracer/utils.h | 4 ++ 6 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 loader/src/injector/clear.c diff --git a/loader/src/injector/clear.c b/loader/src/injector/clear.c new file mode 100644 index 0000000..fde5fa4 --- /dev/null +++ b/loader/src/injector/clear.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" + +static bool seccomp_filters_visible() { + FILE *status_file = fopen("/proc/self/status", "r"); + if (!status_file) { + return true; + } + + const char *needle = "Seccomp_filters:"; + char line[256]; + + while (fgets(line, sizeof(line), status_file)) { + if (strncmp(line, needle, strlen(needle)) == 0) { + fclose(status_file); + return true; + } + } + + fclose(status_file); + return false; +} + +void send_seccomp_event() { + if (seccomp_filters_visible()) { + return; + } + + __u32 args[4] = {0}; + + int rnd_fd = open("/dev/urandom", O_RDONLY); + if (rnd_fd == -1) { + PLOGE("send_seccomp_event: open(/dev/urandom)"); + return; + } + + if (read(rnd_fd, &args, sizeof(args)) != sizeof(args)) { + PLOGE("send_seccomp_event: read(rnd_fd)"); + close(rnd_fd); + return; + } + + close(rnd_fd); + + args[0] |= 0x10000; + + struct sock_filter filter[] = { + /* INFO: Check syscall number */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit_group, 0, 9), + + /* INFO: Load and check arg0 (lower 32 bits) */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[0], 0, 7), + + /* INFO: Load and check arg1 (lower 32 bits) */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[1])), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[1], 0, 5), + + /* INFO: Load and check arg2 (lower 32 bits) */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[2])), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[2], 0, 3), + + /* INFO: Load and check arg3 (lower 32 bits) */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[3])), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, args[3], 0, 1), + + /* INFO: All match: return TRACE => will trigger PTRACE_EVENT_SECCOMP */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE), + + /* INFO: Default: allow */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .filter = filter, + }; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + PLOGE("send_seccomp_event: prctl(SECCOMP)"); + return; + } + + /* INFO: This will trigger a ptrace event, syscall will not execute due to tracee_skip_syscall */ + syscall(__NR_exit_group, args[0], args[1], args[2], args[3]); +} diff --git a/loader/src/injector/entry.cpp b/loader/src/injector/entry.cpp index c2e008e..295db9f 100644 --- a/loader/src/injector/entry.cpp +++ b/loader/src/injector/entry.cpp @@ -25,4 +25,5 @@ void entry(void* addr, size_t size, const char* path) { void *module_addrs[1] = { addr }; clean_trace(path, module_addrs, 1, 1, 0, false); + send_seccomp_event(); } diff --git a/loader/src/injector/zygisk.hpp b/loader/src/injector/zygisk.hpp index 8051de6..4b82768 100644 --- a/loader/src/injector/zygisk.hpp +++ b/loader/src/injector/zygisk.hpp @@ -8,3 +8,5 @@ extern size_t block_size; void hook_functions(); void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload, bool spoof_maps); + +extern "C" void send_seccomp_event(); diff --git a/loader/src/ptracer/ptracer.c b/loader/src/ptracer/ptracer.c index 71d6c63..03dd1d9 100644 --- a/loader/src/ptracer/ptracer.c +++ b/loader/src/ptracer/ptracer.c @@ -403,7 +403,7 @@ bool trace_zygote(int pid) { int status; - if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL) == -1) { + if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACESECCOMP) == -1) { PLOGE("seize"); return false; diff --git a/loader/src/ptracer/utils.c b/loader/src/ptracer/utils.c index 76aae16..dc56f6e 100644 --- a/loader/src/ptracer/utils.c +++ b/loader/src/ptracer/utils.c @@ -522,6 +522,32 @@ int fork_dont_care() { return pid; } +void tracee_skip_syscall(int pid) { + struct user_regs_struct regs; + if (!get_regs(pid, ®s)) { + LOGE("failed to get seccomp regs"); + exit(1); + } + regs.REG_SYSNR = -1; + if (!set_regs(pid, ®s)) { + LOGE("failed to set seccomp regs"); + exit(1); + } + + /* INFO: It might not work, don't check for error */ +#if defined(__aarch64__) + int sysnr = -1; + struct iovec iov = { + .iov_base = &sysnr, + .iov_len = sizeof (int), + }; + ptrace(PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, &iov); +#elif defined(__arm__) + ptrace(PTRACE_SET_SYSCALL, pid, 0, (void*) -1); +#endif + +} + void wait_for_trace(int pid, int *status, int flags) { while (1) { pid_t result = waitpid(pid, status, flags); @@ -532,7 +558,13 @@ void wait_for_trace(int pid, int *status, int flags) { exit(1); } - if (!WIFSTOPPED(*status)) { + if (*status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { + tracee_skip_syscall(pid); + + ptrace(PTRACE_CONT, pid, 0, 0); + + continue; + } else if (!WIFSTOPPED(*status)) { char status_str[64]; parse_status(*status, status_str, sizeof(status_str)); diff --git a/loader/src/ptracer/utils.h b/loader/src/ptracer/utils.h index 4d469ac..4a80e40 100644 --- a/loader/src/ptracer/utils.h +++ b/loader/src/ptracer/utils.h @@ -37,18 +37,22 @@ void free_maps(struct maps *maps); #define REG_SP rsp #define REG_IP rip #define REG_RET rax + #define REG_SYSNR orig_rax #elif defined(__i386__) #define REG_SP esp #define REG_IP eip #define REG_RET eax + #define REG_SYSNR orig_eax #elif defined(__aarch64__) #define REG_SP sp #define REG_IP pc #define REG_RET regs[0] + #define REG_SYSNR regs[8] #elif defined(__arm__) #define REG_SP uregs[13] #define REG_IP uregs[15] #define REG_RET uregs[0] + #define REG_SYSNR uregs[7] #define user_regs_struct user_regs #endif