TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
283 lines
7.5 KiB
JavaScript
283 lines
7.5 KiB
JavaScript
/**
|
|
* TNG — catch Promon exit after runtime code decrypt (mmap/mprotect RX).
|
|
* Frida 17 compatible.
|
|
*/
|
|
"use strict";
|
|
|
|
function log(msg) {
|
|
send("[TNG-native] " + msg);
|
|
}
|
|
|
|
function findExport(moduleName, name) {
|
|
try {
|
|
if (moduleName) {
|
|
var m = Process.findModuleByName(moduleName);
|
|
if (m) {
|
|
var a = m.findExportByName(name);
|
|
if (a) return a;
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
try {
|
|
return Module.getGlobalExportByName(name);
|
|
} catch (e2) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
var patched = {};
|
|
|
|
function looksLikeExitSetup(addr) {
|
|
for (var i = 1; i <= 12; i++) {
|
|
try {
|
|
var w = addr.sub(i * 4).readU32();
|
|
var opc = w & 0xff800000;
|
|
if (opc === 0x52800000 || opc === 0xd2800000) {
|
|
var rd = w & 0x1f;
|
|
var imm = (w >> 5) & 0xffff;
|
|
if (rd === 8 && (imm === 93 || imm === 94)) return imm;
|
|
}
|
|
// mov x8, xN then earlier load — also catch svc after mov x0, #imm (exit code)
|
|
if ((w & 0xffe0ffff) === 0xaa0003e8) return 8; // mov x8, x0.. pattern loose
|
|
} catch (e) {}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function patchRegion(base, size, tag) {
|
|
if (size <= 0 || size > 64 * 1024 * 1024) return;
|
|
var key = base + ":" + size;
|
|
if (patched[key]) return;
|
|
patched[key] = true;
|
|
var nop = [0x1f, 0x20, 0x03, 0xd5];
|
|
var n = 0;
|
|
var totalSvc = 0;
|
|
try {
|
|
// only scan 4-byte aligned by walking manually for reliability
|
|
var end = base.add(size - 4);
|
|
for (var p = base; p.compare(end) <= 0; p = p.add(4)) {
|
|
var w;
|
|
try {
|
|
w = p.readU32();
|
|
} catch (e) {
|
|
break;
|
|
}
|
|
if (w !== 0xd4000001) continue; // svc #0
|
|
totalSvc++;
|
|
var kind = looksLikeExitSetup(p);
|
|
if (!kind) continue;
|
|
try {
|
|
Memory.protect(p, 4, "rwx");
|
|
p.writeByteArray(nop);
|
|
n++;
|
|
log("patched exit SVC#" + kind + " @ " + p + " [" + tag + "]");
|
|
} catch (e2) {
|
|
log("patch err " + p + ": " + e2);
|
|
}
|
|
}
|
|
if (totalSvc > 0) {
|
|
log("region " + tag + " svc#0=" + totalSvc + " patched=" + n + " size=" + size);
|
|
}
|
|
} catch (e) {
|
|
log("scan err " + tag + ": " + e);
|
|
}
|
|
}
|
|
|
|
function scanAllExecutable(tag) {
|
|
Process.enumerateRanges("r-x").forEach(function (r) {
|
|
var file = r.file ? r.file.path : "anon";
|
|
// skip system libs except if anonymous / app
|
|
if (file.indexOf("/system/") === 0 || file.indexOf("/apex/") === 0) return;
|
|
if (file.indexOf("frida") >= 0) return;
|
|
patchRegion(r.base, r.size, tag + ":" + file);
|
|
});
|
|
}
|
|
|
|
function installLibcExitHooks() {
|
|
function blockExit(name, address) {
|
|
try {
|
|
Interceptor.replace(
|
|
address,
|
|
new NativeCallback(
|
|
function (code) {
|
|
log("BLOCKED " + name + "(" + (code | 0) + ")");
|
|
},
|
|
"void",
|
|
["int"]
|
|
)
|
|
);
|
|
log("replaced " + name);
|
|
} catch (e) {
|
|
Interceptor.attach(address, {
|
|
onEnter: function (args) {
|
|
log("BLOCKED(attach) " + name + "(" + args[0].toInt32() + ")");
|
|
while (true) Thread.sleep(60);
|
|
},
|
|
});
|
|
log("attached " + name);
|
|
}
|
|
}
|
|
|
|
["_exit", "exit", "abort", "quick_exit"].forEach(function (n) {
|
|
var a = findExport("libc.so", n);
|
|
if (a) blockExit(n, a);
|
|
});
|
|
|
|
["kill", "tgkill", "pthread_kill", "raise"].forEach(function (n) {
|
|
var a = findExport("libc.so", n);
|
|
if (!a) return;
|
|
Interceptor.attach(a, {
|
|
onEnter: function (args) {
|
|
var pid = args[0].toInt32();
|
|
var sig = args[1].toInt32();
|
|
if ((pid === Process.id || pid === 0 || pid === -1) &&
|
|
(sig === 9 || sig === 15 || sig === 6 || sig === 5)) {
|
|
log("BLOCKED " + n + " sig=" + sig);
|
|
args[1] = ptr(0);
|
|
}
|
|
},
|
|
});
|
|
});
|
|
|
|
var sys = findExport("libc.so", "syscall");
|
|
if (sys) {
|
|
Interceptor.attach(sys, {
|
|
onEnter: function (args) {
|
|
var nr = args[0].toInt32();
|
|
if (nr === 93 || nr === 94) {
|
|
log("BLOCKED syscall exit " + nr);
|
|
args[0] = ptr(-1);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
log("libc hooks OK");
|
|
}
|
|
|
|
function installMprotectWatcher() {
|
|
var mprotect = findExport("libc.so", "mprotect");
|
|
var mmap = findExport("libc.so", "mmap");
|
|
if (mprotect) {
|
|
Interceptor.attach(mprotect, {
|
|
onEnter: function (args) {
|
|
this.addr = args[0];
|
|
this.len = args[1].toInt32();
|
|
this.prot = args[2].toInt32();
|
|
},
|
|
onLeave: function () {
|
|
// PROT_EXEC = 4
|
|
if (this.prot & 4) {
|
|
log("mprotect+EXEC " + this.addr + " len=" + this.len);
|
|
patchRegion(this.addr, this.len, "mprotect");
|
|
}
|
|
},
|
|
});
|
|
}
|
|
if (mmap) {
|
|
Interceptor.attach(mmap, {
|
|
onEnter: function (args) {
|
|
this.len = args[1].toInt32();
|
|
this.prot = args[2].toInt32();
|
|
},
|
|
onLeave: function (retval) {
|
|
if ((this.prot & 4) && !retval.isNull()) {
|
|
log("mmap+EXEC " + retval + " len=" + this.len);
|
|
patchRegion(retval, this.len, "mmap");
|
|
}
|
|
},
|
|
});
|
|
}
|
|
log("mprotect/mmap watchers OK");
|
|
}
|
|
|
|
function installMapsHide() {
|
|
var markers = ["frida", "gadget", "xposed", "lsposed", "vector", "zygisk", "magisk", "liblspd"];
|
|
var tracked = {};
|
|
function hide(line) {
|
|
var l = (line || "").toLowerCase();
|
|
for (var i = 0; i < markers.length; i++) if (l.indexOf(markers[i]) >= 0) return true;
|
|
return false;
|
|
}
|
|
function filter(buf, len) {
|
|
try {
|
|
var text = buf.readUtf8String(len);
|
|
if (!text) return len;
|
|
var out = text.split("\n").filter(function (x) { return !hide(x); }).join("\n");
|
|
var bytes = Memory.allocUtf8String(out);
|
|
var n = Math.min(len, out.length);
|
|
Memory.copy(buf, bytes, 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) tracked[fd] = 1;
|
|
},
|
|
});
|
|
}
|
|
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(filter(this.buf, n)));
|
|
},
|
|
});
|
|
}
|
|
log("maps hide OK");
|
|
}
|
|
|
|
function installJavaGuards() {
|
|
if (typeof Java === "undefined") {
|
|
setTimeout(installJavaGuards, 500);
|
|
return;
|
|
}
|
|
Java.perform(function () {
|
|
try {
|
|
Java.use("java.lang.System").exit.implementation = function (c) {
|
|
log("Java System.exit(" + c + ") blocked");
|
|
};
|
|
} catch (e) {}
|
|
try {
|
|
var R = Java.use("java.lang.Runtime");
|
|
R.exit.overload("int").implementation = function (c) {
|
|
log("Java Runtime.exit(" + c + ") blocked");
|
|
};
|
|
} catch (e) {}
|
|
try {
|
|
var P = Java.use("android.os.Process");
|
|
P.killProcess.implementation = function (pid) {
|
|
if (pid === P.myPid()) {
|
|
log("Java killProcess(self) blocked");
|
|
return;
|
|
}
|
|
return this.killProcess(pid);
|
|
};
|
|
} catch (e) {}
|
|
log("Java guards OK");
|
|
});
|
|
}
|
|
|
|
log("load pid=" + Process.id);
|
|
installLibcExitHooks();
|
|
installMapsHide();
|
|
installMprotectWatcher();
|
|
scanAllExecutable("boot");
|
|
setInterval(function () {
|
|
scanAllExecutable("tick");
|
|
}, 1000);
|
|
installJavaGuards();
|
|
log("ready");
|