# -*- 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)
