# -*- coding: utf-8 -*- """Scan TNG eWallet APK for AppProtect / root detection artifacts.""" import re import sys import zipfile KEYS = [ b"AppProtect", b"appprotect", b"vkey", b"V-Key", b"VKey", b"VGuard", b"jailbroken", b"Jailbroken", b"Rooted Device", b"rooted device", b"Close app", b"tngdigital", b"TNG eWallet", b"How to keep device safe", b"enhanced our security", b"RootBeer", b"SafetyNet", b"PlayIntegrity", b"detectRoot", b"isRooted", b"checkRoot", b"magisk", b"xposed", b"lsposed", b"frida", b"emulator", b"su binary", b"/system/xbin/su", ] CLASS_KEYS = [ b"safemode", b"SafeMode", b"appprotect", b"AppProtect", b"vkey", b"VKey", b"vguard", b"VGuard", b"rooted", b"RootBeer", b"integrity", b"jailbreak", b"security", b"RiskDevice", b"tamper", b"hook", b"frida", ] def scan_strings(apk_path): print("=== STRING SCAN: %s ===" % apk_path) with zipfile.ZipFile(apk_path) as zf: for name in sorted(zf.namelist()): if not name.endswith(".dex"): continue data = zf.read(name) hits = [] for key in KEYS: start = 0 while True: idx = data.find(key, start) if idx < 0: break s = max(0, idx - 60) e = min(len(data), idx + len(key) + 100) chunk = re.sub(rb"[^\x20-\x7e]+", b" ", data[s:e]) chunk = chunk.decode("ascii", "ignore").strip() if chunk and chunk not in hits: hits.append(chunk) start = idx + 1 if hits: print("\n--- %s (%d hits) ---" % (name, len(hits))) for h in sorted(set(hits))[:40]: print(" ", h) if len(hits) > 40: print(" ... +%d more" % (len(hits) - 40)) def scan_classes(apk_path): print("\n=== CLASS SCAN: %s ===" % apk_path) with zipfile.ZipFile(apk_path) as zf: for name in sorted(zf.namelist()): if not name.endswith(".dex"): continue data = zf.read(name) classes = set(re.findall(rb"L[a-zA-Z0-9_$/]+;", data)) hits = [] for raw in classes: low = raw.lower() if any(k.lower() in low for k in CLASS_KEYS): s = raw.decode("ascii", "ignore")[1:-1].replace("/", ".") hits.append(s) if hits: print("\n--- %s (%d classes) ---" % (name, len(hits))) for h in sorted(set(hits))[:60]: print(" ", h) if len(hits) > 60: print(" ... +%d more" % (len(hits) - 60)) def scan_native(apk_path): print("\n=== NATIVE LIB SCAN: %s ===" % apk_path) with zipfile.ZipFile(apk_path) as zf: for name in sorted(zf.namelist()): if not name.endswith(".so"): continue data = zf.read(name) lib = name.split("/")[-1] found = [] for key in KEYS + [b"libvos", b"libvkey", b"libvguard", b"libappprotect"]: if key.lower() in data.lower(): found.append(key.decode("ascii", "ignore")) if found: print(" %s: %s" % (lib, ", ".join(sorted(set(found))))) if __name__ == "__main__": path = sys.argv[1] if len(sys.argv) > 1 else "reverse/apks/tng/base.apk" scan_strings(path) scan_classes(path) scan_native(path)