Scapy捕获TCP报文Raw负载解码及Java旧服务压缩XML解析问题
TCP返回负载解码可行方案
先纠正错误操作
- Scapy中
Raw层的load属性本身就是原生bytes类型,不需要额外调用bytes()方法转码,该操作会破坏原始字节结构,导致后续解压缩/解码失败。 - 不要取
Padding层的内容,Padding是TCP报文末尾的填充数据,不是实际业务负载,你需要取的是Raw层的load字段。
具体处理步骤
第一步:验证负载压缩格式并解压缩
Java旧应用常用的压缩格式为gzip、deflate,部分框架会输出不带zlib头的raw deflate格式,可直接用原始bytes做检测和解压缩:
import gzip import zlib # 直接取Raw层的原生bytes,不要做任何提前转码操作 raw_data = parsed_packet['Raw'].load decompressed_data = None # 检测gzip格式(魔数为0x1f8b,前两个字节为b'\x1f\x8b') if raw_data[:2] == b'\x1f\x8b': try: decompressed_data = gzip.decompress(raw_data) except: pass # 检测deflate格式 if not decompressed_data: try: # 先试带zlib头的标准deflate decompressed_data = zlib.decompress(raw_data) except zlib.error: # 再试不带zlib头的raw deflate try: decompressed_data = zlib.decompress(raw_data, wbits=-15) except zlib.error: pass
第二步:排查TCP分段问题
单包取的Raw.load可能只是完整响应的一个分段,需要先做TCP流重组,将同一条五元组(源IP、源端口、目的IP、目的端口、协议)的TCP payload按seq序号拼接完整后再处理,Scapy可直接用TCPSession实现流重组:
from scapy.all import sniff, TCPSession, Raw def handle_packet(packet): if packet.haslayer(Raw): # 此处取到的是拼接后的完整流负载 raw_data = packet[Raw].load # 后续执行解压缩、解码逻辑 # 替换为你实际的服务端口 sniff(prn=handle_packet, session=TCPSession, filter="tcp port 8080", store=0)
第三步:多编码尝试解码
解压缩完成后再执行解码操作,旧Java应用默认编码不一定是UTF-8,国内研发的旧服务大概率使用GBK/GB2312/GB18030编码,也可能使用ISO-8859-1编码:
if decompressed_data: # 按优先级尝试常见编码,可加errors='ignore'先预览内容结构 for encoding in ['utf-8', 'gbk', 'gb18030', 'gb2312', 'iso-8859-1']: try: content = decompressed_data.decode(encoding) print(f"使用{encoding}解码成功:\n{content}") break except: continue
第四步:特殊场景处理
如果以上步骤都失败,检查原始bytes的前两个字节:
- 如果是
b'\xac\xed',说明是Java序列化对象流,需要用jpype/pyjnius调用Java的ObjectInputStream反序列化后再提取XML内容。
内容的提问来源于stack exchange,提问作者Shlomi Hassid
相关产品推荐
相关产品推荐

