You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scapy捕获TCP二进制数据时末尾字节异常的原因排查

Scapy捕获TCP二进制流量末尾字节异常的问题解决

我写了一个用Scapy嗅探源端口5555的TCP流量并保存为二进制文件的程序,但每次捕获时,最后几个数据段总会出现0-4个错误字节。已经排除了bytes()函数的问题,从Wireshark抓包和Scapy输出的原始数据来看,异常就出在Scapy获取的TCP payload里。

原程序代码:

import scapy.all as scapy
from scapy.layers.inet import IP, TCP, Ether, NoPayload

ba = bytearray()


def packet_handler(packet):
    if packet.haslayer(TCP):
        tcp_packet = packet[TCP]
        if tcp_packet.sport == 5555:
            # print(tcp_packet.flags)
            if tcp_packet.flags == 'PA':
                data = tcp_packet.payload
                ba.extend(bytes(data))


pkts = scapy.sniff(filter="tcp",  # and src port 5555
                   timeout=5,
                   prn=packet_handler,
                   iface="Software Loopback Interface 1")


with open('test.bin', 'wb') as file:
    file.write(bytes(ba))

问题原因分析

  1. TCP流未重组:TCP是面向流的协议,数据可能被拆分成多个分段传输,原程序直接逐个处理单个TCP包的payload,没有做流重组,容易出现分段顺序错乱或末尾分段不完整的情况。
  2. 抓包提前终止:用timeout=5固定时间停止抓包,可能在最后一个完整数据包还没传输完成就停止,导致捕获到不完整的payload。
  3. Payload处理不严谨:直接将tcp_packet.payload转bytes,当payload是NoPayload或存在无效封装时,可能引入错误字节。

解决方案

改用Scapy的TCP流重组功能,并优化抓包停止条件,确保拿到完整的数据流:

修改后的代码:

import scapy.all as scapy
from scapy.layers.inet import IP, TCP
from scapy.contrib.tcp_reassembly import TCP_Reassembler

# 存储重组后的完整TCP流数据
stream_data = {}

def tcp_reassembly_handler(session):
    # 只处理源端口为5555的TCP流
    if session.src[1] == 5555:
        # 用源/目的IP+端口作为唯一标识,区分不同流
        stream_key = (session.src[0], session.src[1], session.dst[0], session.dst[1])
        stream_data[stream_key] = b""
        # 遍历重组后的所有分段,拼接有效数据
        for seg in session.segments:
            if seg.payload:
                stream_data[stream_key] += bytes(seg.payload)

# 初始化TCP流重组器
reassembler = TCP_Reassembler()

def packet_handler(packet):
    if packet.haslayer(TCP):
        # 将捕获的包交给重组器处理
        reassembler.process(packet)

# 开始抓包:过滤源端口5555的TCP流量,捕获到FIN标志时停止(表示传输结束)
pkts = scapy.sniff(
    filter="tcp and src port 5555",
    prn=packet_handler,
    iface="Software Loopback Interface 1",
    stop_filter=lambda p: p.haslayer(TCP) and p[TCP].flags == 'F'
)

# 将所有捕获的完整流数据写入文件
with open('test.bin', 'wb') as file:
    for data in stream_data.values():
        file.write(data)

关键修改点说明

  • TCP流重组:用TCP_Reassembler自动重组同一TCP流的所有分段,确保数据完整且顺序正确。
  • 动态停止抓包:通过stop_filter监听TCP的FIN标志,只有当发送方发送结束包时才停止抓包,避免提前截断。
  • 严谨的Payload处理:只拼接有有效payload的分段,跳过空数据的情况。

额外排查建议

  • 确认抓包接口iface设置正确,避免捕获到其他接口的无关流量。
  • 对比Wireshark中重组后的TCP流数据和程序输出的文件,验证是否解决了字节错误问题。

内容的提问来源于stack exchange,提问作者Владимир Епифанцев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 05:23:12