# -*- coding: utf-8 -*-
# +---------------------------------------------------------------------------+
# | 01001110 01100101 01110100 01111010 01101111 01100010 |
# | |
# | Netzob : Inferring communication protocols |
# +---------------------------------------------------------------------------+
# | Copyright (C) 2011-2017 Georges Bossert and Frédéric Guihéry |
# | This program is free software: you can redistribute it and/or modify |
# | it under the terms of the GNU General Public License as published by |
# | the Free Software Foundation, either version 3 of the License, or |
# | (at your option) any later version. |
# | |
# | This program is distributed in the hope that it will be useful, |
# | but WITHOUT ANY WARRANTY; without even the implied warranty of |
# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
# | GNU General Public License for more details. |
# | |
# | You should have received a copy of the GNU General Public License |
# | along with this program. If not, see <http://www.gnu.org/licenses/>. |
# +---------------------------------------------------------------------------+
# | @url : http://www.netzob.org |
# | @contact : contact@netzob.org |
# | @sponsors : Amossys, http://www.amossys.fr |
# | Supélec, http://www.rennes.supelec.fr/ren/rd/cidre/ |
# +---------------------------------------------------------------------------+
# +---------------------------------------------------------------------------+
# | File contributors : |
# | - Georges Bossert <georges.bossert (a) supelec.fr> |
# | - Frédéric Guihéry <frederic.guihery (a) amossys.fr> |
# +---------------------------------------------------------------------------+
# +---------------------------------------------------------------------------+
# | Standard library imports |
# +---------------------------------------------------------------------------+
import struct
import random
from bitarray import bitarray
from typing import Iterable
# +---------------------------------------------------------------------------+
# | Related third party imports |
# +---------------------------------------------------------------------------+
# +---------------------------------------------------------------------------+
# | Local application imports |
# +---------------------------------------------------------------------------+
from netzob.Model.Vocabulary import partialclass
from netzob.Model.Vocabulary.Types.AbstractType import AbstractType, Endianness, Sign, UnitSize
from netzob.Common.Utils.Decorators import public_api
[docs]class Integer(AbstractType):
r"""The Integer class represents an integer, with the
capability to express constraints regarding the sign, the
endianness and the unit size.
The Integer constructor expects some parameters:
:param value: This parameter is used to describe a domain that contains a fixed integer. If None, the constructed Integer will represent an interval of values (see :attr:`interval` parameter).
:type value: :class:`bitarray` or :class:`int`, optional
:param interval: This parameter is used to describe a domain that contains an interval of permitted values. This information is used to compute the storage size of the Integer. If None, the interval will range from the minimum value to the maximum value that an integer can encode, according to its unit size, endianness and sign attributes.
:type interval: a tuple with the min and the max values specified as :class:`int`, optional
:param unitSize: The unitsize, in bits, of the storage area used to encode the integer. Values must be one of UnitSize.SIZE_*.
The following unit sizes are available:
* UnitSize.SIZE_8
* UnitSize.SIZE_16 (default unit size)
* UnitSize.SIZE_24
* UnitSize.SIZE_32
* UnitSize.SIZE_64
:type unitSize: :class:`UnitSize <netzob.Model.Vocabulary.Types.AbstractType.UnitSize>`, optional
:param endianness: The endianness of the value.
The following endiannesses are available:
* Endianness.BIG (default endianness)
* Endianness.LITTLE
:type endianness: :class:`Endianness <netzob.Model.Vocabulary.Types.AbstractType.Endianness>`, optional
:param sign: The sign of the value.
The following signs are available:
* Sign.SIGNED (default sign)
* Sign.UNSIGNED
:type sign: :class:`Sign <netzob.Model.Vocabulary.Types.AbstractType.Sign>`, optional
:param default: This parameter is the default value used in specialization.
:type default: :class:`bitarray` or :class:`int`, optional
.. note::
:attr:`value` and :attr:`interval` parameters are mutually exclusive.
Setting both values raises an :class:`Exception`.
:attr:`value` and :attr:`default` parameters are mutually exclusive.
Setting both values raises an :class:`Exception`.
The Integer class provides the following public variables:
:var value: The current value of the instance. This value is represented
under the bitarray format.
:var size: The size of the expected data type defined by a tuple (min integer, max integer).
Instead of a tuple, an integer can be used to represent both min and max values.
:var unitSize: The unitSize of the current value.
:var endianness: The endianness of the current value.
:var sign: The sign of the current value.
:var default: The default value used in specialization.
:vartype value: :class:`bitarray`
:vartype size: a tuple (:class:`int`, :class:`int`) or :class:`int`
:vartype unitSize: :class:`str`
:vartype endianness: :class:`str`
:vartype sign: :class:`str`
:vartype default: :class:`bitarray`
**Examples of Integer object instantiations**
The creation of an Integer with no parameter will create a signed,
big-endian integer of 16 bits:
>>> from netzob.all import *
>>> i = Integer()
>>> i.generate().tobytes() # doctest: +SKIP
b'\x94\xba'
The following example shows how to define an integer encoded in
sequences of 8 bits and with a constant value of 12 (thus producing
``\x0c``):
>>> from netzob.all import *
>>> i = Integer(12, unitSize=UnitSize.SIZE_8)
>>> i.generate().tobytes()
b'\x0c'
The following example shows how to define an integer encoded in
sequences of 32 bits and with a constant value of 12 (thus
producing ``\x00\x00\x00\x0c``):
>>> from netzob.all import *
>>> i = Integer(12, unitSize=UnitSize.SIZE_32)
>>> i.generate().tobytes()
b'\x00\x00\x00\x0c'
The following example shows how to define an integer encoded in
sequences of 32 bits in little endian with a constant value of 12
(thus producing ``\x0c\x00\x00\x00``):
>>> from netzob.all import *
>>> i = Integer(12, unitSize=UnitSize.SIZE_32, endianness=Endianness.LITTLE)
>>> i.generate().tobytes()
b'\x0c\x00\x00\x00'
The following example shows how to define a signed integer
encoded in sequences of 16 bits with a constant value of -12 (thus
producing ``\xff\xf4``):
>>> from netzob.all import *
>>> i = Integer(-12, sign=Sign.SIGNED, unitSize=UnitSize.SIZE_16)
>>> i.generate().tobytes()
b'\xff\xf4'
**Examples of pre-defined Integer types**
For convenience, common specific integer types are also available, with
pre-defined values of :attr:`unitSize`, :attr:`sign` and :attr:`endianness`
attributes. They are used to shorten calls of singular definitions.
Available big-endian pre-defined Integer types are:
* int8be (or int8)
* int16be (or int16)
* int24be (or int24)
* int32be (or int32)
* int64be (or int64)
* uint8be (or uint8)
* uint16be (or uint16)
* uint24be (or uint24)
* uint32be (or uint32)
* uint64be (or uint64)
Available little-endian pre-defined Integer types are:
* int8le
* int16le
* int24le
* int32le
* int64le
* uint8le
* uint16le
* uint24le
* uint32le
* uint64le
For example, a *16-bit little-endian unsigned* Integer is classically defined
like this:
>>> from netzob.all import *
>>> i = Integer(42,
... unitSize=UnitSize.SIZE_16,
... sign=Sign.UNSIGNED,
... endianness=Endianness.LITTLE)
Could also be called in an equivalent form:
>>> from netzob.all import *
>>> i = uint16le(42)
There is an equivalence between these two integers, for every
internal value of the type:
>>> from netzob.all import *
>>> i1 = Integer(42,
... unitSize=UnitSize.SIZE_16,
... sign=Sign.UNSIGNED,
... endianness=Endianness.LITTLE)
>>> i2 = uint16le(42)
>>> i1, i2
(42, 42)
>>> i1 == i2
True
But a comparison between two specific integers of different kinds will
always fail, even if their values look equivalent:
>>> from netzob.all import *
>>> i1 = uint16le(42)
>>> i2 = uint32le(42)
>>> i1 == i2
False
And even when the concrete value seems identical, the integer
objects are not:
>>> from netzob.all import *
>>> i1 = uint16le(42)
>>> i2 = int16le(42)
>>> i1, i2
(42, 42)
>>> print(i1, i2)
Integer(42) Integer(42)
>>> i1 == i2
False
**Integer raw representations**
The following examples show how to create integers with different
raw representation, depending on data type attributes. In these
examples, we create a 16-bit little endian, a 16-bit big endian,
a 32-bit little endian and a 32-bit big endian:
>>> from netzob.all import *
>>> int16le(1234).value.tobytes()
b'\xd2\x04'
>>> int16be(1234).value.tobytes()
b'\x04\xd2'
>>> int32le(1234).value.tobytes()
b'\xd2\x04\x00\x00'
>>> int32be(1234).value.tobytes()
b'\x00\x00\x04\xd2'
**Representation of Integer type objects**
The following examples show the representation of Integer objects
with and without a constant value.
>>> from netzob.all import *
>>> i = int16le(12)
>>> print(i)
Integer(12)
>>> from netzob.all import *
>>> i = int16le()
>>> print(i)
Integer(-32768,32767)
**Encoding of Integer type objects**
The following examples show the encoding of Integer objects with
and without a constant value.
>>> from netzob.all import *
>>> i = int32le(12)
>>> repr(i)
'12'
>>> from netzob.all import *
>>> i = int32le()
>>> repr(i)
'None'
**Using a default value**
This next example shows the usage of a default value:
>>> from netzob.all import *
>>> t = uint8(default=3)
>>> t.generate().tobytes()
b'\x03'
>>> from netzob.all import *
>>> t = Integer(interval=(1, 4), default=4)
>>> t.generate().tobytes()
b'\x00\x04'
"""
@public_api
def __init__(self,
value=None,
interval=None,
unitSize=UnitSize.SIZE_16,
endianness=AbstractType.defaultEndianness(),
sign=AbstractType.defaultSign(),
default=None):
if value is not None and interval is not None:
raise ValueError("An Integer should have either its value or its interval set, but not both")
if value is not None and default is not None:
raise ValueError("An Integer should have either its constant value or its default value set, but not both")
# Validate uniSize
valid_unitSizes = [UnitSize.SIZE_8, UnitSize.SIZE_16, UnitSize.SIZE_24, UnitSize.SIZE_32, UnitSize.SIZE_64]
if unitSize not in valid_unitSizes:
raise ValueError("unitSize parameter should be one of '{}', but not '{}'".format(valid_unitSizes, str(unitSize)))
# Convert value to bitarray
if value is not None and not isinstance(value, bitarray):
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
# Check if value is correct
if not isinstance(value, int):
raise ValueError("Input value shoud be a integer. Value received: '{}'".format(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)
# Convert default value to bitarray
if default is not None and not isinstance(default, bitarray):
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
# Check if default value is correct
if not isinstance(default, int):
raise ValueError("Input default value shoud be a integer. Default value received: '{}'".format(default))
default = TypeConverter.convert(
default,
Integer,
BitArray,
src_unitSize=unitSize,
src_endianness=endianness,
src_sign=sign,
dst_unitSize=unitSize,
dst_endianness=endianness,
dst_sign=sign)
# Handle interval
if unitSize is None:
raise TypeError("unitSize cannot be None")
if sign is None:
raise TypeError("sign cannot be None")
if endianness is None:
raise TypeError("endianness cannot be None")
interval = self._normalizeInterval(interval, unitSize, sign)
super().__init__(
self.__class__.__name__,
value,
interval,
unitSize=unitSize,
endianness=endianness,
sign=sign,
default=default)
def __str__(self):
if self.value is not None:
return "{}({})".format(self.typeName, Integer.encode(self.value.tobytes(), unitSize=self.unitSize, endianness=self.endianness, sign=self.sign))
else:
if self.size[0] == self.size[1]:
self._logger.fatal("PAN")
return "{}(interval={})".format(self.typeName, self.size[0])
else:
return "{}({},{})".format(self.typeName, self.size[0], self.size[1])
def _normalizeInterval(self, interval, unitSize, sign):
if interval is None:
interval = (None, None)
elif isinstance(interval, int):
interval = (interval, interval)
else:
if not (isinstance(interval, tuple) and len(interval) == 2):
raise ValueError("Input interval shoud be a tuple of two integers. Value received: '{}'".format(interval))
if not isinstance(interval[0], int):
raise TypeError("First element of interval should be an integer")
if not isinstance(interval[1], int):
raise TypeError("Second element of interval should be an integer")
if interval[0] == interval[1] == 0:
raise TypeError("Interval must be defined with a tuple of integers, and cannot be both equal to zero")
if interval[1] < interval[0]:
ValueError("Internal must be defined with a tuple of integers, where the second value is greater than the first value")
# Compute min and max values
min_interval = getMinStorageValue(unitSize=unitSize, sign=sign)
max_interval = getMaxStorageValue(unitSize=unitSize, sign=sign)
if ((interval[0] is not None and interval[0] < min_interval) or
(interval[1] is not None and interval[1] > max_interval)):
raise ValueError("Specified interval '{}' does not fit in specified unitSize '{}'".format(interval, unitSize))
# Reset min and max values if a valid interval is provided
if interval[0] is not None:
min_interval = interval[0]
if interval[1] is not None:
max_interval = interval[1]
return min_interval, max_interval
@public_api
def count(self):
r"""
>>> from netzob.all import *
>>> Integer().count()
65536
>>> Integer(4).count()
1
>>> Integer(interval=(1, 10)).count()
10
>>> uint8(interval=(1, 10)).count()
10
>>> uint8().count()
256
"""
if self.value is not None:
return 1
else:
return self.getMaxValue() - self.getMinValue() + 1
def getMinValue(self):
if self.value is None:
return self.size[0]
else:
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
return TypeConverter.convert(self.value, BitArray, Integer, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign)
def getMaxValue(self):
if self.value is None:
return self.size[1]
else:
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
return TypeConverter.convert(self.value, BitArray, Integer, dst_unitSize=self.unitSize, dst_endianness=self.endianness, dst_sign=self.sign)
def getMinStorageValue(self):
return getMinStorageValue(self.unitSize, self.sign)
def getMaxStorageValue(self):
return getMaxStorageValue(self.unitSize, self.sign)
def canParse(self,
data,
unitSize=AbstractType.defaultUnitSize(),
endianness=AbstractType.defaultEndianness(),
sign=AbstractType.defaultSign()):
"""This method returns True if data is a Integer.
For the moment its always true because we consider
the integer type to be very similar to the raw type.
:param data: the data to check
:type data: python raw
:return: True if data is can be parsed as a Integer
:rtype: bool
:raise: TypeError if the data is None
>>> from netzob.all import *
>>> from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
>>> Integer().canParse(10)
True
>>> Integer(10).canParse(11)
False
By default, an Integer() with no parameter has a storage size
of 8 bits:
>>> Integer().canParse(-128)
True
>>> Integer().canParse(32768)
Traceback (most recent call last):
...
struct.error: 'h' format requires -32768 <= number <= 32767
To specify a bigger storage, the unitSize should be used:
>>> Integer(unitSize=UnitSize.SIZE_32).canParse(32768)
True
"""
if data is None:
raise TypeError("data cannot be None")
if not isinstance(data, int) and len(data) == 0: # data cannot be an empty string
return False
# Convert data to bitarray
if data is not None and not isinstance(data, bitarray):
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
# Check if data is correct
if not isinstance(data, int):
raise ValueError("Input data shoud be a integer. Data received: '{}'".format(data))
data = TypeConverter.convert(
data,
Integer,
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)
# Compare with self.value if it is defined
if self.value is not None:
return self.value == data
# Else, compare with expected size
if self.size is not None and isinstance(self.size, Iterable) and len(self.size) == 2:
if len(data) != self.unitSize.value:
return False
minSize = min(self.size)
maxSize = max(self.size)
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
int_data = TypeConverter.convert(data, BitArray, Integer,
dst_unitSize=self.unitSize,
dst_endianness=self.endianness,
dst_sign=self.sign)
return minSize <= int_data <= maxSize
raise Exception("Cannot parse this data '{}' because no domain is "
"expected.".format(data))
@staticmethod
def decode(data,
unitSize=AbstractType.defaultUnitSize(),
endianness=AbstractType.defaultEndianness(),
sign=AbstractType.defaultSign()):
r"""This method convert the specified data in python raw format.
>>> from netzob.all import *
>>> Integer.decode(23)
b'\x17'
>>> Integer.decode(-1, sign=Sign.UNSIGNED)
Traceback (most recent call last):
...
struct.error: ubyte format requires 0 <= number <= 255
>>> Integer.decode(-1, sign=Sign.SIGNED)
b'\xff'
>>> Integer.decode(2000000000000000)
Traceback (most recent call last):
...
struct.error: byte format requires -128 <= number <= 127
>>> Integer.decode(2000000000000000, unitSize=UnitSize.SIZE_64)
b'\x00\x07\x1a\xfdI\x8d\x00\x00'
>>> Integer.decode(25, unitSize=UnitSize.SIZE_16, endianness=Endianness.LITTLE)
b'\x19\x00'
>>> Integer.decode(25, unitSize=UnitSize.SIZE_16, endianness=Endianness.BIG)
b'\x00\x19'
>>> val = 167749568
>>> a = Integer.decode(val, unitSize=UnitSize.SIZE_32)
>>> b = Integer.encode(a, unitSize=UnitSize.SIZE_32)
>>> b == val
True
:param data: the data encoded in Integer which will be decoded in raw
:type data: the current type
:keyword unitSize: the unitsize to consider while encoding. Values must be one of UnitSize.SIZE_*
:type unitSize: str
:keyword endianness: the endianness to consider while encoding. Values must be Endianness.BIG or Endianness.LITTLE
:type endianness: str
:keyword sign: the sign to consider while encoding Values must be Sign.SIGNED or Sign.UNSIGNED
:type sign: :class:`Enum`
:return: data encoded in python raw
:rtype: python raw
:raise: TypeError if parameters are not valid.
"""
if data is None:
raise TypeError("data cannot be None")
f = Integer.computeFormat(unitSize, endianness, sign)
data = struct.pack(f, int(data))
# Special case for 24 bits integers
if unitSize == UnitSize.SIZE_24:
if endianness == Endianness.BIG:
data = data[1:]
else:
data = data[:-1]
return data
@staticmethod
def encode(data,
unitSize=AbstractType.defaultUnitSize(),
endianness=AbstractType.defaultEndianness(),
sign=AbstractType.defaultSign()):
r"""This method converts a python raw data to an Integer.
>>> from netzob.all import *
>>> raw = Integer.decode(23)
>>> Integer.encode(raw)
23
>>> raw = Integer.decode(1200, unitSize=UnitSize.SIZE_16)
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_16)
1200
>>> raw = Integer.decode(25, unitSize=UnitSize.SIZE_16, endianness=Endianness.LITTLE)
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_16, endianness=Endianness.BIG)
6400
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_16, endianness=Endianness.LITTLE)
25
>>> Integer.encode(b'\xcc\xac\x9c\x0c\x1c\xacL\x1c,\xac', unitSize=UnitSize.SIZE_8)
-395865088909314208584756
>>> raw = b'\xcc\xac\x9c'
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_16, endianness=Endianness.BIG)
10210476
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_32, endianness=Endianness.BIG)
13413532
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_32, endianness=Endianness.LITTLE)
10267852
:param data: the data encoded in python raw which will be encoded in current type
:type data: python raw
:keyword unitSize: the unitsize to consider while encoding. Values must be one of UnitSize.SIZE_*
:type unitSize: :class:`Enum`
:keyword endianness: the endianness to consider while encoding. Values must be Endianness.BIG or Endianness.LITTLE
:type endianness: :class:`Enum`
:keyword sign: the sign to consider while encoding Values must be Sign.SIGNED or Sign.UNSIGNED
:type sign: :class:`Enum`
:return: data encoded in python raw
:rtype: python raw
:raise: TypeError if parameters are not valid.
"""
if data is None:
raise TypeError("data cannot be None")
perWordFormat = Integer.computeFormat(unitSize, endianness, sign)
nbWords = int(len(data) * 8 / int(unitSize.value))
# Check whether the input data matches unitSize. If not take
# precautions to able to pad it with null bytes later.
padding_nullbytes = 0
rest = (len(data) * 8) % int(unitSize.value)
if rest != 0:
nbWords += 1
padding_nullbytes = (int(unitSize.value) - rest) / 8
finalValue = 0
iWord = 0
start = 0
end = nbWords
inc = 1
if endianness == Endianness.BIG:
end = 0
start = nbWords
inc = -1
for i in range(start, end, inc):
# Extract the portion that represents the current word
startPos = int(iWord * int(unitSize.value) / 8)
endPos = int(iWord * int(unitSize.value) / 8 + int(unitSize.value) / 8)
wordData = data[startPos:endPos]
# Pad with null bytes to statisfy the unitSize.
if padding_nullbytes > 0 and i == (end - inc):
if endianness == Endianness.BIG:
wordData = b'\x00' * int(padding_nullbytes) + wordData
elif endianness == Endianness.LITTLE:
wordData += b'\x00' * int(padding_nullbytes)
else:
raise ValueError(
"Invalid endianness value: {0}".format(endianness))
# Special case for 24 bits integers
if unitSize == UnitSize.SIZE_24:
if endianness == Endianness.BIG:
wordData = b'\x00' + wordData
else:
wordData = wordData + b'\x00'
unpackedWord = struct.unpack(perWordFormat, wordData)[0]
# Special case for 24 bits integers
if unitSize == UnitSize.SIZE_24 and sign == Sign.SIGNED:
unpackedWord = unpackedWord if not (unpackedWord & 0x800000) else unpackedWord - 0x1000000
unpackedWord = unpackedWord << int(unitSize.value) * iWord
finalValue = finalValue + unpackedWord
iWord += 1
return finalValue
@staticmethod
def computeFormat(unitSize, endianness, sign):
# endian
if endianness == Endianness.BIG:
endianFormat = '>'
elif endianness == Endianness.LITTLE:
endianFormat = '<'
else:
raise ValueError(
"Invalid endianness value: {0}".format(endianness))
# unitSize
if unitSize == UnitSize.SIZE_8 or unitSize == UnitSize.SIZE_4:
unitFormat = 'b'
elif unitSize == UnitSize.SIZE_16:
unitFormat = 'h'
elif unitSize == UnitSize.SIZE_24:
unitFormat = 'i'
elif unitSize == UnitSize.SIZE_32:
unitFormat = 'i'
elif unitSize == UnitSize.SIZE_64:
unitFormat = 'q'
else:
raise ValueError(
"Only 8, 16, 32 and 64 bits unitsize are available for integers"
)
# sign
if sign == Sign.UNSIGNED:
unitFormat = unitFormat.upper()
return endianFormat + unitFormat
def generate(self):
"""Generates a random integer inside the given interval.
>>> from netzob.all import *
>>> v1 = Integer(interval=(-10, -1)).generate()
>>> v1[0] is True # sign bit (MSB) is set
True
>>> v1
bitarray('1111111111111111')
>>> v2 = Integer(42, sign=Sign.UNSIGNED)
>>> v2.generate()
bitarray('0000000000101010')
>>> v3 = uint16be(0xff00)
>>> v3.generate()
bitarray('1111111100000000')
"""
if self.value is not None:
return self.value
elif self.default is not None:
return self.default
elif self.size is not None and isinstance(self.size, Iterable) and len(self.size) == 2:
from netzob.Model.Vocabulary.Types.TypeConverter import TypeConverter
from netzob.Model.Vocabulary.Types.BitArray import BitArray
# Size is interpreted here as an interval
val = random.randint(min(self.size), max(self.size))
return TypeConverter.convert(val, Integer, BitArray,
src_sign=self.sign,
src_unitSize=self.unitSize,
src_endianness=self.endianness,
dst_sign=self.sign,
dst_unitSize=self.unitSize,
dst_endianness=self.endianness)
else:
raise Exception("Cannot generate integer value, as nor constant value or interval is defined")
def getFixedBitSize(self):
self._logger.debug("Determine the deterministic size of the value of "
"the type")
return self.unitSize.value
def getMinStorageValue(unitSize, sign):
if sign == Sign.UNSIGNED:
return 0
else:
return -int((2**int(unitSize.value)) / 2)
def getMaxStorageValue(unitSize, sign):
if sign == Sign.UNSIGNED:
d = (1 << unitSize.value) - 1
else:
d = int((1 << unitSize.value) / 2) - 1
return d
int8be = partialclass(Integer,
unitSize=UnitSize.SIZE_8,
sign=Sign.SIGNED,
endianness=Endianness.BIG)
int8le = partialclass(Integer,
unitSize=UnitSize.SIZE_8,
sign=Sign.SIGNED,
endianness=Endianness.LITTLE)
uint8be = partialclass(Integer,
unitSize=UnitSize.SIZE_8,
sign=Sign.UNSIGNED,
endianness=Endianness.BIG)
uint8le = partialclass(Integer,
unitSize=UnitSize.SIZE_8,
sign=Sign.UNSIGNED,
endianness=Endianness.LITTLE)
int16be = partialclass(Integer,
unitSize=UnitSize.SIZE_16,
sign=Sign.SIGNED,
endianness=Endianness.BIG)
int16le = partialclass(Integer,
unitSize=UnitSize.SIZE_16,
sign=Sign.SIGNED,
endianness=Endianness.LITTLE)
uint16be = partialclass(Integer,
unitSize=UnitSize.SIZE_16,
sign=Sign.UNSIGNED,
endianness=Endianness.BIG)
uint16le = partialclass(Integer,
unitSize=UnitSize.SIZE_16,
sign=Sign.UNSIGNED,
endianness=Endianness.LITTLE)
int24be = partialclass(Integer,
unitSize=UnitSize.SIZE_24,
sign=Sign.SIGNED,
endianness=Endianness.BIG)
int24le = partialclass(Integer,
unitSize=UnitSize.SIZE_24,
sign=Sign.SIGNED,
endianness=Endianness.LITTLE)
uint24be = partialclass(Integer,
unitSize=UnitSize.SIZE_24,
sign=Sign.UNSIGNED,
endianness=Endianness.BIG)
uint24le = partialclass(Integer,
unitSize=UnitSize.SIZE_24,
sign=Sign.UNSIGNED,
endianness=Endianness.LITTLE)
int32be = partialclass(Integer,
unitSize=UnitSize.SIZE_32,
sign=Sign.SIGNED,
endianness=Endianness.BIG)
int32le = partialclass(Integer,
unitSize=UnitSize.SIZE_32,
sign=Sign.SIGNED,
endianness=Endianness.LITTLE)
uint32be = partialclass(Integer,
unitSize=UnitSize.SIZE_32,
sign=Sign.UNSIGNED,
endianness=Endianness.BIG)
uint32le = partialclass(Integer,
unitSize=UnitSize.SIZE_32,
sign=Sign.UNSIGNED,
endianness=Endianness.LITTLE)
int64be = partialclass(Integer,
unitSize=UnitSize.SIZE_64,
sign=Sign.SIGNED,
endianness=Endianness.BIG)
int64le = partialclass(Integer,
unitSize=UnitSize.SIZE_64,
sign=Sign.SIGNED,
endianness=Endianness.LITTLE)
uint64be = partialclass(Integer,
unitSize=UnitSize.SIZE_64,
sign=Sign.UNSIGNED,
endianness=Endianness.BIG)
uint64le = partialclass(Integer,
unitSize=UnitSize.SIZE_64,
sign=Sign.UNSIGNED,
endianness=Endianness.LITTLE)
int8, int16, int24, int32, int64 = int8be, int16be, int24be, int32be, int64be
uint8, uint16, uint24, uint32, uint64 = uint8be, uint16be, uint24be, uint32be, uint64be
def _test():
r"""
>>> from netzob.all import *
>>> t = Integer()
>>> print(t)
Integer(-32768,32767)
>>> t.size
(-32768, 32767)
>>> t.unitSize
UnitSize.SIZE_16
>>> t = Integer(interval=(4, 16))
>>> print(t)
Integer(4,16)
>>> t = Integer(4)
>>> print(t)
Integer(4)
Examples of Integer internal attribute access
>>> from netzob.all import *
>>> cDec = Integer(20)
>>> print(repr(cDec))
20
>>> cDec.typeName
'Integer'
>>> cDec.value
bitarray('0000000000010100')
The required size in bits is automatically computed following the specifications:
>>> dec = Integer(10)
>>> dec.size
(-32768, 32767)
>>> dec = Integer(interval=(-120, 10))
>>> dec.size
(-120, 10)
Symbol abstraction:
>>> from netzob.all import Field, Symbol
>>> domains = [
... uint16(1), int8le(), int32be(0x007F0041), uint16le(2)
... ]
>>> symbol = Symbol(fields=[Field(d, str(i)) for i, d in enumerate(domains)])
>>> data = b''.join(next(f.specialize()) for f in symbol.fields)
>>> symbol.abstract(data) #doctest: +ELLIPSIS
OrderedDict([('0', b'\x00\x01'), ('1', b'...'), ('2', b'\x00\x7f\x00A'), ('3', b'\x02\x00')])
# Verify that you cannot create an Integer with a value AND an interval:
>>> i = Integer(2, interval=(2, 10))
Traceback (most recent call last):
...
ValueError: An Integer should have either its value or its interval set, but not both
"""
def _test_24_bits_integers():
r"""
>>> from netzob.all import *
>>> t = Integer(unitSize=UnitSize.SIZE_24)
>>> len(t.generate().tobytes())
3
>>> t = Integer(unitSize=UnitSize.SIZE_24, endianness=Endianness.LITTLE)
>>> len(t.generate().tobytes())
3
>>> raw = b'\xff\xff\xff'
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_24, endianness=Endianness.BIG, sign=Sign.UNSIGNED)
16777215
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_24, endianness=Endianness.LITTLE, sign=Sign.UNSIGNED)
16777215
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_24, endianness=Endianness.BIG, sign=Sign.SIGNED)
-1
>>> Integer.encode(raw, unitSize=UnitSize.SIZE_24, endianness=Endianness.LITTLE, sign=Sign.SIGNED)
-1
"""
def _test_weird_size():
r"""
>>> from netzob.all import *
# This test should trigger an exception
>>> domain = Integer(value=bitarray('10001001000011001001101'), unitSize=UnitSize.SIZE_24, endianness=Endianness.LITTLE, sign=Sign.UNSIGNED)
>>> f = Field(domain=domain, name="field")
>>> symbol = Symbol(fields=[f])
>>> data = next(symbol.specialize())
Traceback (most recent call last):
...
netzob.Model.Vocabulary.AbstractField.GenerationException: specialize() produced 23 bits, which is not aligned on 8 bits. You should review the symbol model.
# This test should work
>>> domain = Integer(value=bitarray('010001001000011001001101'), unitSize=UnitSize.SIZE_24, endianness=Endianness.LITTLE, sign=Sign.UNSIGNED)
>>> f = Field(domain=domain, name="field")
>>> symbol = Symbol(fields=[f])
>>> data = next(symbol.specialize())
>>> data
b'D\x86M'
"""
def _test_int_endianness():
r"""
>>> from netzob.all import *
>>> Conf.apply()
>>> f1 = Field(domain=Raw(nbBytes=2), name="f1")
>>> f2 = Field(name="f2")
>>> f2.domain = Size([f1, f2], dataType=uint16le())
>>> f1.domain.dataType.endianness
Endianness.BIG
>>> f2.domain.dataType.endianness
Endianness.LITTLE
>>> s0 = Symbol([f2, f1])
>>> s1 = Symbol([f1, f2])
>>> print(next(s0.specialize()).hex() + ' - ' + next(s1.specialize()).hex())
0400f707 - ecfb0400
"""
def _test_specialize_abstract():
r"""
>>> from netzob.all import *
>>> from collections import OrderedDict
>>> Conf.apply()
>>> from netzob.Model.Vocabulary.Types.AbstractType import test_type_one_parameter, test_type_multiple_parameters, test_type_specialize_abstract
>>> data_type = Integer
>>> possible_parameters = OrderedDict()
>>> possible_parameters["value"] = [0, 42, -5, 24242]
>>> possible_parameters["interval"] = [None, 3, (4, 10), (-10, 10), (20, 257)]
>>> possible_parameters["unitSize"] = [UnitSize.SIZE_8, UnitSize.SIZE_16, UnitSize.SIZE_24, UnitSize.SIZE_32, UnitSize.SIZE_64]
>>> possible_parameters["endianness"] = [Endianness.LITTLE, Endianness.BIG]
>>> possible_parameters["sign"] = [Sign.SIGNED, Sign.UNSIGNED]
>>> possible_parameters["default"] = [None, 0, 44, -10]
>>> test_type_one_parameter(data_type, possible_parameters)
>>> possible_parameters = OrderedDict()
>>> possible_parameters["value"] = [0, 42]
>>> possible_parameters["interval"] = [None, (4, 10), (-10, 10), (20, 257)]
>>> possible_parameters["unitSize"] = [UnitSize.SIZE_8, UnitSize.SIZE_16, UnitSize.SIZE_24, UnitSize.SIZE_32, UnitSize.SIZE_64]
>>> possible_parameters["endianness"] = [Endianness.LITTLE, Endianness.BIG]
>>> possible_parameters["sign"] = [Sign.SIGNED, Sign.UNSIGNED]
>>> possible_parameters["default"] = [None, 0, 44, -10]
>>> (parameter_names, functional_combinations_possible_parameters) = test_type_multiple_parameters(data_type, possible_parameters)
>>> test_type_specialize_abstract(data_type, parameter_names, functional_combinations_possible_parameters)
"""