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 _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 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 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 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 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 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 __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 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 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 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 __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 __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 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 __repr__(self): if self.value != 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 __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 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 Internet Checksum {0}".format(self)) if variableSpecializerPath is None: raise Exception("VariableSpecializerPath cannot be None") try: newValue = self._computeExpectedValue(variableSpecializerPath) self._logger.fatal("Register ; {0}".format(newValue)) variableSpecializerPath.addResult(self, newValue.copy()) except Exception, e: self._logger.warn( "Cannot specialize since no value is available for the Internet checksum 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.fieldDependencies, self, parsingCB=False) else: raise e
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 encode(self, data): self._logger.debug(data) return TypeConverter.convert(data, BitArray, self.type, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign)
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 __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 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 _generateDataValues(self, cellsData): result = [] for data in cellsData: if len(data) > 0: result.append(TypeConverter.convert( data[:8], Raw, Integer)) # We take only the first 8 octets else: result.append(0) return result
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 __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 _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 _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 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 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): """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 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 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 __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 __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 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 __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 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 generate(self, generationStrategy=None): """Generates a Timestamp that follows the specified generationStrategy >>> from netzob.all import * >>> f = Field(Timestamp()) >>> value = f.specialize() >>> print len(value) 4 >>> f = Field(Timestamp(epoch=Timestamp.EPOCH_WINDOWS, unitSize = AbstractType.UNITSIZE_64)) >>> print len(f.specialize()) 8 """ from netzob.Common.Models.Types.BitArray import BitArray from netzob.Common.Models.Types.TypeConverter import TypeConverter from netzob.Common.Models.Types.Integer import Integer if self.value is not None: return self.value # computes utc now now = datetime.utcnow() # substract the utc now with the epoch timestamp_datetime = now - self.epoch # convert obtained datetime to timestamp in seconds result_sec = timestamp_datetime.total_seconds() # apply the unity result_unity = int(result_sec * self.unity) # convert to bitarray final = TypeConverter.convert(result_unity, Integer, BitArray, src_unitSize=self.unitSize, src_endianness=self.endianness, src_sign=AbstractType.SIGN_UNSIGNED, dst_endianness=self.endianness) return final
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 __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 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 buildRegexForStaticValue(value): """It creates a NetzobRegex which represents a regex with the specified Raw static value. >>> from netzob.all import * >>> import regex as re >>> data = "Hello netzob" >>> hexData = TypeConverter.convert(data, ASCII, HexaString) >>> nRegex = NetzobRegex.buildRegexForStaticValue(data) >>> compiledRegex = re.compile(str(nRegex)) >>> dynamicDatas = compiledRegex.match(hexData) >>> print TypeConverter.convert(hexData[dynamicDatas.start(nRegex.id):dynamicDatas.end(nRegex.id)], HexaString, ASCII) Hello netzob :param value: the static value the regex must represents :type value: python raw (will be encoded in HexaString in the regex) :return: the regex which represents the specified valued encoed in HexaString :type: :class:`netzob.Common.Utils.NetzobRegex.NetzobRegex` """ hexaStringValue = TypeConverter.convert(value, Raw, HexaString) return NetzobStaticRegex(hexaStringValue)
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, Integer, 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(Integer, self).__init__(self.__class__.__name__, value, nbBits, 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)