| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- #!/usr/bin/env python3
- """scan_sourid_pcap.py — 从 pcap 重组 TCP 流, 提取全部 SC51/SC634 地图属性。
- 方法(交接文档定案): scapy 重组 sport=8043 流 → encode6 帧头切帧 →
- SC-1 批量帧 zlib 解压子帧 → 全部 SC51/SC634 decode6 → sourid 提取。
- 用法: sudo .venv/bin/python scripts/scan_sourid_pcap.py /tmp/cap_full.pcap
- """
- from __future__ import annotations
- import json
- import sys
- import zlib
- sys.path.insert(0, "src")
- from pbot.codec.bitbuf import decode6 # noqa: E402
- def frames_from_stream(buf: bytes) -> list[tuple[int, bytes]]:
- """从 TCP 字节流按 encode6 帧头切帧(FrameSplitter: 粘包/半包安全)。"""
- from pbot.codec.frame import FrameSplitter
- sp = FrameSplitter()
- return [(f.msg_id, f.body) for f in sp.feed(buf)]
- def extract_map_bodies(frames: list[tuple[int, bytes]]) -> list[dict]:
- out: list[dict] = []
- for msg, body in frames:
- if msg == -1:
- # 批量帧: zlib 压缩子帧流
- try:
- raw = zlib.decompress(body)
- except zlib.error:
- continue
- out.extend(extract_map_bodies(frames_from_stream(raw)))
- continue
- if msg not in (51, 634):
- continue
- try:
- text = decode6(body).decode("utf-8", errors="replace")
- start = text.find("{")
- if start < 0:
- continue
- j = json.loads(text[start:text.rfind("}") + 1])
- j["_msg"] = f"SC{msg}"
- out.append(j)
- except Exception:
- continue
- return out
- def main() -> None:
- pcap = sys.argv[1] if len(sys.argv) > 1 else "/tmp/cap_full.pcap"
- from scapy.all import rdpcap, TCP, Raw # type: ignore
- packets = rdpcap(pcap)
- streams: dict[tuple, bytearray] = {}
- for pkt in packets:
- if TCP not in pkt or Raw not in pkt:
- continue
- tcp = pkt[TCP]
- sport, dport = tcp.sport, tcp.dport
- # 游戏服端口 8043, 取服务器→客户端方向(sport=8043)
- if sport != 8043:
- continue
- key = (tcp.ack,)
- streams.setdefault(key, bytearray()).extend(bytes(pkt[Raw].load))
- print(f"流数: {len(streams)}, 总字节: {sum(len(v) for v in streams.values())}")
- results: dict[str, dict] = {}
- for buf in streams.values():
- for j in extract_map_bodies(frames_from_stream(bytes(buf))):
- name = str(j.get("MapName") or j.get("MapId") or "?")
- # SC51 优先(含 sourid), SC634 兜底
- if name in results and j["_msg"] == "SC634" \
- and results[name]["_msg"] == "SC51":
- continue
- results[name] = j
- print(f"\n{'显示名':<14} {'MapId':<14} {'sourid':<8} {'Minimap':<8} 来源")
- print("-" * 72)
- for name in sorted(results):
- j = results[name]
- print(f"{name:<14} {str(j.get('MapId','')):<14} "
- f"{str(j.get('sourid','')):<8} {str(j.get('Minimap','')):<8} {j['_msg']}")
- if __name__ == "__main__":
- main()
|