Search code examples
pythonscapy

How to print field offsets in scapy?


I have a scapy protocol and a packet.

Like "First steps" say, it's easy to print a packet with its fields, and the packet's binary dump:

>>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
>>> a
<Ether  type=IPv4 |<IP  frag=0 proto=6 dst=Net("www.slashdot.org/32") |<TCP  
|<Raw  load='GET /index.html HTTP/1.0 \n\n' |>
>>>
>>> hexdump(a)
0000  FF FF FF FF FF FF 02 42 AC 1F 80 3C 08 00 45 00  .......B...<..E.
0010  00 43 00 01 00 00 40 06 C9 F0 AC 1F 80 3C 68 12  .C....@......<h.
0020  1C 56 00 14 00 50 00 00 00 00 00 00 00 00 50 02  .V...P........P.
0030  20 00 0C EE 00 00 47 45 54 20 2F 69 6E 64 65 78   .....GET /index
0040  2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A  .html HTTP/1.0 .
0050  0A                                               .
>>>

Now, I want to know, for example, what offset does dport field in TCP have. Or all of the fields in all the layers.

Can I print them with scapy? Is there a single way to do it for all protocols, including custom ones?


Solution

  • interesting question. I don't believe that there's currently any builtin way of doing that, and I can't really think of a use case.

    The things that gets the closest, for purely informative purpose, are probably either:

    >>> rfc(TCP)
     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             SPORT             |             DPORT             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                              SEQ                              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                              ACK                              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |DATAOFS|RESER|      FLAGS      |             WINDOW            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             CHKSUM            |             URGPTR            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |            OPTIONS            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
                                 Fig. TCP
    

    But if you want really is a numerical value, matching the offset of a field from the first byte, It's not super hard to do it yourself, with something like:

    def calc_offset(packet, layer, field):
        """Calculate the offset of a field, in a packet"""
        offset = 0
        while packet:  # for each payload
            for fld in packet.fields_desc:  # for each field
                if fld.name == field and isinstance(packet, layer):
                    return int(offset)
                offset += fld.i2len(packet, packet.getfieldval(fld.name))  # add length
            packet = packet.payload
        return -1
    

    In your example:

    >>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
    >>> calc_offset(a, TCP, "dport")
    36
    >>> struct.unpack("!H", bytes(a)[36:36+2])[0]
    80
    >>> a.dport
    80
    

    Hope this helped