Reputation: 128
I am new to Scapy and I want to dissect a complicated protocol. Here is where I get blocked: I have a packet holding, in two fields, the type and the number of records. The rest of the packet constitutes the records.
I implemented it with two layers: the first one, the MainPacket class, holding the record_type, the record_nb and a list of Records. The second layer, the Record class, is fully composed of conditional fields, one per record type, as shown below:
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()
Here is the result I get when testing the code:
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'
The sublayer is not dissected. However, Replacing the conditionals by the IntField or the StrNullField works well, except that it cannot handle all cases...
My Python version is 2.7.6. My Scapy version is 2.3.2. I use Linux Mint 17.
Do you have any clue?
Upvotes: 1
Views: 3261
Reputation: 6237
Maybe you can move the condition to MainPacket
? Also, you can use bind_layers()
instead of overwriting .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()
Output:
###[ 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.'
Upvotes: 3
Reputation: 128
After diving into Scapy, I found the reason: pkt.underlayer
in the lambda function was equal to None. Scapy did not report the error.
The solution I found was to store the underlayer in a global variable, as shown here under:
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()
I am still waiting for more elegant solutions :-)
Upvotes: 0