"""Minimal DEX parser: dump methods for target classes."""
import struct
import zipfile
from pathlib import Path

TARGET = {
    "Lcom/tngd/networksdk/common/NativeLib;",
    "Lcom/tngd/networksdk/common/ApiSixSecretKeys;",
    "Lmy/com/tngdigital/common/internal/libs/RetrieveFromNativeLibs;",
}


def uleb(data, i):
    result = 0
    shift = 0
    while True:
        b = data[i]
        i += 1
        result |= (b & 0x7F) << shift
        if (b & 0x80) == 0:
            break
        shift += 7
    return result, i


def parse_dex(data: bytes, label: str):
    if data[:4] != b"dex\n":
        return
    string_ids_size, string_ids_off = struct.unpack_from("<II", data, 56)
    type_ids_size, type_ids_off = struct.unpack_from("<II", data, 64)
    proto_ids_size, proto_ids_off = struct.unpack_from("<II", data, 72)
    field_ids_size, field_ids_off = struct.unpack_from("<II", data, 80)
    method_ids_size, method_ids_off = struct.unpack_from("<II", data, 88)
    class_defs_size, class_defs_off = struct.unpack_from("<II", data, 96)

    def string_at(idx):
        off = struct.unpack_from("<I", data, string_ids_off + idx * 4)[0]
        size, p = uleb(data, off)
        return data[p : p + size].decode("utf-8", "replace")

    def type_at(idx):
        return string_at(struct.unpack_from("<I", data, type_ids_off + idx * 4)[0])

    def proto_at(idx):
        shorty_idx, return_type_idx, parameters_off = struct.unpack_from(
            "<III", data, proto_ids_off + idx * 12
        )
        ret = type_at(return_type_idx)
        params = []
        if parameters_off:
            size = struct.unpack_from("<I", data, parameters_off)[0]
            for i in range(size):
                tidx = struct.unpack_from("<H", data, parameters_off + 4 + i * 2)[0]
                params.append(type_at(tidx))
        return ret, params

    def method_at(idx):
        class_idx, proto_idx, name_idx = struct.unpack_from(
            "<HHI", data, method_ids_off + idx * 8
        )
        ret, params = proto_at(proto_idx)
        return type_at(class_idx), string_at(name_idx), ret, params

    print(f"\n===== {label} =====")
    for c in range(class_defs_size):
        class_idx, access_flags, superclass_idx, interfaces_off, source_file_idx, annotations_off, class_data_off, static_values_off = struct.unpack_from(
            "<IIIIIIII", data, class_defs_off + c * 32
        )
        cname = type_at(class_idx)
        if cname not in TARGET:
            continue
        print(f"\nCLASS {cname} access=0x{access_flags:x}")
        if not class_data_off:
            print("  (no class_data)")
            continue
        p = class_data_off
        static_fields_size, p = uleb(data, p)
        instance_fields_size, p = uleb(data, p)
        direct_methods_size, p = uleb(data, p)
        virtual_methods_size, p = uleb(data, p)
        # skip fields
        for _ in range(static_fields_size + instance_fields_size):
            _, p = uleb(data, p)
            _, p = uleb(data, p)
        mid = 0
        for kind, count in (("direct", direct_methods_size), ("virtual", virtual_methods_size)):
            mid = 0
            for _ in range(count):
                diff, p = uleb(data, p)
                access, p = uleb(data, p)
                code_off, p = uleb(data, p)
                mid += diff
                cls, name, ret, params = method_at(mid)
                flags = []
                if access & 0x100:
                    flags.append("native")
                if access & 0x8:
                    flags.append("static")
                if access & 0x10000:
                    flags.append("constructor")
                print(f"  [{kind}] {' '.join(flags)} {name}({', '.join(params)}){ret} code=0x{code_off:x}")


apk = Path(r"C:\Users\Administrator\Desktop\notiMessage\reverse\apks\tng\base.apk")
with zipfile.ZipFile(apk) as z:
    for n in z.namelist():
        if n.endswith(".dex"):
            data = z.read(n)
            if any(t.encode() in data for t in ("NativeLib;", "ApiSixSecretKeys;", "RetrieveFromNativeLibs;")):
                parse_dex(data, n)
