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

基于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()

原代码问题分析

  1. 协议硬编码:直接修改UDP端口,但目标端口80通常对应TCP协议的HTTP服务,协议不匹配会导致连接失败
  2. PyShark与Scapy混用冗余:两个库都能读取PCAP,混用增加转换复杂度,容易出现格式错误
  3. TCP连接状态缺失:TCP是面向连接的协议,直接重放原始数据包会忽略三次握手流程,服务器会拒绝无状态的TCP包
  4. 转储格式可读性差:直接写入原始字节数据,无法直观查看请求/响应内容

修正后的完整代码

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()

关键改进点

  1. 协议自适应处理:自动识别TCP/UDP协议,针对TCP单独处理连接建立流程
  2. TCP连接状态维护:手动模拟三次握手,维护序列号和确认号,确保服务器接受数据包
  3. 简化PCAP读取:直接使用Scapy的rdpcap读取PCAP,避免跨库转换的错误
  4. 自动校验和计算:删除原始校验和字段,让Scapy自动重新计算,避免校验失败
  5. 可读格式转储:使用Scapy的show(dump=True)生成结构化的数据包内容,便于后续分析

内容的提问来源于stack exchange,提问作者Ankit Sinha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:47:08