基于Scapy与pyshark的pcap包重放服务器连接问题求助
基于Scapy和pyshark的PCAP数据包重放解决方案
问题背景
需要完成以下任务:
- 使用Wireshark捕获发往服务器的数据包
- 编写客户端程序,利用Wireshark生成的pcap文件向服务器重放数据包
- 将请求/响应转储至另一文件
已掌握PCAP捕获方法,但在与服务器建立连接时遇到问题,原代码如下:
import pyshark from scapy.all import * import time def read_pcap(file): cap = pyshark.FileCapture(file) packets = [] for packet in cap: packets.append(packet) return packets def replay_packets(packets, server_ip, server_port): responses = [] for packet in packets: try: # Convert PyShark packet to Scapy packet scapy_pkt = IP(raw(packet.get_raw_packet())) # Change destination IP and port to the server scapy_pkt[IP].dst = server_ip scapy_pkt[UDP].dport = server_port # Send packet and capture response response = sr1(scapy_pkt, timeout=2) responses.append(response) # Wait a bit between packets to mimic real traffic time.sleep(0.1) except Exception as e: print(f"Error processing packet: {e}") return responses def dump_to_file(requests, responses, file): with open(file, 'w') as f: for req, resp in zip(requests, responses): f.write(f"Request: {req}\nResponse: {resp}\n\n") def main(): input_pcap_file = 'current.pcap' output_file = 'output.txt' server_ip = '123.56.8.0' server_port = 80 packets = read_pcap(input_pcap_file) requests = [packet.get_raw_packet() for packet in packets] responses = replay_packets(packets, server_ip, server_port) dump_to_file(requests, responses, output_file) print("Finished processing packets.") if __name__ == "__main__": main()
原代码问题分析
- 协议硬编码:直接修改
UDP端口,但目标端口80通常对应TCP协议的HTTP服务,协议不匹配会导致连接失败 - PyShark与Scapy混用冗余:两个库都能读取PCAP,混用增加转换复杂度,容易出现格式错误
- TCP连接状态缺失:TCP是面向连接的协议,直接重放原始数据包会忽略三次握手流程,服务器会拒绝无状态的TCP包
- 转储格式可读性差:直接写入原始字节数据,无法直观查看请求/响应内容
修正后的完整代码
from scapy.all import rdpcap, IP, TCP, UDP, sr1, send, get_if_addr, conf import time def read_pcap(file_path): # 直接用Scapy读取PCAP,避免跨库转换的冗余问题 return rdpcap(file_path) def replay_packets(packets, server_ip, server_port): responses = [] tcp_conn_states = {} # 维护TCP连接的序列号/确认号状态,key为源端口 for pkt in packets: try: if IP not in pkt: print("跳过非IP数据包") responses.append(None) continue # 复制原始数据包,避免修改原数据 modified_pkt = pkt.copy() # 替换目标IP为服务器IP,源IP使用当前网卡的IP modified_pkt[IP].dst = server_ip modified_pkt[IP].src = get_if_addr(conf.iface) # 删除IP校验和,让Scapy自动重新计算 del modified_pkt[IP].chksum # 处理TCP协议 if TCP in modified_pkt: src_port = modified_pkt[TCP].sport modified_pkt[TCP].dport = server_port # 初始化连接状态 if src_port not in tcp_conn_states: tcp_conn_states[src_port] = {"local_seq": 0, "remote_seq": 0} # 先完成三次握手建立连接 syn_pkt = IP(dst=server_ip)/TCP(sport=src_port, dport=server_port, flags="S", seq=0) syn_ack_resp = sr1(syn_pkt, timeout=3, verbose=0) if syn_ack_resp: tcp_conn_states[src_port]["remote_seq"] = syn_ack_resp[TCP].seq + 1 tcp_conn_states[src_port]["local_seq"] = syn_pkt[TCP].seq + 1 # 发送ACK完成握手 ack_pkt = IP(dst=server_ip)/TCP(sport=src_port, dport=server_port, flags="A", seq=tcp_conn_states[src_port]["local_seq"], ack=tcp_conn_states[src_port]["remote_seq"]) send(ack_pkt, verbose=0) # 更新当前数据包的序列号和确认号,适配已建立的连接 modified_pkt[TCP].seq = tcp_conn_states[src_port]["local_seq"] modified_pkt[TCP].ack = tcp_conn_states[src_port]["remote_seq"] del modified_pkt[TCP].chksum # 自动重新计算TCP校验和 # 发送数据包并捕获响应 resp = sr1(modified_pkt, timeout=3, verbose=0) if resp and TCP in resp: # 更新连接状态的序列号 tcp_conn_states[src_port]["remote_seq"] = resp[TCP].seq + len(resp[TCP].payload) tcp_conn_states[src_port]["local_seq"] = modified_pkt[TCP].seq + len(modified_pkt[TCP].payload) responses.append(resp) # 处理UDP协议 elif UDP in modified_pkt: modified_pkt[UDP].dport = server_port del modified_pkt[UDP].chksum resp = sr1(modified_pkt, timeout=3, verbose=0) responses.append(resp) time.sleep(0.1) except Exception as e: print(f"处理数据包出错: {str(e)}") responses.append(None) return responses def dump_to_file(packets, responses, output_path): with open(output_path, 'w', encoding='utf-8') as f: for idx, (req_pkt, resp_pkt) in enumerate(zip(packets, responses), 1): f.write(f"=== 数据包 #{idx} ===\n") f.write("请求内容:\n") f.write(req_pkt.show(dump=True)) f.write("\n响应内容:\n") if resp_pkt: f.write(resp_pkt.show(dump=True)) else: f.write("无响应或响应超时\n") f.write("\n" + "-"*50 + "\n\n") def main(): input_pcap = 'current.pcap' output_file = 'request_response_dump.txt' server_ip = '123.56.8.0' server_port = 80 packets = read_pcap(input_pcap) responses = replay_packets(packets, server_ip, server_port) dump_to_file(packets, responses, output_file) print("数据包重放及转储完成") if __name__ == "__main__": main()
关键改进点
- 协议自适应处理:自动识别TCP/UDP协议,针对TCP单独处理连接建立流程
- TCP连接状态维护:手动模拟三次握手,维护序列号和确认号,确保服务器接受数据包
- 简化PCAP读取:直接使用Scapy的
rdpcap读取PCAP,避免跨库转换的错误 - 自动校验和计算:删除原始校验和字段,让Scapy自动重新计算,避免校验失败
- 可读格式转储:使用Scapy的
show(dump=True)生成结构化的数据包内容,便于后续分析
内容的提问来源于stack exchange,提问作者Ankit Sinha
相关产品推荐
相关产品推荐

