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,178 @@
# -*- coding: utf-8 -*-
"""Compare: Xposed-only vs Frida-stealth-spawn."""
import subprocess
import time
import frida
ADB = r"C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools\adb.exe"
PKG = "my.com.tngdigital.ewallet"
STEALTH = r"""
"use strict";
function log(m){ send("[stealth] "+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; }
}
// Rename frida threads
try {
var pthread_setname = findExport("libc.so", "pthread_setname_np");
// also patch existing: best-effort via Java later
} catch (e) {}
// Hide maps
var markers = ["frida","gadget","linjector","gum-js","gmain","pool-frida","hluda"];
var tracked = {};
function hideLine(line) {
var l = (line||"").toLowerCase();
for (var i=0;i<markers.length;i++) if (l.indexOf(markers[i])>=0) return true;
return false;
}
function filterBuf(buf, len) {
try {
var t = buf.readUtf8String(len);
if (!t) return len;
var out = t.split("\n").filter(function(x){return !hideLine(x);}).join("\n");
var b = Memory.allocUtf8String(out);
var n = Math.min(len, out.length);
Memory.copy(buf, b, n);
return n;
} catch (e) { return len; }
}
var openat = findExport("libc.so","openat");
var readFn = findExport("libc.so","read");
if (openat) {
Interceptor.attach(openat, {
onEnter: function(args){ this.path = args[1].isNull()?null:args[1].readCString(); },
onLeave: function(retval){
var fd=retval.toInt32();
if (fd>=0 && this.path && (this.path.indexOf("maps")>=0 || this.path.indexOf("status")>=0 || this.path.indexOf("task")>=0))
tracked[fd]=this.path;
}
});
}
if (readFn) {
Interceptor.attach(readFn, {
onEnter: function(args){ this.fd=args[0].toInt32(); this.buf=args[1]; },
onLeave: function(retval){
var n=retval.toInt32();
if (n>0 && tracked[this.fd]) retval.replace(ptr(filterBuf(this.buf, n)));
}
});
}
// Patch libc exit_group SVC
var libc = Process.getModuleByName("libc.so");
libc.enumerateRanges("r-x").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; }
if (w!==0xd2800bc8 && w!==0xd2800ba8 && w!==0x52800bc8 && w!==0x52800ba8) continue;
for (var j=4;j<=24;j+=4) {
var s=p.add(j);
try {
if (s.readU32()===0xd4000001) {
Memory.protect(s,4,"rwx");
s.writeU32(0xd65f03c0);
log("SVC->RET "+s);
}
} catch(e2){}
}
}
});
// Replace exit APIs
["_exit","exit","abort"].forEach(function(n){
var a=findExport("libc.so",n);
if(!a) return;
try {
Interceptor.replace(a, new NativeCallback(function(c){ log("block "+n+"("+c+")"); }, "void", ["int"]));
} catch(e){}
});
log("stealth 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 wait_alive(sec, label):
for i in range(sec):
time.sleep(1)
p = adb("shell", "pidof", PKG).stdout.strip()
act = ""
dump = adb("shell", "dumpsys", "activity", "activities").stdout
for line in dump.splitlines():
if "tngdigital" in line.lower() and any(
x in line for x in ("UserLogin", "SecurityError", "Splash", "topResumedActivity")
):
act = line.strip()[:120]
break
print("%s t=%ds pid=%s act=%s" % (label, i + 1, p or "DEAD", act), flush=True)
if not p:
return i + 1
return sec
def test_xposed_only():
print("=== Xposed-only (monkey) ===", flush=True)
adb("shell", "am", "force-stop", PKG)
adb("logcat", "-c")
time.sleep(0.3)
adb(
"shell",
"monkey",
"-p",
PKG,
"-c",
"android.intent.category.LAUNCHER",
"1",
)
alive = wait_alive(15, "XPOSED")
print("xposed_alive_sec", alive, flush=True)
for line in adb("logcat", "-d").stdout.splitlines():
if "TngRoot" in line and any(
x in line for x in ("install", "UserLogin", "short-circuit", "finishing", "blocked intent")
):
print(line[line.find("TngRoot") :][:180], flush=True)
if "Displayed" in line and "tngdigital" in line:
print(line.strip()[:200], flush=True)
if "exited cleanly" in line:
print(line.strip()[:160], flush=True)
def test_frida_stealth():
print("=== Frida stealth spawn ===", flush=True)
adb("shell", "am", "force-stop", PKG)
time.sleep(0.3)
d = frida.get_usb_device(10)
pid = d.spawn([PKG])
s = d.attach(pid)
sc = s.create_script(STEALTH)
sc.on("message", on_msg)
sc.load()
d.resume(pid)
alive = wait_alive(15, "FRIDA")
print("frida_alive_sec", alive, flush=True)
def main():
test_xposed_only()
test_frida_stealth()
if __name__ == "__main__":
main()