chore: 备份 TNG 注册/captcha 逆向与 MariBank SG bypass 进展

TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
This commit is contained in:
mars
2026-08-03 15:23:02 +08:00
parent 193c04a24b
commit 609635aba1
185 changed files with 60843 additions and 231 deletions

View File

@@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
"""Resolve libc _exit real target and patch its SVC."""
import subprocess
import time
import frida
ADB = r"C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools\adb.exe"
PKG = "my.com.tngdigital.ewallet"
SCRIPT = r"""
"use strict";
function log(m){ send("[TNG-native] "+m); }
function findExport(mod, name) {
try {
var m = Process.getModuleByName(mod);
var a = m.findExportByName(name);
if (a) return a;
} catch (e) {}
try { return Module.getGlobalExportByName(name); } catch (e2) { return null; }
}
function resolveTrampoline(addr, name) {
// Follow simple ADRP+ADD+BR / LDR+BR patterns a few times
var cur = addr;
for (var depth = 0; depth < 5; depth++) {
var w0 = cur.readU32();
var w1 = cur.add(4).readU32();
// BR Xn: 0xD61F0000 | (Rn<<5)
if ((w1 & 0xfffffc1f) === 0xd61f0000) {
var rn = (w1 >> 5) & 0x1f;
// LDR Xn, [PC, #imm] : 0x58000000
if ((w0 & 0xff000000) === 0x58000000) {
var imm19 = (w0 >> 5) & 0x7ffff;
if (imm19 & 0x40000) imm19 -= 0x80000;
var targetPtr = cur.add(imm19 * 4);
var target = targetPtr.readPointer();
log(name + " trampoline LDR+BR -> " + target);
cur = target;
continue;
}
// ADRP Xn, page
if ((w0 & 0x9f000000) === 0x90000000) {
var rd = w0 & 0x1f;
var immhi = (w0 >> 5) & 0x7ffff;
var immlo = (w0 >> 29) & 0x3;
var imm = ((immhi << 2) | immlo) << 12;
if (imm & 0x100000000) imm = imm - 0x200000000;
var page = cur.and(ptr("0xfffffffffffff000")).add(imm);
// next might be LDR/ADD
var w2 = cur.add(8).readU32();
log(name + " ADRP page="+page+" rn="+rn+" rd="+rd+" w2="+w2.toString(16));
}
log(name + " BR X" + rn + " at " + cur + " (stop follow)");
break;
}
break;
}
return cur;
}
function patchSvcNear(addr, name, windowSize) {
var nop = [0x1f, 0x20, 0x03, 0xd5];
var n = 0;
for (var i = 0; i < windowSize; i += 4) {
try {
var p = addr.add(i);
if (p.readU32() !== 0xd4000001) continue;
Memory.protect(p, 4, "rwx");
// replace svc with: mov x0, x0; ret — or just nop and hope
// Better: movz x0, #0; ret so "exit" becomes return 0
// movz x0,#0 = 0xD2800000; ret = 0xD65F03C0
p.writeU32(0xd2800000); // movz x0, #0
if (i + 4 < windowSize) {
var p2 = addr.add(i + 4);
// only overwrite next if also svc/brk or nop pad — safer: write ret at svc place only via branch
}
// Just NOP the svc — caller may hang; use ret instead by overwriting svc with ret
p.writeU32(0xd65f03c0); // RET
n++;
log("patched SVC->RET @ " + p + " (" + name + "+" + i + ")");
} catch (e) {
log("patch fail: " + e);
}
}
return n;
}
function hookByPatchingLibcExit() {
var libc = Process.getModuleByName("libc.so");
// Scan entire libc for the classic exit_group sequence:
// mov x8, #94; mov x0, ...; svc #0 OR svc inside _exit impl
var count = 0;
var svcAddrs = [];
// Focused: exports that must lead to exit
["_exit", "exit"].forEach(function (n) {
var a = libc.findExportByName(n);
if (!a) return;
log(n + " export " + a);
// DebugSymbol / Instruction parse: find first BL/B to real impl
});
// Brute: scan libc executable for movz x8,#94 followed within 16 bytes by svc
var ranges = libc.enumerateRanges("r-x");
ranges.forEach(function (r) {
for (var off = 0; off + 8 < r.size; off += 4) {
var p = r.base.add(off);
var w;
try { w = p.readU32(); } catch (e) { return; }
// movz x8, #94 = 0xD2800BC8 ; movz w8,#94 = 0x52800BC8
// movz x8, #93 = 0xD2800BA8
if (w !== 0xd2800bc8 && w !== 0x52800bc8 && w !== 0xd2800ba8 && w !== 0x52800ba8) continue;
// look ahead for svc
for (var j = 4; j <= 24; j += 4) {
try {
if (p.add(j).readU32() === 0xd4000001) {
svcAddrs.push(p.add(j));
log("exit-seq movz@ " + p + " svc@ " + p.add(j));
}
} catch (e2) {}
}
}
});
svcAddrs.forEach(function (svc) {
try {
Memory.protect(svc, 4, "rwx");
// Replace svc with ret — turns exit into function return
svc.writeU32(0xd65f03c0);
count++;
log("SVC->RET " + svc);
} catch (e) {
log("SVC patch fail " + svc + ": " + e);
}
});
log("libc exit SVC patches=" + count);
}
function installEntryLog() {
["_exit", "exit", "abort"].forEach(function (n) {
var a = findExport("libc.so", n);
if (!a) return;
try {
Interceptor.attach(a, {
onEnter: function (args) {
log("ENTER " + n + "(" + args[0] + ")");
}
});
} catch (e) {}
});
}
// Also Interceptor.replace as backup
function installReplace() {
["_exit", "exit", "abort"].forEach(function (n) {
var a = findExport("libc.so", n);
if (!a) return;
try {
Interceptor.replace(a, new NativeCallback(function (code) {
log("REPLACED-HIT " + n + "(" + (code|0) + ")");
}, "void", ["int"]));
log("replaced " + n);
} catch (e) {
log("replace " + n + " fail: " + e);
}
});
}
log("pid=" + Process.id);
hookByPatchingLibcExit();
installReplace();
installEntryLog();
log("ready");
"""
def adb(*a):
return subprocess.run([ADB, *a], capture_output=True, text=True)
def on_msg(m, d):
print(m.get("payload", m), flush=True)
def main():
adb("shell", "am", "force-stop", PKG)
time.sleep(0.4)
device = frida.get_usb_device(10)
pid = device.spawn([PKG])
session = device.attach(pid)
script = session.create_script(SCRIPT)
script.on("message", on_msg)
script.load()
print("resume", pid, flush=True)
device.resume(pid)
for i in range(25):
time.sleep(1)
p = adb("shell", "pidof", PKG).stdout.strip()
print("t=%ds %s" % (i + 1, p or "DEAD"), flush=True)
if not p:
break
else:
print("STABLE", flush=True)
focus = adb("shell", "dumpsys", "activity", "activities").stdout
for line in focus.splitlines():
if "tngdigital" in line.lower() and any(
x in line for x in ("UserLogin", "SecurityError", "Splash", "mResumed", "topResumed")
):
print("ACT", line.strip()[:200], flush=True)
if __name__ == "__main__":
main()