Skip to content

Trojan 协议

位置:核心协议层 | 难度:中等 | 前置TLS 1.3 深入TLS-in-TLS源码证据:Trojan 官方协议规范 (trojan-gfw.github.io)、proxy/trojan/protocol.goproxy/trojan/server.go外部参考:Trojan Protocol Spec、nthLink White Paper、Trojan-Killer POC


1. 协议定位

Trojan 由 GreaterFire 于 2017 年创建,是最早采用"完全伪装为 HTTPS"思路的代理协议。它的核心假设是:如果代理流量在外观上与普通 HTTPS 无法区分,审查方就无法在不产生巨大误伤的情况下进行阻断

这个假设在 2017-2021 年间基本成立。但 2022 年 10 月的 GFW 大规模阻断事件和 2023 年 5 月的 Trojan-Killer 概念验证证明:TLS-in-TLS 嵌套是可检测的,仅靠外层 TLS 伪装不够。


2. 协议格式

Trojan 协议极其简单。整个协议规范(trojan-gfw.github.io)可以用一张图说明:

TLS 隧道建立后,客户端发送的首个数据包:

+-----------------------+---------+----------------+---------+----------+
| hex(SHA224(password)) |  CRLF   | Trojan Request |  CRLF   | Payload  |
+-----------------------+---------+----------------+---------+----------+
|         56            | X'0D0A' |    Variable    | X'0D0A' | Variable |
+-----------------------+---------+----------------+---------+----------+

Trojan Request = SOCKS5-like 请求:
+-----+------+----------+----------+
| CMD | ATYP | DST.ADDR | DST.PORT |
+-----+------+----------+----------+
|  1  |  1   | Variable |    2     |
+-----+------+----------+----------+

CMD:
  X'01' = CONNECT (TCP)
  X'03' = UDP ASSOCIATE

ATYP:
  X'01' = IPv4
  X'03' = 域名
  X'04' = IPv6

第一个数据包的完整构成

  1. 56 字节 = hex(SHA224(password)):密码的 SHA224 哈希,以十六进制字符串形式发送
  2. 2 字节 = CRLF (\r\n)
  3. 变长 = Trojan Request:目标地址和端口
  4. 2 字节 = CRLF
  5. 变长 = 可选的数据载荷(允许首个数据包就携带实际数据)

2.1 密码哈希格式

hex(SHA224("password_string")) 的结果是 56 个十六进制字符(28 字节的 SHA224 哈希 → 56 字符的十六进制表示)。

例如 SHA224("mysecret")hex = "a1b2c3d4..." 共 56 个字符。

这个固定 56 字节的 hex 字符串有一个值得注意的特征:全部 56 字节都在 ASCII hex 范围内([0x30-0x39, 0x61-0x66]),是连续的可打印 ASCII。这意味着:

  • GFW Algorithm 1 Ex2:前 6 字节全是 ASCII → 豁免 ✓
  • GFW Algorithm 1 Ex4:56 字节连续 ASCII → 豁免 ✓

这也是 Trojan 的设计意图:让首个数据包尽可能通过 GFW 的豁免规则

2.2 服务端验证流程

源码位置:proxy/trojan/server.go + proxy/trojan/protocol.go

1. TLS 握手完成
2. 读取客户端首个数据包
3. 提取前 56 字节 → 与已注册用户的 hex(SHA224(password)) 比对
4. 匹配 → 解析后续的 CRLF + Trojan Request + CRLF + Payload
5. 不匹配 → 将连接转发到本地 Web 服务(回落)

3. 回落机制

Trojan 的回落与 VLESS+REALITY 的回落有本质区别。

3.1 Trojan 的回落

非 Trojan 连接 → 转发到本地 Nginx/Apache
  - 本地 Web 服务器返回真实网页
  - 审查方进行主动探测 → 收到 Nginx 的响应 → 看起来是正常网站

问题:如果服务端没有运行本地 Web 服务器(或 Web 服务器返回的是默认页面/错误页面),主动探测会立即发现异常。

3.2 REALITY 的回落(对比)

非 REALITY 连接 → 转发到任意远程网站 (如 microsoft.com)
  - 审查方收到的是目标网站的真实 TLS 证书和页面
  - 即使是空服务器,也能做到"看起来像 microsoft.com 的镜像"

这是 REALITY 相对于 Trojan 的核心改进——回落不依赖本地环境。


4. GFW 对 Trojan 的检测演进

4.1 2022 年 10 月阻断

2022 年 10 月 3 日前后,超过 100 名反审查社区用户报告 Trojan-go、VLESS+TLS+WebSocket、VMess+TLS 等 TLS 隧道协议被大规模阻断。这是 GFW 首次展示出对 TLS-in-TLS 的检测能力。

4.2 Trojan-Killer(2023 年 5 月)

Xray 的创建者 RPRX 发布了一个名为 "Trojan-Killer" 的概念验证程序,证明了 TLS-in-TLS 检测不需要巨大的计算资源。Trojan-Killer 通过分析 TLS 握手阶段的数据包大小来精确定位 Trojan-go 服务端。

