#!/usr/bin/env python3 """ 环境预检脚本 — 验证 Pixel 本地运行所需的所有依赖。 在 Ubuntu proot 容器内执行。 """ import subprocess, shutil, sys, os, json REPORT = {"timestamp": None, "checks": {}, "overall": None} def check(name, cmd, expect_in_stdout=None, expect_returncode=0): """Run a command and report success/failure.""" try: r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) ok = r.returncode == expect_returncode if expect_in_stdout and ok: ok = expect_in_stdout in r.stdout stdout = r.stdout.strip()[:200] if r.stdout else "" stderr = r.stderr.strip()[:200] if r.stderr else "" REPORT["checks"][name] = { "ok": ok, "returncode": r.returncode, "stdout": stdout, "stderr": stderr } status = "[OK]" if ok else "[FAIL]" print(f" {status} {name}") if not ok: print(f" stdout: {stdout}") print(f" stderr: {stderr}") return ok except Exception as e: REPORT["checks"][name] = {"ok": False, "error": str(e)} print(f" [FAIL] {name}: {e}") return False def main(): print("=" * 60) print("WeChat 本地闭环 — 环境预检") print("=" * 60) all_ok = True # 1. su (root) print("\n[1/8] Root 权限 (su)") all_ok &= check("su", ["su", "-c", "id"], expect_in_stdout="uid=0") # 2. sqlcipher print("\n[2/8] SQLCipher 解密工具") all_ok &= check("sqlcipher", ["sqlcipher", "--version"]) # 3. sqlite3 print("\n[3/8] SQLite3 查询工具") all_ok &= check("sqlite3", ["sqlite3", "--version"]) # 4. am (Activity Manager) print("\n[4/8] Android am 命令") # am without args may return non-zero but still print help; check output only r = subprocess.run(["/system/bin/am"], capture_output=True, text=True, timeout=5) am_ok = "Activity manager" in r.stdout or "activity" in r.stdout.lower() status = "[OK]" if am_ok else "[FAIL]" detail = r.stdout[:60].replace("\n", " ") print(f" {status} am") if detail: print(f" {detail}") REPORT["checks"]["am"] = {"ok": am_ok, "stdout": r.stdout[:100]} all_ok &= am_ok # 5. frida Python 包 (optional - only needed for key capture) print("\n[5/8] Frida Python 包 (可选)") try: import frida print(f" [OK] frida {frida.__version__}") REPORT["checks"]["frida_python"] = {"ok": True, "version": frida.__version__} except ImportError: print(" [WARN] frida 未安装 (仅影响 key 捕获,不影响核心功能)") print(" 如需安装: pip install frida==17.9.10 (可能需要预编译 wheel)") REPORT["checks"]["frida_python"] = {"ok": False, "optional": True} # Don't fail overall for optional dependency # 6. frida-server 运行状态 (optional) print("\n[6/8] Frida Server 运行状态 (可选)") ok = check("frida-server", ["su", "-c", "ps -A | grep frida-server"], expect_in_stdout="frida-server") if not ok: print(" [WARN] frida-server 未运行,仅影响 key 重捕获") REPORT["checks"]["frida_server"]["optional"] = True # Don't fail overall for optional dependency # 7. 微信进程 print("\n[7/8] 微信进程") ok = check("wechat_process", ["su", "-c", "pgrep -x com.tencent.mm"]) all_ok &= ok # 8. 工作目录可写 print("\n[8/8] 工作目录可写") work_dir = os.path.expanduser("~/wxmoment") try: os.makedirs(work_dir, exist_ok=True) test_file = os.path.join(work_dir, ".write_test") with open(test_file, "w") as f: f.write("ok") os.remove(test_file) print(f" [OK] {work_dir} 可写") REPORT["checks"]["workdir"] = {"ok": True, "path": work_dir} except Exception as e: print(f" [FAIL] {work_dir} 不可写: {e}") REPORT["checks"]["workdir"] = {"ok": False, "error": str(e)} all_ok = False # 额外:检查 /sdcard/hermes 可写 print("\n[Extra] /sdcard/hermes 目录") try: os.makedirs("/sdcard/hermes", exist_ok=True) print(" [OK] /sdcard/hermes 可写") REPORT["checks"]["sdcard_hermes"] = {"ok": True} except Exception as e: print(f" [WARN] /sdcard/hermes 可能不可写: {e}") REPORT["checks"]["sdcard_hermes"] = {"ok": False, "warn": True} # Summary REPORT["overall"] = "PASS" if all_ok else "FAIL" REPORT["timestamp"] = subprocess.check_output(["date", "-Iseconds"]).decode().strip() print("\n" + "=" * 60) print(f"结果: {REPORT['overall']}") print("=" * 60) # Save report report_path = "test_report_env.json" with open(report_path, "w") as f: json.dump(REPORT, f, indent=2, ensure_ascii=False) print(f"\n详细报告已保存: {report_path}") return 0 if all_ok else 1 if __name__ == "__main__": sys.exit(main())