使用scapy从pcap发送修改过源/目的地址的数据包

27 投票
4 回答
50069 浏览
提问于 2025-04-17 09:31

我正在尝试使用scapy发送之前录制的网络流量(以pcap格式捕获的)。目前我遇到的问题是去掉原来的以太网层。这个流量是在另一台主机上捕获的,我基本上需要更改IP和以太网层的源地址和目标地址。我已经成功替换了IP层并重新计算了校验和,但以太网层让我很头疼。

有没有人有经验可以分享,如何从捕获文件中重新发送数据包,并对IP和以太网层的源和目标地址进行修改?另外,这个捕获文件相当大,有好几个GB,使用scapy处理这么大的流量性能怎么样?

4 个回答

5

好吧,我用scapy写了以下代码(抱歉我的Python水平)。希望这能帮助到某个人。其实还有一种可能更简单的情况,就是把pcap文件里的所有数据包都读到内存里,但这样在处理大文件时可能会出现问题。

from scapy.all import *
global src_ip, dst_ip
src_ip = 1.1.1.1
dst_ip = 2.2.2.2
infile = "dump.pcap"

try:
    my_reader = PcapReader(infile)
    my_send(my_reader)
except IOError:
    print "Failed reading file %s contents" % infile
    sys.exit(1)

def my_send(rd, count=100):
    pkt_cnt = 0
    p_out = []

    for p in rd:
        pkt_cnt += 1
        np = p.payload
        np[IP].dst = dst_ip
        np[IP].src = src_ip
        del np[IP].chksum
        p_out.append(np)
        if pkt_cnt % count == 0:
            send(PacketList(p_out))
            p_out = []

    # Send remaining in final batch
    send(PacketList(p_out))
    print "Total packets sent %d" % pkt_cn
7

如果我是你,我会让Scapy来处理Ether层,然后使用send()这个函数。比如说:

ip_map = {"1.2.3.4": "10.0.0.1", "1.2.3.5": "10.0.0.2"}
for p in PcapReader("filename.cap"):
    if IP not in p:
        continue
    p = p[IP]
    # if you want to use a constant map, only let the following line
    p.src = "10.0.0.1"
    p.dst = "10.0.0.2"
    # if you want to use the original src/dst if you don't find it in ip_map
    p.src = ip_map.get(p.src, p.src)
    p.dst = ip_map.get(p.dst, p.dst)
    # if you want to drop the packet if you don't find both src and dst in ip_map
    if p.src not in ip_map or p.dst not in ip_map:
        continue
    p.src = ip_map[p.src]
    p.dst = ip_map[p.dst]
    # as suggested by @AliA, we need to let Scapy compute the correct checksum
    del(p.chksum)
    # then send the packet
    send(p)
32

看看这个例子

from scapy.all import *
from scapy.utils import rdpcap

pkts=rdpcap("FileName.pcap")  # could be used like this rdpcap("filename",500) fetches first 500 pkts
for pkt in pkts:
     pkt[Ether].src= new_src_mac  # i.e new_src_mac="00:11:22:33:44:55"
     pkt[Ether].dst= new_dst_mac
     pkt[IP].src= new_src_ip # i.e new_src_ip="255.255.255.255"
     pkt[IP].dst= new_dst_ip
     sendp(pkt) #sending packet at layer 2

评论:

  • 使用rdpcap和wrpcap这两个scapy的方法来读取和写入pcap格式的文件。
  • 你可以用sniff(offline="filename")来读取数据包,如果想对每个读取到的数据包执行某个函数,可以这样写sniff(offline="filename",prn=My_Function),这样My_Function就会应用到每一个捕获到的数据包上。
  • 写新的IP地址或MAC地址时,要把它当作字符串来处理,比如ip="1.1.1.1",就像上面那样。
  • 在这个例子中,sendp方法放在了for循环里,这样会比用其他循环发送数据包慢。
  • 性能提示:在Python中使用for循环太慢了,如果你想要像C语言中的for循环那样快,可以用map参考
  • 上面提到的rdpcap会一次性读取整个文件,如果你可用的内存是1.5GB,而你要读取的文件是2GB或3GB,那就会失败。
  • 如果性能对你来说非常重要,可以使用winpcap,但你需要写更复杂的C代码;用Python/scapy做同样的事情相对简单,但速度没有C快。
  • 使用哪种方法取决于你需要的性能水平。
  • 如果我猜得没错,你是在发送视频流的数据包,在这种情况下,如果你发送的是1百万像素的视频,我会选择winpcap;而在其他情况下(每帧数据较小),可以用scapy。
  • 如果使用C/winpcap,你在读取pcap文件、修改数据和重新发送时会得到很好的性能,但要注意同样的问题(大文件),你需要创建一个合适大小的缓冲区来读取和发送数据包,以确保性能。
  • 如果数据包大小是固定的(这在大多数情况下是比较少见的,我猜),你可能会更好地利用可用内存。
  • 如果你想用Python/scapy来做整个“项目/程序”,可以在C/Wincap中创建高性能的函数并编译成dll,然后把这个dll导入到你的Python程序中,这样你就可以在Python程序里使用它。这样你就能享受到Python/scapy的简单,同时只需在C中编写特定的函数,从而更快地完成工作,代码也更集中和易于维护。

撰写回答