login_fixed_otp.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. """CS 2001 with FULLY corrected body per decrypted Lua sources.
  2. Corrections vs all previous attempts (from LoginOtpPassWordProxy.lua):
  3. - otpType = "1" (getCheckType() hardcoded, appended RAW)
  4. - otpCode = "0" (getOtpCode() default 0, appended RAW — NO Encode6BitBuf)
  5. - account = uid (AuthProxy:GetUID())
  6. - password = env.json token (AuthProxy:GetToken()), Encode6BitBuf'd
  7. - channelID, Encode6BitBuf'd
  8. Body format (LoginProxy.lua line 395):
  9. account / Encode6BitBuf(password) / Encode6BitBuf(channelID) / 1 / 0
  10. (literal string "1") (literal 0)
  11. """
  12. import asyncio
  13. import base64
  14. import json
  15. import random
  16. import struct
  17. import sys
  18. from pathlib import Path
  19. sys.path.insert(0, "src")
  20. from cq2_sdk.utils.encoding import encode6bit
  21. raw = Path(r"D:\ProgramFiles\996box\GameBoxDownload\1\env.json").read_bytes()
  22. text = raw.decode("utf-8", errors="replace")
  23. idx = text.find("eyJ")
  24. b64 = text[idx:].rstrip()
  25. padded = b64 + "=" * ((-len(b64)) % 4)
  26. env = json.loads(base64.urlsafe_b64decode(padded).decode("utf-8", errors="replace"))
  27. uid = str(env["uid"])
  28. token = env["token"]
  29. channel = env.get("channel", "110585")
  30. pwd_enc = encode6bit(token.encode()).decode("ascii")
  31. ch_enc = encode6bit(channel.encode()).decode("ascii")
  32. # *** THE FIX: literal "1" and "0", NOT encoded ***
  33. body = f"{uid}/{pwd_enc}/{ch_enc}/1/0"
  34. main_sid = 14414736
  35. server_port = 8021
  36. param1 = random.randint(100_000_000, 2_100_000_000) & 0xFFFF00FF
  37. frame = struct.pack("<6i", 2001, 900000, param1, main_sid, server_port, len(body.encode())) + body.encode()
  38. print(f"body = {body}")
  39. print(f" (tail check: must end with literal '/1/0' -> ...{body[-8:]!r})")
  40. print(f"frame: {len(frame)}B msg=2001 recog=900000 p1={param1} p2={main_sid} p3={server_port}")
  41. print()
  42. async def send_and_read():
  43. print("connecting gfgame:12000 ...")
  44. r, w = await asyncio.wait_for(asyncio.open_connection("gfgame.dhsf.xqhuyu.com", 12000), timeout=6)
  45. try:
  46. w.write(frame)
  47. await w.drain()
  48. print("sent.")
  49. # read in exact chunks; server replies 6B zeros per earlier capture
  50. total = b""
  51. import time
  52. t0 = time.time()
  53. while time.time() - t0 < 8:
  54. try:
  55. chunk = await asyncio.wait_for(r.readexactly(6), timeout=2)
  56. total += chunk
  57. print(f" +6B: {chunk.hex()}")
  58. if len(total) >= 24:
  59. # could be a 24B frame header
  60. mid, recog, p1, p2, p3, blen = struct.unpack("<6i", total[:24])
  61. print(f" *** 24B frame: msg_id={mid} recog={recog} p2={p2} p3={p3} body_len={blen}")
  62. if blen > 0 and blen < 4096:
  63. rest = await asyncio.wait_for(r.readexactly(blen), timeout=3)
  64. print(f" body: {rest!r}")
  65. return
  66. except asyncio.IncompleteReadError as e:
  67. print(f" closed after {len(total)}B total; partial={e.partial.hex() if e.partial else ''}")
  68. break
  69. except (asyncio.TimeoutError, TimeoutError):
  70. if total:
  71. break
  72. if total and len(total) < 24:
  73. mid = struct.unpack("<i", total[:4])[0] if len(total) >= 4 else None
  74. print(f" total {len(total)}B hex={total.hex()} msg_id_guess={mid}")
  75. finally:
  76. w.close()
  77. try:
  78. await w.wait_closed()
  79. except Exception:
  80. pass
  81. asyncio.run(send_and_read())