做抓包分析和漏洞排查这些年,我越来越觉得,TCP三次握手、四次挥手以及UDP的无连接特性,不是面试题,而是读包的基本功。很多人拿着Wireshark面对一堆报文,不知道哪些是正常的、哪些是异常的,就是因为脑子里没有一张“协议该长什么样”的底图。这篇文章就围绕TCP三次握手、四次挥手、UDP的区别,结合我自己在漏洞抓包分析里遇到的实际场景,把底层逻辑串一遍,适合刚开始接触协议分析、或者被面试题背过但没真正理解状态流转的读者。
1. 为什么抓包分析必须盯住“握手”和“挥手”这两个阶段
1.1 把连接的一生看成状态机,而不是一堆报文
刚学网络时我也喜欢背“SYN、SYN-ACK、ACK”,但真正做起抓包分析才发现,三次握手的意义不在“三”,而在“状态确认”。客户端发SYN,是告诉服务端“我想建立连接,我带的初始序号是x”;服务端回SYN-ACK,是告诉客户端“我收到了,我的初始序号是y,同时我希望你确认”;客户端再回ACK,是告诉服务端“我收到你的序号了,我们进入数据传输阶段”。
这三步缺一不可,因为TCP是全双工的,双方都要确认“你发出的包我能不能收到”。放到抓包分析里,这意味着什么呢?意味着你看到一对IP和端口之间有通信,第一件事不是去看载荷内容,而是先看这条连接是怎么建立的。
我自己的经验是:分析一个pcap文件时,如果第一条报文不是SYN,那说明这个抓包起点在连接建立之后,前面的握手过程丢了;如果看到SYN之后直接是RST而不是SYN-ACK,那说明对端端口不可达或服务没起来;如果看到三个以上的SYN重传且始终没有SYN-ACK,那基本可以断定目标主机在线但端口被防火墙过滤,或者根本没开这个服务。这些判断全部建立在“正常三次握手应该是什么样”这个底图之上。
1.2 握手与挥手的“间隙”,恰恰是异常藏身之处
漏洞抓包分析和普通性能分析最大的区别在于:普通分析关注“快不快”,漏洞分析关注“像不像”。一个恶意程序完全可以让自己的网络行为看起来像正常访问,但它很难伪装底层的状态机行为。
举个我实际遇到的例子:某次排查内网一台主机持续外连,流量不大,载荷也看不出明显特征。我把过滤器只保留TCP SYN包,按目的IP一统计,发现它每秒对几十个不同的高位端口发SYN,但从不完成第三次握手。懂三次握手的人一眼就能判断,这绝不是正常的应用访问,而是典型的端口探测行为。如果不懂握手状态,你可能会盯着载荷内容分析半天,方向完全跑偏。
同样,四次挥手也藏着大量信息。一台正常的服务器关闭连接时,走的是FIN、ACK、FIN、ACK的完整流程;如果一台主机大量主动发RST,而不走正常挥手流程,那就很可能是程序异常崩溃、连接被中间设备重置,或者有人在刻断链路。状态机行为就是协议层的“指纹”,这是抓包分析里最值得花时间打牢的基础。
2. TCP三次握手:连接建立的封包细节与seq/ack推理
2.1 从Wireshark视角看三次握手的完整序列
先给一个最典型的握手序列,我用自己抓包时最常见的相对序号来说明(Wireshark默认显示相对序号,方便人读):
- 第1包:客户端 → 服务端,
SYN,seq=0 - 第2包:服务端 → 客户端,
SYN-ACK,seq=0,ack=1 - 第3包:客户端 → 服务端,
ACK,seq=1,ack=1
注意看ack的推算逻辑:第2包里ack=1是“我期望你下一个包序号从1开始”,也就是对第1包seq=0的确认;第3包里ack=1是对第2包seq=0的确认。TCP的确认号表示的是“期望收到的下一个序号”,而不是“已经收到的最后一个序号”,这个细节很容易记反,但它对理解重传和丢包非常关键。
实际抓包时,有些场景会因为中间设备而让序号出现“偏移”。比如中间有个NAT或者负载均衡设备重写了TCP头,客户端发出的seq可能是1000,服务端看到的却是2000。这时候如果只看单包,会觉得序号对不上;但只要抓住“三包两确认”的状态逻辑,就不会被表象带偏。排查问题时,我习惯直接在Wireshark里输入过滤表达式:tcp.flags.syn==1 and tcp.flags.ack==0只看纯SYN,tcp.flags.syn==1 and tcp.flags.ack==1只看SYN-ACK,然后按源地址统计,一眼就能看出握手失败的范围。
2.2 seq与ack为什么必须用数据包“推”出来,而不是看数字本身
初学者常犯一个毛病:把seq和ack当成单纯的“包序号”,用加减法硬套。实际上,在Wireshark里看到连续的数据包seq不像0、1、2那样间隔均匀,原因有几种:
- 数据负载:如果数据包携带了长度为N的载荷,下一个同方向包的seq会加上N。例如客户端发了一个seq=1、载荷Len=1400的包,那下一个seq就应该是1401,而不是2。
- TCP分段与重组:大文件传输时,TCP会把数据切成MSS大小的段,每个分段的seq都不同。如果抓包点位于发送方或接收方之外,还可能因为中间设备的TSO/GRO卸载产生巨型包,导致观感上的“乱序”。
- 重传:如果某个包没收到ACK,发送方会重传相同的seq。抓到两个相同seq的包,不代表它们重复,极有可能是网络丢包后触发的重传。
我在做漏洞分析时,最常用的是“沿同方向追踪seq差值”的方法。比如某个数据流疑似被篡改:正常情况,一个方向的序号是单调递增且等于上一次seq+载荷长度;如果你发现某个包的seq突然比预期的少或者多了一大截,那就是有人在这个连接里注入了数据包,或者中间设备改了载荷。
2.3 握手阶段的异常行为,每个都对应一种安全信号
三次握手阶段常见的异常我已经在1.2里提了一部分,这里补充几个实战中特别有代表性的:
- SYN重传风暴:客户端不断重传SYN,服务端始终没有回应。除了端口真的没开,还有一种可能是服务端半连接队列已经满了。半连接队列是内核里保存“握手未完成连接”的地方,队列满了内核会丢弃新到的SYN。持续满意味着服务器可能正被连接洪泛。
- SYN-ACK重传:服务端回了SYN-ACK,但客户端一直不补最后的ACK。这种情况通常是客户端IP是伪造的,SYN-ACK根本回不到真实来源,接收方自然无法完成第三次握手。
- 拔线式连接:三次握手刚完成,紧接着就发一个RST,建立连接的表面过程完全正常,但毫秒级就拆线。这种短命连接在漏洞利用里出现过——为了验证某个端口是否存活,或者为了快速建立隧道而抢时间,正常情况下几乎没有业务会这么干。
安全信号的判断核心不是某一个包,而是“时序形态”。单独一个SYN重传可能只是网络抖动,但如果配合目的端口跨度大、源IP分散、持续时间短这些维度,攻击行为的可信度就非常高了。
3. 四次挥手与RST重置:连接结束时那些容易被忽略的信号
3.1 为什么正常关闭要“四”次而不是“两”次
很多人对四次挥手的死记硬背是:A发FIN,B回ACK,B发FIN,A回ACK。但只记住四步,不理解为什么需要四步,在分析时会频繁踩坑。
TCP是全双工的,A发FIN只代表“A这边没有数据要发了”,不代表B没有。B可能手里还有一大堆数据要发给A,所以B先回ACK确认收到A的FIN,然后继续把剩余数据发完,最后再发自己的FIN。这两条独立的单向关闭过程合起来就是四次交互。如果恰好A在发送FIN之前已经收到了B的“数据send完成”信号,场景里可能把中间两步合并成三次交互,这在抓包里是正常现象,不要以为是四次挥手缺失。
抓包分析里遇到的最常见困惑是:关连接时只看到一两个FIN包,后续没有了。这往往是因为抓包点只覆盖了路径的一段。比如在客户端侧抓包,看到客户端发了FIN、收到了ACK,但B方向的FIN可能需要跨网段才到,没有被抓到。遇到这种“残包”不要慌,先把原始数据保存,换个抓包点再验证,而不是直接下结论。
3.2 FIN是协商下线,RST是异常失控
FIN和RST虽然都能拆连接,但性质完全不同。FIN是“打个招呼再走”,RST是“直接撂电话”。这个区别放在漏洞分析里,价值极高:
- 正常业务关闭连接,几乎不会用RST。如果你在一个稳定的内网环境里看到某台服务端的连接大量以RST结束,优先怀疑应用层崩溃、socket被异常关闭,或者有中间安全设备做了丢包重置。
- 端口扫描场景里,扫描方收到目标主机的RST,通常意味着“端口关着,别费劲了”;如果没收到RST也没收到SYN-ACK,则是被防火墙静默丢弃。这个结论对判断目标主机是否在线、防火墙策略是否生效非常关键。
- 某些恶意行为会主动发RST来“抢断”其他进程的TCP连接,比如一些局域网工具实现“防蹭网”会伪造网关发RST给本机所有外部连接。如果你在抓包里发现大量来源不是真实对端IP的RST包,就要警惕存在中间人干预。
还有一个细节:RST包本身是不需要确认的,收到方收到后会丢弃这条连接的后续所有包。所以RST包具有“一次性摧毁连接”的破坏力,在抓包里出现RST的时机越异常,问题的优先级越高。
3.3 TIME_WAIT与CLOSE_WAIT:连接结束后仍会说话的状态
连接从“四次挥手”状态到真正消失,TCP协议栈里还有两个尾巴状态:TIME_WAIT和CLOSE_WAIT。这两个状态在抓包层面很少直接显示,但会强烈影响连接层的现象。
- CLOSE_WAIT:被动关闭方(通常是服务端)收到了对方的FIN,自己也回了ACK,但应用层还没调用close(),导致内核既不能发FIN也不能释放连接。大量连接堆积在CLOSE_WAIT,说明服务端程序的连接管理有bug,多半是忘记读数据或者忘记关闭socket。
- TIME_WAIT:主动关闭方在发出最后一个ACK后进入的状态,持续2个MSL(最大报文段生存期)时长。很多人不理解为什么要等这么久,核心为了两个目的:一是确保最后一个ACK能到达对端,如果丢了还能重发;二是让这条连接里的旧迟到包在网络中彻底消失,避免它们影响下一个使用相同四元组的连接。
在做高并发抓包分析时,TIME_WAIT多不可怕,CLOSE_WAIT多是问题。TIME_WAIT多说明主动关闭方在频繁地“正常关闭”连接,完全可以依赖端口复用;CLOSE_WAIT多才是排查的重点,需要结合应用日志定位哪里没释放句柄。
4. TCP与UDP的核心差异:从状态机看待“可靠”与“无状态”
4.1 TCP的可靠机制,在抓包里都有具体的表现形态
TCP是可靠传输协议,这个“可靠”不是网络不会丢包,而是TCP自己有一套机制来保证“丢了能发现、错了能纠正、快了能限速”。抓包分析里,这些机制都会变成具体的报文形态:
- 确认与重传:发送方发出一个包后,会启动一个定时器;如果超过RTO(重传超时时间)没收到ACK,就重传。抓包里表现为相同seq的包出现了两次,且第二次出现前没有对应的ACK。
- 快速重传:如果一个包丢了,接收方后面收到的都是乱序包,它会立刻发三个连续的重复ACK,发送方收到三个重复ACK后不等定时器超时,马上重传缺失包。这在抓包里表现非常明显:三个相同的
ack=XXX后面紧跟着一个seq=XXX的包。 - 滑动窗口与拥塞控制:TCP的窗口字段会动态变化。抓包里如果发现某段时间的接收窗口突然缩小到接近0,说明接收方缓冲区已经满了,应用层消费速度跟不上,这时发送方会主动降低发送速率甚至停止发送。
做好这些识别之后,漏洞分析里很多“网络慢”的锅才能甩对地方。我遇到过好几次:应用层报告“接口慢”,结果抓包一看是接收窗口持续为零,问题根本不在网络链路,而在服务端应用没有及时读走数据。
4.2 UDP没有“连接”,但为了分析,我们还是会给它造出“会话”
UDP包头的字段比TCP少很多,只有源端口、目的端口、长度、校验和。它没有SYN和FIN,没有seq和ack,没有窗口和状态。UDP一发,就是纯粹的用户数据报文,发送后既不知道对方收没收到,也不会因为丢包而重传。
所以在协议栈层面,UDP是标准无连接协议。但到了抓包分析层面,我们必须“牵强附会”地把它组织成会话:Wireshark会按照“源IP+源端口+目的IP+目的端口”的组合把UDP报文归组显示。很多安全工具也沿用“udp连接”这种说法,其实只是一个方便分析的抽象,内核里并没有UDP连接状态。
搞懂这个区别,对漏洞分析非常重要。比如DNS、TFTP、DHCP这些服务都用UDP,你抓包时会发现客户端一个UDP请求发出去,服务端回一个UDP响应,整个过程只有两个包。少了握手环节,就意味着一切都得靠应用自己防止伪造和重放。因此在分析基于UDP的协议时,我习惯重点关注两个东西:一是源端口和目的端口是否匹配业务约定,二是载荷本身的格式是否完整。没有握手帮你过滤恶意流量,UDP的安全边界全在应用层判断。
4.3 TCP与UDP抓包行为对比表
下面这张表是我在排查问题时经常对照的,把两者的核心差异从抓包视角罗列出来:
| 对比维度 | TCP | UDP |
|---|---|---|
| 连接状态 | 有连接,存在完整的建立/维护/拆除状态机 | 无连接,没有握手,没有状态机 |
| 报文头部开销 | 头部至少20字节,包含序号、确认号、窗口等字段 | 头部仅8字节,只有端口、长度、校验和 |
| 可靠性 | 提供确认、重传、排序、流量控制 | 尽力而为,不保证送达和顺序 |
| 传输模式 | 面向字节流,报文之间无边界 | 面向报文,每个UDP包都带有明确边界 |
| 抓包中识别方式 | 关注SYN、SYN-ACK、ACK、FIN、RST、重传、窗口 | 主要关注源/目的端口、长度、载荷格式 |
| 典型应用 | HTTP、HTTPS、SSH、远程桌面、数据库连接 | DNS、DHCP、TFTP、RTP音视频、部分游戏同步 |
| 安全弱项 | 握手被滥用,如半连接耗尽、序列号推测等 | 无握手导致伪造容易,需要应用层自建安全机制 |
这张表每一条背后都有对应的抓包现象。比如一个流里如果出现大量重传,你首先要检查这是不是TCP——如果对方用的UDP,那就根本没有重传机制,丢包会直接表现为应用层的间歇性卡顿,这时候去责怪“UDP重传不满意”就属于问错人了。
4.4 UDP与应用层可靠性的折中:从QUIC看“伪连接”思想
UDP的无连接特性带来一个直接的后果:应用想用UDP,就得自己解决可靠性和连接管理问题。以前我们习惯说“游戏用UDP因为快、可以容忍偶尔丢包”,但现在是2020年代,一个更典型的例子是QUIC协议。
QUIC在UDP之上实现了类似TCP的连接建立、加密协商、可靠传输和拥塞控制,底层却踩着UDP的报文格式。这意味着它既有UDP的灵活性,又能在应用层完成连接建立、序号管理和重传。对抓包分析来说,QUIC这类协议会给你制造一个错觉:看起来是UDP端口,但里面封装了一个“类TCP”的会话状态。
我特意提这个,是因为很多新人对“UDP无连接”死记硬背,看到QUIC的UDP报文里有连接ID、有包序号,就开始怀疑“UDP不是没有序号吗?”——其实这些字段不在UDP头里,而是QUIC自己定义的载荷格式。UDP头永远简单,复杂的是应用层给它套的“马甲”。看清这一点,你就不会在抓包分析时把传输层协议和应用层协议混为一谈。
5. 漏洞抓包分析中的实战判读:握手异常、UDP行为与检测思路
5.1 SYN扫描与半开连接特征:先看握手,再看载荷
在授权测试和应急排查场景里,识别端口扫描是基本功。SYN扫描的核心特征就是只完成“半次握手”:扫描方发出SYN,收到SYN-ACK说明端口开放,随后直接回RST拆掉连接,不进行第三次ACK;如果收到RST说明端口关闭;如果一直没反应说明被防火墙静默。
用Wireshark验证时,我一般这样操作:先用过滤表达式把纯SYN包提取出来,tcp.flags.syn==1 and tcp.flags.ack==0,观察源IP是不是同一个,目的端口是否连续或分散,SYN包之间的时间间隔是否均匀。如果目的端口从1到1024全部轮询了一遍,且没有后续数据交互,那几乎可以断定是扫描行为。
注意,我这里描述的是识别方法,不是教你拿去扫别人的机器。协议分析能力是用来做防御和检测的,任何未授权的探测行为都可能违反法律法规,也只应该在自建靶场或明确授权的测试环境里进行。
5.2 UDP端口探测:靠“无响应”和ICMP响应反推端口状态
UDP因为没有握手,检测端口是否开放的方法和TCP完全不同。向一个UDP端口发送数据包时:
- 如果端口关闭,目标主机会返回一个ICMP端口不可达(Port Unreachable)消息;
- 如果端口开放,通常没有响应;
- 如果被防火墙丢弃,没有响应也没有ICMP。
所以在抓包里,UDP端口探测的判断逻辑是“反证法”:大量UDP报文飞向某个IP的不同端口,同时伴随ICMP port unreachable回包,说明对方活跃且端口没开;如果UDP请求发出去完全静默,则需要结合其他信息判断是防火墙丢弃还是端口开放。识别这种模式时要特别小心:ICMP响应也有可能成为被利用的对象,攻击者可以借生成ICMP流量造成中间设备压力。分析时必须同时关注ICMP的速率和来源。
5.3 识别SYN Flood:CONNECTION-SYN风暴的典型封包形态
SYN Flood是典型的握手滥用攻击。正常情况下,三次握手应该在极短时间内完成,也就是“SYN → SYN-ACK → ACK”三连一发就完事。攻击场景下,攻击者向着目标端口发送大量SYN,但从不回应SYN-ACK,目标主机的半连接队列最终被填满。
在Wireshark里,识别SYN Flood的方法非常直观:过滤tcp.flags.syn==1 and tcp.flags.ack==0,如果同一时间窗口内SYN包数量异常大,且几乎没有任何关联的ACK包完成三次握手,那就高度可疑。再利用统计功能按目的端口分组,看看SYN是否集中在少数几个端口——通常攻击载荷会集中在服务端口,比如80或443。
防御思路上,常见的是启用SYN Cookie、增大半连接队列、启用防火墙同步代理。这些内容很多,这里不展开,但理解攻击为什么有效,才是调整防护参数的前提。
5.4 协议状态机攻击与模糊测试的安全边界
除了扫描、洪泛这些烂大街的攻击行为,协议状态机本身也是漏洞研究的方向。比如TCP的序列号预测、窗口缩放因子的协商被滥用、同时打开场景的异常处理等,都属于“协议实现bug”。这类研究我强烈建议在本地虚拟机环境或者专门的靶场平台里进行,不要对公网主机做任何验证动作。
我之前自己搭过一套实验环境,用三台虚拟机模拟客户端、服务端和“观察者”,跑通了三次握手、四次挥手、RST注入、UDP丢包等场景,抓包效果非常直观。这个经验分享给卡在理论上的读者:与其反复背状态转换图,不如自己开两个端口,用三方抓包看一眼真实报文。看到SYN-ACK里的ack=seq+1那一下,比背十遍都记得牢。
最后再补充一个自己用得很顺手的排查技巧:分析大量会话时,不要直接在主窗口里一条条翻,先用Wireshark的统计功能按IP、端口、协议大类做聚合,把“对话异常度”最高的几组拎出来,再针对性地打过滤条件看明细。先看森林,再看树木,才能避免在十万个包里迷失方向。