Buckets:
| #!/usr/bin/env python3 | |
| """Verify archive extraction, DEX fallback, decoded resources and saved native analysis.""" | |
| import csv | |
| import hashlib | |
| import json | |
| from pathlib import Path | |
| import re | |
| import zipfile | |
| import xml.etree.ElementTree as ET | |
| ROOT = Path(__file__).resolve().parent | |
| checks = [] | |
| def check(name, condition): | |
| checks.append({"check": name, "passed": bool(condition)}) | |
| inventory = json.loads((ROOT / "reports/apk-inventory.json").read_text()) | |
| for record in inventory: | |
| apk = ROOT.parent / record["apk"] | |
| check(f"{apk.name}: SHA256 unchanged", hashlib.sha256(apk.read_bytes()).hexdigest() == record["sha256"]) | |
| with zipfile.ZipFile(apk) as z: | |
| check(f"{apk.name}: ZIP integrity", z.testzip() is None) | |
| destination = ROOT / "extracted" / apk.stem | |
| check(f"{apk.name}: every entry extracted byte-for-byte", all( | |
| entry.is_dir() or (destination / entry.filename).read_bytes() == z.read(entry) | |
| for entry in z.infolist())) | |
| manifest = ET.parse(ROOT / "jadx/resources/AndroidManifest.xml").getroot() | |
| check("Decoded application manifest", manifest.get("package") == "com.moonshot.kimichat") | |
| check("Decoded XML resources", any((ROOT / "jadx/resources/res").rglob("*.xml"))) | |
| for split in ["arm64", "xxhdpi"]: | |
| check(f"{split}: split resources decoded", (ROOT / f"logs/jadx-split-{split}.exit").read_text().strip() == "0") | |
| split_manifest = ET.parse(ROOT / f"jadx-splits/{split}/resources/AndroidManifest.xml").getroot() | |
| check(f"{split}: split manifest", bool(split_manifest.get("split"))) | |
| check("Primary JADX exit recorded (partial decompilation permitted)", (ROOT / "logs/jadx.exit").read_text().strip() in {"0", "3"}) | |
| check("DEX fallback completed without errors", (ROOT / "logs/jadx-fallback.exit").read_text().strip() == "0") | |
| check("All three original DEX files preserved", len(list((ROOT / "extracted").rglob("*.dex"))) == 3) | |
| missing_fallback = [] | |
| fallback_errors = [] | |
| for path in (ROOT / "jadx/sources").rglob("*.java"): | |
| if "Method not decompiled:" in path.read_text(): | |
| fallback = ROOT / "jadx-fallback/sources" / path.relative_to(ROOT / "jadx/sources") | |
| if not fallback.exists(): | |
| missing_fallback.append(str(path)) | |
| for path in (ROOT / "jadx-fallback/sources").rglob("*.java"): | |
| if "JADX ERROR:" in path.read_text() or "Method not decompiled:" in path.read_text(): | |
| fallback_errors.append(str(path)) | |
| check("Every incomplete structured source has a DEX fallback", not missing_fallback) | |
| check("DEX fallback has no error/stub markers", not fallback_errors) | |
| check("Ghidra analysis process completed", (ROOT / "logs/ghidra.exit").read_text().strip() == "0") | |
| check("Saved Ghidra project reopened successfully", (ROOT / "logs/ghidra-verify.exit").read_text().strip() == "0") | |
| libraries = sorted((ROOT / "extracted").rglob("*.so")) | |
| check("Expected 22 native libraries", len(libraries) == 22) | |
| native_totals = {"functions": 0, "decompiled": 0, "failed": 0, "thunks": 0} | |
| for library in libraries: | |
| directory = ROOT / "ghidra/exports" / library.name | |
| with (directory / "summary.tsv").open() as stream: | |
| summary = next(csv.DictReader(stream, delimiter="\t")) | |
| with (directory / "functions.tsv").open() as stream: | |
| functions = list(csv.DictReader(stream, delimiter="\t")) | |
| check(f"{library.name}: function index covers analysis", len(functions) == int(summary["functions"])) | |
| c = (directory / "decompiled.c").read_text() | |
| for key, status in [("decompiled", "ok"), ("failed", "failed"), ("thunks", "thunk")]: | |
| count = sum(row["status"] == status for row in functions) | |
| check(f"{library.name}: {status} count", count == int(summary[key])) | |
| check(f"{library.name}: exported C bodies", len(re.findall(r"^/\* [0-9a-f]+ .+ \*/$", c, re.MULTILINE)) == int(summary["decompiled"])) | |
| with (directory / "project-verification.tsv").open() as stream: | |
| saved = next(csv.DictReader(stream, delimiter="\t")) | |
| check(f"{library.name}: saved binary verified", saved["verified"] == "true" and | |
| saved["sha256"] == hashlib.sha256(library.read_bytes()).hexdigest() and | |
| saved["functions"] == summary["functions"] and saved["language"] == summary["language"]) | |
| check(f"{library.name}: ARM64", summary["language"] == "AARCH64:LE:64:v8A") | |
| if int(summary["failed"]): | |
| check(f"{library.name}: failed functions have assembly", (directory / "failed-functions.asm").stat().st_size > 0) | |
| for key in native_totals: | |
| native_totals[key] += int(summary[key]) | |
| result = {"passed": all(row["passed"] for row in checks), "checks": checks, | |
| "native_totals": native_totals, "missing_fallback": missing_fallback, | |
| "fallback_errors": fallback_errors} | |
| (ROOT / "reports/verification.json").write_text(json.dumps(result, indent=2) + "\n") | |
| print(json.dumps({"passed": result["passed"], "checks": len(checks), "native_totals": native_totals}, indent=2)) | |
| raise SystemExit(0 if result["passed"] else 1) | |
Xet Storage Details
- Size:
- 4.97 kB
- Xet hash:
- 35e124ee50ca04dfd0f07426daab99891f6a5d728849d38f796f27ad3dc8afe1
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.