socks5_probe.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. """Test the SOCKS5 hypothesis on port 12000.
  2. Evidence: cq2/ ships libsocks5-x64_v2.dll + libsocks5-x86_v2.dll.
  3. serverlist 'x' field (12000) = proxy port; Lua connects to proxyport and the
  4. C++ netClient (with libsocks5) likely does a SOCKS5 handshake transparently.
  5. Probe:
  6. 1. SOCKS5 greeting (05 01 00) -> expect (05 00)
  7. 2. SOCKS5 CONNECT to backend game server (gfgame IP : 8021)
  8. 3. If tunnel established -> send CS 2001 through it
  9. 4. Read reply
  10. """
  11. import asyncio
  12. import base64
  13. import json
  14. import random
  15. import struct
  16. import sys
  17. from pathlib import Path
  18. sys.path.insert(0, "src")
  19. from cq2_sdk.utils.encoding import encode6bit
  20. raw = Path(r"D:\ProgramFiles\996box\GameBoxDownload\1\env.json").read_bytes()
  21. text = raw.decode("utf-8", errors="replace")
  22. idx = text.find("eyJ")
  23. b64 = text[idx:].rstrip()
  24. padded = b64 + "=" * ((-len(b64)) % 4)
  25. env = json.loads(base64.urlsafe_b64decode(padded).decode("utf-8", errors="replace"))
  26. uid = str(env["uid"])
  27. pwd_enc = encode6bit(env["token"].encode()).decode("ascii")
  28. ch_enc = encode6bit(env.get("channel", "110585").encode()).decode("ascii")
  29. BODY = f"{uid}/{pwd_enc}/{ch_enc}/1/0"
  30. PROXY = ("gfgame.dhsf.xqhuyu.com", 12000)
  31. BACKEND_IP = "47.118.169.153"
  32. BACKEND_PORT = 8021
  33. SID = 14414738
  34. def socks5_greeting() -> bytes:
  35. return bytes([0x05, 0x01, 0x00]) # VER5, 1 method, NO-AUTH
  36. def socks5_connect(ip: str, port: int) -> bytes:
  37. # VER CMD RSV ATYP IPv4(4) PORT(2BE)
  38. parts = ip.split(".")
  39. return bytes([0x05, 0x01, 0x00, 0x01] + [int(p) for p in parts] + list(struct.pack(">H", port)))
  40. def frame_2001() -> bytes:
  41. p1 = random.randint(100_000_000, 2_100_000_000) & 0xFFFF00FF
  42. return (
  43. struct.pack("<6i", 2001, 900000, p1, SID, BACKEND_PORT, len(BODY.encode()))
  44. + BODY.encode()
  45. )
  46. async def read_some(r, n=64, timeout=4.0) -> bytes:
  47. return await asyncio.wait_for(r.read(n), timeout=timeout)
  48. async def main():
  49. print(f"connecting to proxy {PROXY} ...")
  50. r, w = await asyncio.wait_for(asyncio.open_connection(*PROXY), timeout=6)
  51. try:
  52. # Step 1: SOCKS5 greeting
  53. w.write(socks5_greeting())
  54. await w.drain()
  55. print("sent greeting: 05 01 00")
  56. resp = await read_some(r, 2)
  57. print(f"greeting reply: {resp.hex()} ({len(resp)}B)")
  58. if len(resp) >= 2 and resp[0] == 0x05:
  59. print("*** SOCKS5 CONFIRMED! server speaks SOCKS5 ***")
  60. # Step 2: CONNECT to backend
  61. req = socks5_connect(BACKEND_IP, BACKEND_PORT)
  62. w.write(req)
  63. await w.drain()
  64. print(f"sent CONNECT to {BACKEND_IP}:{BACKEND_PORT}: {req.hex()}")
  65. resp2 = await read_some(r, 16)
  66. print(f"connect reply: {resp2.hex()} ({len(resp2)}B)")
  67. if len(resp2) >= 4 and resp2[1] == 0x00:
  68. print("*** TUNNEL ESTABLISHED! sending CS 2001 ... ***")
  69. w.write(frame_2001())
  70. await w.drain()
  71. print("sent 2001 through tunnel")
  72. # read game reply
  73. total = b""
  74. import time
  75. t0 = time.time()
  76. while time.time() - t0 < 8:
  77. try:
  78. chunk = await asyncio.wait_for(r.read(4096), timeout=2)
  79. if not chunk:
  80. print("tunnel closed after", len(total), "bytes")
  81. break
  82. total += chunk
  83. print(f" +{len(chunk)}B: {chunk[:40].hex()}")
  84. if len(total) >= 24:
  85. mid, recog, p1, p2, p3, blen = struct.unpack("<6i", total[:24])
  86. print(f" *** GAME FRAME: msg_id={mid} recog={recog} p2={p2} p3={p3} blen={blen} ***")
  87. if 0 < blen < 4096 and len(total) >= 24 + blen:
  88. print(f" body: {total[24:24+blen]!r}")
  89. if mid == 529:
  90. print("*** *** *** SC 529 LOGIN KEY — FULL TCP LOGIN WORKS *** *** ***")
  91. break
  92. except asyncio.TimeoutError:
  93. if total:
  94. break
  95. else:
  96. print(f"CONNECT rejected (reply code {resp2[1] if len(resp2)>1 else '?'})")
  97. except asyncio.IncompleteReadError as e:
  98. print(f"connection closed during handshake; partial={e.partial.hex() if e.partial else ''}")
  99. finally:
  100. w.close()
  101. try:
  102. await w.wait_closed()
  103. except Exception:
  104. pass
  105. asyncio.run(main())