#!/usr/bin/env python3 """Download TNG eWallet XAPK from Uptodown eAPI (arm64-v8a).""" from __future__ import annotations import json import re import subprocess import sys import urllib.request import zipfile from pathlib import Path UA = "Mozilla/5.0 (Linux; Android 13) Chrome/120.0.0.0 Mobile Safari/537.36" APP_CODE = "1000382462" VERSION = "1.9.10" ARCH = "arm64-v8a, armeabi-v7a, x86_64" BASE = "https://touch-n-go-ewallet.en.uptodown.com" OUT_XAPK = Path("reverse/dumps/tng_1.9.10.xapk") OUT_DIR = Path("reverse/dumps/tng_xapk_extracted") def fetch(url: str) -> bytes: req = urllib.request.Request(url, headers={"User-Agent": UA}) with urllib.request.urlopen(req, timeout=120) as resp: return resp.read() def fetch_text(url: str) -> str: return fetch(url).decode("utf-8", "ignore") def main() -> int: versions = json.loads(fetch_text(f"{BASE}/android/apps/{APP_CODE}/versions/1")) entry = next(x for x in versions["data"] if x["version"] == VERSION) version_id = entry["versionURL"]["versionID"] print(f"version {VERSION} fileID={entry['fileID']} kind={entry['kindFile']}") dl_page = fetch_text(f"{BASE}/android/download/{version_id}") m = re.search(r'class="button variants" data-version="(\d+)"', dl_page) if not m: print("variants data-version not found", file=sys.stderr) return 1 data_version = m.group(1) print("data_version", data_version) files_json = json.loads(fetch_text(f"{BASE}/app/{APP_CODE}/version/{data_version}/files")) content = files_json.get("content", "") # parse variant rows from HTML fragment rows = re.findall( r'class="variant".*?data-file-id="(\d+)".*?([^<]+)', content, flags=re.S, ) if not rows: # fallback: any data-file-id near xapk rows = re.findall(r'data-file-id="(\d+)"', content) rows = [(rid, "?") for rid in rows] print("variants", rows) target_file_id = None for fid, arch in rows: if ARCH in arch or "arm64-v8a" in arch: target_file_id = fid print("pick", fid, arch) break if not target_file_id and rows: target_file_id = rows[0][0] print("fallback file_id", target_file_id) if not target_file_id: print("no file id", file=sys.stderr) return 1 variant_page = fetch_text(f"{BASE}/android/download/{target_file_id}-x") token_m = re.search(r'id="detail-download-button"[^>]*data-url="(-[^"]+)"', variant_page) if not token_m: token_m = re.search(r'data-url="(-[^"]+)"', variant_page) if not token_m: print("download token not found", file=sys.stderr) return 1 token = token_m.group(1) print("downloading XAPK...") data = fetch(f"https://dw.uptodown.com/dwn/{token}") OUT_XAPK.write_bytes(data) print("saved", OUT_XAPK, "bytes", len(data)) with zipfile.ZipFile(OUT_XAPK) as z: apks = [n for n in z.namelist() if n.endswith(".apk")] print("apk splits", apks) if not apks: print("no apk inside xapk", file=sys.stderr) return 1 if OUT_DIR.exists(): import shutil shutil.rmtree(OUT_DIR) OUT_DIR.mkdir(parents=True) import zipfile as zf with zf.ZipFile(OUT_XAPK) as z: z.extractall(OUT_DIR) apk_files = sorted(OUT_DIR.rglob("*.apk")) adb = r"C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools\adb.exe" cmd = [adb, "install-multiple", "-r"] + [str(p) for p in apk_files] print("install:", " ".join(cmd)) r = subprocess.run(cmd, capture_output=True, text=True) print(r.stdout) print(r.stderr) return 0 if r.returncode == 0 else r.returncode if __name__ == "__main__": raise SystemExit(main())