了解 TCP/IP 指纹的工作原理:TTL、窗口大小、MSS 与 TCP 选项顺序如何从握手中暴露你的操作系统,以及 p0f 和 JA4T 如何读取它们。
在你的浏览器发出 TLS ClientHello 的第一个字节之前,你的操作系统就已经做了自我介绍。开启每一个连接的 TCP 三次握手都携带着一小撮底层参数——初始 TTL(生存时间)、通告的窗口大小、最大分段大小,以及 TCP 选项的排列顺序——这些值由你的操作系统内核用它自己的默认值填入。它们既不由你选择,也不由你的应用程序选择,而它们的组合足够稳定,足以推断出你运行的是哪个操作系统。这就是 TCP/IP 指纹识别:在协商任何加密之前,从原始数据包头部被动地检测操作系统。
核心要点
- 写下指纹的是操作系统内核,而非浏览器。 SYN 数据包中的初始 TTL、TCP 窗口大小、MSS 和 TCP 选项排序由操作系统的网络栈设置——因此该指纹标识的是操作系统,而非发起请求的应用程序。
- 它发生在 TLS 之前。 TCP/IP 指纹识别读取三次握手的 SYN 数据包,位于 JA3/JA4 所识别的 TLS 握手下方一层。两者层层叠加。
- 它是被动的。 服务器只需接受连接就能对你的操作系统进行指纹识别——无需探测、无需 JavaScript,也不会向你回送任何东西。p0f 等工具开创了这一方法;JA4T 则是现代化、结构化的版本。
- 它能揭示「操作系统与声称不符」的矛盾。 一个默认值表明是「Linux」的 SYN 数据包,却带着声称「Windows Chrome」的
User-Agent到达,这是机器人检测中经典的伪装信号。 - NAT、代理和 VPN 会模糊它。 由于这些信号存在于网关和隧道会改写的头部中,指纹反映的是写下该数据包的最近一个网络栈——这恰恰是它有助于检测 VPN 和代理的原因。
TCP/IP 指纹识别是什么?
每一个 TCP 连接都以三次握手开启:客户端发送一个 SYN,服务器以 SYN-ACK 回复,客户端再以 ACK 确认。第一个 SYN 数据包由操作系统的 TCP/IP 栈组装,而它的若干字段并未由协议标准规定——标准定义了每个字段的含义,却把默认取值留给具体实现。不同的内核选择不同的默认值,而且这些值在多个连接之间很少改变。
由于这些默认值被固化在操作系统网络栈中,指纹描述的是操作系统及其版本,而非浏览器或用户。同一台 Windows 机器上的两个不同浏览器会产生相同的 TCP/IP 指纹;而同一个浏览器分别运行在 Windows 和 Linux 上则会产生两个不同的指纹。这与浏览器指纹识别正好相反——后者标识的是应用程序,对底层内核视而不见。
SYN 数据包中的信号
四个头部值承担了大部分工作,分别来自 IP 层(RFC 791)和 TCP 层(RFC 9293)。
1. 初始 TTL(生存时间,Time To Live)
IP 头部的 TTL 字段每经过一个路由器跳点就减一,因此观察者永远不会直接看到原始值——但操作系统从一小组众所周知的默认值起步:64(Linux、macOS、现代 BSD)、128(Windows)和 255(部分网络设备和较旧的 Unix)。如果一个数据包到达时 TTL 为 57,它几乎可以肯定是从 64 起步、跨越了 7 个跳点。把观察到的 TTL 向上取整到最接近的常见默认值,就能还原出源操作系统家族。
2. TCP 窗口大小
TCP 头部通告一个初始接收窗口——发送方在要求确认之前愿意接收多少字节。具体的起始值因网络栈而异:历史上 Windows、Linux 和 macOS 各自出厂时就带有可辨识的不同初始窗口大小,而该值是 SYN 中最强的区分依据之一。结合**窗口缩放(window scale)**选项(RFC 7323),它能把范围缩小到操作系统,有时甚至能锁定版本。
3. 最大分段大小(MSS)
MSS 选项(RFC 6691)声明客户端愿意在单个分段中接收的最大 TCP 载荷。它的值由链路的 MTU 推导而来——标准以太网上通常为 1460 字节(1500 MTU 减去 40 字节的头部),但在 PPPoE、隧道或 VPN 上会更低。一个异常的 MSS 本身就富含信息:1400 或更低的值往往暴露出流量正穿过某条隧道,这是 VPN 正在使用的一个线索。
4. TCP 选项及其顺序
TCP 选项字段可以携带 MSS、窗口缩放、SACK-permitted、时间戳和 NOP(空操作)填充——而关键在于,一个网络栈列出它们的顺序是实现特定的。Linux、Windows 和 macOS 各自发出一种特征性的选项布局(例如 MSS, SACK, Timestamp, NOP, Window-Scale 对比 MSS, NOP, Window-Scale, SACK, Timestamp)。正如 TLS ClientHello 中的扩展顺序一样,排序泄露的身份信息比任何单一取值都多。
| 信号 | 设置者 | 典型 Linux | 典型 Windows |
|---|---|---|---|
| 初始 TTL | IP 栈 | 64 | 128 |
| 窗口大小 | TCP 栈 | 因栈而异 | 因栈而异 |
| MSS | 由 MTU 推导 | 1460 | 1460 |
| 选项顺序 | TCP 栈 | 独特布局 | 独特布局 |
被动检测:p0f 与 JA4T
TCP/IP 指纹识别的决定性特征是它是被动的。与发送精心构造的探测包并测量响应的主动操作系统检测工具不同,被动指纹识别器只需观察客户端本就会发送的流量。接受一个普通连接就足以读取 SYN。
p0f 是让这一方法广为人知的工具——它是一个被动指纹识别器,将观察到的 SYN 数据包与一个已知操作系统签名的数据库相匹配,从而识别操作系统,有时甚至能识别运行时间或链路类型,而无需向目标发送任何一个数据包。它确立了大多数被动操作系统检测至今仍沿用的签名格式。
JA4T 是 FoxIO 推出的 JA4+ 套件中位于 TCP 层的成员,与现代 JA4 TLS 指纹同出一个项目。JA4T 将 TCP 窗口大小、按顺序排列的 TCP 选项、MSS 值和窗口缩放值组合成一个结构化、人类可读的单一字符串——把 JA4「可排序、可读的指纹」理念下沉到了传输层。由于它位于 TLS 之下,JA4T 甚至能在 ClientHello 被解析之前就对一个连接发出标记。
它如何与 TLS 指纹识别叠加
TCP/IP 指纹识别和 TLS 指纹识别读取的是同一次握手序列中相邻的两层,它们标识的是不同的对象:
| 层 | 技术 | 标识对象 |
|---|---|---|
| IP + TCP | p0f / JA4T | 操作系统内核 |
| TLS | JA3 / JA4 | TLS 库(进而是应用程序) |
正是这种差异让这一对指纹威力强大。TCP/IP 指纹说出哪个操作系统发送了数据包;TLS 指纹说出哪个客户端软件开启了会话。当两者不一致时——一个 Windows 风格的 SYN 携带着与 macOS Safari 相匹配的 TLS 握手——这个连接在内部就是自相矛盾的,而可观察层之间的不一致正是机器人检测技术的核心原则。一个运行在 Linux 服务器上、伪造 Chrome-on-Windows User-Agent 的抓取程序,仍然会发出 Linux 的 SYN 数据包,而它设置的任何头部都无法改变这一点。
它能揭示什么——以及它的局限
一个 TCP/IP 指纹可以揭示操作系统家族和版本,从 MSS 暗示链路类型(以太网、PPPoE、隧道),并暴露声称平台与实际平台之间的不符。但与更高层的指纹相比,它是一件钝器,而且有几样东西会模糊它:
- NAT 和负载均衡器可能改写或规范化 TCP 选项,因此指纹反映的可能是网关而非终端主机。
- VPN 和代理会终结或重新封装连接;服务器随后指纹识别的是 VPN 端点的操作系统,而非你的。这是把双刃剑——它隐藏了你的真实操作系统,但一个看似住宅的 IP 搭配数据中心的操作系统指纹,本身就是一个 VPN/代理信号。
- 分辨率低。 TCP/IP 指纹识别能很好地区分操作系统家族,却无法识别具体的浏览器、设备型号或用户。它是一道粗糙的初筛,而非唯一标识符。
正因如此,它几乎总是被用作众多输入中的一个——与 TLS 和 HTTP/2 指纹、IP 信誉以及行为信号相结合——而非作为独立的判定结论。
常见问题
我能更改自己的 TCP/IP 指纹吗?
只能通过更改产生它的操作系统网络栈来实现。一些隐私和反审查工具提供「操作系统指纹伪装」,会改写 TTL、窗口大小和选项顺序以模仿另一个系统,但这是在内核/防火墙层面运作——浏览器设置、扩展和 User-Agent 覆盖都触及不到它,因为这些值是在任何应用代码运行之前就写好的。
VPN 会隐藏我的 TCP/IP 指纹吗?
部分会。VPN 为你的流量建立隧道,因此目标服务器看到的是 VPN 客户端的网络栈或其出口节点所产生的 SYN 数据包,而非你原始的那个。你真实的操作系统指纹被隐藏了,但隧道往往会留下自己的痕迹——因封装开销导致的 MSS 减小,或者一个与住宅 IP 不匹配的操作系统指纹——检测系统会用这些痕迹推断 VPN 的存在。
TCP/IP 指纹识别和 Nmap 的操作系统检测是一回事吗?
不是。Nmap 执行的是主动操作系统检测——它发送一系列精心构造的探测数据包并分析响应。本文讨论的 TCP/IP 指纹识别是被动的:它只观察客户端在正常连接期间发送的数据包,这使其无声无息,从客户端一侧无法察觉。
为什么仅凭 TTL 无法识别操作系统?
因为 TTL 会在每个路由器跳点处递减,你观察到的值是起始默认值减去跳点数,而经过足够多的跳点后,不同的默认值可能会重叠。TTL 能缩小操作系统家族的范围(64 对 128 对 255),但只有与窗口大小、MSS 和选项排序相结合时才可靠——是完整的签名,而非任何单一字段。


