Esempio n. 1
0
    def __init__(self,
                 messages=None,
                 _id=None,
                 applicativeData=None,
                 name="Session"):
        """
        :parameter messages: the messages exchanged in the current session
        :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
        :parameter _id: the unique identifier of the session
        :type _id: :class:`uuid.UUID`
        :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData`
        """
        self.__messages = SortedTypedList(AbstractMessage)
        self.__applicativeData = TypedList(ApplicativeData)

        if messages is None:
            messages = []
        self.messages = messages
        if _id is None:
            _id = uuid.uuid4()
        self.id = _id
        if applicativeData is None:
            applicativeData = []
        self.applicativeData = applicativeData
        self.name = name
Esempio n. 2
0
    def readMessages(self, filePathList, delimitor=b"\n"):
        """Read all the messages found in the specified filePathList and given a delimitor.

        :param filePathList: paths of the file to parse
        :type filePathList: a list of :class:`str`
        :param delimitor: the delimitor used to find messages in the same file
        :type delimitor: :class:`str`
        :return: a sorted list of messages
        :rtype: a :class:`netzob.Common.Utils.SortedTypedList.SortedTypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """
        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the " +
                                 "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("File", "\n".join(errorMessageList))
                
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, delimitor)
        
        return self.messages
Esempio n. 3
0
    def __init__(self, name=None):
        self.id = uuid.uuid4()
        self.name = name
        self.description = ""

        self.__fields = TypedList(AbstractField)
        self.__parent = None

        self.__encodingFunctions = SortedTypedList(EncodingFunction)
        self.__visualizationFunctions = TypedList(VisualizationFunction)
        self.__transformationFunctions = TypedList(TransformationFunction)

        self._variable = None
