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.'