Files
OpenGFW/docs/minecraft.zh.md
T
Mxmilu666 f6342c4bcf feat(tcp): add Minecraft analyzer for handshake detection
feat(tcp): implement FETAnalyzer with segment buffering
feat(tcp): enhance TCP engine with window scale detection
test(tcp): add tests for Minecraft and FET analyzers
docs: add documentation for Minecraft analyzer and TCP evasion
2026-07-27 01:09:41 +08:00

3.4 KiB
Raw Blame History

Minecraft 分析器

识别 Minecraft Java 版客户端在连接建立后发出的第一个数据包(Handshakepacket ID 0x00),从中取出客户端请求的服务器地址、协议版本和后续状态。基岩版走 UDP/RakNet,不在本分析器范围内。

分析器名是 minecraft。和其他分析器一样,只有当某条规则的表达式里出现了 minecraft 这个标识符时,它才会被启用(见 ruleset/expr.go 的依赖收集逻辑),不写规则就不会有任何开销。

属性

属性 类型 说明
yes bool 确认是 Minecraft 握手
candidate bool 数据还不完整,暂时无法判定,仍在等待后续分段
protocol int 协议版本号,不是游戏版本号,对照表见 minecraft.wiki(例如 767 对应 1.21
server_addr string 客户端在握手包里填写的服务器地址
server_port uint16 客户端填写的端口
next_state int 1 = status2 = login3 = transfer
next_state_name string 上面的可读形式:status / login / transfer
packet_length int 握手包声明的长度(不含长度字段自身)

yescandidate 一定存在,其余字段只在 yes 为 true 时出现。

规则示例

只拦截真正进服的连接,放过服务器列表里的 ping:

- name: block-minecraft-login
  action: block
  log: true
  expr: minecraft != nil && minecraft.yes && minecraft.next_state_name == "login"

只允许连接指定的服务器,其余一律拦截:

- name: minecraft-allowlist
  action: block
  expr: >
    minecraft != nil && minecraft.yes &&
    !hasPrefix(minecraft.server_addr, "mc.example.com")

按协议版本卡老客户端:

- name: block-old-minecraft-clients
  action: block
  expr: minecraft != nil && minecraft.yes && minecraft.protocol < 763

纯记录不拦截,先摸清楚流量情况:

- name: log-minecraft
  log: true
  expr: minecraft != nil && minecraft.yes

写规则时要注意的几点

server_addr 不一定是干净的域名。 握手包里的地址字段被生态里几个东西复用了,客户端可能在真实域名后面追加以 \0 分隔的内容:Forge/FML 客户端会加 \0FML\0\0FML2\0\0FML3\0 之类的标记,BungeeCord 和 Velocity 的 IP 转发会把玩家真实 IP 和 UUID 拼在后面。分析器保存的是原始字节,不做任何清洗。所以不要用 == 精确比较,用 hasPrefix(minecraft.server_addr, ...),或者先 split(minecraft.server_addr, "\x00")[0] 再比。

这个字段是客户端自己填的,不可信。 它反映客户端想连哪个域名(对 SRV 记录的情况是解析前的原始主机名),不是它实际连到的 IP。要认真限制目标,得配合 ip.dst 一起写。

判定发生在第一个数据包。 握手之后的登录、加密、游戏数据都不会再看。Limit() 是 2053 字节,正常握手包只有几十字节,绰绰有余;超过这个量还没解析成功的连接会被判为非 Minecraft。

握手包被拆到多个 TCP 分段时也能正确解析。 分析器内部有缓冲区,数据不够时返回 candidate = true 并继续等待,不会像只看单个分段的分析器那样被切包绕过。相关背景见 TCP 窗口操纵规避