0

I am currently in the process of writing a few custom scapy layers to dissect/build packets for a protocol and I have it mostly working. Since the protocol sits on top of TCP, I've been using a StreamSocket to send/receive the packets, like so:

from scapy.all import *

s = socket.socket()
s.connect(('127.0.0.1', 1337))
ss = StreamSocket(s, MyLayer)
ans, unans = ss.sr(MyLayer())

I have overloaded my layer's answers() method to define what constitutes a "response". This protocol uses ACK numbers similar to TCP, but instead of ACK'ing the sequence number of the next packet, it instead ACK's the sequence number of the last packet received. In addition to the SEQ and ACK numbers, packets of this layer also contain a length field and two checksum fields, so I've overloaded the layer's post_build() method to compute them automatically. Altogether, my layer looks as follows:

from numpy import uint8
from random import randint
from scapy.all import *

class MyLayer(Packet):
    name = 'MyLayer'
    fields_desc = [
        XShortField('sop', 0xFF5A),
        XShortField('len', None),
        FlagsField('ctl', 0x40, 8, [
            'ZERO0',
            'ZERO1',
            'ZERO2',
            'SUS',
            'RST',
            'EAK',
            'ACK',
            'SYN'
        ]),
        ByteField('seq', None),
        ByteField('ack', None),
        ByteField('sess', 0),
        XByteField('chk', None)
        StrLenField('data', '', length_from=lambda pkt: pkt.len - 10),
        XByteField('datachk', None)
    ]
    send_seq = uint8(randint(0, 255))
    recv_seq = uint8(0)

    def post_build(self, p, pay):
        if self.len is None:
            len_off = 2
            len_size = 2
            p = p[:len_off] + struct.pack('!H', len(p)) + p[len_off+len_size:]

        if self.seq is None:
            seq_off = 5
            seq_size = 1

            # Increments send_seq before sending by examining the stack trace.
            for line in traceback.format_stack():
                if '_sndrcv_snd' in line or 'in send' in line:
                    MyLayer.send_seq = uint8(MyLayer.send_seq + 1)
                    break

            p = p[:seq_off] + MyLayer.send_seq + p[seq_off+seq_size:]

        if self.ack is None:
            ack_off = 6
            ack_size = 1
            p = p[:ack_off] + MyLayer.recv_seq + p[ack_off+ack_size:]

        if self.chk is None:
            chk_off = 8
            chk_size = 1
            chk = uint8(0x100 - sum(p[:chk_off]))
            p = p[:chk_off] + chk + p[chk_off+chk_size:]

        if self.datachk is None:
            datachk_off = len(p) - 1
            datachk = uint8(0x100 - sum(p[:datachk_off]))
            p = p[:datachk_off] + datachk

        return p + pay
        
    def update_recv_seq(self):
        MyLayer.recv_seq = uint8(self.seq)

    def answers(self, other):
        self.update_recv_seq()
        if isinstance(other, MyLayer) and self.ack == other.seq:
            return 1
        return 0

Unfortunately, when I set a packet's seq to None in order for it to be computed automatically, answers() fails to recognize the received packet as being a response to the one I sent because other.seq is still set to None. Adding the line self.seq = MyLayer.send_seq to the end of post_build() doesn't seem to work either. I've resorted to recasting the raw bytes after rebuilding the layer, but this seems like a clumsy solution:

pkts = rdpcap('path/to/cap.pcap')
for pkt in pkts:
    if pkt.haslayer(MyLayer):
        del pkt[MyLayer].seq
        del pkt[MyLayer].ack
        del pkt[MyLayer].chk
        del pkt[MyLayer].datachk
        to_send = MyLayer(raw(pkt[MyLayer]))
        ans, unans = ss.sr(to_send)

Is there a better way to address this problem?

  • You could try actually setting `self.seq = ...` in your post_build if it was None. – Cukic0d Apr 03 '21 at 11:26
  • @Cukic0d I've tried that, but unfortunately changes made to fields within the context of post_build don't seem to carry over to the layer - not sure why. It might be operating on a copy of the layer? – Mark Bereza Apr 04 '21 at 09:19

0 Answers0