Scapy:如何实现条件字段列表?
Scapy: How to implement a list of conditional fields?
我是 Scapy 的新手,我想剖析一个复杂的协议。这是我被阻止的地方:我有一个数据包,在两个字段中,记录的类型和数量。数据包的其余部分构成记录。
我用两层实现它:第一层,MainPacket class,包含 record_type、record_nb 和记录列表。第二层,Record class,完全由条件字段组成,每个记录类型一个,如下所示:
from scapy.all import Packet, IntField, IntEnumField, FieldLenField\
,PacketListField, StrNullField, ConditionalField
class Record(Packet):
fields_desc = [
#IntField("data_long_record", 0)
#StrNullField("data_sz_record", "")
ConditionalField(IntField("data_long_record", 0),
lambda pkt:pkt.underlayer.record_type==3),
ConditionalField(StrNullField("data_sz_record", ""),
lambda pkt:pkt.underlayer.record_type==8)
]
def extract_padding(self, s):
return '', s
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
PacketListField("records", None, Record, count_from=lambda pkt:pkt.record_nb)
]
def extract_padding(self, s):
return '', s
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
这是我测试代码时得到的结果:
WARNING: No route found for IPv6 destination :: (no default route?)
###[ MainPacket ]###
record_type= DATA_LONG
record_nb = 4
\records \
|###[ Raw ]###
| load = '\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04'
###[ MainPacket ]###
record_type= DATA_SZ
record_nb = 2
\records \
|###[ Raw ]###
| load = 'Hello\x00 world.\x00'
子层未剖析。但是,用 IntField 或 StrNullField 替换条件语句效果很好,只是它不能处理所有情况...
我的 Python 版本是 2.7.6。
我的 Scapy 版本是 2.3.2。
我用 Linux Mint 17.
你有什么线索吗?
深入研究Scapy后,我发现了原因:lambda函数中的pkt.underlayer
等于None。 Scapy没有报错
我找到的解决方案是将底层存储在全局变量中,如下所示:
from scapy.all import Packet, IntField, IntEnumField, FieldLenField\
,PacketListField, StrNullField, ConditionalField, PacketLenField
current_main_packet = None
class Record(Packet):
current_main_packet
fields_desc = [
#IntField("data_long_record", 0)
#StrNullField("data_sz_record", "")
ConditionalField(IntField("data_long_record", 0),
lambda pkt:pkt.underlayer.record_type==3),
ConditionalField(StrNullField("data_sz_record", ""),
lambda pkt:pkt.underlayer.record_type==8)
]
def extract_padding(self, s):
return '', s
def pre_dissect(self, s):
self.underlayer = current_main_packet
return s
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
PacketListField("records", None, Record, count_from=lambda pkt:pkt.record_nb)
]
def extract_padding(self, s):
return '', s
def pre_dissect(self, s):
global current_main_packet
current_main_packet = self
return s
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
我还在等待更优雅的解决方案:-)
也许您可以将条件移动到 MainPacket
?此外,您可以使用 bind_layers()
而不是覆盖 .extract_padding()
.
from scapy.all import Packet, IntField, IntEnumField, FieldLenField, \
PacketListField, StrNullField, ConditionalField, Padding, bind_layers
class RecordLONG(Packet):
fields_desc = [
IntField("data_long_record", 0),
]
class RecordSZ(Packet):
fields_desc = [
StrNullField("data_sz_record", ""),
]
bind_layers(RecordLONG, Padding)
bind_layers(RecordSZ, Padding)
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
ConditionalField(PacketListField("records", None, RecordLONG, count_from=lambda pkt:pkt.record_nb), lambda pkt: pkt.record_type == 3),
ConditionalField(PacketListField("records", None, RecordSZ, count_from=lambda pkt:pkt.record_nb), lambda pkt: pkt.record_type == 8),
]
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
输出:
###[ MainPacket ]###
record_type= DATA_LONG
record_nb = 4
\records \
|###[ RecordLONG ]###
| data_long_record= 1
|###[ RecordLONG ]###
| data_long_record= 2
|###[ RecordLONG ]###
| data_long_record= 3
|###[ RecordLONG ]###
| data_long_record= 4
###[ MainPacket ]###
record_type= DATA_SZ
record_nb = 2
\records \
|###[ RecordSZ ]###
| data_sz_record= 'Hello'
|###[ RecordSZ ]###
| data_sz_record= ' world.'
我是 Scapy 的新手,我想剖析一个复杂的协议。这是我被阻止的地方:我有一个数据包,在两个字段中,记录的类型和数量。数据包的其余部分构成记录。
我用两层实现它:第一层,MainPacket class,包含 record_type、record_nb 和记录列表。第二层,Record class,完全由条件字段组成,每个记录类型一个,如下所示:
from scapy.all import Packet, IntField, IntEnumField, FieldLenField\
,PacketListField, StrNullField, ConditionalField
class Record(Packet):
fields_desc = [
#IntField("data_long_record", 0)
#StrNullField("data_sz_record", "")
ConditionalField(IntField("data_long_record", 0),
lambda pkt:pkt.underlayer.record_type==3),
ConditionalField(StrNullField("data_sz_record", ""),
lambda pkt:pkt.underlayer.record_type==8)
]
def extract_padding(self, s):
return '', s
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
PacketListField("records", None, Record, count_from=lambda pkt:pkt.record_nb)
]
def extract_padding(self, s):
return '', s
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
这是我测试代码时得到的结果:
WARNING: No route found for IPv6 destination :: (no default route?)
###[ MainPacket ]###
record_type= DATA_LONG
record_nb = 4
\records \
|###[ Raw ]###
| load = '\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04'
###[ MainPacket ]###
record_type= DATA_SZ
record_nb = 2
\records \
|###[ Raw ]###
| load = 'Hello\x00 world.\x00'
子层未剖析。但是,用 IntField 或 StrNullField 替换条件语句效果很好,只是它不能处理所有情况...
我的 Python 版本是 2.7.6。 我的 Scapy 版本是 2.3.2。 我用 Linux Mint 17.
你有什么线索吗?
深入研究Scapy后,我发现了原因:lambda函数中的pkt.underlayer
等于None。 Scapy没有报错
我找到的解决方案是将底层存储在全局变量中,如下所示:
from scapy.all import Packet, IntField, IntEnumField, FieldLenField\
,PacketListField, StrNullField, ConditionalField, PacketLenField
current_main_packet = None
class Record(Packet):
current_main_packet
fields_desc = [
#IntField("data_long_record", 0)
#StrNullField("data_sz_record", "")
ConditionalField(IntField("data_long_record", 0),
lambda pkt:pkt.underlayer.record_type==3),
ConditionalField(StrNullField("data_sz_record", ""),
lambda pkt:pkt.underlayer.record_type==8)
]
def extract_padding(self, s):
return '', s
def pre_dissect(self, s):
self.underlayer = current_main_packet
return s
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
PacketListField("records", None, Record, count_from=lambda pkt:pkt.record_nb)
]
def extract_padding(self, s):
return '', s
def pre_dissect(self, s):
global current_main_packet
current_main_packet = self
return s
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
我还在等待更优雅的解决方案:-)
也许您可以将条件移动到 MainPacket
?此外,您可以使用 bind_layers()
而不是覆盖 .extract_padding()
.
from scapy.all import Packet, IntField, IntEnumField, FieldLenField, \
PacketListField, StrNullField, ConditionalField, Padding, bind_layers
class RecordLONG(Packet):
fields_desc = [
IntField("data_long_record", 0),
]
class RecordSZ(Packet):
fields_desc = [
StrNullField("data_sz_record", ""),
]
bind_layers(RecordLONG, Padding)
bind_layers(RecordSZ, Padding)
class MainPacket(Packet):
fields_desc = [
IntEnumField("record_type", 1, {
3:"DATA_LONG",
8:"DATA_SZ"}),
FieldLenField("record_nb", 0, fmt="I", count_of="records"),
ConditionalField(PacketListField("records", None, RecordLONG, count_from=lambda pkt:pkt.record_nb), lambda pkt: pkt.record_type == 3),
ConditionalField(PacketListField("records", None, RecordSZ, count_from=lambda pkt:pkt.record_nb), lambda pkt: pkt.record_type == 8),
]
if __name__ == "__main__":
p1 = MainPacket("\x00\x00\x00\x03" # Type is DATA_LONG
"\x00\x00\x00\x04" # 4 records
"\x00\x00\x00\x01"
"\x00\x00\x00\x02"
"\x00\x00\x00\x03"
"\x00\x00\x00\x04")
p1.show()
p2 = MainPacket("\x00\x00\x00\x08" # Type is DATA_SZ
"\x00\x00\x00\x02" # 2 records
"Hello\x00"
" world.\x00")
p2.show()
输出:
###[ MainPacket ]###
record_type= DATA_LONG
record_nb = 4
\records \
|###[ RecordLONG ]###
| data_long_record= 1
|###[ RecordLONG ]###
| data_long_record= 2
|###[ RecordLONG ]###
| data_long_record= 3
|###[ RecordLONG ]###
| data_long_record= 4
###[ MainPacket ]###
record_type= DATA_SZ
record_nb = 2
\records \
|###[ RecordSZ ]###
| data_sz_record= 'Hello'
|###[ RecordSZ ]###
| data_sz_record= ' world.'