/** * 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");