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" 的方法是使用生成器(PcapReader
和 PcapWriter
)。这是一个例子:
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()
我需要读取 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" 的方法是使用生成器(PcapReader
和 PcapWriter
)。这是一个例子:
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()