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
3.4 KiB
Minecraft 分析器
识别 Minecraft Java 版客户端在连接建立后发出的第一个数据包(Handshake,packet 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 = status,2 = login,3 = transfer |
next_state_name |
string | 上面的可读形式:status / login / transfer |
packet_length |
int | 握手包声明的长度(不含长度字段自身) |
yes 和 candidate 一定存在,其余字段只在 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 窗口操纵规避。