在Python Scapy中绕过某些数据包层

3 投票
2 回答
2036 浏览
提问于 2025-04-18 02:36

我想构建一个带有Vlan部分的IP数据包。使用Scapy可以很容易做到:

from scapy import inet
newpkt = inet.Ether()/inet.Dot1Q()/inet.IP()

有时候我想在数据包中包含inet.Dot1Q(),有时候又不想。那我应该把inet.Dot1Q()的默认值设成什么,这样在使用'/'操作符时就可以简单地跳过它呢?我试过''None,但都不行。

from scapy import inet
myDot1Q = SOME DEFAULT VALUE
newpkt = inet.Ether()/myDot1Q/inet.IP()
#new packet is a valid IP packet here

编辑:我问题的另一种解释

1) 我可以创建一个带有VLAN标签的数据包。

inet.Ether()/inet.Dot1Q/inet.IP()

2) 我可以创建一个带有双VLAN标签的数据包。

inet.Ether()/inet.Dot1Q/inet.Dot1Q/inet.IP()

3) 我该如何构建一个可以是无标签数据包、带VLAN标签数据包或双VLAN标签数据包的数据包呢?如果能有类似这样的东西就好了:

#No VLAN
myVlan = ???
myDoubleVlan = ???

#VLAN
myVlan = inet.Dot1Q()
myDoubleVlan = ???

#Double VLAN
myVlan = inet.Dot1Q()
myDoubleVlan = inet.Dot1Q()

#In any case the packet structure should remain the same
inet.Ether()/myVlan/myDoubleVlan/inet.IP()

我搞不清楚默认值???应该是什么,才能正确构建这个数据包。

2 个回答

1

我完全忘记了 Raw() 这个东西。

Raw 层就是你想要的。

如果 Raw() 里没有放任何数据,它就不会对你构建的包(Packet)添加任何东西。你可以把它当作 myDot1Q 变量的默认值来用。

>>> b = Raw() / ICMP()
>>> a = Raw() / ICMP()
>>> b = ICMP()
>>> a.show()
###[ Raw ]###
  load= ''
###[ ICMP ]###
     type= echo-request
     code= 0
     chksum= None
     id= 0x0
     seq= 0x0
>>> b.show()
###[ ICMP ]###
  type= echo-request
  code= 0
  chksum= None
  id= 0x0
  seq= 0x0
>>> a.build()
'\x08\x00\xf7\xff\x00\x00\x00\x00'
>>> b.build()
'\x08\x00\xf7\xff\x00\x00\x00\x00'
>>> a.build() == b.build()
True
4

首先,我实在想不出有什么理由不使用这样一个简单的解决方案:

# example for single Vlan
# myVlan = Dot1Q()
# myDoubleVlan = None

# generate the package
package = Ether()
if myVlan: package /= myVlan
if myDoubleVlan: package /= myDoubleVlan
package /= IP()

不过,如果这个方法出于某种原因不可行,还有另一种可能性……
你可以重写 Packet 类,创建一个对除法运算符中立的新类。你只需要重写 __div__ 方法,并加上一些括号,以保持正确的优先级。

这就是你如何实现它的:

from scapy.all import *

class NeutralPacket(Packet):
    def __init__(self,  _pkt="", _internal=0, **fields):
        super(NeutralPacket, self).__init__(_pkt, _internal, **fields)
    def __div__(self, other):
        return other

#No VLAN
myVlan = NeutralPacket()
myDoubleVlan = NeutralPacket()

#VLAN
myVlan = Dot1Q()
myDoubleVlan = NeutralPacket()

#Double VLAN
myVlan = Dot1Q()
myDoubleVlan = Dot1Q()

# this part doesn't need to change, but you need to have two
# additional parenthesis for proper precedence
Ether()/(myVlan/(myDoubleVlan/IP()))

额外说明:

__div__() 方法是实现除法 operator 的地方。解释器会把 a/b 视为 a.__div__(b),而 Packet 类有自己对这个运算符的实现。如果你查看它的 代码,你会发现它基本上是把包 bpayload 附加到包 a 上。
而我们这里做的事情是直接返回 other 包,这里是 b,完全忽略了 a
注意,这只在一个方向上有效,当 NeutralPacket 是左侧运算符时,因为否则就会执行其他类的 __div__() 方法。为了处理这个问题,我们必须加上括号,这样优先级就会让我们的 NeutralPacket 类始终成为左侧运算符。而且由于这不会影响整体结果,因为 Packet 的实现,这对你的情况来说是有效的。唯一不行的情况是如果 NeutralPacket 是最右侧(即最后一个)运算符,因为那样你就无法强制正确的优先级。但对于 VLAN 来说,这不是问题,因为它上面总是有其他层。

撰写回答