Esempio n. 4
0
class PCAPImporter(object):
    """PCAP importer to read pcaps and extract messages out of them.
    We recommend to use static methods such as
    - PCAPImporter.readFiles(...)
    - PCAPimporter.readFile(...)
    refer to their documentation to have an overview of the required parameters.

    >>> from netzob.all import *
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap").values()
    >>> print(len(messages))
    14

    >>> for m in messages:
    ...    print(repr(m.data))
    b'CMDidentify#\\x07\\x00\\x00\\x00Roberto'
    b'RESidentify#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
    b'CMDinfo#\\x00\\x00\\x00\\x00'
    b'RESinfo#\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00info'
    b'CMDstats#\\x00\\x00\\x00\\x00'
    b'RESstats#\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00stats'
    b'CMDauthentify#\\n\\x00\\x00\\x00aStrongPwd'
    b'RESauthentify#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
    b'CMDencrypt#\\x06\\x00\\x00\\x00abcdef'
    b"RESencrypt#\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00$ !&'$"
    b"CMDdecrypt#\\x06\\x00\\x00\\x00$ !&'$"
    b'RESdecrypt#\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00abcdef'
    b'CMDbye#\\x00\\x00\\x00\\x00'
    b'RESbye#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=2).values()
    >>> print(repr(messages[0].data))
    b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00E\\x00\\x003\\xdc\\x11@\\x00@\\x11`\\xa6\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=3).values()
    >>> print(repr(messages[0].data))
    b'E\\x00\\x003\\xdc\\x11@\\x00@\\x11`\\xa6\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=4).values()
    >>> print(repr(messages[0].data))
    b'\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=5).values()
    >>> print(repr(messages[0].data))
    b'CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http.pcap", importLayer=5, bpfFilter="tcp").values()
    >>> print(repr(messages[0].data))
    b'GET / HTTP/1.1\\r\\nHost: www.free.fr\\r\\nUser-Agent: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\\r\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\nAccept-Language: en-US,en;q=0.5\\r\\nAccept-Encoding: gzip, deflate\\r\\nConnection: keep-alive\\r\\n\\r\\n'

    Parameter `mergePacketsInFlow` can be use to merge consecutive messages that share the same source and destination (mimic a TCP flow). In practice, this parameter was introduced for L5 network messages to support TCP flows but it can be use for any level of network messages.

    >>> from netzob.all import *
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http_flow.pcap", mergePacketsInFlow=False).values()
    >>> print(len(messages))
    4
    >>> print(len(messages[1].data))
    1228
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http_flow.pcap", mergePacketsInFlow=True).values()
    >>> print(len(messages))
    2
    >>> print(len(messages[1].data))
    3224
    """

    INVALID_BPF_FILTER = 0
    INVALID_LAYER2 = 1
    INVALID_LAYER3 = 2
    INVALID_LAYER4 = 3

    PROTOCOL201 = 201

    # Supported datalinks (by pcapy)
    SUPPORTED_DATALINKS = {
        pcapy.DLT_ARCNET: "DLT_ARCNET",
        pcapy.DLT_FDDI: "DLT_FDDI",
        pcapy.DLT_LOOP: "DLT_LOOP",
        pcapy.DLT_PPP_ETHER: "DLT_PPP_ETHER",
        pcapy.DLT_ATM_RFC1483: "DLT_ATM_RFC1483",
        pcapy.DLT_IEEE802: "DLT_IEEE802",
        pcapy.DLT_LTALK: "DLT_LTALK",
        pcapy.DLT_PPP_SERIAL: "DLT_PPP_SERIAL",
        pcapy.DLT_C_HDLC: "DLT_C_HDLC",
        pcapy.DLT_IEEE802_11: "IEEE802_11",
        pcapy.DLT_NULL: "DLT_NULL",
        pcapy.DLT_RAW: "DLT_RAW",
        pcapy.DLT_EN10MB: "DLT_EN10MB",
        pcapy.DLT_LINUX_SLL: "LINUX_SLL",
        pcapy.DLT_PPP: "DLT_PPP",
        pcapy.DLT_SLIP: "DLT_SLIP",
    }

    def __init__(self):
        pass

    @typeCheck(str, str, int)
    def __readMessagesFromFile(self, filePath, bpfFilter, nbPackets):
        """Internal methods to read all messages from a given PCAP file."""
        if (filePath is None):
            raise TypeError("filePath cannot be None")
        if (nbPackets < 0):
            raise ValueError(
                "A positive (or null) value is required for the number of packets to read."
            )

        # Check file can be opened (and read)
        try:
            fp = open(filePath, 'r')
            fp.close()
        except IOError as e:
            if e.errno == errno.EACCES:
                raise IOError(
                    "Error while trying to open the file {0}, more permissions are required to read it."
                ).format(filePath)
            else:
                raise e

        # Check (and configure) the bpf filter
        packetReader = pcapy.open_offline(filePath)
        try:
            packetReader.setfilter(bpfFilter)
        except:
            raise ValueError(
                "The provided BPF filter is not valid (it should follow the BPF format)"
            )

        # Check the datalink
        self.datalink = packetReader.datalink()
        if self.datalink not in list(PCAPImporter.SUPPORTED_DATALINKS.keys()):
            self._logger.debug("Unkown datalinks")

        if self.importLayer > 1 and self.datalink != pcapy.DLT_EN10MB and self.datalink != pcapy.DLT_LINUX_SLL and self.datalink != PCAPImporter.PROTOCOL201:
            errorMessage = _("This pcap cannot be imported since the " +
                             "layer 2 is not supported ({0})").format(
                                 str(self.datalink))
            raise NetzobImportException("PCAP", errorMessage,
                                        self.INVALID_LAYER2)
        else:
            packetReader.loop(nbPackets, self.__packetHandler)

    def __packetHandler(self, header, payload):
        """Internal callback executed on each packet when parsing the pcap"""
        (secs, usecs) = header.getts()
        epoch = secs + (usecs / 1000000.0)

        if self.importLayer == 1 or self.importLayer == 2:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2 of a packet: {0}".
                    format(e))
                return
            if len(l2Payload) == 0:
                return

            # Build the L2NetworkMessage
            l2Message = L2NetworkMessage(payload, epoch, l2Proto, l2SrcAddr,
                                         l2DstAddr)

            self.messages.add(l2Message)

        elif self.importLayer == 3:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload, etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload, ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
            except NetzobImportException as e:
                self._logger.warn("An error occured while decoding layer2 and layer3 of a packet: {0}".format(e))
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2 and layer3 of a packet: {0}".
                    format(e))
                return

            if len(l3Payload) == 0:
                return

            # Build the L3NetworkMessage
            l3Message = L3NetworkMessage(l2Payload, epoch, l2Proto, l2SrcAddr,
                                         l2DstAddr, l3Proto, l3SrcAddr,
                                         l3DstAddr)
            self.messages.add(l3Message)

        elif self.importLayer == 4:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
                (l4Proto, l4SrcPort, l4DstPort,
                 l4Payload) = self.__decodeLayer4(ipProtocolNum, l3Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2, layer3 or layer4 of a packet: {0}".
                    format(e))
                return
            if len(l4Payload) == 0:
                return

            # Build the L4NetworkMessage
            l4Message = L4NetworkMessage(
                l3Payload, epoch, l2Proto, l2SrcAddr, l2DstAddr, l3Proto,
                l3SrcAddr, l3DstAddr, l4Proto, l4SrcPort, l4DstPort)

            self.messages.add(l4Message)

        else:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
                (l4Proto, l4SrcPort, l4DstPort,
                 l4Payload) = self.__decodeLayer4(ipProtocolNum, l3Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2, layer3, layer4 or layer5 of a packet: {0}".
                    format(e))
                return
            if len(l4Payload) == 0:
                return

            l5Message = L4NetworkMessage(
                l4Payload, epoch, l2Proto, l2SrcAddr, l2DstAddr, l3Proto,
                l3SrcAddr, l3DstAddr, l4Proto, l4SrcPort, l4DstPort)
            
            self.messages.add(l5Message)

    def __decodeLayer2(self, header, payload):
        """Internal method that parses the specified header and extracts
        layer2 related proprieties."""

        def formatMacAddress(arrayMac):
            return ":".join("{0:0>2}".format(hex(b)[2:])
                            for b in arrayMac.tolist())

        if self.datalink == pcapy.DLT_EN10MB:
            l2Decoder = Decoders.EthDecoder()
            l2Proto = "Ethernet"
            layer2 = l2Decoder.decode(payload)
            l2SrcAddr = formatMacAddress(layer2.get_ether_shost())
            l2DstAddr = formatMacAddress(layer2.get_ether_dhost())
            l2Payload = payload[layer2.get_header_size():]
            etherType = layer2.get_ether_type()
        elif self.datalink == pcapy.DLT_LINUX_SLL:
            l2Decoder = Decoders.LinuxSLLDecoder()
            l2Proto = "Linux SLL"
            layer2 = l2Decoder.decode(payload)
            l2SrcAddr = layer2.get_addr()
            l2DstAddr = None
            l2Payload = payload[layer2.get_header_size():]
            etherType = layer2.get_ether_type()
        elif self.datalink == PCAPImporter.PROTOCOL201:
            l2Proto = "Protocol 201"
            hdr = payload.encode('hex')[0:8]
            if hdr[6:] == "01":
                l2SrcAddr = "Received"
            else:
                l2SrcAddr = "Sent"
            l2DstAddr = None
            l2Payload = payload[8:]
            etherType = payload[4:6]

        return (l2Proto, l2SrcAddr, l2DstAddr, l2Payload, etherType)

    def __decodeLayer3(self, etherType, l2Payload):
        """Internal method that parses the specified header and extracts
        layer3 related proprieties."""

        if etherType == Packets.IP.ethertype:
            l3Proto = "IP"
            l3Decoder = Decoders.IPDecoder()
            layer3 = l3Decoder.decode(l2Payload)
            paddingSize = len(l2Payload) - layer3.get_ip_len()

            l3SrcAddr = layer3.get_ip_src()
            l3DstAddr = layer3.get_ip_dst()
            l3Payload = l2Payload[layer3.get_header_size():]
            if paddingSize > 0 and len(l3Payload) > paddingSize:
                l3Payload = l3Payload[:len(l3Payload) - paddingSize]
            ipProtocolNum = layer3.get_ip_p()
            return (l3Proto, l3SrcAddr, l3DstAddr, l3Payload, ipProtocolNum)
        else:
            warnMessage = _("Cannot import one of the provided packets since "
                            + "its layer 3 is unsupported (Only IP is " +
                            "currently supported, packet ethernet " +
                            "type = {0})").format(etherType)
            self._logger.warn(warnMessage)
            raise NetzobImportException("PCAP", warnMessage,
                                        self.INVALID_LAYER3)

    def __decodeLayer4(self, ipProtocolNum, l3Payload):
        """Internal method that parses the specified header and extracts
        layer4 related proprieties."""

        if ipProtocolNum == Packets.UDP.protocol:
            l4Proto = "UDP"
            l4Decoder = Decoders.UDPDecoder()
            layer4 = l4Decoder.decode(l3Payload)
            l4SrcPort = layer4.get_uh_sport()
            l4DstPort = layer4.get_uh_dport()
            l4Payload = layer4.get_data_as_string()
            return (l4Proto, l4SrcPort, l4DstPort, l4Payload)
        elif ipProtocolNum == Packets.TCP.protocol:
            l4Proto = "TCP"
            l4Decoder = Decoders.TCPDecoder()
            layer4 = l4Decoder.decode(l3Payload)
            l4SrcPort = layer4.get_th_sport()
            l4DstPort = layer4.get_th_dport()
            l4Payload = layer4.get_data_as_string()
            return (l4Proto, l4SrcPort, l4DstPort, l4Payload)
        else:
            warnMessage = _("Cannot import one of the provided packets since " +
                            "its layer 4 is unsupported (Only UDP and TCP " +
                            "are currently supported, packet IP protocol " +
                            "number = {0})").format(ipProtocolNum)
            self._logger.warn(warnMessage)
            raise NetzobImportException("PCAP", warnMessage, self.INVALID_LAYER4)

    @typeCheck(list, str, int, int)
    def readMessages(self, filePathList, bpfFilter="", importLayer=5, nbPackets=0):
            warnMessage = _("Cannot import one of the provided packets since "
                            + "its layer 4 is unsupported (Only UDP and TCP " +
                            "are currently supported, packet IP protocol " +
                            "number = {0})").format(ipProtocolNum)
            self._logger.warn(warnMessage)
            raise NetzobImportException("PCAP", warnMessage,
                                        self.INVALID_LAYER4)

    @typeCheck(list, str, int, int, bool)
    def readMessages(self,
                     filePathList,
                     bpfFilter="",
                     importLayer=5,
                     nbPackets=0,
                     mergePacketsInFlow=False,
                    ):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload).
         Finally, the number of packets to capture can be specified.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the "
                                 + "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("PCAP", "\n".join(errorMessageList))

        # Verify the expected import layer
        availableLayers = [1, 2, 3, 4, 5]
        if importLayer not in availableLayers:
            raise Exception(
                "Only layers level {0} are available.".format(availableLayers))
        self.importLayer = importLayer

        # Call the method that does the import job for each PCAP file
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, bpfFilter, nbPackets)
            #Create a session and attribute it to messages:
            session = Session(list(self.messages.values()),name=filePath)
            for message in self.messages.values():
                message.session = session
        
        # if requested, we merge consecutive messages that share same source and destination
        if mergePacketsInFlow:
            mergedMessages = SortedTypedList(AbstractMessage)
            previousMessage = None
            for message in self.messages.values():
                if previousMessage is not None and message.source == previousMessage.source and message.destination == previousMessage.destination:
                    previousMessage.data += message.data
                else:
                    mergedMessages.add(message)
                    previousMessage = message
            self.messages = mergedMessages
            
        return self.messages

    @staticmethod
    @typeCheck(list, str, int, int, bool)
    def readFiles(filePathList, bpfFilter="", importLayer=5, nbPackets=0, mergePacketsInFlow=False):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload)
         The number of packets to capture can be specified.

        :param filePathList: a list of pcap files to read
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`        
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        importer = PCAPImporter()
        return importer.readMessages(filePathList,bpfFilter, importLayer, nbPackets, mergePacketsInFlow)

    @staticmethod
    @typeCheck(str, str, int, int, bool)
    def readFile(filePath, bpfFilter="", importLayer=5, nbPackets=0, mergePacketsInFlow=False):
        """Read all messages from the specified PCAP file. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload) and merge consecutive messages with same source and destination.
         Finally, the number of packets to capture can be specified.

        :param filePath: the pcap path
        :type filePath: :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        importer = PCAPImporter()
        return importer.readFiles([filePath], bpfFilter, importLayer,
                                  nbPackets, mergePacketsInFlow)

    @staticmethod
    @typeCheck(L2NetworkMessage)
    def getMessageDetails(message):
        """Decode a raw network message and print the content of each
        encapsulated layer.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        """

        decoder = Decoders.EthDecoder()
        return decoder.decode(
            TypeConverter.convert(message.data, HexaString, Raw))
Esempio n. 5
0
    def readMessages(self,
                     filePathList,
                     bpfFilter="",
                     importLayer=5,
                     nbPackets=0,
                     mergePacketsInFlow=False,
                    ):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload).
         Finally, the number of packets to capture can be specified.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the "
                                 + "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("PCAP", "\n".join(errorMessageList))

        # Verify the expected import layer
        availableLayers = [1, 2, 3, 4, 5]
        if importLayer not in availableLayers:
            raise Exception(
                "Only layers level {0} are available.".format(availableLayers))
        self.importLayer = importLayer

        # Call the method that does the import job for each PCAP file
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, bpfFilter, nbPackets)
            #Create a session and attribute it to messages:
            session = Session(list(self.messages.values()),name=filePath)
            for message in self.messages.values():
                message.session = session
        
        # if requested, we merge consecutive messages that share same source and destination
        if mergePacketsInFlow:
            mergedMessages = SortedTypedList(AbstractMessage)
            previousMessage = None
            for message in self.messages.values():
                if previousMessage is not None and message.source == previousMessage.source and message.destination == previousMessage.destination:
                    previousMessage.data += message.data
                else:
                    mergedMessages.add(message)
                    previousMessage = message
            self.messages = mergedMessages
            
        return self.messages
Esempio n. 6
0
 def clearEncodingFunctions(self):
     """Remove all the encoding functions attached to the current element"""
     self.__encodingFunctions = SortedTypedList(EncodingFunction)
     for child in self.fields:
         child.clearEncodingFunctions()
Esempio n. 7
0
class AbstractField(AbstractMementoCreator, metaclass=abc.ABCMeta):
    """Represents all the different classes which participates in fields definitions of a message format."""
    def __init__(self, name=None):
        self.id = uuid.uuid4()
        self.name = name
        self.description = ""

        self.__fields = TypedList(AbstractField)
        self.__parent = None

        self.__encodingFunctions = SortedTypedList(EncodingFunction)
        self.__visualizationFunctions = TypedList(VisualizationFunction)
        self.__transformationFunctions = TypedList(TransformationFunction)

        self._variable = None

    @typeCheck(bool, bool, bool)
    def getCells(self, encoded=True, styled=True, transposed=False):
        """Returns a matrix with a different line for each messages attached to the symbol of the current element.

        The matrix includes a different column for each leaf children of the current element.
        In each cell, the slices of messages once aligned.
        Attached :class:`EncodingFunction` can also be considered if parameter encoded is set to True.
        In addition, visualizationFunctions are also applied if parameter styled is set to True.
        If parameter Transposed is set to True, the matrix is built with rows for fields and columns for messages.

        >>> from netzob.all import *
        >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
        >>> fh1 = Field(ASCII("hello "), name="hello")
        >>> fh2 = Field(Alt([ASCII("netzob"), ASCII("zoby"), ASCII("lapy"), ASCII("sygus")]), name="pseudo")
        >>> fheader = Field(name="header")
        >>> fheader.fields = [fh1, fh2]
        >>> fb1 = Field(ASCII(", what's up in "), name="whatsup")
        >>> fb2 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> fb3 = Field(" ?", name="end")
        >>> fbody = Field(name="body")
        >>> fbody.fields = [fb1, fb2, fb3]
        >>> symbol = Symbol([fheader, fbody], messages=messages)

        >>> print(symbol)
        hello    | pseudo   | whatsup           | city       | end 
        -------- | -------- | ----------------- | ---------- | ----
        'hello ' | 'netzob' | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'New-York' | ' ?'
        -------- | -------- | ----------------- | ---------- | ----

        >>> fh1.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> fb2.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> print(symbol)
        hello          | pseudo   | whatsup           | city               | end 
        -------------- | -------- | ----------------- | ------------------ | ----
        '68656c6c6f20' | 'netzob' | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'netzob' | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'netzob' | ", what's up in " | '4e65772d596f726b' | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '4e65772d596f726b' | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '4e65772d596f726b' | ' ?'
        -------------- | -------- | ----------------- | ------------------ | ----

        >>> print(fheader.getCells()) 
        Field          | Field   
        -------------- | --------
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'lapy'  
        '68656c6c6f20' | 'lapy'  
        '68656c6c6f20' | 'lapy'  
        -------------- | --------

        >>> print(fh1.getCells())
        Field         
        --------------
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        --------------

        >>> print(fh2.getCells())
        Field   
        --------
        'netzob'
        'netzob'
        'netzob'
        'zoby'  
        'zoby'  
        'zoby'  
        'lapy'  
        'lapy'  
        'lapy'  
        --------

        >>> print(fbody.getCells())
        Field             | Field              | Field
        ----------------- | ------------------ | -----
        ", what's up in " | '5061726973'       | ' ?' 
        ", what's up in " | '4265726c696e'     | ' ?' 
        ", what's up in " | '4e65772d596f726b' | ' ?' 
        ", what's up in " | '5061726973'       | ' ?' 
        ", what's up in " | '4265726c696e'     | ' ?' 
        ", what's up in " | '4e65772d596f726b' | ' ?' 
        ", what's up in " | '5061726973'       | ' ?' 
        ", what's up in " | '4265726c696e'     | ' ?' 
        ", what's up in " | '4e65772d596f726b' | ' ?' 
        ----------------- | ------------------ | -----

        >>> print(fb1.getCells())
        Field            
        -----------------
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        -----------------

        >>> print(fb2.getCells())
        Field             
        ------------------
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'
        ------------------

        >>> print(fb3.getCells())
        Field
        -----
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        ' ?' 
        -----

        :keyword encoded: if set to True, encoding functions are applied on returned cells
        :type encoded: :class:`bool`
        :keyword styled: if set to True, visualization functions are applied on returned cells
        :type styled: :class:`bool`
        :keyword transposed: is set to True, the returned matrix is transposed (1 line for each field)
        :type transposed: :class:`bool`

        :return: a matrix representing the aligned messages following fields definitions.
        :rtype: a :class:`netzob.Common.Utils.MatrixList.MatrixList`
        :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages
        """

        if len(self.messages) < 1:
            raise ValueError("This symbol does not contain any message.")

        # Fetch all the data to align
        data = [message.data for message in self.messages]

        # [DEBUG] set to false for debug only. A sequential alignment is more simple to debug
        useParallelAlignment = False

        if useParallelAlignment:
            # Execute a parallel alignment
            from netzob.Common.Utils.DataAlignment.ParallelDataAlignment import ParallelDataAlignment
            return ParallelDataAlignment.align(data, self, encoded=encoded)
        else:
            # Execute a sequential alignment
            from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment
            return DataAlignment.align(data, self, encoded=encoded)

    @typeCheck(bool, bool)
    def getValues(self, encoded=True, styled=True):
        """Returns all the values the current element can take following messages attached to the symbol of current element.

        Specific encodingFunctions can also be considered if parameter encoded is set to True.
        In addition, visualizationFunctions are also applied if parameter styled is set to True.

        >>> from netzob.all import *
        >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
        >>> f1 = Field("hello ", name="hello")
        >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f3 = Field(", what's up in ", name="whatsup")
        >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f5 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages)
        >>> print(symbol)
        hello    | pseudo   | whatsup           | city       | end 
        -------- | -------- | ----------------- | ---------- | ----
        'hello ' | 'netzob' | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'New-York' | ' ?'
        -------- | -------- | ----------------- | ---------- | ----

        >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> print(symbol)
        hello          | pseudo         | whatsup                          | city               | end   
        -------------- | -------------- | -------------------------------- | ------------------ | ------
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'
        -------------- | -------------- | -------------------------------- | ------------------ | ------

        >>> print(symbol.getValues())
        [b'68656c6c6f206e65747a6f622c2077686174277320757020696e205061726973203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f207a6f62792c2077686174277320757020696e205061726973203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f206c6170792c2077686174277320757020696e205061726973203f', b'68656c6c6f206c6170792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206c6170792c2077686174277320757020696e204e65772d596f726b203f']
        >>> print(f1.getValues())
        [b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20']
        >>> print(f2.getValues())
        [b'6e65747a6f62', b'6e65747a6f62', b'6e65747a6f62', b'7a6f6279', b'7a6f6279', b'7a6f6279', b'6c617079', b'6c617079', b'6c617079']
        >>> print(f3.getValues())
        [b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20']
        >>> print(f4.getValues())
        [b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b']
        >>> print(f5.getValues())
        [b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f']

        :keyword encoded: if set to True, encoding functions are applied on returned cells
        :type encoded: :class:`bool`
        :keyword styled: if set to True, visualization functions are applied on returned cells
        :type styled: :class:`bool`

        :return: a list detailling all the values current element takes.
        :rtype: a :class:`list` of :class:`str`
        :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages
        """
        cells = self.getCells(encoded=encoded, styled=styled)
        values = []
        for line in cells:
            values.append(b''.join(line))
        return values

    @typeCheck(bool, bool)
    def getMessageCells(self, encoded=False, styled=False):
        """Computes and returns the alignment of each message belonging to
        the current field as proposed by getCells() method but indexed
        per message.

        >>> from netzob.all import *
        >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]
        >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f2 = Field(", what's up in ", name="whatsup")
        >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f4 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4], messages=messages)
        >>> print(symbol)
        pseudo   | whatsup           | city     | end 
        -------- | ----------------- | -------- | ----
        'netzob' | ", what's up in " | 'Paris'  | ' ?'
        'netzob' | ", what's up in " | 'Berlin' | ' ?'
        'zoby'   | ", what's up in " | 'Paris'  | ' ?'
        'zoby'   | ", what's up in " | 'Berlin' | ' ?'
        -------- | ----------------- | -------- | ----

        >>> messageCells = symbol.getMessageCells()
        >>> for message in symbol.messages:
        ...    print(message.data, messageCells[message])
        netzob, what's up in Paris ? [b'netzob', b", what's up in ", b'Paris', b' ?']
        netzob, what's up in Berlin ? [b'netzob', b", what's up in ", b'Berlin', b' ?']
        zoby, what's up in Paris ? [b'zoby', b", what's up in ", b'Paris', b' ?']
        zoby, what's up in Berlin ? [b'zoby', b", what's up in ", b'Berlin', b' ?']

        :keyword encoded: if set to true, values are encoded
        :type encoded: :class:`bool`
        :keyword styled: if set to true, values are styled
        :type styled: :class:`bool`
        :return: a dict indexed by messages that denotes their cells
        :rtype: a :class:`dict`

        """
        if encoded is None:
            raise TypeError("Encoded cannot be None")
        if styled is None:
            raise TypeError("Styled cannot be None")

        result = OrderedDict()
        fieldCells = self.getCells(encoded=encoded, styled=styled)

        for iMessage, message in enumerate(self.messages):
            result[message] = fieldCells[iMessage]

        return result

    @typeCheck(bool, bool)
    def getMessageValues(self, encoded=False, styled=False):
        """Computes and returns the alignment of each message belonging to
        the current field as proposed by getValues() method but indexed
        per message.

        >>> from netzob.all import *
        >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]
        >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f2 = Field(", what's up in ", name="whatsup")
        >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f4 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4], messages=messages)
        >>> print(symbol)
        pseudo   | whatsup           | city     | end 
        -------- | ----------------- | -------- | ----
        'netzob' | ", what's up in " | 'Paris'  | ' ?'
        'netzob' | ", what's up in " | 'Berlin' | ' ?'
        'zoby'   | ", what's up in " | 'Paris'  | ' ?'
        'zoby'   | ", what's up in " | 'Berlin' | ' ?'
        -------- | ----------------- | -------- | ----

        >>> messageValues = f3.getMessageValues()
        >>> for message in symbol.messages:
        ...    print(message.data, messageValues[message])
        netzob, what's up in Paris ? b'Paris'
        netzob, what's up in Berlin ? b'Berlin'
        zoby, what's up in Paris ? b'Paris'
        zoby, what's up in Berlin ? b'Berlin'

        :keyword encoded: if set to true, values are encoded
        :type encoded: :class:`bool`
        :keyword styled: if set to true, values are styled
        :type styled: :class:`bool`
        :return: a dict indexed by messages that denotes their values
        :rtype: a :class:`dict`

        """
        if encoded is None:
            raise TypeError("Encoded cannot be None")
        if styled is None:
            raise TypeError("Styled cannot be None")

        result = OrderedDict()
        fieldValues = self.getValues(encoded=encoded, styled=styled)

        for iMessage, message in enumerate(self.messages):
            result[message] = fieldValues[iMessage]

        return result

    # def getMessagesWithValue(self, value):
    #     """Computes and returns the messages that have a specified value
    #     in the current field.

    #     >>> from netzob.all import *
    #     >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
    #     >>> f1 = Field("hello ", name="hello")
    #     >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
    #     >>> f3 = Field(", what's up in ", name="whatsup")
    #     >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city")
    #     >>> f5 = Field(" ?", name="end")
    #     >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages)
    #     >>> print(symbol.specialize())
    #     >>> print(symbol)
    #     hello  | netzob | , what's up in  | Paris    |  ?
    #     hello  | netzob | , what's up in  | Berlin   |  ?
    #     hello  | netzob | , what's up in  | New-York |  ?
    #     hello  | zoby   | , what's up in  | Paris    |  ?
    #     hello  | zoby   | , what's up in  | Berlin   |  ?
    #     hello  | zoby   | , what's up in  | New-York |  ?
    #     hello  | lapy   | , what's up in  | Paris    |  ?
    #     hello  | lapy   | , what's up in  | Berlin   |  ?
    #     hello  | lapy   | , what's up in  | New-York |  ?
    #     >>> lapySymbol = Symbol(messages=symbol.fields[1].getMessagesWithValue("lapy"))
    #     >>> print(lapySymbol)
    #     hello lapy, what's up in Paris ?
    #     hello lapy, what's up in Berlin ?
    #     hello lapy, what's up in New-York ?
    #     >>> Format.splitStatic(lapySymbol)
    #     >>> lapySymbol.encodingFunctions.add(TypeEncodingFunction(HexaString))
    #     >>> print(lapySymbol)
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 5061726973203f
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 4265726c696e203f
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 4e65772d596f726b203f

    #     :parameter value: a Raw value
    #     :type value: :class:`object`
    #     :return: a list of messages
    #     :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
    #     """

    #     if value is None:
    #         raise TypeError("Value cannot be None")

    #     fieldValues = self.getValues(encoded=False, styled=False)
    #     result = []
    #     for i_message, message in enumerate(self.messages):
    #         if fieldValues[i_message] == value:
    #             result.append(message)
    #     return result

    @abc.abstractmethod
    def specialize(self, mutator=None):
        """Specialize and generate a :class:`netzob.Model.Vocabulary.Messages.RawMessage` which content
        follows the fields definitions attached to current element.

        :keyword mutator: if set, the mutator will be used to mutate the fields definitions
        :type mutator: :class:`netzob.Model.Mutators.AbstractMutator`

        :return: a generated content represented with an hexastring
        :rtype: :class:`str`
        :raises: :class:`netzob.Model.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message
        """
        return

    @staticmethod
    def abstract(data, fields):
        """Search in the fields/symbols the first one that can abstract the data.

        >>> from netzob.all import *
        >>> messages = ["{0}, what's up in {1} ?".format(pseudo, city) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]

        >>> f1a = Field(name="name", domain="netzob")
        >>> f2a = Field(name="question", domain=", what's up in ")
        >>> f3a = Field(name="city", domain=Alt(["Paris", "Berlin"]))
        >>> f4a = Field(name="mark", domain=" ?")
        >>> s1 = Symbol([f1a, f2a, f3a, f4a], name="Symbol-netzob")

        >>> f1b = Field(name="name", domain="zoby")
        >>> f2b = Field(name="question", domain=", what's up in ")
        >>> f3b = Field(name="city", domain=Alt(["Paris", "Berlin"]))
        >>> f4b = Field(name="mark", domain=" ?")
        >>> s2 = Symbol([f1b, f2b, f3b, f4b], name="Symbol-zoby")

        >>> for m in messages:
        ...    (abstractedSymbol, structured_data) = AbstractField.abstract(m, [s1, s2])
        ...    print(structured_data)
        ...    print(abstractedSymbol.name)
        OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')])
        Symbol-netzob
        OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')])
        Symbol-netzob
        OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')])
        Symbol-zoby
        OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')])
        Symbol-zoby

        :parameter data: the data that should be abstracted in symbol
        :type data: :class:`str`
        :parameter fields: a list of fields/symbols targeted during the abstraction process
        :type fields: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField`

        :return: a field/symbol and the structured received message
        :rtype: a tuple (:class:`netzob.Model.Vocabulary.AbstractField`, dict)
        :raises: :class:`netzob.Model.Vocabulary.AbstractField.AbstractionException` if an error occurs while abstracting the data
        """
        from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment
        for field in fields:
            try:
                # Try to align/parse the data with the current field
                alignedData = DataAlignment.align([data], field, encoded=False)

                # If it matches, we build a dict that contains, for each field, the associated value that was present in the message
                structured_data = OrderedDict()
                for fields_value in alignedData:
                    for i, field_value in enumerate(fields_value):
                        structured_data[alignedData.headers[i]] = field_value
                return (field, structured_data)
            except:
                pass

        from netzob.Model.Vocabulary.UnknownSymbol import UnknownSymbol
        from netzob.Model.Vocabulary.Messages.RawMessage import RawMessage
        unknown_symbol = UnknownSymbol(RawMessage(data))
        structured_data = OrderedDict()
        logging.error(
            "Impossible to abstract the message in one of the specified symbols, we create an unknown symbol for it: '%s'",
            unknown_symbol)

        return (unknown_symbol, structured_data)

    def getSymbol(self):
        """Computes the symbol to which this field is attached.

        To retrieve it, this method recursively call the parent of the current object until the root is found.
        If the last root is not a :class:`netzob.Model.Vocabulary.Symbol`, it raises an Exception.

        :returns: the symbol if available
        :type: :class:`netzob.Model.Vocabulary.Symbol`
        :raises: :class:`netzob.Model.Vocabulary.AbstractField.NoSymbolException`
        """
        from netzob.Model.Vocabulary.Symbol import Symbol
        if isinstance(self, Symbol):
            return self
        elif self.hasParent():
            return self.parent.getSymbol()
        else:
            raise NoSymbolException(
                "Impossible to retrieve the symbol attached to this element")

    def getLeafFields(self,
                      depth=None,
                      currentDepth=0,
                      includePseudoFields=False):
        """Extract the leaf fields to consider regarding the specified depth

        >>> from netzob.all import *
        >>> field = Field("hello", name="F0")
        >>> print([f.name for f in field.getLeafFields()])
        ['F0']

        >>> field = Field(name="L0")
        >>> headerField = Field(name="L0_header")
        >>> payloadField = Field(name="L0_payload")
        >>> footerField = Field(name="L0_footer")

        >>> fieldL1 = Field(name="L1")
        >>> fieldL1_header = Field(name="L1_header")
        >>> fieldL1_payload = Field(name="L1_payload")
        >>> fieldL1.fields = [fieldL1_header, fieldL1_payload]

        >>> payloadField.fields = [fieldL1]
        >>> field.fields = [headerField, payloadField, footerField]

        >>> print([f.name for f in field.getLeafFields(depth=None)])
        ['L0_header', 'L1_header', 'L1_payload', 'L0_footer']

        >>> print([f.name for f in field.getLeafFields(depth=0)])
        ['L0']

        >>> print([f.name for f in field.getLeafFields(depth=1)])
        ['L0_header', 'L0_payload', 'L0_footer']

        >>> print([f.name for f in field.getLeafFields(depth=2)])
        ['L0_header', 'L1', 'L0_footer']

        :return: the list of leaf fields
        :rtype: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField.AbstractField`.
        """
        if currentDepth is None:
            currentDepth = 0

        if len(self.fields) == 0:
            return [self]

        if currentDepth == depth:
            return [self]

        leafFields = []
        for fields in self.fields:

            # Handle case where the field is pseudo (meaning it does not procude concrete value)
            if fields.isPseudoField:
                if includePseudoFields:
                    pass
                else:
                    continue
            if fields is not None:
                leafFields.extend(
                    fields.getLeafFields(depth, currentDepth + 1,
                                         includePseudoFields))

        return leafFields

    def hasParent(self):
        """Computes if the current element has a parent.

        :returns: True if current element has a parent.
        :rtype: :class:`bool`
        """
        return self.__parent is not None

    def clearFields(self):
        """Remove all the children attached to the current element"""

        while (len(self.__fields) > 0):
            self.__fields.pop()

    def clearEncodingFunctions(self):
        """Remove all the encoding functions attached to the current element"""
        self.__encodingFunctions.clear()
        for child in self.fields:
            child.clearEncodingFunctions()

    def clearVisualizationFunctions(self):
        """Remove all the visualization functions attached to the current element"""

        while (len(self.__visualizationFunctions) > 0):
            self.__visualizationFunctions.pop()

    def clearTransformationFunctions(self):
        """Remove all the transformation functions attached to the current element"""

        while (len(self.__transformationFunctions) > 0):
            self.__transformationFunctions.pop()

    # Standard methods
    def __str__(self):
        result = self.getCells(encoded=True)
        return str(result)

    @typeCheck(int)
    def _str_debug(self, deepness=0):
        """Returns a string which denotes
        the current field definition using a tree display"""

        tab = ["|--  " for x in range(deepness)]
        tab.append(str(self.name))
        lines = [''.join(tab)]
        from netzob.Model.Vocabulary.Field import Field
        if isinstance(self, Field):
            lines.append(self.domain._str_debug(deepness + 1))
        for f in self.fields:
            lines.append(f._str_debug(deepness + 1))
        return '\n'.join(lines)

    # PROPERTIES

    @property
    def id(self):
        """Unique identifier of the field.

        This value must be a unique UUID instance (generated with uuid.uuid4()).

        :type: :class:`uuid.UUID`
        :raises: :class:`TypeError`, :class:`ValueError`
        """

        return self.__id

    @id.setter
    @typeCheck(uuid.UUID)
    def id(self, id):
        if id is None:
            raise ValueError("id is Mandatory.")
        self.__id = id

    @property
    def name(self):
        """Public name (may not be unique), default value is None

        :type: :class:`str`
        :raises: :class:`TypeError`
        """

        return self.__name

    @name.setter
    @typeCheck(str)
    def name(self, name):
        self.__name = name

    @property
    def description(self):
        """User description of the field. Default value is ''.

        :type: :class:`str`
        :raises: :class:`TypeError`
        """

        return self.__description

    @description.setter
    @typeCheck(str)
    def description(self, description):
        self.__description = description

    @property
    def encodingFunctions(self):
        """Sorted typed list of encoding function to attach on field.

        .. note:: list implemented as a :class:`netzob.Common.Utils.TypedList.TypedList`

        :type: a list of :class:`netzob.Model.Vocabulary.Functions.EncodingFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """
        return self.__encodingFunctions

    @encodingFunctions.setter
    def encodingFunctions(self, encodingFunctions):
        self.clearEncodingFunctions()
        for encodingFunction in encodingFunctions:
            self.addEncodingFunction(encodingFunction)

    def addEncodingFunction(self, encodingFunction):
        self.encodingFunctions.add(encodingFunction)
        for child in self.fields:
            child.addEncodingFunction(encodingFunction)

    @property
    def visualizationFunctions(self):
        """Sorted list of visualization function to attach on field.

        :type: a list of :class:`netzob.Model.Vocabulary.Functions.VisualizationFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """

        return self.__visualizationFunctions

    @visualizationFunctions.setter
    def visualizationFunctions(self, visualizationFunctions):
        self.clearVisualizationFunctions()
        self.visualizationFunctions.extend(visualizationFunctions)

    @property
    def transformationFunctions(self):
        """Sorted list of transformation function to attach on field.

        :type: a list of :class:`netzob.Model.Vocabulary.Functions.TransformationFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """

        return self.__transformationFunctions

    @transformationFunctions.setter
    def transformationFunctions(self, transformationFunctions):
        self.clearTransformationFunctions()
        self.transformationFunctions.extend(transformationFunctions)

    @property
    def fields(self):
        """Sorted list of field fields."""

        return self.__fields

    @fields.setter
    def fields(self, fields):
        from netzob.Model.Vocabulary.Field import Field
        # First it checks the specified children are abstractfiled
        if fields is not None:
            for c in fields:
                if not isinstance(c, Field):
                    raise TypeError(
                        "Cannot edit the fields because at least one specified element is not an AbstractField its a {0}."
                        .format(type(c)))

        self.clearFields()
        if fields is not None:
            for c in fields:
                c.parent = self
                self.__fields.append(c)

    @property
    def parent(self):
        """The parent of this current element.

        If current element has no parent, its value is **None**.

        :type: a :class:`netzob.Model.Vocabulary.AbstractField.AbstractField`
        :raises: :class:`TypeError`
        """

        return self.__parent

    @parent.setter
    def parent(self, parent):
        if not isinstance(parent, AbstractField):
            raise TypeError(
                "Specified parent must be an AbstractField and not an {0}".
                format(type(parent)))
        self.__parent = parent

    def storeInMemento(self):
        pass

    def restoreFromMemento(self, memento):
        pass
Esempio n. 8
0
class Session(object):
    """A session includes messages exchanged in the same session. Messages
    are automaticaly sorted.
    Applicative data can be attached to sessions.


    >>> import time
    >>> from netzob.all import *
    >>> # we create 3 messages
    >>> msg1 = RawMessage("ACK", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:05", "%d %b %y %H:%M:%S")))
    >>> msg2 = RawMessage("SYN", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:01", "%d %b %y %H:%M:%S")))
    >>> msg3 = RawMessage("SYN/ACK", source="B", destination="A", date=time.mktime(time.strptime("9 Aug 13 10:45:03", "%d %b %y %H:%M:%S")))
    >>> session = Session([msg1, msg2, msg3])
    >>> print(session.messages.values()[0].data)
    SYN
    >>> print(session.messages.values()[1].data)
    SYN/ACK
    >>> print(session.messages.values()[2].data)
    ACK

    """
    def __init__(self,
                 messages=None,
                 _id=None,
                 applicativeData=None,
                 name="Session"):
        """
        :parameter messages: the messages exchanged in the current session
        :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
        :parameter _id: the unique identifier of the session
        :type _id: :class:`uuid.UUID`
        :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData`
        """
        self.__messages = SortedTypedList(AbstractMessage)
        self.__applicativeData = TypedList(ApplicativeData)

        if messages is None:
            messages = []
        self.messages = messages
        if _id is None:
            _id = uuid.uuid4()
        self.id = _id
        if applicativeData is None:
            applicativeData = []
        self.applicativeData = applicativeData
        self.name = name

    @property
    def id(self):
        """The unique identifier of the session.

        :type: :class:`uuid.UUID`
        """
        return self.__id

    @id.setter
    @typeCheck(uuid.UUID)
    def id(self, _id):
        if _id is None:
            raise TypeError("Id cannot be None")
        self.__id = _id

    @property
    def messages(self):
        """The messages exchanged in the current session.
        Messages are sorted.

        :type: a :class:`netzob.Common.Utils.TypedList.TypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
        """
        return self.__messages

    def clearMessages(self):
        """Delete all the messages attached to the current session"""
        for msg in list(self.__messages.values()):
            msg.session = None

        self.__messages.clear()

    @messages.setter
    def messages(self, messages):
        if messages is None:
            messages = []

        # First it checks the specified messages are all AbstractMessages
        for msg in messages:
            if not isinstance(msg, AbstractMessage):
                raise TypeError(
                    "Cannot add messages of type {0} in the session, only AbstractMessages are allowed."
                    .format(type(msg)))

        self.clearMessages()
        for message in messages:
            self.__messages.add(message)
            message.session = self

    @property
    def applicativeData(self):
        """Applicative data attached to the current session.

        >>> from netzob.all import *
        >>> appData = ApplicativeData("test", Integer(20))
        >>> session = Session(applicativeData=[appData])
        >>> print(len(session.applicativeData))
        1
        >>> appData2 = ApplicativeData("test2", ASCII("helloworld"))
        >>> session.applicativeData.append(appData2)
        >>> print(len(session.applicativeData))
        2
        >>> print(session.applicativeData[0])
        Applicative Data: test=Integer=20 ((8, 8)))
        >>> print(session.applicativeData[1])
        Applicative Data: test2=ASCII=helloworld ((0, 80)))

        :type: a list of :class:`netzob.Model.Vocabulary.ApplicativeData.ApplicativeData`.
        """
        return self.__applicativeData

    def clearApplicativeData(self):
        while (len(self.__applicativeData) > 0):
            self.__applicativeData.pop()

    @applicativeData.setter
    def applicativeData(self, applicativeData):
        for app in applicativeData:
            if not isinstance(app, ApplicativeData):
                raise TypeError(
                    "Cannot add an applicative data with type {0}, only ApplicativeData accepted."
                    .format(type(app)))
        self.clearApplicativeData()
        for app in applicativeData:
            self.applicativeData.append(app)

    @property
    def name(self):
        return self.__name

    @name.setter
    @typeCheck(str)
    def name(self, _name):
        if _name is None:
            raise TypeError("Name cannot be None")
        self.__name = _name

    def getEndpointsList(self):
        """Retrieve all the endpoints couples that are present in the
        session.

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="C")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(len(session.getEndpointsList()))
        2
        >>> print(session.getEndpointsList())
        [('A', 'B'), ('A', 'C')]

        :return: a list containing couple of endpoints (src, dst).
        :rtype: a :class:`list`

        """

        endpointsList = []
        for message in list(self.messages.values()):
            src = message.source
            dst = message.destination
            endpoints1 = (src, dst)
            endpoints2 = (dst, src)
            if (not endpoints1 in endpointsList) and (not endpoints2
                                                      in endpointsList):
                endpointsList.append(endpoints1)
        return endpointsList

    def getTrueSessions(self):
        """Retrieve the true sessions embedded in the current
        session. A session is here characterized by a uniq endpoints
        couple.

        TODO: a more precise solution would be to use flow
        reconstruction (as in TCP).

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="C")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(len(session.getTrueSessions()))
        2
        >>> for trueSession in session.getTrueSessions():
        ...    print(trueSession.name)
        Session: 'A' - 'B'
        Session: 'A' - 'C'

        :return: a list containing true sessions embedded in the current session.
        :rtype: a :class:`list`

        """

        trueSessions = []
        for endpoints in self.getEndpointsList():
            trueSessionMessages = []
            src = None
            dst = None
            for message in list(self.messages.values()):
                if message.source in endpoints and message.destination in endpoints:
                    trueSessionMessages.append(message)
                    if src is None:
                        src = message.source
                    if dst is None:
                        dst = message.destination
            trueSession = Session(messages=trueSessionMessages,
                                  applicativeData=self.applicativeData,
                                  name="Session: '" + str(src) + "' - '" +
                                  str(dst) + "'")
            trueSessions.append(trueSession)
        return trueSessions

    def isTrueSession(self):
        """Tell if the current session is true. A session is said to
        be true if the communication flow pertain to a uniq
        applicative session between a couple of endpoints.

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="B")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(session.isTrueSession())
        True

        :return: a boolean telling if the current session is a true one (i.e. it corresponds to a uniq applicative session between two endpoints).
        :rtype: a :class:`bool`

        """

        if len(self.getTrueSessions()) == 1:
            return True
        else:
            return False

    @typeCheck(list)
    def abstract(self, symbolList):
        """This method abstract each message of the current session
        into symbols according to a list of symbols given as
        parameter.

        >>> from netzob.all import *
        >>> symbolSYN = Symbol([Field(ASCII("SYN"))], name="Symbol_SYN")
        >>> symbolSYNACK = Symbol([Field(ASCII("SYN/ACK"))], name="Symbol_SYNACK")
        >>> symbolACK = Symbol([Field(ASCII("ACK"))], name="Symbol_ACK")
        >>> symbolList = [symbolSYN, symbolSYNACK, symbolACK]

        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="B")

        >>> session = Session([msg1, msg2, msg3])
        >>> if session.isTrueSession():
        ...    for src, dst, sym in session.abstract(symbolList):
        ...        print(str(src) + " - " + str(dst) + " : " + str(sym.name))
        A - B : Symbol_SYN
        B - A : Symbol_SYNACK
        A - B : Symbol_ACK

        :return: a list of tuples containing the following elements : (source, destination, symbol).
        :rtype: a :class:`list`

        """

        abstractSession = []
        if not self.isTrueSession():
            self._logger.warn(
                "The current session cannot be abstracted as it not a true session (i.e. it may contain inner true sessions)."
            )
            return abstractSession
        for message in list(self.messages.values()):
            symbol = AbstractField.abstract(message.data, symbolList)
            abstractSession.append(
                (message.source, message.destination, symbol))
        return abstractSession
Esempio n. 9
0
class Session(object):
    """A session includes messages exchanged in the same session. Messages
    are automaticaly sorted.
    Applicative data can be attached to sessions.


    >>> import time
    >>> from netzob.all import *
    >>> # we create 3 messages
    >>> msg1 = RawMessage("ACK", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:05", "%d %b %y %H:%M:%S")))
    >>> msg2 = RawMessage("SYN", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:01", "%d %b %y %H:%M:%S")))
    >>> msg3 = RawMessage("SYN/ACK", source="B", destination="A", date=time.mktime(time.strptime("9 Aug 13 10:45:03", "%d %b %y %H:%M:%S")))
    >>> session = Session([msg1, msg2, msg3])
    >>> print(session.messages.values()[0].data)
    SYN
    >>> print(session.messages.values()[1].data)
    SYN/ACK
    >>> print(session.messages.values()[2].data)
    ACK

    """

    def __init__(self,
                 messages=None,
                 _id=None,
                 applicativeData=None,
                 name="Session"):
        """
        :parameter messages: the messages exchanged in the current session
        :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
        :parameter _id: the unique identifier of the session
        :type _id: :class:`uuid.UUID`
        :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData`
        """
        self.__messages = SortedTypedList(AbstractMessage)
        self.__applicativeData = TypedList(ApplicativeData)

        if messages is None:
            messages = []
        self.messages = messages
        if _id is None:
            _id = uuid.uuid4()
        self.id = _id
        if applicativeData is None:
            applicativeData = []
        self.applicativeData = applicativeData
        self.name = name

    @property
    def id(self):
        """The unique identifier of the session.

        :type: :class:`uuid.UUID`
        """
        return self.__id

    @id.setter
    @typeCheck(uuid.UUID)
    def id(self, _id):
        if _id is None:
            raise TypeError("Id cannot be None")
        self.__id = _id

    @property
    def messages(self):
        """The messages exchanged in the current session.
        Messages are sorted.

        :type: a :class:`netzob.Common.Utils.TypedList.TypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage`
        """
        return self.__messages

    def clearMessages(self):
        """Delete all the messages attached to the current session"""
        for msg in list(self.__messages.values()):
            msg.session = None

        self.__messages.clear()

    @messages.setter
    def messages(self, messages):
        if messages is None:
            messages = []

        # First it checks the specified messages are all AbstractMessages
        for msg in messages:
            if not isinstance(msg, AbstractMessage):
                raise TypeError(
                    "Cannot add messages of type {0} in the session, only AbstractMessages are allowed.".
                    format(type(msg)))

        self.clearMessages()
        for message in messages:
            self.__messages.add(message)
            message.session = self

    @property
    def applicativeData(self):
        """Applicative data attached to the current session.

        >>> from netzob.all import *
        >>> appData = ApplicativeData("test", Integer(20))
        >>> session = Session(applicativeData=[appData])
        >>> print(len(session.applicativeData))
        1
        >>> appData2 = ApplicativeData("test2", ASCII("helloworld"))
        >>> session.applicativeData.append(appData2)
        >>> print(len(session.applicativeData))
        2
        >>> print(session.applicativeData[0])
        Applicative Data: test=Integer=20 ((8, 8)))
        >>> print(session.applicativeData[1])
        Applicative Data: test2=ASCII=helloworld ((0, 80)))

        :type: a list of :class:`netzob.Model.Vocabulary.ApplicativeData.ApplicativeData`.
        """
        return self.__applicativeData

    def clearApplicativeData(self):
        while (len(self.__applicativeData) > 0):
            self.__applicativeData.pop()

    @applicativeData.setter
    def applicativeData(self, applicativeData):
        for app in applicativeData:
            if not isinstance(app, ApplicativeData):
                raise TypeError(
                    "Cannot add an applicative data with type {0}, only ApplicativeData accepted.".
                    format(type(app)))
        self.clearApplicativeData()
        for app in applicativeData:
            self.applicativeData.append(app)

    @property
    def name(self):
        return self.__name

    @name.setter
    @typeCheck(str)
    def name(self, _name):
        if _name is None:
            raise TypeError("Name cannot be None")
        self.__name = _name

    def getEndpointsList(self):
        """Retrieve all the endpoints couples that are present in the
        session.

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="C")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(len(session.getEndpointsList()))
        2
        >>> print(session.getEndpointsList())
        [('A', 'B'), ('A', 'C')]

        :return: a list containing couple of endpoints (src, dst).
        :rtype: a :class:`list`

        """

        endpointsList = []
        for message in list(self.messages.values()):
            src = message.source
            dst = message.destination
            endpoints1 = (src, dst)
            endpoints2 = (dst, src)
            if (not endpoints1 in endpointsList) and (
                    not endpoints2 in endpointsList):
                endpointsList.append(endpoints1)
        return endpointsList

    def getTrueSessions(self):
        """Retrieve the true sessions embedded in the current
        session. A session is here characterized by a uniq endpoints
        couple.

        TODO: a more precise solution would be to use flow
        reconstruction (as in TCP).

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="C")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(len(session.getTrueSessions()))
        2
        >>> for trueSession in session.getTrueSessions():
        ...    print(trueSession.name)
        Session: 'A' - 'B'
        Session: 'A' - 'C'

        :return: a list containing true sessions embedded in the current session.
        :rtype: a :class:`list`

        """

        trueSessions = []
        for endpoints in self.getEndpointsList():
            trueSessionMessages = []
            src = None
            dst = None
            for message in list(self.messages.values()):
                if message.source in endpoints and message.destination in endpoints:
                    trueSessionMessages.append(message)
                    if src is None:
                        src = message.source
                    if dst is None:
                        dst = message.destination
            trueSession = Session(
                messages=trueSessionMessages,
                applicativeData=self.applicativeData,
                name="Session: '" + str(src) + "' - '" + str(dst) + "'")
            trueSessions.append(trueSession)
        return trueSessions

    def isTrueSession(self):
        """Tell if the current session is true. A session is said to
        be true if the communication flow pertain to a uniq
        applicative session between a couple of endpoints.

        >>> from netzob.all import *
        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="B")
        >>> session = Session([msg1, msg2, msg3])
        >>> print(session.isTrueSession())
        True

        :return: a boolean telling if the current session is a true one (i.e. it corresponds to a uniq applicative session between two endpoints).
        :rtype: a :class:`bool`

        """

        if len(self.getTrueSessions()) == 1:
            return True
        else:
            return False

    @typeCheck(list)
    def abstract(self, symbolList):
        """This method abstract each message of the current session
        into symbols according to a list of symbols given as
        parameter.

        >>> from netzob.all import *
        >>> symbolSYN = Symbol([Field(ASCII("SYN"))], name="Symbol_SYN")
        >>> symbolSYNACK = Symbol([Field(ASCII("SYN/ACK"))], name="Symbol_SYNACK")
        >>> symbolACK = Symbol([Field(ASCII("ACK"))], name="Symbol_ACK")
        >>> symbolList = [symbolSYN, symbolSYNACK, symbolACK]

        >>> msg1 = RawMessage("SYN", source="A", destination="B")
        >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A")
        >>> msg3 = RawMessage("ACK", source="A", destination="B")

        >>> session = Session([msg1, msg2, msg3])
        >>> if session.isTrueSession():
        ...    for src, dst, sym in session.abstract(symbolList):
        ...        print(str(src) + " - " + str(dst) + " : " + str(sym.name))
        A - B : Symbol_SYN
        B - A : Symbol_SYNACK
        A - B : Symbol_ACK

        :return: a list of tuples containing the following elements : (source, destination, symbol).
        :rtype: a :class:`list`

        """

        abstractSession = []
        if not self.isTrueSession():
            self._logger.warn(
                "The current session cannot be abstracted as it not a true session (i.e. it may contain inner true sessions)."
            )
            return abstractSession
        for message in list(self.messages.values()):
            (symbol, structured_message) = AbstractField.abstract(message.data, symbolList)
            abstractSession.append((message.source, message.destination, symbol))
        return abstractSession
Esempio n. 10
0
class PCAPImporter(object):
    """PCAP importer to read pcaps and extract messages out of them.
    We recommend to use static methods such as
    - PCAPImporter.readFiles(...)
    - PCAPimporter.readFile(...)
    refer to their documentation to have an overview of the required parameters.

    >>> from netzob.all import *
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap").values()
    >>> print(len(messages))
    14

    >>> for m in messages:
    ...    print(repr(m.data))
    b'CMDidentify#\\x07\\x00\\x00\\x00Roberto'
    b'RESidentify#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
    b'CMDinfo#\\x00\\x00\\x00\\x00'
    b'RESinfo#\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00info'
    b'CMDstats#\\x00\\x00\\x00\\x00'
    b'RESstats#\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00stats'
    b'CMDauthentify#\\n\\x00\\x00\\x00aStrongPwd'
    b'RESauthentify#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
    b'CMDencrypt#\\x06\\x00\\x00\\x00abcdef'
    b"RESencrypt#\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00$ !&'$"
    b"CMDdecrypt#\\x06\\x00\\x00\\x00$ !&'$"
    b'RESdecrypt#\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00abcdef'
    b'CMDbye#\\x00\\x00\\x00\\x00'
    b'RESbye#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=2).values()
    >>> print(repr(messages[0].data))
    b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00E\\x00\\x003\\xdc\\x11@\\x00@\\x11`\\xa6\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=3).values()
    >>> print(repr(messages[0].data))
    b'E\\x00\\x003\\xdc\\x11@\\x00@\\x11`\\xa6\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=4).values()
    >>> print(repr(messages[0].data))
    b'\\xe1\\xe7\\x10\\x92\\x00\\x1f\\xfe2CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_udp.pcap", importLayer=5).values()
    >>> print(repr(messages[0].data))
    b'CMDidentify#\\x07\\x00\\x00\\x00Roberto'

    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http.pcap", importLayer=5, bpfFilter="tcp").values()
    >>> print(repr(messages[0].data))
    b'GET / HTTP/1.1\\r\\nHost: www.free.fr\\r\\nUser-Agent: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb)ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\\r\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\nAccept-Language: en-US,en;q=0.5\\r\\nAccept-Encoding: gzip, deflate\\r\\nConnection: keep-alive\\r\\n\\r\\n'

    Parameter `mergePacketsInFlow` can be use to merge consecutive messages that share the same source and destination (mimic a TCP flow). In practice, this parameter was introduced for L5 network messages to support TCP flows but it can be use for any level of network messages.

    >>> from netzob.all import *
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http_flow.pcap", mergePacketsInFlow=False).values()
    >>> print(len(messages))
    4
    >>> print(len(messages[1].data))
    1228
    >>> messages = PCAPImporter.readFile("./test/resources/pcaps/test_import_http_flow.pcap", mergePacketsInFlow=True).values()
    >>> print(len(messages))
    2
    >>> print(len(messages[1].data))
    3224
    """

    INVALID_BPF_FILTER = 0
    INVALID_LAYER2 = 1
    INVALID_LAYER3 = 2
    INVALID_LAYER4 = 3

    PROTOCOL201 = 201

    # Supported datalinks (by pcapy)
    SUPPORTED_DATALINKS = {
        pcapy.DLT_ARCNET: "DLT_ARCNET",
        pcapy.DLT_FDDI: "DLT_FDDI",
        pcapy.DLT_LOOP: "DLT_LOOP",
        pcapy.DLT_PPP_ETHER: "DLT_PPP_ETHER",
        pcapy.DLT_ATM_RFC1483: "DLT_ATM_RFC1483",
        pcapy.DLT_IEEE802: "DLT_IEEE802",
        pcapy.DLT_LTALK: "DLT_LTALK",
        pcapy.DLT_PPP_SERIAL: "DLT_PPP_SERIAL",
        pcapy.DLT_C_HDLC: "DLT_C_HDLC",
        pcapy.DLT_IEEE802_11: "IEEE802_11",
        pcapy.DLT_NULL: "DLT_NULL",
        pcapy.DLT_RAW: "DLT_RAW",
        pcapy.DLT_EN10MB: "DLT_EN10MB",
        pcapy.DLT_LINUX_SLL: "LINUX_SLL",
        pcapy.DLT_PPP: "DLT_PPP",
        pcapy.DLT_SLIP: "DLT_SLIP",
    }

    def __init__(self):
        pass

    @typeCheck(str, str, int)
    def __readMessagesFromFile(self, filePath, bpfFilter, nbPackets):
        """Internal methods to read all messages from a given PCAP file."""
        if (filePath is None):
            raise TypeError("filePath cannot be None")
        if (nbPackets < 0):
            raise ValueError(
                "A positive (or null) value is required for the number of packets to read."
            )

        # Check file can be opened (and read)
        try:
            fp = open(filePath, 'r')
            fp.close()
        except IOError as e:
            if e.errno == errno.EACCES:
                raise IOError(
                    "Error while trying to open the file {0}, more permissions are required to read it."
                ).format(filePath)
            else:
                raise e

        # Check (and configure) the bpf filter
        packetReader = pcapy.open_offline(filePath)
        try:
            packetReader.setfilter(bpfFilter)
        except:
            raise ValueError(
                "The provided BPF filter is not valid (it should follow the BPF format)"
            )

        # Check the datalink
        self.datalink = packetReader.datalink()
        if self.datalink not in list(PCAPImporter.SUPPORTED_DATALINKS.keys()):
            self._logger.debug("Unkown datalinks")

        if self.importLayer > 1 and self.datalink != pcapy.DLT_EN10MB and self.datalink != pcapy.DLT_LINUX_SLL and self.datalink != PCAPImporter.PROTOCOL201:
            errorMessage = _("This pcap cannot be imported since the " +
                             "layer 2 is not supported ({0})").format(
                                 str(self.datalink))
            raise NetzobImportException("PCAP", errorMessage,
                                        self.INVALID_LAYER2)
        else:
            packetReader.loop(nbPackets, self.__packetHandler)

    def __packetHandler(self, header, payload):
        """Internal callback executed on each packet when parsing the pcap"""
        (secs, usecs) = header.getts()
        epoch = secs + (usecs / 1000000.0)

        if self.importLayer == 1 or self.importLayer == 2:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2 of a packet: {0}".
                    format(e))
                return
            if len(l2Payload) == 0:
                return

            # Build the L2NetworkMessage
            l2Message = L2NetworkMessage(payload, epoch, l2Proto, l2SrcAddr,
                                         l2DstAddr)

            self.messages.add(l2Message)

        elif self.importLayer == 3:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2 and layer3 of a packet: {0}".
                    format(e))
                return

            if len(l3Payload) == 0:
                return

            # Build the L3NetworkMessage
            l3Message = L3NetworkMessage(l2Payload, epoch, l2Proto, l2SrcAddr,
                                         l2DstAddr, l3Proto, l3SrcAddr,
                                         l3DstAddr)
            self.messages.add(l3Message)

        elif self.importLayer == 4:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
                (l4Proto, l4SrcPort, l4DstPort,
                 l4Payload) = self.__decodeLayer4(ipProtocolNum, l3Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2, layer3 or layer4 of a packet: {0}".
                    format(e))
                return
            if len(l4Payload) == 0:
                return

            # Build the L4NetworkMessage
            l4Message = L4NetworkMessage(
                l3Payload, epoch, l2Proto, l2SrcAddr, l2DstAddr, l3Proto,
                l3SrcAddr, l3DstAddr, l4Proto, l4SrcPort, l4DstPort)

            self.messages.add(l4Message)

        else:
            try:
                (l2Proto, l2SrcAddr, l2DstAddr, l2Payload,
                 etherType) = self.__decodeLayer2(header, payload)
                (l3Proto, l3SrcAddr, l3DstAddr, l3Payload,
                 ipProtocolNum) = self.__decodeLayer3(etherType, l2Payload)
                (l4Proto, l4SrcPort, l4DstPort,
                 l4Payload) = self.__decodeLayer4(ipProtocolNum, l3Payload)
            except NetzobImportException as e:
                self._logger.warn(
                    "An error occured while decoding layer2, layer3, layer4 or layer5 of a packet: {0}".
                    format(e))
                return
            if len(l4Payload) == 0:
                return

            l5Message = L4NetworkMessage(
                l4Payload, epoch, l2Proto, l2SrcAddr, l2DstAddr, l3Proto,
                l3SrcAddr, l3DstAddr, l4Proto, l4SrcPort, l4DstPort)
            
            self.messages.add(l5Message)

    def __decodeLayer2(self, header, payload):
        """Internal method that parses the specified header and extracts
        layer2 related proprieties."""

        def formatMacAddress(arrayMac):
            return ":".join("{0:0>2}".format(hex(b)[2:])
                            for b in arrayMac.tolist())

        if self.datalink == pcapy.DLT_EN10MB:
            l2Decoder = Decoders.EthDecoder()
            l2Proto = "Ethernet"
            layer2 = l2Decoder.decode(payload)
            l2SrcAddr = formatMacAddress(layer2.get_ether_shost())
            l2DstAddr = formatMacAddress(layer2.get_ether_dhost())
            l2Payload = payload[layer2.get_header_size():]
            etherType = layer2.get_ether_type()
        elif self.datalink == pcapy.DLT_LINUX_SLL:
            l2Decoder = Decoders.LinuxSLLDecoder()
            l2Proto = "Linux SLL"
            layer2 = l2Decoder.decode(payload)
            l2SrcAddr = layer2.get_addr()
            l2DstAddr = None
            l2Payload = payload[layer2.get_header_size():]
            etherType = layer2.get_ether_type()
        elif self.datalink == PCAPImporter.PROTOCOL201:
            l2Proto = "Protocol 201"
            hdr = payload.encode('hex')[0:8]
            if hdr[6:] == "01":
                l2SrcAddr = "Received"
            else:
                l2SrcAddr = "Sent"
            l2DstAddr = None
            l2Payload = payload[8:]
            etherType = payload[4:6]

        return (l2Proto, l2SrcAddr, l2DstAddr, l2Payload, etherType)

    def __decodeLayer3(self, etherType, l2Payload):
        """Internal method that parses the specified header and extracts
        layer3 related proprieties."""

        if etherType == Packets.IP.ethertype:
            l3Proto = "IP"
            l3Decoder = Decoders.IPDecoder()
            layer3 = l3Decoder.decode(l2Payload)
            paddingSize = len(l2Payload) - layer3.get_ip_len()

            l3SrcAddr = layer3.get_ip_src()
            l3DstAddr = layer3.get_ip_dst()
            l3Payload = l2Payload[layer3.get_header_size():]
            if paddingSize > 0 and len(l3Payload) > paddingSize:
                l3Payload = l3Payload[:len(l3Payload) - paddingSize]
            ipProtocolNum = layer3.get_ip_p()
            return (l3Proto, l3SrcAddr, l3DstAddr, l3Payload, ipProtocolNum)
        else:
            warnMessage = _("Cannot import one of the provided packets since "
                            + "its layer 3 is unsupported (Only IP is " +
                            "currently supported, packet ethernet " +
                            "type = {0})").format(etherType)
            self._logger.warn(warnMessage)
            raise NetzobImportException("PCAP", warnMessage,
                                        self.INVALID_LAYER3)

    def __decodeLayer4(self, ipProtocolNum, l3Payload):
        """Internal method that parses the specified header and extracts
        layer4 related proprieties."""

        if ipProtocolNum == Packets.UDP.protocol:
            l4Proto = "UDP"
            l4Decoder = Decoders.UDPDecoder()
            layer4 = l4Decoder.decode(l3Payload)
            l4SrcPort = layer4.get_uh_sport()
            l4DstPort = layer4.get_uh_dport()
            l4Payload = layer4.get_data_as_string()
            return (l4Proto, l4SrcPort, l4DstPort, l4Payload)
        elif ipProtocolNum == Packets.TCP.protocol:
            l4Proto = "TCP"
            l4Decoder = Decoders.TCPDecoder()
            layer4 = l4Decoder.decode(l3Payload)
            l4SrcPort = layer4.get_th_sport()
            l4DstPort = layer4.get_th_dport()
            l4Payload = layer4.get_data_as_string()
            return (l4Proto, l4SrcPort, l4DstPort, l4Payload)
        else:
            warnMessage = _("Cannot import one of the provided packets since "
                            + "its layer 4 is unsupported (Only UDP and TCP " +
                            "are currently supported, packet IP protocol " +
                            "number = {0})").format(ipProtocolNum)
            self._logger.warn(warnMessage)
            raise NetzobImportException("PCAP", warnMessage,
                                        self.INVALID_LAYER4)

    @typeCheck(list, str, int, int, bool)
    def readMessages(self,
                     filePathList,
                     bpfFilter="",
                     importLayer=5,
                     nbPackets=0,
                     mergePacketsInFlow=False,
                    ):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload).
         Finally, the number of packets to capture can be specified.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the " +
                                 "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("PCAP", "\n".join(errorMessageList))

        # Verify the expected import layer
        availableLayers = [1, 2, 3, 4, 5]
        if not importLayer in availableLayers:
            raise Exception(
                "Only layers level {0} are available.".format(availableLayers))
        self.importLayer = importLayer

        # Call the method that does the import job for each PCAP file
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, bpfFilter, nbPackets)
        
        # if requested, we merge consecutive messages that share same source and destination
        if mergePacketsInFlow:
            mergedMessages = SortedTypedList(AbstractMessage)
            previousMessage = None
            for message in self.messages.values():
                if previousMessage is not None and message.source == previousMessage.source and message.destination == previousMessage.destination:
                    previousMessage.data += message.data
                else:
                    mergedMessages.add(message)
                    previousMessage = message
            self.messages = mergedMessages
            
        return self.messages

    @staticmethod
    @typeCheck(list, str, int, int, bool)
    def readFiles(filePathList, bpfFilter="", importLayer=5, nbPackets=0, mergePacketsInFlow=False):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload)
         The number of packets to capture can be specified.

        :param filePathList: a list of pcap files to read
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`        
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        importer = PCAPImporter()
        return importer.readMessages(filePathList,bpfFilter, importLayer, nbPackets, mergePacketsInFlow)

    @staticmethod
    @typeCheck(str, str, int, int, bool)
    def readFile(filePath, bpfFilter="", importLayer=5, nbPackets=0, mergePacketsInFlow=False):
        """Read all messages from the specified PCAP file. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload) and merge consecutive messages with same source and destination.
         Finally, the number of packets to capture can be specified.

        :param filePath: the pcap path
        :type filePath: :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        importer = PCAPImporter()
        return importer.readFiles([filePath], bpfFilter, importLayer,
                                  nbPackets, mergePacketsInFlow)

    @staticmethod
    @typeCheck(L2NetworkMessage)
    def getMessageDetails(message):
        """Decode a raw network message and print the content of each
        encapsulated layer.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        """

        decoder = Decoders.EthDecoder()
        return decoder.decode(
            TypeConverter.convert(message.data, HexaString, Raw))
