在Python Scapy中绕过某些数据包层
我想构建一个带有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 个回答
我完全忘记了 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
首先,我实在想不出有什么理由不使用这样一个简单的解决方案:
# 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
类有自己对这个运算符的实现。如果你查看它的 代码,你会发现它基本上是把包 b
的 payload
附加到包 a
上。
而我们这里做的事情是直接返回 other
包,这里是 b
,完全忽略了 a
。
注意,这只在一个方向上有效,当 NeutralPacket
是左侧运算符时,因为否则就会执行其他类的 __div__()
方法。为了处理这个问题,我们必须加上括号,这样优先级就会让我们的 NeutralPacket
类始终成为左侧运算符。而且由于这不会影响整体结果,因为 Packet
的实现,这对你的情况来说是有效的。唯一不行的情况是如果 NeutralPacket
是最右侧(即最后一个)运算符,因为那样你就无法强制正确的优先级。但对于 VLAN 来说,这不是问题,因为它上面总是有其他层。