#!/usr/bin/env python3 """Inspect an APK (ZIP) and inventory its DEX, native libraries, and assets. Lists the components relevant to triage — DEX files, native ``.so`` libraries, the manifest, and notable assets — without decompiling or executing anything. Use jadx/apktool for the actual decompilation step. """ from __future__ import annotations import argparse import json import sys import zipfile from pathlib import Path def inspect(path: str) -> dict: if not zipfile.is_zipfile(path): return {"error": "not a ZIP/APK"} dex, native, assets, other = [], [], [], [] with zipfile.ZipFile(path) as zf: for name in zf.namelist(): if name.endswith(".dex"): dex.append(name) elif name.startswith("lib/") and name.endswith(".so"): native.append(name) elif name.startswith("assets/"): assets.append(name) elif name == "AndroidManifest.xml": other.append(name) return { "dex": sorted(dex), "native_libs": sorted(native), "assets": sorted(assets), "has_manifest": "AndroidManifest.xml" in other, "multidex": len(dex) > 1, "notes": _notes(dex, native, assets), } def _notes(dex, native, assets) -> list[str]: notes = [] if len(dex) > 1: notes.append("multidex: analyze all classesN.dex") if native: notes.append("native libraries present: analyze .so separately") enc = [a for a in assets if a.endswith((".enc", ".dat", ".bin"))] if enc: notes.append(f"possible encrypted payload assets: {enc}") return notes def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="Inspect an APK") sub = parser.add_subparsers(dest="cmd", required=True) p = sub.add_parser("inspect") p.add_argument("file") args = parser.parse_args(argv) print(json.dumps(inspect(args.file), indent=2)) return 0 if __name__ == "__main__": sys.exit(main())