TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
566 lines
19 KiB
C++
566 lines
19 KiB
C++
/*
|
||
* TNG eWallet — Zygisk companion.
|
||
*
|
||
* stable: PLT + ABRT/SIGTRAP swallow + exit_group seccomp@400ms.
|
||
* Promon worker SIGSEGV (libtngdigital_ewallet.so null deref): LR-return skip (cap N).
|
||
* pc==lr 循环 SEGV 也 skip;libc++abi __cxa_guard_acquire → SIGABRT 吞掉。
|
||
*/
|
||
#include <android/log.h>
|
||
#include <errno.h>
|
||
#include <linux/audit.h>
|
||
#include <linux/filter.h>
|
||
#include <linux/seccomp.h>
|
||
#include <pthread.h>
|
||
#include <signal.h>
|
||
#include <stddef.h>
|
||
#include <sys/mman.h>
|
||
#include <sys/prctl.h>
|
||
#include <sys/syscall.h>
|
||
#include <sys/sysmacros.h>
|
||
#include <ucontext.h>
|
||
#include <unistd.h>
|
||
|
||
#include <atomic>
|
||
#include <cstdint>
|
||
#include <cstdio>
|
||
#include <cstring>
|
||
|
||
#include "zygisk.hpp"
|
||
|
||
#define SC_RET_ALLOW 0x7fff0000U
|
||
#define SC_RET_ERRNO_EPERM (0x00050000U | 1U)
|
||
|
||
#define LOG_TAG "TngExitGuard"
|
||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||
|
||
static constexpr const char *kTargetPkg = "my.com.tngdigital.ewallet";
|
||
static constexpr const char *kPromonSo = "libtngdigital_ewallet.so";
|
||
static bool g_enabled = false;
|
||
static zygisk::Api *g_api = nullptr;
|
||
static std::atomic<int> g_cxx_plt{0};
|
||
static std::atomic<int> g_seccomp_ok{0};
|
||
static std::atomic<int> g_stack_chk{0};
|
||
static std::atomic<int> g_promon_segv{0};
|
||
static std::atomic<uintptr_t> g_promon_start{0};
|
||
static std::atomic<uintptr_t> g_promon_end{0};
|
||
static std::atomic<pid_t> g_main_tid{0};
|
||
static std::atomic<int> g_abrt_swallow{0};
|
||
static std::atomic<pid_t> g_abrt_last_tid{0};
|
||
static std::atomic<uintptr_t> g_abrt_last_pc{0};
|
||
static std::atomic<int> g_abrt_streak{0};
|
||
/* 隔离进程 :goacqowmmt 会循环 SEGV;主进程 worker 也狂刷。cap 后 freeze 该线程 */
|
||
static constexpr int kMaxPromonSegvSkip = 200;
|
||
static constexpr int kMaxAbrtStreak = 3;
|
||
static std::atomic<int> g_soft_sig_logged{0};
|
||
|
||
static void freeze_forever() {
|
||
for (;;) pause();
|
||
}
|
||
|
||
static void refresh_promon_so_range() {
|
||
FILE *fp = fopen("/proc/self/maps", "r");
|
||
if (!fp) return;
|
||
char line[1024];
|
||
uintptr_t start = 0, end = 0;
|
||
while (fgets(line, sizeof(line), fp)) {
|
||
unsigned long s = 0, e = 0;
|
||
char path[512] = {};
|
||
int n = sscanf(line, "%lx-%lx %*s %*s %*s %*s %511[^\n]", &s, &e, path);
|
||
if (n < 3) continue;
|
||
char *p = path;
|
||
while (*p == ' ') ++p;
|
||
if (strstr(p, kPromonSo) == nullptr) continue;
|
||
if (start == 0 || s < start) start = s;
|
||
if (e > end) end = e;
|
||
}
|
||
fclose(fp);
|
||
if (start != 0 && end > start) {
|
||
g_promon_start.store(start);
|
||
g_promon_end.store(end);
|
||
}
|
||
}
|
||
|
||
static bool pc_in_promon_so(uintptr_t pc) {
|
||
uintptr_t start = g_promon_start.load();
|
||
uintptr_t end = g_promon_end.load();
|
||
return start != 0 && pc >= start && pc < end;
|
||
}
|
||
|
||
static void promon_segv_handler(int sig, siginfo_t *info, void *ctx) {
|
||
(void)sig;
|
||
(void)info;
|
||
ucontext_t *uc = reinterpret_cast<ucontext_t *>(ctx);
|
||
#if defined(__aarch64__)
|
||
uintptr_t pc = uc->uc_mcontext.pc;
|
||
uintptr_t lr = uc->uc_mcontext.regs[30];
|
||
#else
|
||
uintptr_t pc = 0;
|
||
uintptr_t lr = 0;
|
||
#endif
|
||
refresh_promon_so_range();
|
||
/* Promon 典型 pc==lr 自旋 null deref;maps 尚未刷新时也按此 skip */
|
||
if (pc != 0 && pc == lr) {
|
||
int n = ++g_promon_segv;
|
||
if (n <= 3 || n % 50 == 0) {
|
||
LOGI("promon pc==lr SIGSEGV tid=%d pc=%lx n=%d — skip to pc+4",
|
||
(int)gettid(), (unsigned long)pc, n);
|
||
}
|
||
uc->uc_mcontext.pc = pc + 4;
|
||
return;
|
||
}
|
||
if (pc_in_promon_so(pc) || pc_in_promon_so(lr)) {
|
||
int n = ++g_promon_segv;
|
||
uintptr_t target = lr;
|
||
if (target == 0 || target == pc) {
|
||
target = pc + 4;
|
||
}
|
||
if (kMaxPromonSegvSkip <= 0 || n <= kMaxPromonSegvSkip) {
|
||
if (n <= 3 || n % 50 == 0) {
|
||
LOGI("promon SIGSEGV tid=%d pc=%lx lr=%lx n=%d — skip to %lx",
|
||
(int)gettid(), (unsigned long)pc, (unsigned long)lr, n,
|
||
(unsigned long)target);
|
||
}
|
||
uc->uc_mcontext.pc = target;
|
||
return;
|
||
}
|
||
LOGI("promon SIGSEGV tid=%d n=%d — cap hit, freeze", (int)gettid(), n);
|
||
freeze_forever();
|
||
}
|
||
/* 1.9.10:Login 后出现 Promon so 外 SEGV → 旧逻辑 re-raise 直接闪退 */
|
||
{
|
||
int n = ++g_promon_segv;
|
||
pid_t tid = gettid();
|
||
if (n <= 5 || n % 50 == 0) {
|
||
LOGI("non-promon SIGSEGV tid=%d pc=%lx lr=%lx n=%d — pc+4",
|
||
(int)tid, (unsigned long)pc, (unsigned long)lr, n);
|
||
}
|
||
if (n > 200 && tid != g_main_tid.load()) {
|
||
LOGI("non-promon SEGV storm tid=%d — freeze", (int)tid);
|
||
freeze_forever();
|
||
}
|
||
if (pc != 0) {
|
||
uc->uc_mcontext.pc = pc + 4;
|
||
return;
|
||
}
|
||
}
|
||
freeze_forever();
|
||
}
|
||
|
||
/**
|
||
* ABRT/TRAP:一律 pc+4。跳远距 LR 会弄坏主线程 Looper(闪退观感)。
|
||
* 工作线程同 PC 连 abort 超限 → freeze;主线程始终 pc+4。
|
||
*/
|
||
static void fatal_skip_handler(int sig, siginfo_t *info, void *ctx) {
|
||
(void)info;
|
||
ucontext_t *uc = reinterpret_cast<ucontext_t *>(ctx);
|
||
#if defined(__aarch64__)
|
||
uintptr_t pc = uc->uc_mcontext.pc;
|
||
uintptr_t lr = uc->uc_mcontext.regs[30];
|
||
pid_t tid = gettid();
|
||
|
||
if (sig == SIGABRT) {
|
||
int streak = 1;
|
||
if (g_abrt_last_tid.load() == tid && g_abrt_last_pc.load() == pc) {
|
||
streak = g_abrt_streak.fetch_add(1) + 1;
|
||
} else {
|
||
g_abrt_last_tid.store(tid);
|
||
g_abrt_last_pc.store(pc);
|
||
g_abrt_streak.store(1);
|
||
}
|
||
if (streak > kMaxAbrtStreak && tid != g_main_tid.load()) {
|
||
LOGI("ABRT streak cap tid=%d pc=%lx n=%d — freeze worker", (int)tid,
|
||
(unsigned long)pc, streak);
|
||
freeze_forever();
|
||
}
|
||
int n = ++g_abrt_swallow;
|
||
if (n <= 5 || n % 50 == 0) {
|
||
LOGI("ABRT pc+4 tid=%d pc=%lx lr=%lx streak=%d", (int)tid,
|
||
(unsigned long)pc, (unsigned long)lr, streak);
|
||
}
|
||
uc->uc_mcontext.pc = pc + 4;
|
||
return;
|
||
}
|
||
|
||
if (sig == SIGTRAP) {
|
||
int n = ++g_abrt_swallow;
|
||
if (n <= 5 || n % 50 == 0) {
|
||
LOGI("TRAP pc+4 tid=%d pc=%lx", (int)tid, (unsigned long)pc);
|
||
}
|
||
uc->uc_mcontext.pc = pc != 0 ? pc + 4 : lr;
|
||
return;
|
||
}
|
||
#endif
|
||
freeze_forever();
|
||
}
|
||
|
||
static void install_fatal_skip_handlers() {
|
||
struct sigaction sa {};
|
||
sa.sa_sigaction = fatal_skip_handler;
|
||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||
sigemptyset(&sa.sa_mask);
|
||
sigaction(SIGABRT, &sa, nullptr);
|
||
sigaction(SIGTRAP, &sa, nullptr);
|
||
LOGI("fatal skip handlers (ABRT/TRAP always pc+4)");
|
||
}
|
||
|
||
static void install_promon_segv_handler() {
|
||
refresh_promon_so_range();
|
||
struct sigaction sa {};
|
||
sa.sa_sigaction = promon_segv_handler;
|
||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||
sigemptyset(&sa.sa_mask);
|
||
if (sigaction(SIGSEGV, &sa, nullptr) != 0) {
|
||
LOGE("SIGSEGV handler install failed errno=%d", errno);
|
||
return;
|
||
}
|
||
LOGI("promon SIGSEGV handler armed range=%lx-%lx",
|
||
(unsigned long)g_promon_start.load(),
|
||
(unsigned long)g_promon_end.load());
|
||
}
|
||
|
||
static void install_soft_signals() {
|
||
struct sigaction sa {};
|
||
sa.sa_sigaction = fatal_skip_handler;
|
||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||
sigemptyset(&sa.sa_mask);
|
||
sigaction(SIGABRT, &sa, nullptr);
|
||
sigaction(SIGTRAP, &sa, nullptr);
|
||
if (g_soft_sig_logged.fetch_add(1) == 0) {
|
||
LOGI("soft signals (ABRT/TRAP always pc+4)");
|
||
}
|
||
}
|
||
|
||
#if defined(__aarch64__)
|
||
#define _BPFI(code, jt, jf, k) \
|
||
((struct sock_filter){(unsigned short)(code), (jt), (jf), (unsigned int)(k)})
|
||
|
||
static int install_seccomp_exit_group_only() {
|
||
struct sock_filter filter[] = {
|
||
_BPFI(BPF_LD | BPF_W | BPF_ABS, 0, 0, offsetof(struct seccomp_data, arch)),
|
||
_BPFI(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, AUDIT_ARCH_AARCH64),
|
||
_BPFI(BPF_RET | BPF_K, 0, 0, SC_RET_ALLOW),
|
||
_BPFI(BPF_LD | BPF_W | BPF_ABS, 0, 0, offsetof(struct seccomp_data, nr)),
|
||
_BPFI(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 94),
|
||
_BPFI(BPF_RET | BPF_K, 0, 0, SC_RET_ERRNO_EPERM),
|
||
_BPFI(BPF_RET | BPF_K, 0, 0, SC_RET_ALLOW),
|
||
};
|
||
struct sock_fprog prog = {
|
||
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
|
||
.filter = filter,
|
||
};
|
||
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||
long rc = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
|
||
SECCOMP_FILTER_FLAG_TSYNC, &prog);
|
||
if (rc != 0) {
|
||
rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
||
if (rc != 0) {
|
||
LOGE("seccomp failed errno=%d", errno);
|
||
return -1;
|
||
}
|
||
LOGI("seccomp exit_group via prctl");
|
||
} else {
|
||
LOGI("seccomp exit_group via TSYNC");
|
||
}
|
||
g_seccomp_ok.store(1);
|
||
return 0;
|
||
}
|
||
#else
|
||
static int install_seccomp_exit_group_only() { return -1; }
|
||
#endif
|
||
|
||
using exit_fn = void (*)(int);
|
||
using kill_fn = int (*)(pid_t, int);
|
||
using tgkill_fn = int (*)(int, int, int);
|
||
using raise_fn = int (*)(int);
|
||
using pthread_kill_fn = int (*)(pthread_t, int);
|
||
using cxa_guard_acquire_fn = int (*)(void *);
|
||
using cxa_guard_abort_fn = void (*)();
|
||
using dlopen_fn = void *(*)(const char *, int);
|
||
|
||
static cxa_guard_acquire_fn orig_cxa_guard_acquire = nullptr;
|
||
static cxa_guard_abort_fn orig_cxa_guard_abort = nullptr;
|
||
static dlopen_fn orig_dlopen = nullptr;
|
||
|
||
static exit_fn orig_exit = nullptr;
|
||
static exit_fn orig__exit = nullptr;
|
||
static void (*orig_abort)() = nullptr;
|
||
static void (*orig_stack_chk_fail)() = nullptr;
|
||
static kill_fn orig_kill = nullptr;
|
||
static tgkill_fn orig_tgkill = nullptr;
|
||
static raise_fn orig_raise = nullptr;
|
||
static pthread_kill_fn orig_pthread_kill = nullptr;
|
||
|
||
static bool deadly(int sig) {
|
||
return sig == SIGKILL || sig == SIGABRT || sig == SIGTERM ||
|
||
sig == SIGTRAP || sig == SIGILL ||
|
||
sig == 9 || sig == 6 || sig == 5 || sig == 4 || sig == 15;
|
||
}
|
||
|
||
static void hooked_exit(int code) { LOGI("blocked exit(%d)", code); }
|
||
static void hooked__exit(int code) { LOGI("blocked _exit(%d)", code); }
|
||
static void hooked_abort() {
|
||
LOGI("blocked abort() tid=%d", (int)gettid());
|
||
}
|
||
static void hooked_stack_chk_fail() {
|
||
int n = ++g_stack_chk;
|
||
LOGI("blocked __stack_chk_fail tid=%d n=%d", (int)gettid(), n);
|
||
}
|
||
static int hooked_raise(int sig) {
|
||
if (deadly(sig)) {
|
||
LOGI("blocked raise(%d)", sig);
|
||
return 0;
|
||
}
|
||
return orig_raise ? orig_raise(sig) : -1;
|
||
}
|
||
static int hooked_kill(pid_t pid, int sig) {
|
||
if (deadly(sig)) {
|
||
LOGI("blocked kill(%d,%d)", (int)pid, sig);
|
||
return 0;
|
||
}
|
||
return orig_kill ? orig_kill(pid, sig) : -1;
|
||
}
|
||
static int hooked_tgkill(int tgid, int tid, int sig) {
|
||
if (deadly(sig)) {
|
||
LOGI("blocked tgkill(%d,%d,%d)", tgid, tid, sig);
|
||
return 0;
|
||
}
|
||
return orig_tgkill ? orig_tgkill(tgid, tid, sig) : -1;
|
||
}
|
||
static int hooked_pthread_kill(pthread_t thread, int sig) {
|
||
if (deadly(sig)) {
|
||
LOGI("blocked pthread_kill(sig=%d)", sig);
|
||
return 0;
|
||
}
|
||
return orig_pthread_kill ? orig_pthread_kill(thread, sig) : -1;
|
||
}
|
||
|
||
/** 仅打断递归初始化;系统 libc++ 静态 ctor(含 gralloc)必须真实执行。 */
|
||
static thread_local void *tl_cxa_guard = nullptr;
|
||
static thread_local int tl_cxa_depth = 0;
|
||
|
||
static int hooked_cxa_guard_acquire(void *guard) {
|
||
if (guard != nullptr && tl_cxa_guard == guard) {
|
||
LOGI("cxa_guard recursive skip tid=%d", (int)gettid());
|
||
return 1;
|
||
}
|
||
if (!orig_cxa_guard_acquire) {
|
||
return 1;
|
||
}
|
||
void *prev = tl_cxa_guard;
|
||
tl_cxa_guard = guard;
|
||
++tl_cxa_depth;
|
||
int r = orig_cxa_guard_acquire(guard);
|
||
--tl_cxa_depth;
|
||
tl_cxa_guard = prev;
|
||
return r;
|
||
}
|
||
static void hooked_cxa_guard_abort() {
|
||
LOGI("blocked __cxa_guard_abort tid=%d depth=%d", (int)gettid(), tl_cxa_depth);
|
||
}
|
||
|
||
static void *hooked_dlopen(const char *name, int flags) {
|
||
void *handle = orig_dlopen ? orig_dlopen(name, flags) : nullptr;
|
||
if (handle != nullptr || name == nullptr) {
|
||
return handle;
|
||
}
|
||
// Compose/HWUI 依赖 libandroid + gralloc mapper;sphal 失败时尝试绝对路径兜底
|
||
if (strstr(name, "libandroid.so") != nullptr
|
||
|| strstr(name, "mapper.pixel") != nullptr
|
||
|| strstr(name, "android.hardware.graphics.mapper") != nullptr) {
|
||
static const char *kFallbacks[] = {
|
||
"/system/lib64/libandroid.so",
|
||
"/system/lib/libandroid.so",
|
||
"/vendor/lib64/hw/mapper.pixel.so",
|
||
"/vendor/lib/hw/mapper.pixel.so",
|
||
"libandroid.so",
|
||
"mapper.pixel.so",
|
||
};
|
||
for (const char *path : kFallbacks) {
|
||
handle = orig_dlopen ? orig_dlopen(path, flags) : nullptr;
|
||
if (handle != nullptr) {
|
||
LOGI("dlopen fallback %s -> %p (from %s)", path, handle, name);
|
||
return handle;
|
||
}
|
||
}
|
||
LOGI("dlopen fallback failed name=%s tid=%d", name, (int)gettid());
|
||
}
|
||
return handle;
|
||
}
|
||
|
||
static bool find_lib_match(const char *suffix, const char *contains,
|
||
dev_t *dev, ino_t *ino) {
|
||
FILE *fp = fopen("/proc/self/maps", "r");
|
||
if (!fp) return false;
|
||
char line[1024];
|
||
bool ok = false;
|
||
while (fgets(line, sizeof(line), fp)) {
|
||
uintptr_t start = 0, end = 0;
|
||
char perms[8] = {};
|
||
unsigned long long offset = 0;
|
||
char deststr[32] = {};
|
||
unsigned long inode = 0;
|
||
char path[512] = {};
|
||
int n = sscanf(line, "%lx-%lx %7s %llx %31s %lu %511[^\n]",
|
||
&start, &end, perms, &offset, &deststr, &inode, path);
|
||
if (n < 7 || inode == 0) continue;
|
||
char *p = path;
|
||
while (*p == ' ') ++p;
|
||
bool match = false;
|
||
if (suffix != nullptr) {
|
||
size_t plen = strlen(p);
|
||
size_t slen = strlen(suffix);
|
||
match = plen >= slen && strcmp(p + plen - slen, suffix) == 0;
|
||
} else if (contains != nullptr) {
|
||
match = strstr(p, contains) != nullptr;
|
||
}
|
||
if (!match) continue;
|
||
unsigned maj = 0, min = 0;
|
||
if (sscanf(deststr, "%x:%x", &maj, &min) != 2) continue;
|
||
*dev = makedev(maj, min);
|
||
*ino = inode;
|
||
ok = true;
|
||
break;
|
||
}
|
||
fclose(fp);
|
||
return ok;
|
||
}
|
||
|
||
static bool find_lib_by_suffix(const char *suffix, dev_t *dev, ino_t *ino) {
|
||
return find_lib_match(suffix, nullptr, dev, ino);
|
||
}
|
||
|
||
static bool find_lib_contains(const char *needle, dev_t *dev, ino_t *ino) {
|
||
return find_lib_match(nullptr, needle, dev, ino);
|
||
}
|
||
|
||
static bool find_libc(dev_t *dev, ino_t *ino) {
|
||
return find_lib_by_suffix("libc.so", dev, ino);
|
||
}
|
||
|
||
static void register_plt(zygisk::Api *api, dev_t dev, ino_t ino,
|
||
const char *sym, void *hook, void **orig) {
|
||
if (!api || dev == 0 || ino == 0) return;
|
||
api->pltHookRegister(dev, ino, sym, hook, orig);
|
||
}
|
||
|
||
static void install_plt(zygisk::Api *api) {
|
||
if (!api) return;
|
||
dev_t dev = 0;
|
||
ino_t ino = 0;
|
||
if (find_libc(&dev, &ino)) {
|
||
register_plt(api, dev, ino, "exit", (void *)hooked_exit, (void **)&orig_exit);
|
||
register_plt(api, dev, ino, "_exit", (void *)hooked__exit, (void **)&orig__exit);
|
||
register_plt(api, dev, ino, "abort", (void *)hooked_abort, (void **)&orig_abort);
|
||
register_plt(api, dev, ino, "__stack_chk_fail",
|
||
(void *)hooked_stack_chk_fail, (void **)&orig_stack_chk_fail);
|
||
register_plt(api, dev, ino, "raise", (void *)hooked_raise, (void **)&orig_raise);
|
||
register_plt(api, dev, ino, "kill", (void *)hooked_kill, (void **)&orig_kill);
|
||
register_plt(api, dev, ino, "tgkill", (void *)hooked_tgkill, (void **)&orig_tgkill);
|
||
register_plt(api, dev, ino, "pthread_kill",
|
||
(void *)hooked_pthread_kill, (void **)&orig_pthread_kill);
|
||
register_plt(api, dev, ino, "dlopen", (void *)hooked_dlopen, (void **)&orig_dlopen);
|
||
}
|
||
bool ok = api->pltHookCommit();
|
||
LOGI("PLT commit=%d", ok ? 1 : 0);
|
||
}
|
||
|
||
static void try_install_cxx_guard_plt() {
|
||
if (g_cxx_plt.load() || !g_api) return;
|
||
dev_t dev = 0;
|
||
ino_t ino = 0;
|
||
bool any = false;
|
||
// libc++:必须走 orig acquire(否则 gralloc 静态初始化被跳过 → mapper missing 黑屏)。
|
||
// 仅在同 guard 递归时 return 1;abort 仍拦截。
|
||
if (find_lib_by_suffix("libc++.so", &dev, &ino)
|
||
|| find_lib_by_suffix("libc++_shared.so", &dev, &ino)) {
|
||
register_plt(g_api, dev, ino, "__cxa_guard_acquire",
|
||
(void *)hooked_cxa_guard_acquire, (void **)&orig_cxa_guard_acquire);
|
||
register_plt(g_api, dev, ino, "__cxa_guard_abort",
|
||
(void *)hooked_cxa_guard_abort, (void **)&orig_cxa_guard_abort);
|
||
any = true;
|
||
LOGI("cxx guard target libc++");
|
||
}
|
||
if (find_lib_by_suffix("libtngdigital_ewallet.so", &dev, &ino)) {
|
||
register_plt(g_api, dev, ino, "__cxa_guard_acquire",
|
||
(void *)hooked_cxa_guard_acquire, (void **)&orig_cxa_guard_acquire);
|
||
register_plt(g_api, dev, ino, "__cxa_guard_abort",
|
||
(void *)hooked_cxa_guard_abort, (void **)&orig_cxa_guard_abort);
|
||
any = true;
|
||
LOGI("cxx guard target libtngdigital_ewallet");
|
||
}
|
||
if (!any) return;
|
||
if (g_api->pltHookCommit()) {
|
||
g_cxx_plt.store(1);
|
||
LOGI("PLT cxx guards committed (orig acquire + recursive skip)");
|
||
}
|
||
}
|
||
|
||
static void *phase_thread(void *) {
|
||
install_promon_segv_handler();
|
||
usleep(400 * 1000);
|
||
install_seccomp_exit_group_only();
|
||
LOGI("seccomp armed @400ms ok=%d", g_seccomp_ok.load());
|
||
for (int i = 0; i < 40; i++) {
|
||
usleep(1000 * 1000);
|
||
install_soft_signals();
|
||
install_promon_segv_handler();
|
||
try_install_cxx_guard_plt();
|
||
if (i % 5 == 0) refresh_promon_so_range();
|
||
}
|
||
LOGI("phase done seccomp=%d stack_chk=%d promon_segv=%d",
|
||
g_seccomp_ok.load(), g_stack_chk.load(), g_promon_segv.load());
|
||
return nullptr;
|
||
}
|
||
|
||
static void install_all(zygisk::Api *api) {
|
||
g_api = api;
|
||
g_main_tid.store(gettid());
|
||
LOGI("install pid=%d main_tid=%d (PLT+cxx-guard+ABRT-pc+4+SEGV-skip+exit_group@400ms)",
|
||
getpid(), (int)g_main_tid.load());
|
||
install_fatal_skip_handlers();
|
||
install_soft_signals();
|
||
install_plt(api);
|
||
pthread_t th;
|
||
if (pthread_create(&th, nullptr, phase_thread, nullptr) == 0) {
|
||
pthread_detach(th);
|
||
}
|
||
LOGI("ready");
|
||
}
|
||
|
||
class TngExitGuardModule : public zygisk::ModuleBase {
|
||
public:
|
||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||
this->api = api;
|
||
this->env = env;
|
||
}
|
||
|
||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||
const char *nice = nullptr;
|
||
if (args->nice_name) {
|
||
nice = env->GetStringUTFChars(args->nice_name, nullptr);
|
||
}
|
||
bool match = nice && (
|
||
std::strncmp(nice, kTargetPkg, std::strlen(kTargetPkg)) == 0);
|
||
if (nice) env->ReleaseStringUTFChars(args->nice_name, nice);
|
||
g_enabled = match;
|
||
if (!match) {
|
||
api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
|
||
return;
|
||
}
|
||
LOGI("preAppSpecialize matched");
|
||
}
|
||
|
||
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
||
(void)args;
|
||
if (!g_enabled) return;
|
||
install_all(api);
|
||
}
|
||
|
||
private:
|
||
zygisk::Api *api = nullptr;
|
||
JNIEnv *env = nullptr;
|
||
};
|
||
|
||
REGISTER_ZYGISK_MODULE(TngExitGuardModule)
|