数据包伪装系统(Finalmask)
位置:传输机制层 | 难度:较难 | 前置:深度包检测机制源码证据:
transport/internet/finalmask/全部 16 个子模块
1. 系统定位
Finalmask 是 xray-core 的数据包伪装层——一个可组合、可链式堆叠的混淆框架。当代理流量在网络层面传输时,finalmask 在数据包上应用多层变换,使流量在 DPI 系统中表现为特定已知协议(DNS、DTLS、WeChat 视频、WireGuard 等)或完全随机的不可识别格式。
每层可选、可组合、可排序。用户配置决定了哪些伪装层以何种顺序生效。
2. 架构核心
源码位置:transport/internet/finalmask/finalmask.go
Finalmask 通过两个接口和两个管理器组织所有伪装模块:
type Udpmask interface {
UDP() bool
WrapPacketConnClient(net.PacketConn) (net.PacketConn, error)
WrapPacketConnServer(net.PacketConn) (net.PacketConn, error)
}
type Tcpmask interface {
TCP() bool
WrapConnClient(net.Conn) (net.Conn, error)
WrapConnServer(net.Conn) (net.Conn, error)
}UdpmaskManager / TcpmaskManager 按配置顺序链式调用各 mask 的 Wrap 方法,每一层返回的包装连接作为下一层的输入。最内层是原始 socket,最外层最终交给代理协议使用。
3. 伪装模块分类
3.1 载荷混淆 — 加密/编码数据内容
Salamander(蝾螈)
源码位置:finalmask/salamander/ | 核心文件:salamander.go
原理:每包独立的 XOR 混淆,使用 BLAKE2b-256 从预共享密钥 + 8 字节随机盐派生加密流。
数据包格式:
[8 bytes Salt] [XOR 混淆后的载荷]func (s *SalamanderObfuscator) Obfuscate(b []byte) {
salt := random(8)
key := blake2b256(psk || salt) // 32 字节派生密钥
for i := range payload {
payload[i] ^= key[i % 32]
}
// 输出:salt[0:8] + 混淆后的payload
}对 DPI 的对抗:相同明文产生不同密文(盐不同)。8 字节盐集成到头管理层,被伪装头包裹。BLAKE2b 密钥派生使每个包的 XOR 模式独立。
Sudoku
源码位置:finalmask/sudoku/ | 核心文件:codec.go, table.go
原理:将每个字节编码为 4 个"提示"字节(从 4x4 数独网格派生),使流量表现为特定字节分布。
三种字节布局:
| 布局 | 输出范围 | 效果 |
|---|---|---|
| ASCII | 0x20-0x7E(可打印 ASCII) | 模仿 HTTP 明文 |
| Entropy | 0x00-0x8F | 模仿任意二进制 |
| Custom | 用户定义模式 | 精确控制字节分布 |
编码过程:
- 从密码 SHA-256 生成 288 个 4x4 数独网格
- 对每个网格,计算哪些 4 位置组合能唯一标识该网格
- 输入字节值选择对应的网格,输出该网格的 4 个位置坐标
- 每个位置的字节值 =
base + value,其中base由布局类型决定
Packed 模式(TCP 下行):使用 6-bit 编码替代 4-byte 编码,将开销从 4x 降到约 1.33x。
mKCP AES-128-GCM
源码位置:finalmask/mkcp/aes128gcm/
标准 AES-128-GCM 认证加密。每包生成随机 Nonce (12 字节),前置在数据包前。密码从配置的 Password 经 SHA-256 截断为 16 字节密钥。
mKCP Original(XOR 链)
源码位置:finalmask/mkcp/original/
轻量级 XOR 混淆:前置 4 字节 FNV-32a 校验和 + 2 字节长度,然后对载荷应用 4 字节向前/向后 XOR 链。提供 Go 通用和 amd64 汇编两个版本。
3.2 外观伪装 — 添加已知协议包头
每个外观伪装模块在 UDP 数据包前添加固定大小的协议特征头,使包在 DPI 系统中匹配已知白名单协议。
| 模块 | 包头大小 | 伪装目标 | 头部特征 |
|---|---|---|---|
header/dns | 12B + 域名段 | DNS 查询 (RFC 1035) | 随机 Transaction ID + A-record 查询 |
header/dtls | 13B | DTLS 1.2 应用数据 | 17 FE FD + epoch + seq |
header/srtp | 4B | 加密 RTP(VoIP) | B5 E8 + 序列号 |
header/utp | 4B | μTP(BitTorrent) | 连接 ID + 类型/扩展字节 |
header/wechat | 13B | 微信视频通话 | A1 08 + 序列号 + 固定数据 |
header/wireguard | 4B | WireGuard 握手 | 04 00 00 00 |
选择依据:这些协议的共同特征是——它们在绝大多数网络中不被阻断(VoIP、DNS、微信都是基础通信服务),且 DPI 系统通常不对它们进行深度内容检查。
自定义头(header/custom)
源码位置:finalmask/header/custom/ | 核心文件:evaluator.go
最灵活的伪装模块。用户通过 Protobuf 配置定义任意字节序列作为握手头。支持表达式求值器:
- 运算符:
concat,slice,xor16,xor32,be16,be32,le16,le32,pad,truncate,add,sub,and,or,shl,shr - 变量:
src_port_u16,src_ip4_u32,dst_port_u16,dst_ip4_u32(连接元数据) - 状态存储:TTL 键值对,同一客户端地址的连接共享状态
UDP 独立模式:对 UDP 执行两步握手——客户端发送请求头,服务端验证并返回响应头,验证通过后才开始传输数据。
3.3 载体传输 — 将数据嵌入另一个协议
xDNS
源码位置:finalmask/xdns/ | 核心文件:dns.go(581行), client.go(417行), server.go(512行)
将数据编码为 DNS 查询/响应:
- 客户端将载荷 base32 编码
- 分片为 DNS 标签段(每段 ≤ 63 字节)
- 构造 DNS 查询包:
<编码数据>.<配置域名> - 服务端接收查询 → 解码 base32 → 交给上层协议
- 回复数据封装为 DNS Answer Records(支持 TXT、A、AAAA 类型)
- 使用
record_transport.go进行分段/多路复用
限制:每包载荷约 300 字节(DNS 域名长度限制)。不适合大流量场景,但几乎不可能被阻断——DNS 是互联网基础设施。
xICMP
源码位置:finalmask/xicmp/ | 核心文件:client.go(346行), server.go(375行)
将数据编码为 ICMP Echo(Ping)包:
- 客户端将载荷封装为 ICMP Echo Request
- 服务端接收 → 解析载荷 → 返回 ICMP Echo Reply
- 使用滑动窗口(
windowSize=1000)管理序列号 - 空闲时发送空载荷的 poll 消息维持通道
限制:必须是最外层伪装。需要 raw socket 权限。许多云环境限制 ICMP 流量。
3.4 流量混淆 — 改变包大小和时序
Noise
源码位置:finalmask/noise/conn.go
在首个数据包之前发送配置数量(Burst)的随机内容包,包间有随机延迟。之后使用 TTL 清除状态。目的:混淆 DPI 对"连接首个数据包"的时序和长度分析。
Fragment
源码位置:finalmask/fragment/conn.go
将 TCP 写入拆分为多个小片段。对 TLS 有专门处理:
- 检测 TLS Handshake(
0x16)记录 - 将记录体拆分为多个小 TLS 记录
- 每个片段可有随机延迟(
DelayMin/DelayMax) - 限制每写入的最大拆分数(
MaxSplitMin/MaxSplitMax)
对 DPI 的对抗:针对检查 TLS ClientHello 大小/时序的 DPI 规则。标准 ClientHello 是一个完整的 TLS 记录(通常 200-500B),分片后变成多个小记录,破坏 DPI 的协议识别。
4. 伪装链的组合策略
| 使用场景 | 推荐组合 | 原理 |
|---|---|---|
| UDP 流 + 严格 DPI | [dns header] → [salamander] | 外层看起来是 DNS 查询,内层载荷加密 |
| TCP 流 + 规避 TLS 指纹 | [fragment] → [custom header] | 拆分 TCP 写入,添加自定义握手头 |
| 低带宽隐蔽信道 | [xdns] 或 [xicmp] | 完全嵌入 DNS/ICMP 协议内 |
| 高带宽 + 最大伪装 | [noise] → [dtls header] → [aes128gcm] → [sudoku] | 噪声前置 + DTLS 外观 + AES 加密 + 字节编码 |
5. 源码文件索引
| 目录 | 核心文件 | 功能 |
|---|---|---|
finalmask/ | finalmask.go | Udpmask/Tcpmask 接口 + Manager 链式调度 |
finalmask/fragment/ | conn.go | TCP 写入分段 |
finalmask/salamander/ | salamander.go | BLAKE2b XOR 混淆 |
finalmask/sudoku/ | codec.go, table.go | 数独字节编码 |
finalmask/noise/ | conn.go | 随机噪声前置 |
finalmask/mkcp/aes128gcm/ | conn.go | AES-128-GCM 加密 |
finalmask/mkcp/original/ | conn.go, xor.go | FNV+XOR 轻量混淆 |
finalmask/header/dns/ | conn.go | DNS 查询头 |
finalmask/header/dtls/ | conn.go | DTLS 记录头 |
finalmask/header/srtp/ | conn.go | SRTP 头 |
finalmask/header/utp/ | conn.go | μTP 头 |
finalmask/header/wechat/ | conn.go | 微信视频头 |
finalmask/header/wireguard/ | conn.go | WireGuard 头 |
finalmask/header/custom/ | evaluator.go, tcp.go, udp.go | 自定义表达式头 |
finalmask/xdns/ | dns.go, client.go, server.go | DNS 隧道 |
finalmask/xicmp/ | client.go, server.go | ICMP 隧道 |