def _deserializeAlignment(self, regex, mask, unitSize=AbstractType.UNITSIZE_8): """ deserializeAlignment: Transforms the C extension results in a python readable way @param regex the C returned regex @param mask the C returned mask @param unitSize the unitSize @returns the python alignment """ if not (unitSize == AbstractType.UNITSIZE_8 or unitSize == AbstractType.UNITSIZE_4): raise ValueError("Deserializing with unitSize {0} not yet implemented, only 4 and 8 supported.".format(unitSize)) align = "" for i, c in enumerate(mask): if c != '\x02': if c == '\x01': if unitSize == AbstractType.UNITSIZE_8: align += "--" elif unitSize == AbstractType.UNITSIZE_4: align += "-" else: if unitSize == AbstractType.UNITSIZE_8: align += TypeConverter.convert(regex[i:i + 1], Raw, HexaString) elif unitSize == AbstractType.UNITSIZE_4: align += TypeConverter.convert(regex[i:i + 1], Raw, HexaString)[1:] return align
def _computeExpectedValue(self, parsingPath): self._logger.debug("compute expected value for Internet checksum field") # first checks the pointed fields all have a value hasValue = True for field in self.fieldDependencies: if field.domain != self and not parsingPath.isDataAvailableForVariable(field.domain): self._logger.debug("Field : {0} has no value".format(field.id)) hasValue = False if not hasValue: raise Exception("Expected value cannot be computed, some dependencies are missing for domain {0}".format(self)) else: fieldValues = [] for field in self.fieldDependencies: if field.domain is self: fieldSize = random.randint(field.domain.dataType.size[0], field.domain.dataType.size[1]) fieldValue = "\x00"* (fieldSize / 8) else: fieldValue = TypeConverter.convert(parsingPath.getDataAssignedToVariable(field.domain), BitArray, Raw) if fieldValue is None: break else: fieldValues.append(fieldValue) fieldValues = ''.join(fieldValues) # compute the checksum of this value chsum = self.__checksum(fieldValues) b = TypeConverter.convert(chsum, Integer, BitArray, src_unitSize=AbstractType.UNITSIZE_16, src_sign = AbstractType.SIGN_UNSIGNED) return b
def mutate(self, prefixDescription=None): """Generate various mutations of the current ASCII value. Mutations are first applied on the ASCII value than, each obtained mutations generates new bitarray mutations. ASCII mutations are: * Original Version * Original Version in Upper case * Original Version in Lower case >>> from netzob.all import * >>> t = ASCII("helloworld") >>> print t.mutate() {'ascii(inversed)-bits(littleEndian)': bitarray('00100110001101100100111011110110111011101111011000110110001101101010011000010110'), 'ascii(inversed-upper)-bits(littleEndian)': bitarray('00100010001100100100101011110010111010101111001000110010001100101010001000010010'), 'ascii(upper)-bits(littleEndian)': bitarray('00010010101000100011001000110010111100101110101011110010010010100011001000100010'), 'ascii-bits(bigEndian)': bitarray('01101000011001010110110001101100011011110111011101101111011100100110110001100100'), 'ascii(inversed)-bits(bigEndian)': bitarray('01100100011011000111001001101111011101110110111101101100011011000110010101101000'), 'ascii(upper)-bits(bigEndian)': bitarray('01001000010001010100110001001100010011110101011101001111010100100100110001000100'), 'ascii-bits(littleEndian)': bitarray('00010110101001100011011000110110111101101110111011110110010011100011011000100110'), 'ascii(inversed-upper)-bits(bigEndian)': bitarray('01000100010011000101001001001111010101110100111101001100010011000100010101001000')} :keyword prefixDescription: prefix to attach to the description of the generated mutation. :type prefixDescription: :class:`str` :return: a dict of computed mutations having the same types than the initial one. :rtype: :class:`dict`<str>=:class:`netzob.Common.Models.Types.AbstractType.AbstractType` """ if prefixDescription is None: prefixDescription = "" else: prefixDescription += "-" from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray if self.value is None: val = self.generate() else: val = self.value strValue = TypeConverter.convert(val, BitArray, ASCII) mutations = dict() mutations["{0}ascii".format(prefixDescription)] = strValue mutations["{0}ascii(inversed)".format(prefixDescription)] = strValue[::-1] if strValue != strValue.upper(): mutations["{0}ascii(upper)".format(prefixDescription)] = strValue.upper() mutations["{0}ascii(inversed-upper)".format(prefixDescription)] = strValue[::-1].upper() if strValue != strValue.lower(): mutations["{0}ascii(lower)".format(prefixDescription)] = strValue.lower() mutations["{0}ascii(inversed-lower)".format(prefixDescription)] = strValue[::-1].lower() results = dict() for mutationName, mutationValue in mutations.iteritems(): ba = BitArray(TypeConverter.convert(mutationValue, ASCII, BitArray)) results.update(ba.mutate(mutationName)) return results
def writeSymbol(self, symbol): """Write the specified symbol on the communication channel after specializing it into a contextualized message. :param symbol: the symbol to write on the channel :type symbol: :class:`netzob.Common.Models.Vocabulary.Symbol.Symbol` :raise TypeError if parameter is not valid and Exception if an exception occurs. """ if symbol is None: raise TypeError("The symbol to write on the channel cannot be None") self._logger.info("Going to specialize symbol: '{0}' (id={1}).".format(symbol.name, symbol.id)) dataBin = self.specializer.specializeSymbol(symbol).generatedContent self.memory = self.specializer.memory self.parser.memory = self.memory data = TypeConverter.convert(dataBin, BitArray, Raw) symbol.messages.append(RawMessage(data)) self._logger.info("Data generated from symbol '{0}':\n{1}.".format(symbol.name, symbol)) self._logger.info("Going to write to communication channel...") self.channel.write(data) self._logger.info("Writing to commnunication channel donne..")
def _computeExpectedValue(self, parsingPath): self._logger.debug("compute expected value for Size field") # first checks the pointed fields all have a value hasValue = True for field in self.fieldDependencies: if field.domain != self and not parsingPath.isDataAvailableForVariable(field.domain): self._logger.debug("Field : {0} has no value".format(field.id)) hasValue = False if not hasValue: raise Exception("Expected value cannot be computed, some dependencies are missing for domain {0}".format(self)) else: size = 0 for field in self.fieldDependencies: if field.domain is self: fieldValue = self.dataType.generate() else: fieldValue = parsingPath.getDataAssignedToVariable(field.domain) if fieldValue is None: break else: tmpLen = len(fieldValue) size += tmpLen size = int(size * self.factor + self.offset) b = TypeConverter.convert(size, Decimal, BitArray) # while len(b)<self.dataType.size[0]: # b.insert(0, False) return b
def generate(self, generationStrategy=None): """Generates a random Raw that respects the requested size. >>> from netzob.all import * >>> a = Raw(nbBytes=(10)) >>> gen = a.generate() >>> print len(gen) 80 >>> from netzob.all import * >>> a = Raw(nbBytes=(10, 20)) >>> gen = a.generate() >>> print 10<=len(gen) and 20<=len(gen) True """ from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray minSize, maxSize = self.size if maxSize is None: maxSize = AbstractType.MAXIMUM_GENERATED_DATA_SIZE if minSize is None: minSize = 0 generatedSize = random.randint(minSize, maxSize) return TypeConverter.convert(os.urandom(generatedSize / 8), Raw, BitArray)
def __repr__(self): if self.value is not None: from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray return str(TypeConverter.convert(self.value, BitArray, self.__class__)) else: return str(self.value)
def generate(self, generationStrategy=None): """Generates a random ASCII that respects the requested size. >>> from netzob.all import * >>> a = ASCII(nbChars=10) >>> gen = a.generate() >>> len(gen)/8 10 >>> b = ASCII("netzob") >>> gen = b.generate() >>> print len(gen)>0 True """ from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray minSize, maxSize = self.nbChars if maxSize is None: maxSize = AbstractType.MAXIMUM_GENERATED_DATA_SIZE if minSize is None: minSize = 0 generatedSize = random.randint(minSize, maxSize) randomContent = ''.join([random.choice(string.letters + string.digits) for i in xrange(generatedSize)]) return TypeConverter.convert(randomContent, ASCII, BitArray)
def __init__(self, value=None, size=(None, None)): if value is not None and not isinstance(value, bitarray): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray value = TypeConverter.convert(value, HexaString, BitArray) super(HexaString, self).__init__(self.__class__.__name__, value, size)
def __str__(self): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray if self.value is not None: return "{0}={1} ({2})".format(self.typeName, TypeConverter.convert(self.value, BitArray, self.__class__), self.size) else: return "{0}={1} ({2})".format(self.typeName, self.value, self.size)
def specialize(self, memory=None, generationStrategy=None): """Specialize and generate an hexastring which content follows the fields definitions attached to the field of the symbol. >>> from netzob.all import * >>> f1 = Field(domain=ASCII(nbChars=5)) >>> f0 = Field(domain=Size(f1)) >>> s = Symbol(fields=[f0, f1]) >>> result = s.specialize() >>> print result[0] \x05 >>> print len(result) 6 :keyword generationStrategy: if set, the strategy will be used to generate the fields definitions :type generaionrStrategy: :class:`` :return: a generated content represented as a Raw :rtype: :class:`str`` :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message """ from netzob.Common.Models.Vocabulary.Domain.Specializer.MessageSpecializer import MessageSpecializer msg = MessageSpecializer(memory=memory) spePath = msg.specializeSymbol(self) if spePath is not None: return TypeConverter.convert(spePath.generatedContent, BitArray, Raw)
def specializeSymbol(self, symbol): """This method generates a message based on the provided symbol definition.""" if symbol is None: raise Exception("Specified symbol is None") self._logger.debug("Specifies symbol '{0}'.".format(symbol.name)) # this variable host all the specialization path specializingPaths = [SpecializingPath(memory=self.memory)] for field in symbol.fields: self._logger.debug("Specializing field {0}".format(field.name)) fieldDomain = field.domain if fieldDomain is None: raise Exception("Cannot specialize field '{0}' since it defines no domain".format(fieldDomain)) fs = FieldSpecializer(field, presets = self.presets) newSpecializingPaths = [] for specializingPath in specializingPaths: newSpecializingPaths.extend(fs.specialize(specializingPath)) specializingPaths = newSpecializingPaths if len(specializingPaths) > 1: self._logger.info("TODO: multiple valid paths found when specializing this message.") if len(specializingPaths) == 0: raise Exception("Cannot specialize this symbol.") retainedPath = specializingPaths[0] generatedContent = None # let's configure the generated content for field in symbol.fields: # TODO: only support one level of children... must be improved if len(field.fields) > 0: d = None for child in field.fields: if d is None: d = retainedPath.getDataAssignedToVariable(child.domain).copy() else: d += retainedPath.getDataAssignedToVariable(child.domain).copy() else: d = retainedPath.getDataAssignedToVariable(field.domain) if generatedContent is None: generatedContent = d.copy() else: generatedContent += d.copy() retainedPath.generatedContent = generatedContent self._logger.debug("Specialized message: {0}".format(TypeConverter.convert(retainedPath.generatedContent, BitArray, ASCII))) self.memory = retainedPath.memory return retainedPath
def __str__(self): if self.value is not None: from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray from netzob.Common.Models.Types.HexaString import HexaString return "{0}={1} ({2})".format(self.typeName, repr(TypeConverter.convert(self.value, BitArray, Raw)), self.size) else: return "{0}={1} ({2})".format(self.typeName, self.value, self.size)
def _generateDataValues(self, cellsData): result = [] for data in cellsData: if len(data) > 0: result.append(TypeConverter.convert(data[:8], Raw, Decimal)) # We take only the first 8 octets else: result.append(0) return result
def encode(data, unitSize=AbstractType.UNITSIZE_32, endianness=AbstractType.defaultEndianness(), sign=AbstractType.SIGN_UNSIGNED): from netzob.Common.Models.Types.Raw import Raw from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.Integer import Integer intValue = TypeConverter.convert(data, Raw, Integer, dst_unitSize=AbstractType.UNITSIZE_32, dst_sign=AbstractType.SIGN_UNSIGNED) parsedTimestamp = datetime.fromtimestamp(intValue) return parsedTimestamp.strftime("%c")
def __init__(self, value=None, nbBytes=None, unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): if value is not None and not isinstance(value, bitarray): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray value = TypeConverter.convert(value, Raw, BitArray) nbBits = self._convertNbBytesinNbBits(nbBytes) super(Raw, self).__init__(self.__class__.__name__, value, nbBits, unitSize=unitSize, endianness=endianness, sign=sign)
def __init__(self, value=None, nbChars=(None, None), unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): if value is not None and not isinstance(value, bitarray): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray value = TypeConverter.convert(value, ASCII, BitArray, src_unitSize=unitSize, src_endianness=endianness, src_sign=sign, dst_unitSize=unitSize, dst_endianness=endianness, dst_sign=sign) else: value = None self.nbChars = nbChars nbBits = self._convertNbCharsInNbBits(self.nbChars) super(ASCII, self).__init__(self.__class__.__name__, value, nbBits, unitSize=unitSize, endianness=endianness, sign=sign)
def _sizeRelation(self, x, x_attribute, y, y_attribute): if x_attribute == self.ATTR_SIZE: if len(x) > 0: x = len(x) else: if len(x) > 0: x = TypeConverter.convert(x[:8], Raw, Integer) else: x = 0 if y_attribute == self.ATTR_SIZE: if len(y) > 0: y = len(y) else: if len(y) > 0: y = TypeConverter.convert(y[:8], Raw, Integer) else: y = 0 if x == y: return True else: return False
def canParse(self, data, unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): """Computes if specified data can be parsed as a Timestamp with the predefined constraints. >>> from netzob.all import * >>> time = Timestamp() >>> time.canParse(TypeConverter.convert(1444494130, Integer, BitArray, src_unitSize=AbstractType.UNITSIZE_32)) True >>> # A timestamp is nothing else than 32bits parsed as an unsigned long >>> time.canParse(TypeConverter.convert("test", ASCII, BitArray)) True >>> time.canParse(TypeConverter.convert("te", ASCII, BitArray)) False However, some constrains over the definition of the Timestamp can be set to restrain the accepted values >>> from netzob.all import * >>> time = Timestamp(epoch=Timestamp.EPOCH_WINDOWS, unity=Timestamp.UNITY_NANOSECOND, unitSize = AbstractType.UNITSIZE_64) >>> # the returned year is < 1900 >>> time.canParse(TypeConverter.convert("test", ASCII, BitArray)) False """ if data is None: raise TypeError("data cannot be None") # Timestamp must be 8 bits modulo length if len(data) % 8 != 0: return False if len(data) < int(self.unitSize): return False try: value = TypeConverter.convert(data[:int(self.unitSize)], BitArray, Integer, dst_unitSize=AbstractType.UNITSIZE_32, dst_sign=AbstractType.SIGN_UNSIGNED) # convert the value in seconds value = value / self.unity # add the utc now with the epoch timestamp_datetime = self.epoch + timedelta(seconds=value) # convert obtained datetime to timestamp in seconds result_sec = int( timestamp_datetime.strftime('%s') ) datetime.fromtimestamp(result_sec) except Exception: return False return True
def _computeExpectedValue(self, parsingPath): self._logger.debug("compute expected value for Size field") # first checks the pointed fields all have a value hasValue = True for field in self.fieldDependencies: if field.domain != self and not parsingPath.isDataAvailableForVariable(field.domain): self._logger.debug("Field : {0} has no value".format(field.id)) hasValue = False if not hasValue: raise Exception("Expected value cannot be computed, some dependencies are missing for domain {0}".format(self)) else: size = 0 for field in self.fieldDependencies: if field.domain is self: fieldValue = self.dataType.generate() else: fieldValue = parsingPath.getDataAssignedToVariable(field.domain) if fieldValue is None: break else: tmpLen = len(fieldValue) size += tmpLen size = int(size * self.factor + self.offset) size_raw = TypeConverter.convert(size, Integer, Raw, src_unitSize=self.dataType.unitSize) b = TypeConverter.convert(size_raw, Raw, BitArray) # add heading '0' while len(b)<self.dataType.size[0]: b.insert(0, False) # in some cases (when unitSize and size are not equal), it may require to delete some '0' in front while len(b)>self.dataType.size[0]: b.remove(0) return b
def specialize(self, memory=None, generationStrategy=None, presets=None): """Specialize and generate an hexastring which content follows the fields definitions attached to the field of the symbol. >>> from netzob.all import * >>> f1 = Field(domain=ASCII(nbChars=5)) >>> f0 = Field(domain=Size(f1)) >>> s = Symbol(fields=[f0, f1]) >>> result = s.specialize() >>> print result[0] \x05 >>> print len(result) 6 You can also preset the value of some variables included in the symbol definition. >>> from netzob.all import * >>> f1 = Field(domain=ASCII("hello ")) >>> f2 = Field(domain=ASCII(nbChars=(1,10))) >>> s = Symbol(fields = [f1, f2]) >>> presetValues = dict() >>> presetValues[f2] = TypeConverter.convert("antoine", ASCII, BitArray) >>> print s.specialize(presets = presetValues) hello antoine A preseted valued bypasses all the constraints checks on your field definition. For example, in the following example it can be use to bypass a size field definition. >>> from netzob.all import * >>> f1 = Field() >>> f2 = Field(domain=Raw(nbBytes=(10,15))) >>> f1.domain = Size(f2) >>> s = Symbol(fields=[f1, f2]) >>> presetValues = {f1: TypeConverter.convert("\xff", Raw, BitArray)} >>> print repr(s.specialize(presets = presetValues)[0]) '\\xff' :keyword generationStrategy: if set, the strategy will be used to generate the fields definitions :type generaionrStrategy: :class:`` :return: a generated content represented as a Raw :rtype: :class:`str`` :raises: :class:`netzob.Common.Models.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message """ from netzob.Common.Models.Vocabulary.Domain.Specializer.MessageSpecializer import MessageSpecializer msg = MessageSpecializer(memory=memory, presets=presets) spePath = msg.specializeSymbol(self) if spePath is not None: return TypeConverter.convert(spePath.generatedContent, BitArray, Raw)
def generate(self, generationStrategy=None): """Generates a random IPv4 which follows the constraints. >>> from netzob.all import * >>> f = Field(IPv4()) >>> len(f.specialize()) 4 >>> f = Field(IPv4("192.168.0.10")) >>> TypeConverter.convert(f.specialize(), Raw, IPv4) IPAddress('192.168.0.10') >>> f = Field(IPv4(network="10.10.10.0/24")) >>> TypeConverter.convert(f.specialize(), Raw, IPv4) in IPNetwork("10.10.10.0/24") True """ from netzob.Common.Models.Types.BitArray import BitArray from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.Raw import Raw if self.value is not None: return self.value elif self.network is not None: ip = random.choice(self.network) return TypeConverter.convert(ip.packed, Raw, BitArray, src_unitSize=self.unitSize, src_endianness=self.endianness, src_sign=self.sign, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign) else: not_valid = [10, 127, 169, 172, 192] first = random.randrange(1, 256) while first in not_valid: first = random.randrange(1, 256) strip = ".".join([str(first), str(random.randrange(1, 256)), str(random.randrange(1, 256)), str(random.randrange(1, 256))]) ip = IPv4.encode(strip) return TypeConverter.convert(ip.packed, Raw, BitArray, src_unitSize=self.unitSize, src_endianness=self.endianness, src_sign=self.sign, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign)
def __checksum(self, msg): self._logger.fatal("Computing checksum of {0}, {1}".format(TypeConverter.convert(msg, Raw, HexaString), len(msg))) def carry_around_add(a, b): c = a + b return (c & 0xffff) + (c >> 16) s = 0 for i in range(0, len(msg), 2): if i + 1 >= len(msg): w = ord(msg[i]) & 0xFF else: w = ord(msg[i]) + (ord(msg[i+1]) << 8) s = carry_around_add(s, w) res = ~s & 0xffff self._logger.fatal(res) return res
def __init__(self, value=None, network=None, unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): """Builds an IPv4 domain with optional constraints. :parameter value: specify a constraints over the expected value. :type value: an str, an IPAddress or an int which can be parsed as an IPv4 (ex. "192.168.0.10") :parameter network: if no value is specified (None), a constraints over the network the parsed IP belongs can be specified with this parameter (ex. "192.168.0.0/24") :type network: an str or an IPAddress which can be parsed as a network IPv4 """ if value is not None and not isinstance(value, bitarray): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray value = TypeConverter.convert(value, IPv4, BitArray, src_unitSize=unitSize, src_endianness=endianness, src_sign=sign, dst_unitSize=unitSize, dst_endianness=endianness, dst_sign=sign) self.network = network super(IPv4, self).__init__(self.__class__.__name__, value, 32, unitSize=unitSize, endianness=endianness, sign=sign)
def __init__(self, value=None, interval=None, nbUnits=None, unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): if value is not None and not isinstance(value, bitarray): from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray interval = value value = TypeConverter.convert(value, Decimal, BitArray, src_unitSize=unitSize, src_endianness=endianness, src_sign=sign, dst_unitSize=unitSize, dst_endianness=endianness, dst_sign=sign) else: value = None if interval is not None: nbBits = int(self._computeNbUnitSizeForInterval(interval, unitSize, sign)) * int(unitSize) elif nbUnits is not None: nbBits = nbUnits * int(unitSize) else: nbBits = int(unitSize) super(Decimal, self).__init__(self.__class__.__name__, value, nbBits, unitSize=unitSize, endianness=endianness, sign=sign)
def specialize(self): """Specialize the current field to build a raw data that follows the fields definitions attached to current element. This method allows to generate some content following the field definition: >>> from netzob.all import * >>> f = Field("hello") >>> print '\\n'.join([f.specialize() for x in range(3)]) hello hello hello This method also applies on multiple fields using a Symbol >>> fHello = Field("hello ") >>> fName = Field("zoby") >>> s = Symbol([fHello, fName]) >>> print '\\n'.join([s.specialize() for x in range(3)]) hello zoby hello zoby hello zoby :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 """ self._logger.debug("Specializes field {0}".format(self.name)) if self.__domain is None: raise InvalidDomainException("The domain is not defined.") from netzob.Common.Models.Vocabulary.Domain.Specializer.FieldSpecializer import FieldSpecializer fs = FieldSpecializer(self) specializingPaths = fs.specialize() if len(specializingPaths) < 1: raise Exception("Cannot specialize this field") specializingPath = specializingPaths[0] self._logger.debug("field specializing done: {0}".format(specializingPath)) if specializingPath is None: raise Exception("The specialization of the field {0} returned no result.".format(self.name)) return TypeConverter.convert(specializingPath.getDataAssignedToVariable(self.domain), BitArray, Raw)
def execute(self): """Execute the alignment of data following specified field """ if self.data is None: raise TypeError("Data cannot be None") if self.field is None: raise TypeError("Field cannot be None") # Aligned messages are stored in a MatrixList for better display result = MatrixList() # We retrieve all the leaf fields of the root of the provided field rootLeafFields = self.__root._getLeafFields(depth=self.depth) # if self.__root != self.field: # targetedFieldLeafFields = self.field._getLeafFields(depth=self.depth) # else: targetedFieldLeafFields = rootLeafFields result.headers = [str(field.name) for field in targetedFieldLeafFields] from netzob.Common.Models.Vocabulary.Domain.Parser.MessageParser import MessageParser for d in self.data: mp = MessageParser() # alignedMsg = mp.parseRaw(TypeConverter.convert(d, HexaString, Raw), targetedFieldLeafFields) alignedMsg = mp.parseRaw(d, targetedFieldLeafFields) alignedEncodedMsg = [] for ifield, currentField in enumerate(targetedFieldLeafFields): # now we apply encoding and mathematic functions fieldValue = alignedMsg[ifield] if self.encoded and len(currentField.encodingFunctions.values()) > 0: for encodingFunction in currentField.encodingFunctions.values(): fieldValue = encodingFunction.encode(fieldValue) else: fieldValue = TypeConverter.convert(fieldValue, BitArray, Raw) if currentField in self.field._getLeafFields(depth=self.depth): alignedEncodedMsg.append(fieldValue) result.append(alignedEncodedMsg) return result
def _splitFieldFollowingAlignment(self, field, align): """Update the field definition with new fields following the specified align.""" # STEP 1 : Create a field separation based on static and dynamic fields leftAlign, rightAlign = self._splitAlignment(align) splited = self._mergeAlign(leftAlign, rightAlign) step1Fields = [] for (entryVal, entryDyn) in splited: if entryDyn: newField = Field(Raw(nbBytes=(0, len(entryVal) / 2))) else: newField = Field(Raw(TypeConverter.convert(entryVal, HexaString, Raw))) step1Fields.append(newField) for f in step1Fields: f.encodingFunctions = field.encodingFunctions.values() field.fields = step1Fields
def parseRaw_new(self, dataToParse, fields): self._logger.debug("New parsing method executed on {}".format(dataToParse)) bitArrayToParse = TypeConverter.convert(dataToParse, Raw, BitArray) # building a new parsing path currentParsingPath = ParsingPath(bitArrayToParse.copy(), self.memory) currentParsingPath.assignDataToField(bitArrayToParse.copy(), fields[0]) # field iterator i_current_field = 0 parsingResult = self.parseField_new(currentParsingPath, fields, i_current_field) result = [] for field in fields: result.append(parsingResult.getDataAssignedToField(field)) self.memory = parsingResult.memory return result
def __init__(self, value=None, epoch=EPOCH_UNIX, unity=UNITY_SECOND, unitSize=AbstractType.UNITSIZE_32, endianness=AbstractType.defaultEndianness(), sign=AbstractType.SIGN_UNSIGNED): """Builds a Timestamp domain with optional constraints. :param value: specifies the value of the timestamp. :type value: an int, a long or a bitarray :param epoch: the initial date expressed in UTC from which timestamp is measured. Default value is the UNIX Epoch. :type datetime.datetime :param unity: specifies the unity of the timestamp (seconds, milliseconds, nanoseconds). Default value is SECOND. :type unity: int """ if value is not None and not isinstance(value, bitarray): # converts the specified value in bitarray from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray from netzob.Common.Models.Types.Integer import Integer value = TypeConverter.convert(value, Integer, BitArray, src_unitSize=unitSize, src_endianness=endianness, src_sign=sign) self.epoch = epoch self.unity = unity super(Timestamp, self).__init__(self.__class__.__name__, value, 32, unitSize=unitSize, endianness=endianness, sign=sign)
def encodeChild(self, variable, readingToken): result = [] if not readingToken.isValueForVariableAvailable(variable): return result if variable.varType == "Data" or variable.varType == "Size" or variable.varType == "InternetChecksum": val = readingToken.getValueForVariable(variable) encodedVal = TypeConverter.convert(val, BitArray, variable.dataType.__class__) result.append(str(encodedVal)) elif variable.varType == "Agg" or variable.varType == "Alt": for child in variable.children: result.extend(self.encodeChild(child, readingToken)) elif variable.varType == "Eol": # nothing to encode when child is EOL pass else: raise Exception("Unknown type of variable: {0}".format(variable.varType)) if len(result) == 0: return '' else: return ''.join(result)
def regenerate(self, variableSpecializerPath, moreCallBackAccepted=True): """This method participates in the specialization proces. It creates a VariableSpecializerResult in the provided path that contains a generated value that follows the definition of the Data """ self._logger.debug("Regenerate size {0}".format(self)) if variableSpecializerPath is None: raise Exception("VariableSpecializerPath cannot be None") try: newValue = self._computeExpectedValue(variableSpecializerPath) variableSpecializerPath.addResult(self, newValue) except Exception, e: self._logger.debug("Cannot specialize since no value is available for the size dependencies, we create a callback function in case it can be computed later: {0}".format(e)) pendingValue = TypeConverter.convert("PENDING VALUE", ASCII, BitArray) variableSpecializerPath.addResult(self, pendingValue) if moreCallBackAccepted: # for field in self.fields: variableSpecializerPath.registerFieldCallBack(self.fields, self, parsingCB=False) else: raise e
def convertValue(self, typeClass, dst_unitSize=None, dst_endianness=None, dst_sign=None): """Convert the current data in the netzob type specified in parameter. :parameter typeClass: the netzob class to which the current data must be converted :type typeClass: type :keyword dst_unitSize: the unitsize of the destination value. Values must be one of AbstractType.UNITSIZE_*. if None, the value is the default one. :type dst_unitSize: str :keyword dst_endianness: the endianness of the destination value. Values must be AbstractType.ENDIAN_BIG or AbstractType.ENDIAN_LITTLE. if None, the value is the default one. :type dst_endianness: str :keyword dst_sign: the sign of the destination. Values must be AbstractType.SIGN_SIGNED or AbstractType.SIGN_UNSIGNED. if None, the value is the default one. :type dst_sign: str :return: the converted current value in the specified netzob type :rtype: :class:`netzob.Common.Models.AbstractType.AbstractType` """ if typeClass is None: raise TypeError("TypeClass cannot be None") if typeClass not in AbstractType.supportedTypes(): raise TypeError("Requested typeClass ({0}) is not supported.".format(typeClass)) if dst_unitSize is None: dst_unitSize = AbstractType.defaultUnitSize() if dst_endianness is None: dst_endianness = AbstractType.defaultEndianness() if dst_sign is None: dst_sign = AbstractType.defaultSign() if dst_unitSize not in AbstractType.supportedUnitSizes(): raise TypeError("Dst_unitsize is not supported.") if dst_endianness not in AbstractType.supportedEndianness(): raise TypeError("Dst_endianness is not supported.") if dst_sign not in AbstractType.supportedSign(): raise TypeError("Sign is not supported.") from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray return typeClass(TypeConverter.convert(self.value, BitArray, typeClass, src_unitSize=self.unitSize, src_endianness=self.endianness, src_sign=self.sign, dst_unitSize=dst_unitSize, dst_endianness=dst_endianness, dst_sign=dst_sign), unitSize=dst_unitSize, endianness=dst_endianness, sign=dst_sign)
def execute(self, field): """Executes the field edition following the specified messages. Children of the specified field will be replaced with new fields. :param field: the format definition that will be user :type field: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField` :raise Exception: if something bad happens """ if field is None: raise TypeError("The field cannot be None") fieldValues = [ TypeConverter.convert(data, Raw, HexaString) for data in field.getValues(encoded=False) ] if len(fieldValues) == 0: raise Exception("No value found in the field.") # Retrieve longuest field value maxLengthFieldValue = len(max(fieldValues, key=len)) # definies the step following specified unitsize stepUnitsize = self.__computeStepForUnitsize() # Vertical identification of variation indexedValues = [] for i in range(0, maxLengthFieldValue, stepUnitsize): currentIndexValue = [] for fieldValue in fieldValues: if i < len(fieldValue): currentIndexValue.append( fieldValue[i:min(len(fieldValue), i + stepUnitsize)]) else: currentIndexValue.append('') indexedValues.append(currentIndexValue) # If requested, merges the adjacent static fields if self.mergeAdjacentStaticFields: result = [] staticSequences = [] for values in indexedValues: if len(set(values)) == 1: # static staticSequences.append(values[0]) else: # dynamic if len(staticSequences) > 0: result.append([''.join(staticSequences)]) staticSequences = [] result.append(values) if len(staticSequences) > 0: result.append([''.join(staticSequences)]) indexedValues = result # If requested, merges the adjacent dynamic fields if self.mergeAdjacentDynamicFields: result = [] dynamicSequences = [] for values in indexedValues: if len(set(values)) > 1: # dynamic dynamicSequences.append(values) else: # static if len(dynamicSequences) > 0: dynValues = map(None, *dynamicSequences) tmp_result = [] for d in dynValues: tmp_result.append(''.join( [x if x is not None else '' for x in d])) result.append(tmp_result) dynamicSequences = [] result.append(values) if len(dynamicSequences) > 0: dynValues = map(None, *dynamicSequences) tmp_result = [] for d in dynValues: tmp_result.append(''.join( [x if x is not None else '' for x in d])) result.append(tmp_result) indexedValues = result # Create a field for each entry newFields = [] for (i, val) in enumerate(indexedValues): fName = "Field-{0}".format(i) fDomain = DomainFactory.normalizeDomain([ Raw(TypeConverter.convert(v, HexaString, BitArray)) for v in set(val) ]) newFields.append(Field(domain=fDomain, name=fName)) # attach encoding functions for newField in newFields: newField.encodingFunctions = field.encodingFunctions.values() field.fields = newFields
def cluster(self, field, keyField): """Create and return new symbols according to a specific key field. >>> import binascii >>> from netzob.all import * >>> samples = ["00ff2f000000", "000020000000", "00ff2f000000"] >>> messages = [RawMessage(data=binascii.unhexlify(sample)) for sample in samples] >>> f1 = Field(Raw(nbBytes=1)) >>> f2 = Field(Raw(nbBytes=2)) >>> f3 = Field(Raw(nbBytes=3)) >>> symbol = Symbol([f1, f2, f3], messages=messages) >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> newSymbols = Format.clusterByKeyField(symbol, f2) >>> for sym in newSymbols.values(): ... sym.addEncodingFunction(TypeEncodingFunction(HexaString)) ... print sym.name + ":" ... print sym Symbol_ff2f: Field | Field | Field ----- | ------ | -------- '00' | 'ff2f' | '000000' '00' | 'ff2f' | '000000' ----- | ------ | -------- Symbol_0020: Field | Field | Field ----- | ------ | -------- '00' | '0020' | '000000' ----- | ------ | -------- :param field: the field we want to split in new symbols :type field: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField` :param keyField: the field used as a key during the splitting operation :type field: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField` :raise Exception if something bad happens """ # Safe checks if field is None: raise TypeError("'field' should not be None") if keyField is None: raise TypeError("'keyField' should not be None") if keyField not in field.fields: raise TypeError("'keyField' is not a child of 'field'") newSymbols = {} keyFieldMessageValues = keyField.getMessageValues(encoded=False, styled=False) newSymbolsSplittedMessages = {} # we identify what would be the best type of the key field keyFieldType = ASCII for message, keyFieldValue in keyFieldMessageValues.iteritems(): # If the value cannot be parsed as ASCII, we convert it to HexaString if not ASCII().canParse( TypeConverter.convert(keyFieldValue, Raw, BitArray)): keyFieldType = HexaString break # Even if the value is theoritically parsable as ASCII, some caracters cannot be encoded, so we double check tmp_value = TypeConverter.convert(keyFieldValue, Raw, ASCII) tmp2_value = TypeConverter.convert(tmp_value, ASCII, Raw) if keyFieldValue != tmp2_value: # This means we cannot retrieve the original value by encoding and then decoding in ASCII keyFieldType = HexaString break # we create a symbol for each of these uniq values for message, keyFieldValue in keyFieldMessageValues.iteritems(): keyFieldValue = TypeConverter.convert(keyFieldValue, Raw, keyFieldType) if keyFieldValue not in newSymbols.keys(): symbolName = "Symbol_{0}".format(keyFieldValue) newSymbols[keyFieldValue] = Symbol(name=symbolName, messages=[message]) splittedMessages = DataAlignment.align([message.data], field, encoded=False) newSymbolsSplittedMessages[keyFieldValue] = [ splittedMessages[0] ] else: newSymbols[keyFieldValue].messages.append(message) splittedMessages = DataAlignment.align([message.data], field, encoded=False) newSymbolsSplittedMessages[keyFieldValue].append( splittedMessages[0]) for newSymbolKeyValue, newSymbol in newSymbols.iteritems(): # we recreate the same fields in this new symbol as the fields that exist in the original symbol newSymbol.clearFields() for i, f in enumerate(field.fields): if f == keyField: newFieldDomain = TypeConverter.convert( newSymbolKeyValue, keyFieldType, Raw) else: newFieldDomain = set() for j in range( len(newSymbolsSplittedMessages[newSymbolKeyValue]) ): newFieldDomain.add( newSymbolsSplittedMessages[newSymbolKeyValue][j] [i]) newFieldDomain = list(newFieldDomain) newF = Field(name=f.name, domain=newFieldDomain) newF.parent = newSymbol newSymbol.fields.append(newF) # we remove endless fields that accepts no values cells = newSymbol.getCells(encoded=False, styled=False, transposed=False) max_i_cell_with_value = 0 for line in cells: for i_cell, cell in enumerate(line): if cell != '' and max_i_cell_with_value < i_cell: max_i_cell_with_value = i_cell newSymbol.clearFields() for i, f in enumerate(field.fields[:max_i_cell_with_value + 1]): if f == keyField: newFieldDomain = TypeConverter.convert( newSymbolKeyValue, keyFieldType, Raw) else: newFieldDomain = set() for j in range( len(newSymbolsSplittedMessages[newSymbolKeyValue]) ): newFieldDomain.add( newSymbolsSplittedMessages[newSymbolKeyValue][j] [i]) newFieldDomain = list(newFieldDomain) newF = Field(name=f.name, domain=newFieldDomain) newF.parent = newSymbol newSymbol.fields.append(newF) return newSymbols
def __str__(self): result = [] for var, value in self.memory.iteritems(): result.append("{0}: {1}".format( var, TypeConverter.convert(value, BitArray, Raw))) return '\n'.join(result)
def canParse(self, data, unitSize=AbstractType.defaultUnitSize(), endianness=AbstractType.defaultEndianness(), sign=AbstractType.defaultSign()): """Computes if specified data can be parsed as an IPv4 with the predefined constraints. >>> from netzob.all import * >>> ip = IPv4() >>> ip.canParse("192.168.0.10") True >>> ip.canParse("198.128.0.100") True >>> ip.canParse("256.0.0.1") False >>> ip.canParse("127.0.0.1") True >>> ip.canParse("127.0.0.-1") False >>> ip.canParse("::") False >>> ip.canParse("0.0.0.0") False And with some constraints over the expected IPv4: >>> ip = IPv4("192.168.0.10") >>> ip.canParse("192.168.0.10") True >>> ip.canParse("192.168.1.10") False >>> ip.canParse(3232235530) True >>> ip = IPv4("167.20.14.20") >>> ip.canParse(3232235530) False >>> ip.canParse(3232235530) False or with contraints over the expected network the ipv4 belongs to: >>> ip = IPv4(network="192.168.0.0/24") >>> ip.canParse("192.168.0.10") True >>> ip.canParse("192.168.1.10") False :param data: the data to check :type data: python raw :return: True if data can be parsed as a Raw which is always the case (if len(data)>0) :rtype: bool :raise: TypeError if the data is None """ if data is None: raise TypeError("data cannot be None") try: ip = IPv4.encode(data, unitSize=unitSize, endianness=endianness, sign=sign) if ip is None or ip.version != 4 or ip.is_netmask(): return False except: return False try: if self.value is not None: from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.BitArray import BitArray return self.value == TypeConverter.convert( data, IPv4, BitArray, src_unitSize=unitSize, src_endianness=endianness, src_sign=sign, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign) elif self.network is not None: return ip in self.network except: return False return True
def searchDataInMessage(self, data, message, addTags=True, dataLabels=None): """Search in the specified message any of the given data. These data will be searched as it but also under various format. >>> from netzob.all import * >>> message = RawMessage("Reversing protocols with Netzob") >>> sData = [ASCII("protocol")] >>> se = SearchEngine() >>> results = se.searchDataInMessage(sData, message) >>> print results 1 occurence(s) found. >>> for result in results: ... print result ... print repr(result.searchTask.properties["data"]) Found ascii-bits(bigEndian) at [(80L, 144L)] of bitarray('01010010011001010111011001100101011100100111001101101001011011100110011100100000011100000111001001101111011101000110111101100011011011110110110001110011001000000111011101101001011101000110100000100000010011100110010101110100011110100110111101100010') protocol :parameter data: the data to search after. Data must be provided with their netzob type. :type data: a list of :class:`netzob.Common.Models.Types.AbstractType.AbstractType`. :parameter message: the message in which the search will take place :type message: :class:`netzob.Common.Models.Vocabulary.Messages.AbstractMessage` :keyword addTags: if set to True, visualization functions are added to the message to highlights found results. :type addTags: :class:`bool` :keyword dataLabels: an optionnal dict to attach to each data a label to simplify search results identification :type dataLabels: dict :return: a search results detailling where and how occurrences where found. Occurences are also identified in the message through dedicated visualization functions automaticaly added to the message. :rtype: :class:`netzob.Inference.Vocabulary.SearchEngine.SearchResults.SearchResults` """ if data is None or len(data) == 0: raise TypeError("At least one data should be specified.") if message is None: raise TypeError("Message cannot be None") searchTasks = [] for d in data: # normalize the given data normedData = AbstractType.normalize(d) # build search tasks props = dict() props['message'] = message props['data'] = d if dataLabels is not None and d in dataLabels.keys(): props['label'] = dataLabels[d] searchTasks.extend(self.__buildSearchTasks(normedData, props)) # fetch the content of the message and convert it to bitarray target = TypeConverter.convert(message.data, Raw, BitArray) # Generate search cases searchCases = itertools.product([target], searchTasks) searchResults = self.__search(searchCases) # If requested, we tag the results in the message using visualization functions # if addTags: # for searchResult in searchResults: # for (startPos, endPos) in searchResult.ranges: # self._logger.info("function from {} to {}".format(startPos, endPos)) # message.visualizationFunctions.append(HighlightFunction(startPos, endPos)) return searchResults
def specializeSymbol(self, symbol): """This method generates a message based on the provided symbol definition.""" if symbol is None: raise Exception("Specified symbol is None") self._logger.debug("Specifies symbol '{0}'.".format(symbol.name)) # this variable host all the specialization path specializingPaths = [SpecializingPath(memory=self.memory)] for field in symbol.fields: self._logger.debug("Specializing field {0}".format(field.name)) fieldDomain = field.domain if fieldDomain is None: raise Exception( "Cannot specialize field '{0}' since it defines no domain". format(fieldDomain)) fs = FieldSpecializer(field, presets=self.presets) newSpecializingPaths = [] for specializingPath in specializingPaths: newSpecializingPaths.extend(fs.specialize(specializingPath)) specializingPaths = newSpecializingPaths if len(specializingPaths) > 1: self._logger.info( "TODO: multiple valid paths found when specializing this message." ) if len(specializingPaths) == 0: raise Exception("Cannot specialize this symbol.") retainedPath = specializingPaths[0] generatedContent = None # let's configure the generated content for field in symbol.fields: # TODO: only support one level of children... must be improved if len(field.fields) > 0: d = None for child in field.fields: if d is None: d = retainedPath.getDataAssignedToVariable( child.domain).copy() else: d += retainedPath.getDataAssignedToVariable( child.domain).copy() else: d = retainedPath.getDataAssignedToVariable(field.domain) if generatedContent is None: generatedContent = d.copy() else: generatedContent += d.copy() retainedPath.generatedContent = generatedContent self._logger.debug("Specialized message: {0}".format( TypeConverter.convert(retainedPath.generatedContent, BitArray, ASCII))) self.memory = retainedPath.memory return retainedPath
def split(field, delimiter): """Split a field (or symbol) with a specific delimiter. The delimiter can be passed either as an ASCII, a Raw, an HexaString, or any objects that inherit from AbstractType. >>> from netzob.all import * >>> samples = ["aaaaff000000ff10", "bbff110010ff00000011", "ccccccccfffe1f000000ff12"] >>> messages = [RawMessage(data=sample) for sample in samples] >>> symbol = Symbol(messages=messages[:3]) >>> Format.splitDelimiter(symbol, ASCII("ff")) >>> print symbol Field-0 | Field-sep-6666 | Field-2 | Field-sep-6666 | Field-4 ---------- | -------------- | ------------ | -------------- | ---------- 'aaaa' | 'ff' | '000000' | 'ff' | '10' 'bb' | 'ff' | '110010' | 'ff' | '00000011' 'cccccccc' | 'ff' | 'fe1f000000' | 'ff' | '12' ---------- | -------------- | ------------ | -------------- | ---------- >>> samples = ["434d446964656e74696679230400000066726564", "5245536964656e74696679230000000000000000", "434d44696e666f2300000000", "524553696e666f230000000004000000696e666f","434d4473746174732300000000","52455373746174732300000000050000007374617473","434d4461757468656e7469667923090000006d7950617373776421","52455361757468656e74696679230000000000000000","434d44656e6372797074230a00000031323334353674657374","524553656e637279707423000000000a00000073707176777436273136","434d4464656372797074230a00000073707176777436273136","5245536465637279707423000000000a00000031323334353674657374","434d446279652300000000","524553627965230000000000000000","434d446964656e746966792307000000526f626572746f","5245536964656e74696679230000000000000000","434d44696e666f2300000000","524553696e666f230000000004000000696e666f","434d4473746174732300000000","52455373746174732300000000050000007374617473","434d4461757468656e74696679230a000000615374726f6e67507764","52455361757468656e74696679230000000000000000","434d44656e63727970742306000000616263646566","524553656e6372797074230000000006000000232021262724","434d44646563727970742306000000232021262724","52455364656372797074230000000006000000616263646566","434d446279652300000000","524553627965230000000000000000"] >>> messages = [RawMessage(data=TypeConverter.convert(sample, HexaString, Raw)) for sample in samples] >>> symbol = Symbol(messages=messages) >>> symbol.encodingFunctions.add(TypeEncodingFunction(ASCII)) # Change visualization to hexastring >>> Format.splitDelimiter(symbol, ASCII("#")) >>> print symbol Field-0 | Field-sep-23 | Field-2 | Field-sep-23 | Field-4 --------------- | ------------ | -------------------- | ------------ | ------- 'CMDidentify' | '#' | '....fred' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....myPasswd!' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....123456test' | '' | '' 'RESencrypt' | '#' | "........spqvwt6'16" | '' | '' 'CMDdecrypt' | '#' | "....spqvwt6'16" | '' | '' 'RESdecrypt' | '#' | '........123456test' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' 'CMDidentify' | '#' | '....Roberto' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....aStrongPwd' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....abcdef' | '' | '' 'RESencrypt' | '#' | '........' | '#' | " !&'$" 'CMDdecrypt' | '#' | '....' | '#' | " !&'$" 'RESdecrypt' | '#' | '........abcdef' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' --------------- | ------------ | -------------------- | ------------ | ------- >>> print symbol.fields[0]._str_debug() Field-0 |-- Alt |-- Data (Raw='CMDidentify' ((0, 88))) |-- Data (Raw='RESidentify' ((0, 88))) |-- Data (Raw='CMDinfo' ((0, 56))) |-- Data (Raw='RESinfo' ((0, 56))) |-- Data (Raw='CMDstats' ((0, 64))) |-- Data (Raw='RESstats' ((0, 64))) |-- Data (Raw='CMDauthentify' ((0, 104))) |-- Data (Raw='RESauthentify' ((0, 104))) |-- Data (Raw='CMDencrypt' ((0, 80))) |-- Data (Raw='RESencrypt' ((0, 80))) |-- Data (Raw='CMDdecrypt' ((0, 80))) |-- Data (Raw='RESdecrypt' ((0, 80))) |-- Data (Raw='CMDbye' ((0, 48))) |-- Data (Raw='RESbye' ((0, 48))) :param field : the field to consider when spliting :type: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField` :param delimiter : the delimiter used to split messages of the field :type: :class:`netzob.Common.Models.Types.AbstractType.AbstractType` """ if delimiter is None: raise TypeError("Delimiter cannot be None.") if field is None: raise TypeError("Field cannot be None.") if len(field.messages) < 1: raise ValueError( "The associated symbol does not contain any message.") # Find message substrings after applying delimiter splittedMessages = [] for cell in field.getValues(encoded=False, styled=False): splittedMessage = cell.split(delimiter.value.tobytes()) splittedMessages.append(splittedMessage) import itertools # Inverse the array, so that columns contains observed values for each field splittedMessages = list(itertools.izip_longest(*splittedMessages)) # If the delimiter does not create splitted fields if len(splittedMessages) <= 1: return # Else, we add (2*len(splittedMessages)-1) fields newFields = [] iField = -1 for i in range(len(splittedMessages)): iField += 1 fieldDomain = list() # temporary set that hosts all the observed values to prevent useless duplicate ones observedValues = set() has_inserted_empty_value = False isEmptyField = True # To avoid adding an empty field for v in splittedMessages[i]: if v != "" and v is not None: isEmptyField = False if v not in observedValues: fieldDomain.append(Raw(v)) observedValues.add(v) else: if not has_inserted_empty_value: fieldDomain.append(Raw(nbBytes=0)) has_inserted_empty_value = True if not isEmptyField: newField = Field( domain=DomainFactory.normalizeDomain(fieldDomain), name="Field-" + str(iField)) newField.encodingFunctions = field.encodingFunctions.values() newFields.append(newField) iField += 1 fieldName = "Field-sep-" + TypeConverter.convert( delimiter.value, BitArray, HexaString) newFields.append( Field(domain=Alt([delimiter, Raw(nbBytes=0)]), name=fieldName)) newFields.pop() # Reset the field from netzob.Inference.Vocabulary.Format import Format Format.resetFormat(field) # Create a field for each entry field.fields = newFields