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

68 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Minecraft 分析器
识别 Minecraft Java 版客户端在连接建立后发出的第一个数据包(Handshakepacket ID `0x00`),从中取出客户端请求的服务器地址、协议版本和后续状态。基岩版走 UDP/RakNet,不在本分析器范围内。
分析器名是 `minecraft`。和其他分析器一样,**只有当某条规则的表达式里出现了 `minecraft` 这个标识符时,它才会被启用**(见 `ruleset/expr.go` 的依赖收集逻辑),不写规则就不会有任何开销。
## 属性
| 属性 | 类型 | 说明 |
| --- | --- | --- |
| `yes` | bool | 确认是 Minecraft 握手 |
| `candidate` | bool | 数据还不完整,暂时无法判定,仍在等待后续分段 |
| `protocol` | int | 协议版本号,**不是游戏版本号**,对照表见 [minecraft.wiki](https://minecraft.wiki/w/Protocol_version)(例如 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 | 握手包声明的长度(不含长度字段自身) |
`yes``candidate` 一定存在,其余字段只在 `yes` 为 true 时出现。
## 规则示例
只拦截真正进服的连接,放过服务器列表里的 ping:
```yaml
- name: block-minecraft-login
action: block
log: true
expr: minecraft != nil && minecraft.yes && minecraft.next_state_name == "login"
```
只允许连接指定的服务器,其余一律拦截:
```yaml
- name: minecraft-allowlist
action: block
expr: >
minecraft != nil && minecraft.yes &&
!hasPrefix(minecraft.server_addr, "mc.example.com")
```
按协议版本卡老客户端:
```yaml
- name: block-old-minecraft-clients
action: block
expr: minecraft != nil && minecraft.yes && minecraft.protocol < 763
```
纯记录不拦截,先摸清楚流量情况:
```yaml
- 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 窗口操纵规避](tcp-window-evasion.zh.md)。