检测原理:当 Trojan 服务端将非 Trojan 连接转发到本地 Web 服务器时,Web 服务器返回的 HTTP 响应与 Trojan 转发的代理数据有不同的包大小特征。Trojan-Killer 利用 TLS 握手中ServerHello 后首个应用数据包的大小来进行区分。

4.3 2025 年 8 月 GFW 升级

据社区估算,2025 年 8 月 GFW 升级后,Trojan 的检测率上升到约 90%。主要利用的特征:

  1. TLS-in-TLS 数据包大小分布异常:外层 TLS 记录中的数据部分 = 内层 TLS + Trojan 请求头,长度偏大
  2. TLS 握手完成后首个数据包的固定长度模式:Trojan 的密码哈希 56B + CRLF 2B + 地址 ≈ 70-90B,这是一个集中的长度区间
  3. 流量的时序模式:Trojan 连接在 TLS 握手完成后立即推送数据(代理流量),而正常 HTTPS 在 TLS 握手后通常先等待客户端 HTTP 请求

5. 与 VLESS+REALITY 的详细对比

维度TrojanVLESS + REALITY
认证方式SHA224(password) 的 hex 字符串,明文发送在 TLS 隧道内ECDH + HKDF + HMAC,认证信息嵌入 TLS ClientHello 的 Session ID
证书需要真实 CA 签发的证书(如 Let's Encrypt)临时自签 Ed25519 证书,无需 CA
回落目标本地 Web 服务器任意远程网站
TLS-in-TLS 处理无特殊处理XTLS Vision 分流,消除嵌套特征
服务端 TLS 指纹可被 JA3/JA4 指纹识别(Nginx/Apache 指纹)无服务端指纹(使用目标网站的 TLS 栈)
domain 依赖需要自己的域名 + CA 证书不需要自己的域名(借用目标网站的 SNI)
部署复杂度需要配置域名、CA 证书、Web 服务器只需配置目标网站地址和密钥对
当前检测率(估算)~90%< 5%

6. 密码哈希的已知弱点

6.1 固定长度特征

56 字节是一个固定值。GFW 可以通过统计学习识别"TLS 握手后首个应用数据包长度为 60-100 字节且前 56 字节熵偏低(全是 hex 字符)"的连接模式。

6.2 被动探测

Trojan 服务端需要先读取 56 字节才能判断连接是否为合法客户端。如果 GFW 发送一个小于 56 字节的探测包,服务端的行为取决于具体实现:

Go 版本实现(在 io.ReadFull 被阻塞时):

  • TCP 的 read() 可能返回少于 56 字节(TCP 是流协议)
  • 如果 Go 版本使用 io.ReadFull 读取 56 字节,不足时会阻塞等待更多数据
  • 这个等待行为本身是可被探测的

C 版本实现(trojan-gfw/trojan):

  • 正确处理了 TCP 流的分段问题

SHA224 的 hex 输出范围完全在 [0-9a-f],熵远低于随机数据。这恰好是 GFW 豁免规则(Ex2-Ex4)会放过的东西——但也使 Trojan 的首包具有了另一个维度的可检测特征:56 字节的 hex 字符串 + CRLF + SOCKS5 请求的固定格式。


7. 源码结构

文件核心内容
proxy/trojan/trojan.go协议注册、MemoryAccount 定义
proxy/trojan/config.go服务端/客户端配置
proxy/trojan/protocol.go协议编解码(密码验证、请求格式)
proxy/trojan/server.go入站处理器:TLS 握手 → 密码验证 → 解析地址 → Dispatcher
proxy/trojan/client.go出站处理器:TLS 连接 → 发送密码哈希 → 发送目标地址 → 中继
proxy/trojan/validator.go密码哈希验证

8. 外部参考资料

资料:The Trojan Protocol — 官方协议规范 类型:协议规范 链接:https://trojan-gfw.github.io/trojan/protocol.html 可信度:A

资料:Design discussion (Trojan Issue #14) 类型:项目设计讨论 链接:https://github.com/trojan-gfw/trojan/issues/14 可信度:A 建议参考:Trojan 设计者关于 TLS-in-TLS 检测的原始讨论

资料:nthLink White Paper 类型:技术白皮书 链接:https://www.opentech.fund/wp-content/uploads/2023/11/nthLink_whitepaper_are_shadowshocks_and_trojan-go_still_relevant.pdf 可信度:B 建议参考:Trojan-go 在 2022 年 GFW 升级后的阻断事件分析


读者自检

  • Trojan 的首个数据包由哪些部分构成、每部分的字节数和格式
  • 为什么 Trojan 的 56 字节 hex 密码哈希让首个数据包通过 GFW 的 ASCII 豁免规则
  • Trojan 的回落与 REALITY 的回落有什么本质区别
  • Trojan-Killer 如何通过 TLS 握手后首个应用数据包大小来识别 Trojan 服务端