Search code examples
pythonscapy

Trying to get bytes in ASCII format in sniff function in scapy


I am having a problem where I can't get the raw bytes in ascii format when using the sniff() function in scapy. Example, i have this function:

a = sniff(filter="tcp port 5900",iface="eth0", prn=lambda x: x.show(), count=20)

And the output of a.summary() of one of the packets:

image1

I would like the raw output to be in ascii format, since when I open the pcap file in Wireshark, it normally comes in ascii format. For example:

image2

I would like the output of the sniff function to come in ascii format just like in wireshark. Is it possible?

I tried several parameters in the documentation but was unsuccessful. I was hoping to be able to extract this data in ASCII format like in wireshark using the sniff function.


Solution

  • You are trying to change the behavior of the show method of the Packet class (see source code). It boils down to patch _show_or_dump. You should identify values of type bytes and convert them to hex with fvalue.hex():

    from scapy.all import sniff
    from scapy.packet import *
    from unittest.mock import patch
    
    def show_or_dump_hex(self,
                    dump=False,  # type: bool
                    indent=3,  # type: int
                    lvl="",  # type: str
                    label_lvl="",  # type: str
                    first_call=True  # type: bool
                    ):
        # type: (...) -> Optional[str]
        """
        Internal method that shows or dumps a hierarchical view of a packet.
        Called by show.
    
        :param dump: determine if it prints or returns the string value
        :param int indent: the size of indentation for each layer
        :param str lvl: additional information about the layer lvl
        :param str label_lvl: additional information about the layer fields
        :param first_call: determine if the current function is the first
        :return: return a hierarchical view if dump, else print it
        """
    
        if dump:
            from scapy.themes import AnsiColorTheme
            ct = AnsiColorTheme()  # No color for dump output
        else:
            ct = conf.color_theme
        s = "%s%s %s %s \n" % (label_lvl,
                            ct.punct("###["),
                            ct.layer_name(self.name),
                            ct.punct("]###"))
        for f in self.fields_desc:
            if isinstance(f, ConditionalField) and not f._evalcond(self):
                continue
            if isinstance(f, Emph) or f in conf.emph:
                ncol = ct.emph_field_name
                vcol = ct.emph_field_value
            else:
                ncol = ct.field_name
                vcol = ct.field_value
            fvalue = self.getfieldval(f.name)
            if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and isinstance(fvalue, list)):  # noqa: E501
                pad = max(0, 10 - len(f.name)) * " "
                s += "%s  \\%s%s\\\n" % (label_lvl + lvl, ncol(f.name), pad)
                fvalue_gen = SetGen(
                    fvalue,
                    _iterpacket=0
                )  # type: SetGen[Packet]
                for fvalue in fvalue_gen:
                    s += fvalue._show_or_dump(dump=dump, indent=indent, label_lvl=label_lvl + lvl + "   |", first_call=False)  # noqa: E501
            else:
                pad = max(0, 10 - len(f.name)) * " "
                begn = "%s  %s%s%s " % (label_lvl + lvl,
                                        ncol(f.name),
                                        pad,
                                        ct.punct("="),)
                if isinstance(fvalue, bytes):   # this is where we check if the value is of type bytes
                    reprval = fvalue.hex()
                else:
                    reprval = f.i2repr(self, fvalue)
                if isinstance(reprval, str):
                    reprval = reprval.replace("\n", "\n" + " " * (len(label_lvl) +  # noqa: E501
                                                                len(lvl) +
                                                                len(f.name) +
                                                                4))
                s += "%s%s\n" % (begn, vcol(reprval))
        if self.payload:
            s += self.payload._show_or_dump(  # type: ignore
                dump=dump,
                indent=indent,
                lvl=lvl + (" " * indent * self.show_indent),
                label_lvl=label_lvl,
                first_call=False
            )
    
        if first_call and not dump:
            print(s)
            return None
        else:
            return s
    
    with patch.object(Packet, '_show_or_dump', show_or_dump_hex):
        a = sniff(iface="eth0", prn=lambda x: x.show(), count=20)
    

    Output sample:

    ###[ TCP ]###
            sport     = 54346
            dport     = 8080
            seq       = 3963954686
            ack       = 1203731561
            dataofs   = 5
            reserved  = 0
            flags     = PA
            window    = 1026
            chksum    = 0x5518
            urgptr    = 0
            options   = []
    ###[ Raw ]###
               load      = 160303002510000021204b4a9d45e1716324cfe25d7535735aadec14fde479f6a238ac2aa83fb24e0b6614030300010116030300280000000000000000589efd0d4da6de55e816d04f0055e664628e1f11a43ad68a1fb8a45ff26af4ef
    

    Edit: this can be done in a less verbose way by patching _StrField.i2repr() (source code):

    from scapy.all import sniff
    from scapy.fields import _StrField
    from unittest.mock import patch
    
    def i2repr_hex(self, pkt, x):
            # type: (Optional[Packet], I) -> str
            if isinstance(x, bytes):
                return x.hex()
            return super(_StrField, self).i2repr(pkt, x)
    
    with patch.object(_StrField, 'i2repr', i2repr_hex):
        a = sniff(iface="eth0", prn=lambda x: x.show(), count=20)