如何创建大于64KB的TCP数据包并保存为pcap文件?
如何创建大于64KB的TCP数据包并保存为pcap文件?
我明白你遇到的问题了——Scapy默认会严格遵循TCP/IP协议规范:IP层的总长度字段是16位(最大值65535,对应约64KB,包含IP头和负载),TCP伪头的长度校验也依赖这个16位字段,所以当你尝试构造超大数据包时,Scapy的自动校验逻辑就会抛出'H' format requires 0 <= number <= 65535的错误。
要构造这种不符合协议规范但用于测试的超大TCP数据包,我们可以通过两种方式实现:
方法1:绕过Scapy的自动协议校验
我们可以关闭Scapy的自动校验和计算、重写协议层的处理逻辑,强制让它生成并保存不符合规范的数据包:
from scapy.all import * import socket # 1. 关闭Scapy的自动校验与地址检查 conf.checkIPaddr = False conf.checkTCPsum = False conf.checkUDPsum = False # 2. 重写IP层的post_build方法,跳过自动长度计算 original_ip_post_build = IP.post_build def custom_ip_post_build(pkt, pay): # 直接返回原始IP头+负载,不自动计算总长度 return pkt + pay IP.post_build = custom_ip_post_build # 3. 重写TCP层的post_build方法,跳过自动校验和计算 original_tcp_post_build = TCP.post_build def custom_tcp_post_build(pkt, pay): # 直接返回原始TCP头+负载,不自动计算校验和 return pkt + pay TCP.post_build = custom_tcp_post_build # 4. 构造超大数据包 big_payload = Raw(RandString(size=120000)) big_packet = IP(src="127.0.0.1", dst="192.168.2.71", chksum=0) / \ TCP(sport=123, dport=456, chksum=0) / \ big_payload # 5. 写入pcap文件 wrpcap("/tmp/big_tcp_scapy.pcap", [big_packet]) # 6. 恢复Scapy的原始处理逻辑(避免影响后续操作) IP.post_build = original_ip_post_build TCP.post_build = original_tcp_post_build
代码解释:
- 关闭Scapy的各类校验开关,避免它因为"非法"的长度或校验和报错
- 重写
post_build方法:这是Scapy在生成最终数据包字节流前的处理钩子,默认会自动计算IP长度、TCP校验和等字段,我们直接跳过这些步骤,保留原始的头部与负载拼接结果 - 手动设置
chksum=0,明确告诉Scapy不要自动计算校验和
方法2:手动构造pcap文件(无工具限制)
如果Scapy的限制还是太严格,我们可以完全绕过工具,手动构造pcap文件的所有字节内容,这种方法能生成任何格式的数据包:
import struct import random import time # 1. 构造原始的IP+TCP+超大负载字节流 # 简化版IP头(不计算校验和) ip_header = struct.pack('!BBHHHBBH4s4s', 0x45, # 版本(4) + IHL(5,即20字节IP头) 0, # TOS字段 0, # 总长度(我们不设置,直接用实际总长度) 0, # 标识ID 0, # 标志+片偏移 64, # TTL 6, # 协议类型:TCP 0, # IP校验和(设为0) socket.inet_aton('127.0.0.1'), socket.inet_aton('192.168.2.71') ) # 简化版TCP头(不计算校验和) tcp_header = struct.pack('!HHLLBBHHH', 123, # 源端口 456, # 目的端口 0, # 序列号 0, # 确认号 5 << 4, # 数据偏移(5,即20字节TCP头) + 保留位 0, # 标志位 65535, # 窗口大小 0, # TCP校验和(设为0) 0 # 紧急指针 ) # 120KB的随机负载 big_payload = bytes(random.getrandbits(8) for _ in range(120000)) raw_packet = ip_header + tcp_header + big_payload # 2. 构造pcap文件的全局头部 pcap_global_header = struct.pack('!IHHIIII', 0xa1b2c3d4, # 魔术数字,标识大端格式的pcap文件 2, 4, # pcap版本2.4 0, # 时区偏移(0表示UTC) 0, # 时间戳精度 262144, # SnapLen:设置为256KB,确保能容纳超大数据包 2 # 数据链路类型:以太网(大多数pcap文件的默认类型) ) # 3. 构造单个数据包的头部 ts_sec = int(time.time()) ts_usec = 0 packet_len = len(raw_packet) pcap_packet_header = struct.pack('!IIII', ts_sec, # 时间戳(秒) ts_usec, # 时间戳(微秒) packet_len, # 文件中保存的数据包长度 packet_len # 原始数据包的实际长度 ) # 4. 写入pcap文件 with open('/tmp/big_tcp_raw.pcap', 'wb') as f: f.write(pcap_global_header) f.write(pcap_packet_header) f.write(raw_packet)
代码解释:
- 直接构造IP、TCP头的字节流,完全跳过任何协议校验
- 手动生成pcap文件的全局头和数据包头部:pcap文件有固定的格式,只要遵循这个格式就能被Wireshark、tcpreplay等工具识别
- SnapLen设置为足够大的值(比如256KB),确保能容纳我们的超大数据包
注意事项
- 这种超大数据包完全不符合TCP/IP协议规范,正常网络中根本不会存在,只能用于测试网络设备对异常数据包的处理能力
- 用Wireshark等工具打开生成的pcap文件时,会弹出协议错误的警告,这是正常现象,因为数据包本身就是非法的
- 如果需要用tcpreplay等工具重放,确保工具的配置允许处理超大数据包(比如调整缓冲区大小)