Esempio n. 11
0
    def readMessages(self,
                     filePathList,
                     bpfFilter="",
                     importLayer=5,
                     nbPackets=0,
                     mergePacketsInFlow=False,
                    ):
        """Read all messages from a list of PCAP files. A BPF filter
        can be set to limit the captured packets. The layer of import
        can also be specified:
          - When layer={1, 2}, it means we want to capture a raw layer (such as Ethernet).
          - If layer=3, we capture at the network level (such as IP).
          - If layer=4, we capture at the transport layer (such as TCP or UDP).
          - If layer=5, we capture at the applicative layer (such as the TCP or UDP payload).
         Finally, the number of packets to capture can be specified.

        :param filePathList: the messages to cluster.
        :type filePathList: a list of :class:`str`
        :param bpfFilter: a string representing a BPF filter.
        :type bpfFilter: :class:`str`
        :param importLayer: an integer representing the protocol layer to start importing.
        :type importLayer: :class:`int`
        :param nbPackets: the number of packets to import
        :type nbPackets: :class:`int`
        :param mergePacketsInFlow: if True, consecutive packets with same source and destination ar merged (i.e. to mimic a flow) 
        :type mergePacketsInFlow: :class:`bool`
        :return: a list of captured messages
        :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """

        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the " +
                                 "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("PCAP", "\n".join(errorMessageList))

        # Verify the expected import layer
        availableLayers = [1, 2, 3, 4, 5]
        if not importLayer in availableLayers:
            raise Exception(
                "Only layers level {0} are available.".format(availableLayers))
        self.importLayer = importLayer

        # Call the method that does the import job for each PCAP file
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, bpfFilter, nbPackets)
        
        # if requested, we merge consecutive messages that share same source and destination
        if mergePacketsInFlow:
            mergedMessages = SortedTypedList(AbstractMessage)
            previousMessage = None
            for message in self.messages.values():
                if previousMessage is not None and message.source == previousMessage.source and message.destination == previousMessage.destination:
                    previousMessage.data += message.data
                else:
                    mergedMessages.add(message)
                    previousMessage = message
            self.messages = mergedMessages
            
        return self.messages
