115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
#!/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+)".*?<span>([^<]+)</span>',
|
|
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())
|