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
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)
def decodeLayer4(self, ipProtocolNum, l3Payload): 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.log.warn(warnMessage) raise NetzobImportException("PCAP", warnMessage, WARNING, self.INVALID_LAYER4)
def saveMessagesInCurrentProject(self, messageIDList): """Retrieve messages from the provided list of IDs and add them to the current project""" addMessages = [] # Compute the step step = float(100.0 / float(len(messageIDList))) status = 0.0 old_status = 0.0 for messageID in messageIDList: message = self.getMessageByID(str(messageID)) if message is not None: addMessages.append(message) else: errorMessage = _( "Message ID: {0} not found in importer message list" ).format(messageID) raise NetzobImportException("PCAP", errorMessage, ERROR) status += step if self.status_cb is not None: self.status_cb(status, None) old_status = status self.saveMessagesInProject(self.netzob.getCurrentWorkspace(), self.netzob.getCurrentProject(), addMessages)
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)
def getMessageDetails(self, messageID): if not messageID in self._payloadDict: errorMessage = "Message ID: {0} not found in importer message list".format(messageID) logging.error(errorMessage) raise NetzobImportException("PCAP", errorMessage, ERROR) decoder = Decoders.EthDecoder() payload = self._payloadDict[messageID] return decoder.decode(payload)
def readMessages(self, filePathList, bpfFilter="", importLayer=5, nbPackets=0): """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` :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) return self.messages
def getMessageDetails(self, messageID): if not messageID in self._payloadDict: errorMessage = _("Message ID: {0} not found in importer " + "message list").format(messageID) logging.error(errorMessage) raise NetzobImportException("IPC", errorMessage, ERROR) payload = self._payloadDict[messageID] return TypeConvertor.hexdump( TypeConvertor.netzobRawToPythonRaw(payload))
def __decodeLayer2(self, packet: Packet): """Internal method that parses the specified header and extracts layer2 related proprieties.""" l2Proto = packet.name if isinstance(packet, Raw): print("Ignoring undecoded packet with values:", bytes(packet).hex()) return l2Proto, None, None, "" if isinstance(packet, Dot11): l2DstAddr = packet.fields['addr1'] # receiver address, alt: packet.fields['addr3'] destination address l2SrcAddr = packet.fields['addr2'] # transmitter address, alt: packet.fields['addr4'] source address else: raise NetzobImportException("NEMERE_PCAP", "Unsupported layer 2 protocol " + l2Proto, PCAPImporter.INVALID_LAYER2) l2Payload = bytes(packet.payload) return l2Proto, l2SrcAddr, l2DstAddr, l2Payload
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 != pcapy.DLT_RAW and self.datalink != PCAPImporter.PROTOCOL201 and self.datalink != pcapy.DLT_NULL: self._logger.debug('Datalink: ' + str(self.datalink)) 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 sniffingThread(self, device, count, time): logging.info("Launching sniff process on dev {0} with: count={1}, timeout={2}, filter=\"{3}\"".format(device, count, time, self.bpfFilter)) sniffer = pcapy.open_live(device, 1024, False, int(time)) try: sniffer.setfilter(self.bpfFilter) except: logging.warn("The provided filter is not valid (it should respects the BPF format") return self.datalink = sniffer.datalink() if self.datalink != pcapy.DLT_EN10MB and self.datalink != pcapy.DLT_LINUX_SLL: errorMessage = _("This device cannot be sniffed since the " + "layer 2 is not supported ({0})").format(str(self.datalink)) self.log.warn(errorMessage) raise NetzobImportException("PCAP", errorMessage, ERROR, self.INVALID_LAYER2) else: sniffer.loop(int(count), self._packetHandler)
class PCAPImporter(AbstractImporter): """Model of PCAP importer plugin""" INVALID_BPF_FILTER = 0 INVALID_LAYER2 = 1 INVALID_LAYER3 = 2 INVALID_LAYER4 = 3 PROTOCOL201 = 201 def __init__(self, netzob): super(PCAPImporter, self).__init__("PCAP IMPORT", netzob) # create logger with the given configuration self.log = logging.getLogger('netzob.Import.PcapImport.py') self.filesToBeImported = [] self.bpfFilter = "" self.importLayer = 4 self._payloadDict = {} @property def payloadDict(self): return self._payloadDict.copy() def setSourceFiles(self, filePathList): errorMessageList = [] for filePath in filePathList: try: fp = open(filePath) fp.close() except IOError, 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.log.warn(errorMessage) if errorMessageList != []: raise NetzobImportException("PCAP", "\n".join(errorMessageList), ERROR) self.filesToBeImported = filePathList
def decodeLayer3(self, etherType, l2Payload): if etherType == Packets.IP.ethertype: l3Proto = "IP" l3Decoder = Decoders.IPDecoder() layer3 = l3Decoder.decode(l2Payload) l3SrcAddr = layer3.get_ip_src() l3DstAddr = layer3.get_ip_dst() l3Payload = l2Payload[layer3.get_header_size():] 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.log.warn(warnMessage) raise NetzobImportException("PCAP", warnMessage, WARNING, self.INVALID_LAYER3)
def packetHandler(self, packet: Packet): epoch = packet.time l1Payload = bytes(packet) if len(l1Payload) == 0: return # Build the RawMessage rawMessage = RawMessage(l1Payload, epoch, source=None, destination=None) if isinstance(packet, RadioTap): # lift layer to Dot11 if there is a RadioTap dummy frame packet = packet.payload if self.importLayer == 2: (l2Proto, l2SrcAddr, l2DstAddr, l2Payload) = self.__decodeLayer2(packet) if len(l2Payload) == 0: return # Build the L2NetworkMessage l2Message = L2NetworkMessage(l2Payload, epoch, l2Proto, l2SrcAddr, l2DstAddr) self._messages.add(l2Message) self._rawmessages.add(rawMessage) else: # Use Netzob's PCAPImporter if layer 2 is not WLAN raise NetzobImportException("PCAP", "Unsupported import layer. Currently only handles layer 2.", PCAPImporter.INVALID_LAYER2)
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