decrypt_srvlist.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. """Decrypt the game serverlist using XXTEA with the game_signkey from modlist.
  2. modlist says isEncrypt=1 and provides game_signkey per sub-module:
  3. 玖扬复古176 (206923): game_signkey = 47f67f306cbdffa3a6606292c1a3fa01
  4. Per cq2-00 docs, client Lua is XXTEA-encrypted with key=34UFXMgtkz.
  5. The serverlist is likely XXTEA with game_signkey (hex-decoded or raw).
  6. Try multiple key derivations.
  7. """
  8. import asyncio
  9. import base64
  10. import sys
  11. import httpx
  12. import xxtea
  13. if hasattr(sys.stdout, "reconfigure"):
  14. sys.stdout.reconfigure(encoding="utf-8", errors="replace")
  15. SIGNKEY_HEX = "47f67f306cbdffa3a6606292c1a3fa01"
  16. LUA_KEY = "34UFXMgtkz"
  17. async def main() -> None:
  18. url = "https://list-box.dhsf.xqhuyu.com/serverlist/110585/serverlist.txt"
  19. async with httpx.AsyncClient(timeout=15, follow_redirects=True) as c:
  20. r = await c.get(url)
  21. raw = r.content
  22. print(f"downloaded {len(raw)} bytes")
  23. key_candidates = [
  24. ("signkey raw ascii (32B)", SIGNKEY_HEX.encode()),
  25. ("signkey hex-decoded (16B)", bytes.fromhex(SIGNKEY_HEX)),
  26. ("lua key 34UFXMgtkz", LUA_KEY.encode()),
  27. ("lua key padded to 16", LUA_KEY.encode().ljust(16, b"\0")),
  28. ("signkey first16", SIGNKEY_HEX[:16].encode()),
  29. ]
  30. for label, key in key_candidates:
  31. try:
  32. dec = xxtea.decrypt(raw, key)
  33. if not dec:
  34. print(f"[{label}] -> empty result")
  35. continue
  36. # Check if it looks like text/JSON
  37. head = dec[:60]
  38. printable = sum(1 for b in dec[:200] if 32 <= b < 127 or b in (9, 10, 13))
  39. ratio = printable / min(len(dec), 200)
  40. print(f"[{label}] -> {len(dec)} bytes, printable={ratio:.0%}, head={head[:50]!r}")
  41. if ratio > 0.85:
  42. print(" *** LOOKS LIKE TEXT! First 500 chars:")
  43. try:
  44. txt = dec.decode("utf-8", errors="replace")
  45. print(txt[:500])
  46. except Exception:
  47. pass
  48. except Exception as e:
  49. print(f"[{label}] -> error: {type(e).__name__}: {str(e)[:80]}")
  50. asyncio.run(main())