tcp_semantics_probe.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """Full-fidelity TCP behavior analysis of the game gateway's 6-byte zero replies.
  2. Records ALL packets (with TCP flags, seq/ack, timing) — not just payloads —
  3. to answer:
  4. 1. Are the 6B zero packets PSH-flagged DATA, or keepalive probes?
  5. 2. Does the server FIN or RST after them?
  6. 3. What's the exact timing pattern (immediate? delayed?)
  7. Runs capture in-process while sending one CS 2001 probe.
  8. """
  9. import asyncio
  10. import base64
  11. import json
  12. import random
  13. import struct
  14. import sys
  15. import time
  16. from pathlib import Path
  17. sys.path.insert(0, "src")
  18. from cq2_sdk.utils.encoding import encode6bit
  19. raw = Path(r"D:\ProgramFiles\996box\GameBoxDownload\1\env.json").read_bytes()
  20. text = raw.decode("utf-8", errors="replace")
  21. idx = text.find("eyJ")
  22. b64 = text[idx:].rstrip()
  23. padded = b64 + "=" * ((-len(b64)) % 4)
  24. env = json.loads(base64.urlsafe_b64decode(padded).decode("utf-8", errors="replace"))
  25. uid = str(env["uid"])
  26. pwd_enc = encode6bit(env["token"].encode()).decode("ascii")
  27. ch_enc = encode6bit(env.get("channel", "110585").encode()).decode("ascii")
  28. BODY = f"{uid}/{pwd_enc}/{ch_enc}/1/0"
  29. FLAG_NAMES = {0x01: "FIN", 0x02: "SYN", 0x04: "RST", 0x08: "PSH", 0x10: "ACK", 0x20: "URG"}
  30. def flags_str(f: int) -> str:
  31. return "+".join(n for b, n in FLAG_NAMES.items() if f & b) or "."
  32. def make_probe() -> bytes:
  33. p1 = random.randint(100_000_000, 2_100_000_000) & 0xFFFF00FF
  34. return (
  35. struct.pack("<6i", 2001, 900000, p1, 14414738, 8021, len(BODY.encode()))
  36. + BODY.encode()
  37. )
  38. async def main() -> None:
  39. from scapy.config import conf
  40. from scapy.sendrecv import AsyncSniffer
  41. events: list[dict] = []
  42. def on_pkt(pkt):
  43. try:
  44. # WLAN iface reports linktype=1 which confuses scapy auto-detection;
  45. # packets arrive as Raw — re-parse as Ether (proven fix from capture_cq2.py)
  46. if not pkt.haslayer("IP"):
  47. from scapy.layers.l2 import Ether
  48. raw_bytes = bytes(pkt)
  49. if len(raw_bytes) < 34:
  50. return
  51. pkt = Ether(raw_bytes)
  52. if not pkt.haslayer("IP"):
  53. return
  54. if not pkt.haslayer("TCP"):
  55. return
  56. tcp, ip = pkt["TCP"], pkt["IP"]
  57. payload = bytes(tcp.payload)
  58. rec = {
  59. "t": round(time.time(), 3),
  60. "src": f"{ip.src}:{tcp.sport}",
  61. "dst": f"{ip.dst}:{tcp.dport}",
  62. "flags": flags_str(int(tcp.flags)),
  63. "seq": int(tcp.seq),
  64. "payload_len": len(payload),
  65. "payload_hex": payload.hex()[:48] if payload else "",
  66. }
  67. events.append(rec)
  68. pl = f" DATA[{len(payload)}B]={payload.hex()[:32]}" if payload else ""
  69. print(f" {rec['t']%1000:7.3f} {rec['src']:>22} -> {rec['dst']:<22} {rec['flags']:<12} seq={rec['seq']:<10}{pl}")
  70. except Exception as e:
  71. print("pkt err:", e)
  72. sniffer = AsyncSniffer(iface=conf.iface, prn=on_pkt, store=False)
  73. sniffer.start()
  74. print("sniffer running; waiting 2s for baseline...")
  75. await asyncio.sleep(2)
  76. print(f"\nsending CS 2001 probe to gfgame:12000 ...")
  77. try:
  78. r, w = await asyncio.wait_for(asyncio.open_connection("gfgame.dhsf.xqhuyu.com", 12000), timeout=6)
  79. except Exception as e:
  80. print(f"connect failed: {e}")
  81. sniffer.stop()
  82. return
  83. sent_at = time.time()
  84. try:
  85. w.write(make_probe())
  86. await w.drain()
  87. print(f"probe sent at t={sent_at%1000:.3f}; listening 8s for server behavior...")
  88. await asyncio.sleep(8)
  89. finally:
  90. try:
  91. w.close()
  92. try:
  93. await w.wait_closed()
  94. except Exception:
  95. pass
  96. except Exception:
  97. pass
  98. await asyncio.sleep(1)
  99. sniffer.stop()
  100. # Summary analysis
  101. print(f"\n=== {len(events)} TCP packets captured ===")
  102. data_pkts = [e for e in events if e["payload_len"] > 0]
  103. rst = [e for e in events if "RST" in e["flags"]]
  104. fin = [e for e in events if "FIN" in e["flags"]]
  105. from_server = [e for e in events if e["src"].endswith(":12000")]
  106. print(f"data packets: {len(data_pkts)} (ours: 1 probe)")
  107. print(f"server->us data: {len([e for e in from_server if e['payload_len']>0])}")
  108. print(f"RST: {len(rst)} FIN: {len(fin)}")
  109. print(f"server packets total: {len(from_server)}")
  110. zs = [e for e in from_server if e["payload_len"] == 6 and set(bytes.fromhex(e["payload_hex"])) == {0}]
  111. if zs:
  112. print(f"\n6B-zero packets from server: {len(zs)}")
  113. base_t = events[0]["t"]
  114. for z in zs:
  115. print(f" t+{z['t']-base_t:6.3f}s flags={z['flags']} seq={z['seq']}")
  116. if rst:
  117. print(f"\nRST packet(s): {[e['flags'] for e in rst]} — server ABORTS the connection")
  118. elif fin:
  119. print(f"\nFIN packet(s): {[e['flags'] for e in fin]} — server closes gracefully")
  120. asyncio.run(main())