Esempio n. 12
0
class AbstractField(AbstractMementoCreator):
    """Represents all the different classes which participates in fields definitions of a message format."""

    __metaclass__ = abc.ABCMeta

    def __init__(self, name=None, layer=False):
        self.id = uuid.uuid4()
        self.name = name
        self.layer = layer
        self.description = ""

        self.__fields = TypedList(AbstractField)
        self.__parent = None

        self.__encodingFunctions = SortedTypedList(EncodingFunction)
        self.__visualizationFunctions = TypedList(VisualizationFunction)
        self.__transformationFunctions = TypedList(TransformationFunction)

        self._variable = None

    @typeCheck(bool, bool, bool)
    def getCells(self, encoded=True, styled=True, transposed=False):
        """Returns a matrix with a different line for each messages attached to the symbol of the current element.

        The matrix includes a different column for each leaf children of the current element.
        In each cell, the slices of messages once aligned.
        Attached :class:`EncodingFunction` can also be considered if parameter encoded is set to True.
        In addition, visualizationFunctions are also applied if parameter styled is set to True.
        If parameter Transposed is set to True, the matrix is built with rows for fields and columns for messages.

        >>> from netzob.all import *
        >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
        >>> fh1 = Field(ASCII("hello "), name="hello")
        >>> fh2 = Field(Alt([ASCII("netzob"), ASCII("zoby"), ASCII("lapy"), ASCII("sygus")]), name="pseudo")
        >>> fheader = Field(name="header")
        >>> fheader.fields = [fh1, fh2]
        >>> fb1 = Field(", what's up in ", name="whatsup")
        >>> fb2 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> fb3 = Field(" ?", name="end")
        >>> fbody = Field(name="body")
        >>> fbody.fields = [fb1, fb2, fb3]
        >>> symbol = Symbol([fheader, fbody], messages=messages)

        >>> print symbol
        'hello ' | 'netzob' | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'New-York' | ' ?'

        >>> fh1.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> fb2.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> print symbol
        '68656c6c6f20' | 'netzob' | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'netzob' | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'netzob' | ", what's up in " | '4e65772d596f726b' | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'zoby'   | ", what's up in " | '4e65772d596f726b' | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '5061726973'       | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '4265726c696e'     | ' ?'
        '68656c6c6f20' | 'lapy'   | ", what's up in " | '4e65772d596f726b' | ' ?'

        >>> print fheader.getCells()
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'netzob'
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'zoby'  
        '68656c6c6f20' | 'lapy'  
        '68656c6c6f20' | 'lapy'  
        '68656c6c6f20' | 'lapy'  

        >>> print fh1.getCells()
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'
        '68656c6c6f20'

        >>> print fh2.getCells()
        'netzob'
        'netzob'
        'netzob'
        'zoby'  
        'zoby'  
        'zoby'  
        'lapy'  
        'lapy'  
        'lapy'  

        >>> print fbody.getCells()
        ", what's up in " | '5061726973'       | ' ?'
        ", what's up in " | '4265726c696e'     | ' ?'
        ", what's up in " | '4e65772d596f726b' | ' ?'
        ", what's up in " | '5061726973'       | ' ?'
        ", what's up in " | '4265726c696e'     | ' ?'
        ", what's up in " | '4e65772d596f726b' | ' ?'
        ", what's up in " | '5061726973'       | ' ?'
        ", what's up in " | '4265726c696e'     | ' ?'
        ", what's up in " | '4e65772d596f726b' | ' ?'

        >>> print fb1.getCells()
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "
        ", what's up in "

        >>> print fb2.getCells()
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'
        '5061726973'      
        '4265726c696e'    
        '4e65772d596f726b'

        >>> print fb3.getCells()
        ' ?'
        ' ?'
        ' ?'
        ' ?'
        ' ?'
        ' ?'
        ' ?'
        ' ?'
        ' ?'

        :keyword encoded: if set to True, encoding functions are applied on returned cells
        :type encoded: :class:`bool`
        :keyword styled: if set to True, visualization functions are applied on returned cells
        :type styled: :class:`bool`
        :keyword transposed: is set to True, the returned matrix is transposed (1 line for each field)
        :type transposed: :class:`bool`

        :return: a matrix representing the aligned messages following fields definitions.
        :rtype: a :class:`netzob.Common.Utils.MatrixList.MatrixList`
        :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages
        """

        if len(self.messages) < 1:
            raise ValueError("This symbol does not contain any message.")

        # Fetch all the data to align
        data = [message.data for message in self.messages]

        # [DEBUG] set to false for debug only. A sequential alignment is more simple to debug
        useParallelAlignment = True

        if useParallelAlignment:
            # Execute a parallel alignment
            from netzob.Common.Utils.DataAlignment.ParallelDataAlignment import ParallelDataAlignment
            return ParallelDataAlignment.align(data, self, encoded=encoded)
        else:
            # Execute a sequential alignment
            from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment
            return DataAlignment.align(data, self, encoded=encoded)

    @typeCheck(bool, bool)
    def getValues(self, encoded=True, styled=True):
        """Returns all the values the current element can take following messages attached to the symbol of current element.

        Specific encodingFunctions can also be considered if parameter encoded is set to True.
        In addition, visualizationFunctions are also applied if parameter styled is set to True.

        >>> from netzob.all import *
        >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
        >>> f1 = Field("hello ", name="hello")
        >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f3 = Field(", what's up in ", name="whatsup")
        >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f5 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages)
        >>> print symbol
        'hello ' | 'netzob' | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'zoby'   | ", what's up in " | 'New-York' | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Paris'    | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'Berlin'   | ' ?'
        'hello ' | 'lapy'   | ", what's up in " | 'New-York' | ' ?'

        >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString))
        >>> print symbol
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '7a6f6279'     | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '5061726973'       | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '4265726c696e'     | '203f'
        '68656c6c6f20' | '6c617079'     | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f'

        >>> print symbol.getValues()
        ['68656c6c6f206e65747a6f622c2077686174277320757020696e205061726973203f', '68656c6c6f206e65747a6f622c2077686174277320757020696e204265726c696e203f', '68656c6c6f206e65747a6f622c2077686174277320757020696e204e65772d596f726b203f', '68656c6c6f207a6f62792c2077686174277320757020696e205061726973203f', '68656c6c6f207a6f62792c2077686174277320757020696e204265726c696e203f', '68656c6c6f207a6f62792c2077686174277320757020696e204e65772d596f726b203f', '68656c6c6f206c6170792c2077686174277320757020696e205061726973203f', '68656c6c6f206c6170792c2077686174277320757020696e204265726c696e203f', '68656c6c6f206c6170792c2077686174277320757020696e204e65772d596f726b203f']
        >>> print f1.getValues()
        ['68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20', '68656c6c6f20']
        >>> print f2.getValues()
        ['6e65747a6f62', '6e65747a6f62', '6e65747a6f62', '7a6f6279', '7a6f6279', '7a6f6279', '6c617079', '6c617079', '6c617079']
        >>> print f3.getValues()
        ['2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20', '2c2077686174277320757020696e20']
        >>> print f4.getValues()
        ['5061726973', '4265726c696e', '4e65772d596f726b', '5061726973', '4265726c696e', '4e65772d596f726b', '5061726973', '4265726c696e', '4e65772d596f726b']
        >>> print f5.getValues()
        ['203f', '203f', '203f', '203f', '203f', '203f', '203f', '203f', '203f']

        :keyword encoded: if set to True, encoding functions are applied on returned cells
        :type encoded: :class:`bool`
        :keyword styled: if set to True, visualization functions are applied on returned cells
        :type styled: :class:`bool`

        :return: a list detailling all the values current element takes.
        :rtype: a :class:`list` of :class:`str`
        :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages
        """
        cells = self.getCells(encoded=encoded, styled=styled)
        values = []
        for line in cells:
            values.append(''.join(line))
        return values

    @typeCheck(bool, bool)
    def getMessageCells(self, encoded=False, styled=False):
        """Computes and returns the alignment of each message belonging to
        the current field as proposed by getCells() method but indexed
        per message.

        >>> from netzob.all import *
        >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]
        >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f2 = Field(", what's up in ", name="whatsup")
        >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f4 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4], messages=messages)
        >>> print symbol
        'netzob' | ", what's up in " | 'Paris'  | ' ?'
        'netzob' | ", what's up in " | 'Berlin' | ' ?'
        'zoby'   | ", what's up in " | 'Paris'  | ' ?'
        'zoby'   | ", what's up in " | 'Berlin' | ' ?'

        >>> messageCells = symbol.getMessageCells()
        >>> for message in symbol.messages:
        ...    print message.data, messageCells[message]
        netzob, what's up in Paris ? ['netzob', ", what's up in ", 'Paris', ' ?']
        netzob, what's up in Berlin ? ['netzob', ", what's up in ", 'Berlin', ' ?']
        zoby, what's up in Paris ? ['zoby', ", what's up in ", 'Paris', ' ?']
        zoby, what's up in Berlin ? ['zoby', ", what's up in ", 'Berlin', ' ?']

        :keyword encoded: if set to true, values are encoded
        :type encoded: :class:`bool`
        :keyword styled: if set to true, values are styled
        :type styled: :class:`bool`
        :return: a dict indexed by messages that denotes their cells
        :rtype: a :class:`dict`

        """
        if encoded is None:
            raise TypeError("Encoded cannot be None")
        if styled is None:
            raise TypeError("Styled cannot be None")

        result = OrderedDict()
        fieldCells = self.getCells(encoded=encoded, styled=styled)

        for iMessage, message in enumerate(self.messages):
            result[message] = fieldCells[iMessage]

        return result

    @typeCheck(bool, bool)
    def getMessageValues(self, encoded=False, styled=False):
        """Computes and returns the alignment of each message belonging to
        the current field as proposed by getValues() method but indexed
        per message.

        >>> from netzob.all import *
        >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]
        >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
        >>> f2 = Field(", what's up in ", name="whatsup")
        >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city")
        >>> f4 = Field(" ?", name="end")
        >>> symbol = Symbol([f1, f2, f3, f4], messages=messages)
        >>> print symbol
        'netzob' | ", what's up in " | 'Paris'  | ' ?'
        'netzob' | ", what's up in " | 'Berlin' | ' ?'
        'zoby'   | ", what's up in " | 'Paris'  | ' ?'
        'zoby'   | ", what's up in " | 'Berlin' | ' ?'

        >>> messageValues = f3.getMessageValues()
        >>> for message in symbol.messages:
        ...    print message.data, messageValues[message]
        netzob, what's up in Paris ? Paris
        netzob, what's up in Berlin ? Berlin
        zoby, what's up in Paris ? Paris
        zoby, what's up in Berlin ? Berlin

        :keyword encoded: if set to true, values are encoded
        :type encoded: :class:`bool`
        :keyword styled: if set to true, values are styled
        :type styled: :class:`bool`
        :return: a dict indexed by messages that denotes their values
        :rtype: a :class:`dict`

        """
        if encoded is None:
            raise TypeError("Encoded cannot be None")
        if styled is None:
            raise TypeError("Styled cannot be None")

        result = OrderedDict()
        fieldValues = self.getValues(encoded=encoded, styled=styled)

        for iMessage, message in enumerate(self.messages):
            result[message] = fieldValues[iMessage]

        return result

    # def getMessagesWithValue(self, value):
    #     """Computes and returns the messages that have a specified value
    #     in the current field.

    #     >>> from netzob.all import *
    #     >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']]
    #     >>> f1 = Field("hello ", name="hello")
    #     >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo")
    #     >>> f3 = Field(", what's up in ", name="whatsup")
    #     >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city")
    #     >>> f5 = Field(" ?", name="end")
    #     >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages)
    #     >>> print symbol.specialize()
    #     >>> print symbol
    #     hello  | netzob | , what's up in  | Paris    |  ?
    #     hello  | netzob | , what's up in  | Berlin   |  ?
    #     hello  | netzob | , what's up in  | New-York |  ?
    #     hello  | zoby   | , what's up in  | Paris    |  ?
    #     hello  | zoby   | , what's up in  | Berlin   |  ?
    #     hello  | zoby   | , what's up in  | New-York |  ?
    #     hello  | lapy   | , what's up in  | Paris    |  ?
    #     hello  | lapy   | , what's up in  | Berlin   |  ?
    #     hello  | lapy   | , what's up in  | New-York |  ?
    #     >>> lapySymbol = Symbol(messages=symbol.fields[1].getMessagesWithValue("lapy"))
    #     >>> print lapySymbol
    #     hello lapy, what's up in Paris ?   
    #     hello lapy, what's up in Berlin ?  
    #     hello lapy, what's up in New-York ?
    #     >>> Format.splitStatic(lapySymbol)
    #     >>> lapySymbol.encodingFunctions.add(TypeEncodingFunction(HexaString))
    #     >>> print lapySymbol
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 5061726973203f      
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 4265726c696e203f    
    #     68656c6c6f206c6170792c2077686174277320757020696e20 | 4e65772d596f726b203f

    #     :parameter value: a Raw value
    #     :type value: :class:`object`
    #     :return: a list of messages
    #     :rtype: a list of :class:`netzob.Common.Models.Vocabulary.Messages.AbstractMessage.AbstractMessage`
    #     """

    #     if value is None:
    #         raise TypeError("Value cannot be None")

    #     fieldValues = self.getValues(encoded=False, styled=False)
    #     result = []
    #     for i_message, message in enumerate(self.messages):
    #         if fieldValues[i_message] == value:
    #             result.append(message)
    #     return result

    @abc.abstractmethod
    def specialize(self, mutator=None):
        """Specialize and generate a :class:`netzob.Common.Models.Vocabulary.Messages.RawMessage` which content
        follows the fields definitions attached to current element.

        :keyword mutator: if set, the mutator will be used to mutate the fields definitions
        :type mutator: :class:`netzob.Common.Models.Mutators.AbstractMutator`

        :return: a generated content represented with an hexastring
        :rtype: :class:`str`
        :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message
        """
        return

    @staticmethod
    def abstract(data, fields):
        """Search in the fields/symbols the first one that can abstract the data.

        >>> from netzob.all import *
        >>> messages = ["{0}, what's up in {1} ?".format(pseudo, city) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']]

        >>> f1a = Field("netzob")
        >>> f2a = Field(", what's up in ")
        >>> f3a = Field(Alt(["Paris", "Berlin"]))
        >>> f4a = Field(" ?")
        >>> s1 = Symbol([f1a, f2a, f3a, f4a], name="Symbol-netzob")

        >>> f1b = Field("zoby")
        >>> f2b = Field(", what's up in ")
        >>> f3b = Field(Alt(["Paris", "Berlin"]))
        >>> f4b = Field(" ?")
        >>> s2 = Symbol([f1b, f2b, f3b, f4b], name="Symbol-zoby")

        >>> for m in messages:
        ...    abstractedSymbol = AbstractField.abstract(m, [s1, s2])
        ...    print abstractedSymbol.name
        Symbol-netzob
        Symbol-netzob
        Symbol-zoby
        Symbol-zoby

        :parameter data: the data that should be abstracted in symbol
        :type data: :class:`str`
        :parameter fields: a list of fields/symbols targeted during the abstraction process
        :type fields: :class:`list` of :class:`netzob.Common.Models.Vocabulary.AbstractField`

        :return: a field/symbol
        :rtype: :class:`netzob.Common.Models.Vocabulary.AbstractField`
        :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractionException` if an error occurs while abstracting the data
        """
        from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment
        for field in fields:
            try:                
                DataAlignment.align([data], field, encoded=False)
                return field
            except:
                pass

        logging.error("Impossible to abstract the message in one of the specified symbols, we create an unknown symbol for it.")
        
        from netzob.Common.Models.Vocabulary.UnknownSymbol import UnknownSymbol
        from netzob.Common.Models.Vocabulary.Messages.RawMessage import RawMessage
        return UnknownSymbol(RawMessage(data))

    def getSymbol(self):
        """Computes the symbol to which this field is attached.

        To retrieve it, this method recursively call the parent of the current object until the root is found.
        If the last root is not a :class:`netzob.Common.Models.Vocabulary.Symbol`, it raises an Exception.

        :returns: the symbol if available
        :type: :class:`netzob.Common.Models.Vocabulary.Symbol`
        :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.NoSymbolException`
        """
        from netzob.Common.Models.Vocabulary.Symbol import Symbol
        if isinstance(self, Symbol):
            return self
        elif self.hasParent():
            return self.parent.getSymbol()
        else:
            raise NoSymbolException("Impossible to retrieve the symbol attached to this element")

    def _getLeafFields(self, depth=None, currentDepth=0):
        """Extract the leaf fields to consider regarding the specified depth

        >>> from netzob.all import *
        >>> field = Field("hello", name="F0")
        >>> print [f.name for f in field._getLeafFields()]
        ['F0']

        >>> field = Field(name="L0")
        >>> headerField = Field(name="L0_header")
        >>> payloadField = Field(name="L0_payload")
        >>> footerField = Field(name="L0_footer")

        >>> fieldL1 = Field(name="L1")
        >>> fieldL1_header = Field(name="L1_header")
        >>> fieldL1_payload = Field(name="L1_payload")
        >>> fieldL1.fields = [fieldL1_header, fieldL1_payload]

        >>> payloadField.fields = [fieldL1]
        >>> field.fields = [headerField, payloadField, footerField]

        >>> print [f.name for f in field._getLeafFields(depth=None)]
        ['L0_header', 'L1_header', 'L1_payload', 'L0_footer']

        >>> print [f.name for f in field._getLeafFields(depth=0)]
        ['L0']

        >>> print [f.name for f in field._getLeafFields(depth=1)]
        ['L0_header', 'L0_payload', 'L0_footer']

        >>> print [f.name for f in field._getLeafFields(depth=2)]
        ['L0_header', 'L1', 'L0_footer']

        :return: the list of leaf fields
        :rtype: :class:`list` of :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField`.
        """
        if currentDepth is None:
            currentDepth = 0

        if len(self.fields) == 0:
            return [self]

        if currentDepth == depth:
            return [self]

        leafFields = []
        for fields in self.fields:
            if fields is not None:
                leafFields.extend(fields._getLeafFields(depth, currentDepth + 1))

        return leafFields

    def hasParent(self):
        """Computes if the current element has a parent.

        :returns: True if current element has a parent.
        :rtype: :class:`bool`
        """
        return self.__parent is not None

    def clearFields(self):
        """Remove all the children attached to the current element"""

        while(len(self.__fields) > 0):
            self.__fields.pop()

    def clearEncodingFunctions(self):
        """Remove all the encoding functions attached to the current element"""
        self.__encodingFunctions.clear()
        for child in self.fields:
            child.clearEncodingFunctions()

    def clearVisualizationFunctions(self):
        """Remove all the visualization functions attached to the current element"""

        while(len(self.__visualizationFunctions) > 0):
            self.__visualizationFunctions.pop()

    def clearTransformationFunctions(self):
        """Remove all the transformation functions attached to the current element"""

        while(len(self.__transformationFunctions) > 0):
            self.__transformationFunctions.pop()

    # Standard methods
    def __str__(self):
        return str(self.getCells(encoded=True))

    @typeCheck(int)
    def _str_debug(self, deepness=0):
        """Returns a string which denotes
        the current field definition using a tree display"""

        tab = ["|--  " for x in xrange(deepness)]
        tab.append(str(self.name))
        lines = [''.join(tab)]
        from netzob.Common.Models.Vocabulary.Field import Field
        if isinstance(self, Field):
            lines.append(self.domain._str_debug(deepness + 1))
        for f in self.fields:
            lines.append(f._str_debug(deepness + 1))
        return '\n'.join(lines)

    # PROPERTIES

    @property
    def id(self):
        """Unique identifier of the field.

        This value must be a unique UUID instance (generated with uuid.uuid4()).

        :type: :class:`uuid.UUID`
        :raises: :class:`TypeError`, :class:`ValueError`
        """

        return self.__id

    @id.setter
    @typeCheck(uuid.UUID)
    def id(self, id):
        if id is None:
            raise ValueError("id is Mandatory.")
        self.__id = id

    @property
    def name(self):
        """Public name (may not be unique), default value is None

        :type: :class:`str`
        :raises: :class:`TypeError`
        """

        return self.__name

    @name.setter
    @typeCheck(str)
    def name(self, name):
        self.__name = name


    @property
    def layer(self):
        """Flag describing if element is a layer.

        :type: :class:`bool`
        :raises: :class:`TypeError`
        """

        return self.__layer

    @layer.setter
    @typeCheck(bool)
    def layer(self, layer):
        if layer is None:
            layer = False
        self.__layer = layer

    @property
    def description(self):
        """User description of the field. Default value is ''.

        :type: :class:`str`
        :raises: :class:`TypeError`
        """

        return self.__description

    @description.setter
    @typeCheck(str)
    def description(self, description):
        self.__description = description

    @property
    def encodingFunctions(self):
        """Sorted typed list of encoding function to attach on field.

        .. note:: list implemented as a :class:`netzob.Common.Utils.TypedList.TypedList`

        :type: a list of :class:`netzob.Common.Models.Vocabulary.Functions.EncodingFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """
        return self.__encodingFunctions

    @encodingFunctions.setter
    def encodingFunctions(self, encodingFunctions):
        self.clearEncodingFunctions()
        for encodingFunction in encodingFunctions:
            self.addEncodingFunction(encodingFunction)

    def addEncodingFunction(self, encodingFunction):
        self.encodingFunctions.add(encodingFunction)
        for child in self.fields:
            child.addEncodingFunction(encodingFunction)

    @property
    def visualizationFunctions(self):
        """Sorted list of visualization function to attach on field.

        :type: a list of :class:`netzob.Common.Models.Vocabulary.Functions.VisualizationFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """

        return self.__visualizationFunctions

    @visualizationFunctions.setter
    def visualizationFunctions(self, visualizationFunctions):
        self.clearVisualizationFunctions()
        self.visualizationFunctions.extend(visualizationFunctions)

    @property
    def transformationFunctions(self):
        """Sorted list of transformation function to attach on field.

        :type: a list of :class:`netzob.Common.Models.Vocabulary.Functions.TransformationFunction`
        :raises: :class:`TypeError`

        .. warning:: Setting this value with a list copies its members and not the list itself.
        """

        return self.__transformationFunctions

    @transformationFunctions.setter
    def transformationFunctions(self, transformationFunctions):
        self.clearTransformationFunctions()
        self.transformationFunctions.extend(transformationFunctions)

    @property
    def fields(self):
        """Sorted list of field fields."""

        return self.__fields

    @fields.setter
    def fields(self, fields):
        from netzob.Common.Models.Vocabulary.Field import Field
        # First it checks the specified children are abstractfiled
        if fields is not None:
            for c in fields:
                if not isinstance(c, Field):
                    raise TypeError("Cannot edit the fields because at least one specified element is not an AbstractField its a {0}.".format(type(c)))

        self.clearFields()
        if fields is not None:
            for c in fields:
                c.parent = self
                self.__fields.append(c)

    @property
    def parent(self):
        """The parent of this current element.

        If current element has no parent, its value is **None**.

        :type: a :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField`
        :raises: :class:`TypeError`
        """

        return self.__parent

    @parent.setter
    def parent(self, parent):
        if not isinstance(parent, AbstractField):
            raise TypeError("Specified parent must be an AbstractField and not an {0}".format(type(parent)))
        self.__parent = parent

    def storeInMemento(self):
        pass

    def restoreFromMemento(self, memento):
        pass
