Files
notiMessage/reverse/frida/trace_tng_tiger_fread.js
mars 609635aba1 chore: 备份 TNG 注册/captcha 逆向与 MariBank SG bypass 进展
TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
2026-08-03 15:23:02 +08:00

148 lines
5.5 KiB
JavaScript

"use strict";
/*
* TNG eWallet — 定位 TigerTally(libtiger_tally.so) 启动期 fread 阻塞。观察不改行为。
*
* 背景(ANR 栈): CaptchaInitializer → TigerTallyAPI.init → t.B.genericNt1(native)
* → libtiger_tally.so (mNYjyzyN23) → fread → __sread → read 永远读不到数据
*/
const TIGER_SO = "libtiger_tally.so";
const LIBC = Process.getModuleByName("libc.so");
const readlink = new NativeFunction(
LIBC.findExportByName("readlink"), "long", ["pointer", "pointer", "ulong"]);
/* ---- Tiger 模块范围(热路径缓存,每 2s 刷新一次) ---- */
let tigerMod = null;
function refreshTiger() {
const m = Process.findModuleByName(TIGER_SO);
if (m) tigerMod = m;
return !!tigerMod;
}
function inTiger(addr) {
if (!addr) return false;
if (!tigerMod) return false;
return addr.compare(tigerMod.base) >= 0 && addr.compare(tigerMod.base.add(tigerMod.size)) < 0;
}
setInterval(() => { refreshTiger(); }, 2000);
function resolveFd(fd) {
try {
const link = Memory.allocUtf8String(`/proc/self/fd/${fd}`);
const out = Memory.alloc(256);
const n = readlink(link, out, 256);
if (n > 0) return out.readUtf8String(Math.min(n, 255));
} catch (e) { /* ignore */ }
return "?";
}
function fdKind(fd) {
const p = resolveFd(fd);
if (p.indexOf("socket:") === 0) return "SOCKET " + p;
if (p.indexOf("pipe:") === 0) return "PIPE " + p;
if (p.indexOf("anon_inode:") === 0) return "ANON " + p;
return p;
}
function threadName() {
try { return Process.getCurrentThreadName(); } catch (e) { return "?"; }
}
function fmtAddr(a) { return a ? a.toString(16) : "?"; }
const stats = {}; // tid -> info
function bump(fd, kind, ret) {
const tid = Process.getCurrentThreadId();
let s = stats[tid];
if (!s) { s = { name: threadName(), reads: 0, lastFd: fd, lastFdKind: kind, lastRet: ret }; stats[tid] = s; }
s.name = threadName();
s.reads++;
s.lastFd = fd;
s.lastFdKind = kind;
s.lastRet = ret;
}
/* ---- fread: FILE* 第4参数; bionic __sFILE._file 偏移约 18 ---- */
Interceptor.attach(LIBC.findExportByName("fread"), {
onEnter(args) {
const caller = this.returnAddress;
if (!inTiger(caller)) return;
const fp = args[3];
let fd = -1;
for (const off of [18, 16, 24, 20]) {
try { const v = fp.add(off).readU16(); if (v > 0 && v < 4096) { fd = v; break; } }
catch (e) { /* try next */ }
}
const kind = fd > 0 ? fdKind(fd) : "?";
bump(fd, kind, "pending");
console.log(`[FREAD] tid=${Process.getCurrentThreadId()} "${threadName()}" caller=${fmtAddr(caller)} fd=${fd} kind=${kind}`);
},
onLeave(ret) {
if (!inTiger(this.returnAddress)) return;
bump(-1, "", ret.toInt32());
console.log(`[FREAD-LEAVE] tid=${Process.getCurrentThreadId()} ret=${ret.toInt32()}`);
}
});
/* ---- read: 只观察调用者位于 libtiger_tally.so 的 ---- */
Interceptor.attach(LIBC.findExportByName("read"), {
onEnter(args) {
const caller = this.returnAddress;
if (!inTiger(caller)) return;
const fd = args[0].toInt32();
const kind = fdKind(fd);
bump(fd, kind, "?");
console.log(`[READ] tid=${Process.getCurrentThreadId()} "${threadName()}" caller=${fmtAddr(caller)} fd=${fd} kind=${kind}`);
},
onLeave(ret) {
if (!inTiger(this.returnAddress)) return;
const tid = Process.getCurrentThreadId();
const s = stats[tid];
const fd = s ? s.lastFd : -1;
const r = ret.toInt32();
if (s) s.lastRet = r;
if (r < 0) console.log(`[READ-RET] tid=${tid} fd=${fd} ret=${r} (blocked/error)`);
else if (r === 0) console.log(`[READ-EOF] tid=${tid} fd=${fd} ret=0 (EOF/closed)`);
else console.log(`[READ-RET] tid=${tid} fd=${fd} ret=${r}`);
}
});
/* ---- 周期 dump ---- */
function dumpThreads() {
try {
const dir = new File("/proc/self/task", "r");
const entries = dir.list();
dir.close();
let relevant = [];
for (const e of entries) {
let nm = "?";
try { const nf = new File(`/proc/self/task/${e}/comm`, "r"); nm = nf.readString().trim(); nf.close(); } catch (err) {}
const s = stats[e] || null;
const lower = nm.toLowerCase();
if (lower.indexOf("location") >= 0 || lower.indexOf("tally") >= 0 || s) {
let info = `tid=${e} "${nm}"`;
if (s) info += ` tigerReads=${s.reads} lastFd=${s.lastFd} kind=${s.lastFdKind} lastRet=${s.lastRet}`;
relevant.push(info);
}
}
console.log(`[DUMP] tiger-fread threads: ${relevant.length ? relevant.join(" | ") : "(none)"}`);
} catch (e) {
console.log(`[DUMP] failed: ${e}`);
}
}
setInterval(() => {
dumpThreads();
try {
const dir = new File("/proc/self/fd", "r");
const fds = dir.list();
dir.close();
let pipes = [], socks = [];
for (const f of fds) {
const kind = fdKind(parseInt(f, 10));
if (kind.indexOf("PIPE") === 0) pipes.push(f + ":" + kind.split(" ").slice(1).join(" "));
if (kind.indexOf("SOCKET") === 0) socks.push(f + ":" + kind.split(" ").slice(1).join(" "));
}
if (pipes.length) console.log(`[DUMP-FD] pipes: ${pipes.join(" | ")}`);
if (socks.length) console.log(`[DUMP-FD] sockets: ${socks.join(" | ")}`);
} catch (e) { /* ignore */ }
}, 3000);
console.log("[TIGER-FREAD] armed (observe-only)");