6

I am trying to dissect packets, which encapsulate another packet-like structure, called "tags". The structure looks like this

+---------+
|Ether    |
+---------+
|IP       |                   a tag
+---------+
|UDP      |                +------------+
+---------+                |tagNumber   |
|BVLC     |                +------------+
+---------+                |tagClass    |
|NPDU     |                +------------+
+---------+           +-+  |LVT field   |
|APDU     |           |    +------------+
|  +------+--+        |    |            |
|  |Tag 1    | <------+    |  data      |
|  +---------+             |            |
|  |Tag 2    |             +------------+
|  +---------+
|  |Tag n    |
+------------+

For this I created a class derived from the existing PacketListField, which looks like this:

class TagListField(PacketListField):
    def __init__(self):
        PacketListField.__init__(
            self,
            "tags",
            [],
            guessBACNetTagClass,

The referenced guessBACNetTagClass is a function which returns the correct class needed to dissect the tag.

BACNetTagClasses = {
    0x2: "BACNetTag_U_int",
    0xC: "BACNetTag_Object_Identifier"
}

def guessBACNetTagClass(packet, **kargs):

    """ Returns the correct BACNetTag Class needed to dissect
        the current tag

        @type    packet:    binary string
        @param   packet:    the current packet

        @type    cls:       class
        @param   cls:       the correct class for dissection
    """

    tagByteBinary = "{0:b}".format(int(struct.unpack("!B", packet[0])[0]))
    tagNumber = int(tagByteBinary[0:4],2)
    clsName = BACNetTagClasses.get(tagNumber)
    cls = globals()[clsName]
    return cls(packet, **kargs)

Currently there are, as you can see from the BACNetTagClasses dictionary from above, two of these classes.

class BACNetTag_Object_Identifier(Packet):
    name = "BACNetTag_Object_Identifier"
    fields_desc =[
        # fields
    ]

class BACNetTag_U_int(Packet):
    name = "BACNetTag_U_int"
    fields_desc = [
        # fields
    ]

In the encapsulating layer, called APDU I added the TagListField just like another field.

class APDU(Packet):

    name = "APDU"
    fields_desc = [
        # Some other fields

        TagListField()
    ]

A packet, which I am currently trying to dissect, contains multiple tags. The first one (of the type BACNetTag_Object_Identifier) can be correctly dissected, but the remaining tags are just listed as raw payload.

[<Ether |<UDP |<BVLC |<NPDU |<APDU 
pduType=UNCONFIRMED_SERVICE_REQUEST reserved=None serviceChoice=I_AM
tags=[<BACNetTag_Object_Identifier  tagNumber=BACNET_OBJECT_IDENTIFIER
tagClass=APPLICATION lengthValueType=4L objectType=DEVICE
instanceNumber=640899L |<Raw  load='"\x01\xe0\x91\x00!\xde'
|>>] |>>>>>>]

Is there something wrong with my implementation of PacketListField? As I understand, the field should try to dissect the remaining tags until no more Bytes are left.


UPDATE:

Using .show() reveals a bit more about the packets structure

###[ APDU ]###
  pduType   = UNCONFIRMED_SERVICE_REQUEST
  reserved  = None
  serviceChoice= I_AM
  \tags      \
   |###[ BACNetTag_Object_Identifier ]###
   |  tagNumber = BACNET_OBJECT_IDENTIFIER
   |  tagClass  = APPLICATION
   |  lengthValueType= 4L
   |  objectType= DEVICE
   |  instanceNumber= 640899L
   |###[ Raw ]###
   |     load      = '"\x01\xe0\x91\x00!\xde'

Scapy just appends the remaining Bytes as a Raw layer to the existing tags field. That's interesting, but I still don't know why it is doing so.

vicco
  • 1,049
  • 2
  • 14
  • 33
  • 1
    Hey, do you know what is the expected parser result for `\x01\xe0\x91\x00!\xde`? Is it another tag? In which case the tagNumber is `0x1` which your code does not support atm? You could make a generic `BACNetTag_Base` which reads all the tags in an content-agnostic way - consume the bytes without identifying the `data` field and make it the default class returned in `guessBACNetTagClass` (which can be extended by `BACNetTag_U_int`). That way all tags (known and unknown) would be parsed. Finally, can this be FCS or similar? Sorry for the too many questions! :) – urban Dec 25 '15 at 10:22
  • I meant return default class if none was identified: `clsName = BACNetTagClasses.get(tagNumber, "BACNetTag_Base")` – urban Dec 25 '15 at 10:24
  • Yes, the expected output are two other tags. Yes, the code here does not support this tag number, but the current version does, yet it still does not work. The generic class is a good idea, I'll try that and update my question. What's FCS? – vicco Dec 25 '15 at 15:13
  • [Frame check sequence](https://en.wikipedia.org/wiki/Frame_check_sequence) – urban Dec 25 '15 at 19:36

1 Answers1

3

Try to overwrite the extract_padding method of your BACNet_Tag* classes with:

def extract_padding(self, s):
return '', s

I had similar problems with PacketListField and found this stackoverflow post:

Scapy: Adding new protocol with complex field groupings

smehner
  • 71
  • 5