Esempio n. 13
0
class FileImporter(object):
    """An Importer than can extracts messages out of files.
    We recommend to use static methods such as
    - FileImporter.readFiles(...)
    - Fileimporter.readFile(...)
    refer to their documentation to have an overview of the required parameters.

    >>> from netzob.all import *
    >>> messages = FileImporter.readFile("./test/resources/files/test_import_text_message.txt").values()
    >>> print(len(messages))
    13
    >>> for m in messages:
    ...    print(repr(m.data))
    b'The life that I have'
    b'Is all that I have'
    b'And the life that I have'
    b'Is yours.'
    b'The love that I have'
    b'Of the life that I have'
    b'Is yours and yours and yours.'
    b'A sleep I shall have'
    b'A rest I shall have'
    b'Yet death will be but a pause.'
    b'For the peace of my years'
    b'In the long green grass'
    b'Will be yours and yours and yours.'

    >>> from netzob.all import *
    >>> file1 = "./test/resources/files/test_import_raw_message1.dat"
    >>> file2 = "./test/resources/files/test_import_raw_message2.dat"
    >>> messages = FileImporter.readFiles([file1, file2], delimitor=b"\\x00\\x00").values()
    >>> print(len(messages))
    802
    >>> print(messages[10].data)
    b'\\xbdq75\\x18'
    >>> print(messages[797].data)
    b'\\xfcJ\\xd1\\xbf\\xff\\xd90\\x98m\\xeb'
    >>> print(messages[797].file_path)
    ./test/resources/files/test_import_raw_message2.dat
    >>> print(messages[707].file_message_number)
    353
    """

    def __init__(self):
        pass

    @typeCheck(list, bytes)
    def readMessages(self, filePathList, delimitor=b"\n"):
        """Read all the messages found in the specified filePathList and given a delimitor.

        :param filePathList: paths of the file to parse
        :type filePathList: a list of :class:`str`
        :param delimitor: the delimitor used to find messages in the same file
        :type delimitor: :class:`str`
        :return: a sorted list of messages
        :rtype: a :class:`netzob.Common.Utils.SortedTypedList.SortedTypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """
        # Verify the existence of input files
        errorMessageList = []
        for filePath in filePathList:
            try:
                fp = open(filePath)
                fp.close()
            except IOError as e:
                errorMessage = _("Error while trying to open the " +
                                 "file {0}.").format(filePath)
                if e.errno == errno.EACCES:
                    errorMessage = _("Error while trying to open the file " +
                                     "{0}, more permissions are required for "
                                     + "reading it.").format(filePath)
                errorMessageList.append(errorMessage)
                self._logger.warn(errorMessage)

        if errorMessageList != []:
            raise NetzobImportException("File", "\n".join(errorMessageList))
                
        self.messages = SortedTypedList(AbstractMessage)
        for filePath in filePathList:
            self.__readMessagesFromFile(filePath, delimitor)
        
        return self.messages
    
    @typeCheck(str, bytes)
    def __readMessagesFromFile(self, filePath, delimitor=b'\n'):
        if filePath is None or len(str(filePath).strip()) == 0:
            raise TypeError("Filepath cannot be None or empty")
 
        if delimitor is None or len(str(delimitor)) == 0:
            raise TypeError("Delimitor cannot be None or empty")

        file_content = None
        with open(filePath, 'rb') as fd:
            file_content = fd.read()

        if file_content is None:
            raise Exception("No content found in '{}'".format(filePath))

        for i_data, data in enumerate(file_content.split(delimitor)):
            if len(data) > 0:
                self.messages.add(FileMessage(data, file_path = filePath, file_message_number = i_data))

    @staticmethod
    @typeCheck(list, bytes)
    def readFiles(filePathList, delimitor=b'\n'):
        """Read all messages from a list of files. A delimitor must be specified to delimit messages.

        :param filePathList: a list of files to read
        :type filePathList: a list of :class:`str`
        :param delimitor: the delimitor.
        :type delimitor: :class:`str`
        :return: a list of captured messages
        :rtype: a :class:`netzob.Common.Utils.SortedTypedList.SortedTypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """
        importer = FileImporter()
        return importer.readMessages(filePathList, delimitor = delimitor)
    
    @staticmethod
    @typeCheck(str, bytes)
    def readFile(filePath, delimitor=b'\n'):
        """Read all messages from the specified file. 
        Messages are found based on the specified delimitor. 

        :param filePath: the pcap path
        :type filePath: :class:`str`
        :param delimitor: the delimitor used to find messages in the specified file
        :type delimitor: :class:`str`
        :return: a list of captured messages
        :rtype: a :class:`netzob.Common.Utils.SortedTypedList.SortedTypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage`
        """
        importer = FileImporter()        
        return importer.readFiles([filePath], delimitor = delimitor)