Files

175 lines
6.0 KiB
Python

# -*- coding: utf-8 -*-
"""密钥提取失败诊断脚本。
在微信【已登录】状态下运行(务必让微信窗口保持打开):
python -m wechatauto.diagnose_keys
或直接:
python wechatauto/diagnose_keys.py
把输出完整发给维护者。
"""
import json
import os
import subprocess
import sys
import traceback
print("=" * 60)
print("wechatauto-replica key diagnostic")
print("=" * 60)
print("Python:", sys.version.split()[0])
print("Python bits:", 64 if sys.maxsize > 2**32 else 32)
print("OS:", sys.platform)
# 1. library version
try:
import wechatauto
from wechatauto import WeChatDB
from wechatauto.db import _find_account_dirs
print("lib version:", getattr(wechatauto, "__version__", "?"))
import wechatauto.db as dbmod
print("db.py:", dbmod.__file__)
except Exception as e:
print("import error:", repr(e))
traceback.print_exc()
# 2. Weixin processes
print("\n--- Weixin processes ---")
weixin_pids = []
try:
r = subprocess.run(
["tasklist", "/FI", "IMAGENAME eq Weixin.exe", "/FO", "CSV", "/NH"],
capture_output=True, text=True)
print("tasklist:\n%s" % (r.stdout.strip() or "(no Weixin.exe)"))
for line in r.stdout.strip().splitlines():
parts = line.strip('"').split('","')
if len(parts) >= 2 and parts[1].isdigit():
weixin_pids.append(int(parts[1]))
except Exception as e:
print("tasklist failed:", repr(e))
# 2b. per-PID permission / read test (key root-cause for silent 0-key extraction)
print("\n--- per-PID access test ---")
if not weixin_pids:
print("(no Weixin.exe running - open WeChat and log in first)")
else:
import ctypes
from ctypes import wintypes
from wechatauto.db import _MBI
k32 = ctypes.WinDLL("kernel32", use_last_error=True)
k32.OpenProcess.restype = wintypes.HANDLE
k32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
k32.ReadProcessMemory.argtypes = [
wintypes.HANDLE, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t,
ctypes.POINTER(ctypes.c_size_t)]
k32.VirtualQueryEx.argtypes = [
wintypes.HANDLE, ctypes.c_void_p, ctypes.POINTER(_MBI), ctypes.c_size_t]
for pid in weixin_pids:
h = k32.OpenProcess(0x0010 | 0x0400, False, pid) # VM_READ | QUERY_INFORMATION
if not h:
err = ctypes.get_last_error()
print("PID %d: OpenProcess FAILED (error %d - likely needs admin / same-elevation)" % (pid, err))
continue
ok = 0
first_region = None
addr = ctypes.c_void_p(0)
for _ in range(4000):
mbi = _MBI()
n = k32.VirtualQueryEx(h, addr, ctypes.byref(mbi), ctypes.sizeof(_MBI))
if n == 0:
break
if (mbi.State == 0x1000 and (mbi.Protect & 0xFF) & 0xE6
and not (mbi.Protect & 0x100) and 0 < mbi.RegionSize < 0x10000000):
buf = ctypes.create_string_buffer(8)
br = ctypes.c_size_t(0)
if k32.ReadProcessMemory(h, ctypes.c_void_p(mbi.BaseAddress or 0), buf, 8, ctypes.byref(br)) and br.value == 8:
ok += 1
if first_region is None:
first_region = mbi.BaseAddress or 0
break
addr = ctypes.c_void_p((mbi.BaseAddress or 0) + mbi.RegionSize)
ctypes.windll.kernel32.CloseHandle(h)
print("PID %d: OpenProcess OK, first readable region: %s (readable-region check %s)"
% (pid, "0x%x" % first_region if first_region else "NONE", "OK" if ok else "FAILED"))
# 3. data dir detection
print("\n--- data dir ---")
try:
from wechatauto.db import auto_detect_db_dir
d = auto_detect_db_dir()
print("auto_detect_db_dir:", d)
if d and os.path.isdir(d):
for x in os.listdir(d):
if os.path.isdir(os.path.join(d, x, "db_storage")):
print(" account dir:", x)
except Exception as e:
print("dir detect error:", repr(e))
# 4. WeChatDB init (uses cached keys)
print("\n--- WeChatDB init ---")
db = None
try:
db = WeChatDB()
print("workdir:", db.workdir)
print("keys_file:", db.keys_file, "exists:", os.path.exists(db.keys_file))
print("account:", db.account)
if os.path.exists(db.keys_file):
with open(db.keys_file, encoding="utf-8") as f:
keys = json.load(f)
print("keys cached:", len(keys))
for k in sorted(keys):
print(" ", k)
else:
print("keys cached: (file not found)")
print("db_files:", len(db._db_files))
print("keys loaded:", len(db._keys))
missing = [rel for rel, _, _ in db._db_files if rel not in db._keys]
print("missing:", missing)
print("unkeyed:", db.unkeyed)
# which account dirs exist vs which one was picked
print("accounts on disk:", sorted(
os.path.basename(x) for x in _find_account_dirs(db.db_dir)))
print("picked account: ", db.account)
except Exception as e:
print("init error:", repr(e))
traceback.print_exc()
# 5. fresh key extraction from memory
print("\n--- extract_keys from Weixin.exe memory ---")
try:
if db is None:
db = WeChatDB()
pids = db._find_weixin_pids()
print("Weixin PIDs:", pids)
if not pids:
print("no Weixin.exe running - open WeChat and log in first")
else:
keys = db.extract_keys()
print("extracted:", len(keys))
for k in sorted(keys):
print(" ", k)
missing2 = [rel for rel, _, _ in db._db_files if rel not in keys]
print("still missing:", missing2)
except Exception as e:
print("extract error:", repr(e))
traceback.print_exc()
# 6. verify cached keys actually work
print("\n--- verify cached keys ---")
try:
if db is None:
db = WeChatDB()
works = 0
for rel, _, _ in db._db_files:
if db._key_works(rel):
works += 1
print("keys that verify: %d / %d" % (works, len(db._db_files)))
except Exception as e:
print("verify error:", repr(e))
print("=" * 60)
print("done. send the full output to the maintainer.")
print("=" * 60)