使用TCPSession时Scapy的tcp_reassembly类方法该如何正确使用?
Scapy自定义协议TCP重组逻辑实现方案
核心问题说明
当前代码的核心问题是没有用到metadata存储不完整的分段数据,导致单次收到的数据小于bundle_len时数据直接丢失,无法和后续到达的数据包拼接;同时也没有处理单次收到的数据包含多个完整协议包的场景。
修正后代码
import struct from scapy.packet import Packet # 你的自定义协议类 class OWN_PROTOCOL(Packet): # 协议字段定义放在此处 @classmethod def tcp_reassemble(cls, data, metadata): """ 由sniff(session=TCPSession)自动调用,完成自定义协议的TCP流重组 Args: data: TCP层剥离后收到的原始二进制数据 metadata: 跨调用存储中间状态的字典,用来存未拼接完成的分段数据 Returns: 重组完成的单个/多个协议包,无完整包则返回None """ # 初始化当前协议的分段数据缓存,避免和其他协议冲突 partial_data = metadata.get("own_proto_partial", b"") # 拼接历史残留数据和新收到的数据 current_data = partial_data + data processed_pkts = [] # 至少要够读到bundle_len字段的长度再处理,避免struct.unpack报错 while len(current_data) >= 28: # 读取当前数据包的bundle_len bundle_len = struct.unpack("<I", current_data[24:28])[0] # 异常长度防护,可根据你的协议实际最大包长调整阈值 if bundle_len > 10000 or bundle_len < 28: bundle_len = 64 if len(current_data) >= bundle_len: # 切出一个完整的协议包 pkt = cls(current_data[:bundle_len]) processed_pkts.append(pkt) # 剩余数据留到下一轮判断 current_data = current_data[bundle_len:] else: # 剩余数据不够一个完整包,跳出循环存入缓存 break # 把未处理完的残留数据存回metadata,供下次调用拼接 metadata["own_proto_partial"] = current_data # 返回处理结果:多个包返回列表,单个包返回对象本身,无完整包返回None if len(processed_pkts) == 0: return None elif len(processed_pkts) == 1: return processed_pkts[0] else: return processed_pkts
逻辑说明
- 每次调用重组函数时优先拼接之前缓存的不完整数据和新收到的数据,避免分段数据丢失
- 增加最小长度判断,只有当前数据够读取
bundle_len字段时才执行解析,避免下标越界异常 - 循环拆包处理:单次收到的数据可能包含多个完整的协议包,循环判断可以把所有完整包都解析出来
- 最后把未处理完的不完整数据存回
metadata,等待后续数据到达后再拼接处理 - 不需要手动调用payload的
tcp_reassemble方法,Scapy的TCPSession会自动处理上层协议的重组逻辑
内容的提问来源于stack exchange,提问作者JRSmile
相关产品推荐
相关产品推荐

