Python 的 PCAP 修改

PCAP modification with Python

我需要读取 PCAP 文件,修改一些字段(实际上是 IPv4 源和目标以及以太网源和目标)。 PCAP 被预过滤为仅包含 IPv4 over Ethernet 数据包。

到目前为止,我尝试使用 scapy 执行此操作,但是它存在严重的内存问题。读取 ~350MB PCAP 文件时,我的 16GB RAM 已完全填满。其实就是看书。我还没有对这个文件做任何其他事情。我也有 found this answer,有了这些变化,阅读速度非常快。一旦我开始修改数据包,内存就会再次膨胀。 Scapy 实际上在这种情况下不可用

我也想过用tcprewrite等其他工具,但不能达到我的目的。每个数据包的 Source MAC 始终相同,这也可以通过 tcprewrite 完成。源 IP 在给定的子网范围内应该是随机的,例如均匀分布在 10.0.0.0/16。不太容易。更复杂的是目标IP,需要根据给定的流量矩阵计算出来。

所以问题是:我怎样才能读入PCAP文件,用自定义函数修改四个基本字段(Ethernet src+dst,IP src+dst),然后写回(另一个)PCAP文件?

实际上,我的框架的其余部分是用 Python 编写的,所以我更喜欢基于 python 的解决方案。但是,因为我可以简单地调用其他脚本,所以这不是强制性的。谢谢!

我不知道是否有办法用 scapy 做到这一点,但你也可以使用非常简单的 PcapFile.py 库,它可以让你 read/write pcap 文件逐包(免责声明: 我是作者之一)。如果您的需求不是太复杂(例如,您不需要重新生成校验和),您可以使用 Python 切片和 Python 的结构模块简单地修改框架的字节串。

但我认为也应该可以让 scapy 使用 p = Ether(packet_bytes) 分析帧并使用 str(p) 转换回 PcapFile.py 的字节流。这样你就可以让scapy为你重新计算一个有效的校验和。

正如你所说,Scapy 似乎有 "a severe memory problem",可能是因为你用 rdpcap() 读取内存中的整个 PCAP 文件,然后对其进行修改(仍在内存中),然后将其写回另一个文件,所有一次,根据你的记忆,wrpcap().

但是最 "Pythonic" 和 "Scapyist" 的方法是使用生成器(PcapReaderPcapWriter)。这是一个例子:

from scapy.all import *

ETHER_ADDR_TRANSLATION = {
    "orig_mac_1": "new_mac_1",
    # [...]
}

IP_ADDR_TRANSLATION = {
    "orig_ip_1": "new_ip_1",
    # [...]
}

def addr_translation_pcap(source, destination):
    out = PcapWriter(destination)
    for pkt in PcapReader(source):
        # In case we have complex encapsulations, like IP-in-IP, etc.,
        # we have to do something like this. If we know for sure that's
        # not the case, there's no need for such a (time-consuming) code.
        layer = pkt
        while not isinstance(layer, NoPayload):
            if isinstance(layer, Ether):
                for field in ['src', 'dst']:
                    fval = getattr(layer, field)
                    if fval in ETHER_ADDR_TRANSLATION:
                        setattr(layer, field, ETHER_ADDR_TRANSLATION[fval])
            # Let's not forget IP-in-ICMP-error
            elif isinstance(layer, (IP, IPerror)):
                for field in ['src', 'dst']:
                    fval = getattr(layer, field)
                    if fval in IP_ADDR_TRANSLATION:
                        setattr(layer, field, IP_ADDR_TRANSLATION[fval])
            elif isinstance(layer, ARP):
                fields = {}
                if layer.hwtype == 1:
                    fields.update({'hwsrc': ETHER_ADDR_TRANSLATION,
                                   'hwdst': ETHER_ADDR_TRANSLATION})
                if layer.ptype == 2048:
                    fields.update({'psrc': IP_ADDR_TRANSLATION,
                                   'pdst': IP_ADDR_TRANSLATION})
                for field, translator in fields.iteritems():
                    fval = getattr(layer, field)
                    if fval in translator:
                        setattr(layer, field, translator[fval])
            layer = layer.payload
        out.write(pkt)
    out.close()