def __init__(self, data, _id=None, session=None, date=None, source=None, destination=None): """ :parameter data: the content of the message :type data: a :class:`object` :parameter _id: the unique identifier of the message :type _id: :class:`uuid.UUID` :keyword session: the session in which the message was captures :type session: :class:`netzob.Model.Vocabulary.Session.Session` :parameter date: the timestamp of the message :type date: a :class:`int` :parameter source: the optional source address of the message :type source: a :class:`str` :parameter destination: the optional destination address of the message :type destination: a :class:`str` """ if data is None: data = '' self.data = data self.session = session if _id is None: _id = uuid.uuid4() self.id = _id if date is None: date = time.mktime(time.gmtime()) self.__date = date self.__source = source self.__destination = destination self.__visualizationFunctions = TypedList(VisualizationFunction) self.__metadata = OrderedDict() self.__semanticTags = OrderedDict()
def __init__(self, messages=None, _id=None, applicativeData=None, name="Session"): """ :parameter messages: the messages exchanged in the current session :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` :parameter _id: the unique identifier of the session :type _id: :class:`uuid.UUID` :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData` """ self.__messages = SortedTypedList(AbstractMessage) self.__applicativeData = TypedList(ApplicativeData) if messages is None: messages = [] self.messages = messages if _id is None: _id = uuid.uuid4() self.id = _id if applicativeData is None: applicativeData = [] self.applicativeData = applicativeData self.name = name
def __init__(self, name=None): self.id = uuid.uuid4() self.name = name self.description = "" self.__fields = TypedList(AbstractField) self.__parent = None self.__encodingFunctions = SortedTypedList(EncodingFunction) self.__visualizationFunctions = TypedList(VisualizationFunction) self.__transformationFunctions = TypedList(TransformationFunction) self._variable = None
def __init__(self, data, _id=None, session=None, date=None, source=None, destination=None): """ :parameter data: the content of the message :type data: a :class:`object` :parameter _id: the unique identifier of the message :type _id: :class:`uuid.UUID` :keyword session: the session in which the message was captures :type session: :class:`netzob.Common.Models.Vocabulary.Session.Session` :parameter date: the timestamp of the message :type date: a :class:`int` :parameter source: the optional source address of the message :type source: a :class:`str` :parameter destination: the optional destination address of the message :type destination: a :class:`str` """ if data is None: data = '' self.data = data self.session = session if _id is None: _id = uuid.uuid4() self.id = _id if date is None: date = time.mktime(time.gmtime()) self.__date = date self.__source = source self.__destination = destination self.__visualizationFunctions = TypedList(VisualizationFunction) self.__metadata = OrderedDict() self.__semanticTags = OrderedDict()
class NetzobAggregateRegex(NetzobRegex): """Represents an aggregate regex. Below is an example of such aggregate regex with two aggregated regexes.. >>> from netzob.all import * >>> import regex as re >>> data = "Hello netzob, what's up ?" >>> hexData = TypeConverter.convert(data, ASCII, HexaString) >>> regex1 = NetzobRegex.buildRegexForStaticValue("Hello netzob") >>> regex2 = NetzobRegex.buildRegexForStaticValue(", what's up ?") >>> nRegex = NetzobRegex.buildRegexForAggregateRegexes([regex1, regex2]) >>> compiledRegex = re.compile(str(nRegex)) >>> dynamicDatas = compiledRegex.match(hexData) >>> print TypeConverter.convert(hexData[dynamicDatas.start(regex1.id):dynamicDatas.end(regex1.id)], HexaString, ASCII) Hello netzob >>> print TypeConverter.convert(hexData[dynamicDatas.start(regex2.id):dynamicDatas.end(regex2.id)], HexaString, ASCII) , what's up ? >>> print TypeConverter.convert(hexData[dynamicDatas.start(nRegex.id):dynamicDatas.end(nRegex.id)], HexaString, ASCII) Hello netzob, what's up ? """ def __init__(self, children): super(NetzobAggregateRegex, self).__init__() self.__children = TypedList(NetzobRegex) self.children = children def __updateRegex(self): self.regex = "".join([str(child) for child in self.children]) @property def children(self): return self.__children @children.setter def children(self, children): self._logger.debug("PAN {0}".format(children)) # for child in children: # if child is None: # raise TypeError("No child can be None") for child in children: if child is not None: self.__children.append(child) self.__updateRegex()
def __init__(self, fields=None, messages=None, name="Symbol", meta=False): """ :keyword fields: the fields which participate in symbol definition :type fields: a :class:`list` of :class:`netzob.Model.Vocabulary.Field` :keyword messages: the message that represent the symbol :type messages: a :class:`list` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` :keyword name: the name of the symbol :type name: :class:`str` """ super(Symbol, self).__init__(name, meta) self.__messages = TypedList(AbstractMessage) if messages is None: messages = [] self.messages = messages if fields is None: # create a default empty field fields = [Field()] self.fields = fields
class NetzobAlternativeRegex(NetzobRegex): """Represents an alternative regex. >>> from netzob.all import * >>> import random >>> import regex as re >>> possibleData =["Netzob", "Zoby"] >>> data = random.choice(possibleData) >>> hexData = TypeConverter.convert(data, ASCII, HexaString) >>> regex1 = NetzobRegex.buildRegexForStaticValue("Netzob") >>> regex2 = NetzobRegex.buildRegexForStaticValue("Zoby") >>> nRegex = NetzobRegex.buildRegexForAlternativeRegexes([regex1, regex2]) >>> compiledRegex = re.compile(str(nRegex)) >>> dynamicDatas = compiledRegex.match(hexData) >>> matchedData = TypeConverter.convert(hexData[dynamicDatas.start(nRegex.id):dynamicDatas.end(nRegex.id)], HexaString, ASCII) >>> matchedData in possibleData True """ def __init__(self, children): super(NetzobAlternativeRegex, self).__init__() self.__children = TypedList(NetzobRegex) self.children = children def __updateRegex(self): self.regex = "|".join([str(child) for child in self.children]) @property def children(self): return self.__children @children.setter def children(self, children): for child in children: if child is None: raise TypeError("No child can be None") for child in children: self.__children.append(child) self.__updateRegex()
def __init__(self, fields=None, messages=None, name="Symbol"): """ :keyword fields: the fields which participate in symbol definition :type fields: a :class:`list` of :class:`netzob.Common.Models.Vocabulary.Field` :keyword messages: the message that represent the symbol :type messages: a :class:`list` of :class:`netzob.Common.Models.Vocabulary.Messages.AbstractMessage.AbstractMessage` :keyword name: the name of the symbol :type name: :class:`str` """ super(Symbol, self).__init__(name, True) self.__messages = TypedList(AbstractMessage) if messages is None: messages = [] self.messages = messages if fields is None: # create a default empty field fields = [Field()] self.fields = fields
def __init__(self, children): super(NetzobAlternativeRegex, self).__init__() self.__children = TypedList(NetzobRegex) self.children = children
class Session(object): """A session includes messages exchanged in the same session. Messages are automaticaly sorted. Applicative data can be attached to sessions. >>> import time >>> from netzob.all import * >>> # we create 3 messages >>> msg1 = RawMessage("ACK", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:05", "%d %b %y %H:%M:%S"))) >>> msg2 = RawMessage("SYN", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:01", "%d %b %y %H:%M:%S"))) >>> msg3 = RawMessage("SYN/ACK", source="B", destination="A", date=time.mktime(time.strptime("9 Aug 13 10:45:03", "%d %b %y %H:%M:%S"))) >>> session = Session([msg1, msg2, msg3]) >>> print(session.messages.values()[0].data) SYN >>> print(session.messages.values()[1].data) SYN/ACK >>> print(session.messages.values()[2].data) ACK """ def __init__(self, messages=None, _id=None, applicativeData=None, name="Session"): """ :parameter messages: the messages exchanged in the current session :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` :parameter _id: the unique identifier of the session :type _id: :class:`uuid.UUID` :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData` """ self.__messages = SortedTypedList(AbstractMessage) self.__applicativeData = TypedList(ApplicativeData) if messages is None: messages = [] self.messages = messages if _id is None: _id = uuid.uuid4() self.id = _id if applicativeData is None: applicativeData = [] self.applicativeData = applicativeData self.name = name @property def id(self): """The unique identifier of the session. :type: :class:`uuid.UUID` """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, _id): if _id is None: raise TypeError("Id cannot be None") self.__id = _id @property def messages(self): """The messages exchanged in the current session. Messages are sorted. :type: a :class:`netzob.Common.Utils.TypedList.TypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` """ return self.__messages def clearMessages(self): """Delete all the messages attached to the current session""" for msg in list(self.__messages.values()): msg.session = None self.__messages.clear() @messages.setter def messages(self, messages): if messages is None: messages = [] # First it checks the specified messages are all AbstractMessages for msg in messages: if not isinstance(msg, AbstractMessage): raise TypeError( "Cannot add messages of type {0} in the session, only AbstractMessages are allowed." .format(type(msg))) self.clearMessages() for message in messages: self.__messages.add(message) message.session = self @property def applicativeData(self): """Applicative data attached to the current session. >>> from netzob.all import * >>> appData = ApplicativeData("test", Integer(20)) >>> session = Session(applicativeData=[appData]) >>> print(len(session.applicativeData)) 1 >>> appData2 = ApplicativeData("test2", ASCII("helloworld")) >>> session.applicativeData.append(appData2) >>> print(len(session.applicativeData)) 2 >>> print(session.applicativeData[0]) Applicative Data: test=Integer=20 ((8, 8))) >>> print(session.applicativeData[1]) Applicative Data: test2=ASCII=helloworld ((0, 80))) :type: a list of :class:`netzob.Model.Vocabulary.ApplicativeData.ApplicativeData`. """ return self.__applicativeData def clearApplicativeData(self): while (len(self.__applicativeData) > 0): self.__applicativeData.pop() @applicativeData.setter def applicativeData(self, applicativeData): for app in applicativeData: if not isinstance(app, ApplicativeData): raise TypeError( "Cannot add an applicative data with type {0}, only ApplicativeData accepted." .format(type(app))) self.clearApplicativeData() for app in applicativeData: self.applicativeData.append(app) @property def name(self): return self.__name @name.setter @typeCheck(str) def name(self, _name): if _name is None: raise TypeError("Name cannot be None") self.__name = _name def getEndpointsList(self): """Retrieve all the endpoints couples that are present in the session. >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="C") >>> session = Session([msg1, msg2, msg3]) >>> print(len(session.getEndpointsList())) 2 >>> print(session.getEndpointsList()) [('A', 'B'), ('A', 'C')] :return: a list containing couple of endpoints (src, dst). :rtype: a :class:`list` """ endpointsList = [] for message in list(self.messages.values()): src = message.source dst = message.destination endpoints1 = (src, dst) endpoints2 = (dst, src) if (not endpoints1 in endpointsList) and (not endpoints2 in endpointsList): endpointsList.append(endpoints1) return endpointsList def getTrueSessions(self): """Retrieve the true sessions embedded in the current session. A session is here characterized by a uniq endpoints couple. TODO: a more precise solution would be to use flow reconstruction (as in TCP). >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="C") >>> session = Session([msg1, msg2, msg3]) >>> print(len(session.getTrueSessions())) 2 >>> for trueSession in session.getTrueSessions(): ... print(trueSession.name) Session: 'A' - 'B' Session: 'A' - 'C' :return: a list containing true sessions embedded in the current session. :rtype: a :class:`list` """ trueSessions = [] for endpoints in self.getEndpointsList(): trueSessionMessages = [] src = None dst = None for message in list(self.messages.values()): if message.source in endpoints and message.destination in endpoints: trueSessionMessages.append(message) if src is None: src = message.source if dst is None: dst = message.destination trueSession = Session(messages=trueSessionMessages, applicativeData=self.applicativeData, name="Session: '" + str(src) + "' - '" + str(dst) + "'") trueSessions.append(trueSession) return trueSessions def isTrueSession(self): """Tell if the current session is true. A session is said to be true if the communication flow pertain to a uniq applicative session between a couple of endpoints. >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="B") >>> session = Session([msg1, msg2, msg3]) >>> print(session.isTrueSession()) True :return: a boolean telling if the current session is a true one (i.e. it corresponds to a uniq applicative session between two endpoints). :rtype: a :class:`bool` """ if len(self.getTrueSessions()) == 1: return True else: return False @typeCheck(list) def abstract(self, symbolList): """This method abstract each message of the current session into symbols according to a list of symbols given as parameter. >>> from netzob.all import * >>> symbolSYN = Symbol([Field(ASCII("SYN"))], name="Symbol_SYN") >>> symbolSYNACK = Symbol([Field(ASCII("SYN/ACK"))], name="Symbol_SYNACK") >>> symbolACK = Symbol([Field(ASCII("ACK"))], name="Symbol_ACK") >>> symbolList = [symbolSYN, symbolSYNACK, symbolACK] >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="B") >>> session = Session([msg1, msg2, msg3]) >>> if session.isTrueSession(): ... for src, dst, sym in session.abstract(symbolList): ... print(str(src) + " - " + str(dst) + " : " + str(sym.name)) A - B : Symbol_SYN B - A : Symbol_SYNACK A - B : Symbol_ACK :return: a list of tuples containing the following elements : (source, destination, symbol). :rtype: a :class:`list` """ abstractSession = [] if not self.isTrueSession(): self._logger.warn( "The current session cannot be abstracted as it not a true session (i.e. it may contain inner true sessions)." ) return abstractSession for message in list(self.messages.values()): symbol = AbstractField.abstract(message.data, symbolList) abstractSession.append( (message.source, message.destination, symbol)) return abstractSession
class Symbol(AbstractField): """A symbol represents a common abstraction for all its messages. For example, we can create a symbol based on two raw messages >>> from netzob.all import * >>> m1 = RawMessage("hello world") >>> m2 = RawMessage("hello earth") >>> fields = [Field("hello ", name="f0"), Field(["world", "earth"], name="f1")] >>> symbol = Symbol(fields, messages=[m1, m2]) >>> print symbol f0 | f1 -------- | ------- 'hello ' | 'world' 'hello ' | 'earth' -------- | ------- Another example >>> from netzob.all import * >>> s = Symbol([Field("hello ", name="f0"), Field(ASCII(nbChars=(0, 10)), name="f1")]) >>> s.messages.append(RawMessage("hello toto")) >>> print s f0 | f1 -------- | ------ 'hello ' | 'toto' -------- | ------ """ def __init__(self, fields=None, messages=None, name="Symbol"): """ :keyword fields: the fields which participate in symbol definition :type fields: a :class:`list` of :class:`netzob.Common.Models.Vocabulary.Field` :keyword messages: the message that represent the symbol :type messages: a :class:`list` of :class:`netzob.Common.Models.Vocabulary.Messages.AbstractMessage.AbstractMessage` :keyword name: the name of the symbol :type name: :class:`str` """ super(Symbol, self).__init__(name, True) self.__messages = TypedList(AbstractMessage) if messages is None: messages = [] self.messages = messages if fields is None: # create a default empty field fields = [Field()] self.fields = fields @typeCheck(Memory, object) 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 clearMessages(self): """Delete all the messages attached to the current symbol""" while(len(self.__messages) > 0): self.__messages.pop() # Properties @property def messages(self): """A list containing all the messages that this symbol represent. :type : a :class:`list` of :class:`netzob.Common.Models.Vocabulary.Messages.AbstractMessage.AbstractMessage` """ return self.__messages @messages.setter def messages(self, messages): if messages is None: messages = [] # First it checks the specified messages are all AbstractMessages for msg in messages: if not isinstance(msg, AbstractMessage): raise TypeError("Cannot add messages of type {0} in the session, only AbstractMessages are allowed.".format(type(msg))) self.clearMessages() for msg in messages: self.__messages.append(msg)
class Symbol(AbstractField): """A symbol represents a common abstraction for all its messages. For example, we can create a symbol based on two raw messages >>> from netzob.all import * >>> m1 = RawMessage("hello world") >>> m2 = RawMessage("hello earth") >>> fields = [Field("hello ", name="f0"), Field(["world", "earth"], name="f1")] >>> symbol = Symbol(fields, messages=[m1, m2]) >>> print(symbol) f0 | f1 -------- | ------- 'hello ' | 'world' 'hello ' | 'earth' -------- | ------- Another example >>> from netzob.all import * >>> s = Symbol([Field("hello ", name="f0"), Field(ASCII(nbChars=(0, 10)), name="f1")]) >>> s.messages.append(RawMessage("hello toto")) >>> print(s) f0 | f1 -------- | ------ 'hello ' | 'toto' -------- | ------ """ def __init__(self, fields=None, messages=None, name="Symbol", meta=False): """ :keyword fields: the fields which participate in symbol definition :type fields: a :class:`list` of :class:`netzob.Model.Vocabulary.Field` :keyword messages: the message that represent the symbol :type messages: a :class:`list` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` :keyword name: the name of the symbol :type name: :class:`str` """ super(Symbol, self).__init__(name, meta) self.__messages = TypedList(AbstractMessage) if messages is None: messages = [] self.messages = messages if fields is None: # create a default empty field fields = [Field()] self.fields = fields def __eq__(self, other): if not isinstance(other, Symbol): return False if other is None: return False return self.name == other.name def __ne__(self, other): if other is None: return True if not isinstance(other, Symbol): return True return other.name != self.name def __key(self): return self.id def __hash__(self): return hash(frozenset(self.name)) @typeCheck(Memory, object) 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]) 5 >>> 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)) b'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(s.specialize(presets = presetValues)[0]) 195 :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.Model.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message """ from netzob.Model.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 clearMessages(self): """Delete all the messages attached to the current symbol""" while (len(self.__messages) > 0): self.__messages.pop() # Properties @property def messages(self): """A list containing all the messages that this symbol represent. :type : a :class:`list` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` """ return self.__messages @messages.setter def messages(self, messages): if messages is None: messages = [] # First it checks the specified messages are all AbstractMessages for msg in messages: if not isinstance(msg, AbstractMessage): raise TypeError( "Cannot add messages of type {0} in the session, only AbstractMessages are allowed." .format(type(msg))) self.clearMessages() for msg in messages: self.__messages.append(msg) def __repr__(self): return self.name
class AbstractMessage(SortableObject): """Every message must inherits from this class""" def __init__(self, data, _id=None, session=None, date=None, source=None, destination=None): """ :parameter data: the content of the message :type data: a :class:`object` :parameter _id: the unique identifier of the message :type _id: :class:`uuid.UUID` :keyword session: the session in which the message was captures :type session: :class:`netzob.Common.Models.Vocabulary.Session.Session` :parameter date: the timestamp of the message :type date: a :class:`int` :parameter source: the optional source address of the message :type source: a :class:`str` :parameter destination: the optional destination address of the message :type destination: a :class:`str` """ if data is None: data = '' self.data = data self.session = session if _id is None: _id = uuid.uuid4() self.id = _id if date is None: date = time.mktime(time.gmtime()) self.__date = date self.__source = source self.__destination = destination self.__visualizationFunctions = TypedList(VisualizationFunction) self.__metadata = OrderedDict() self.__semanticTags = OrderedDict() @typeCheck(AbstractField) def isValidForField(self, field): """Checks if the specified field can be used to parse the current message. It returns a boolean that indicates this. >>> from netzob.all import * >>> f0 = Field(Raw(nbBytes=4)) >>> f1 = Field(", hello ", name="F1") >>> f2 = Field(Raw(nbBytes=(2,5)), name="F2") >>> symbol = Symbol([f0, f1, f2], name="Symbol") >>> m2 = RawMessage("Toto, hello you", source="server", destination="client") >>> m2.isValidForField(symbol) True >>> m1 = RawMessage("Toto, hello !", source="server", destination="client") >>> m1.isValidForField(symbol) False :parameter field: the current message will be parsed with this field :type field: :class:`netzob.Common.Models.Vocabulary.AbstractField.AbstractField` :return: a boolean indicating if the current message can be parsed with the specified field :rtype: :class:`bool` """ if field is None: raise TypeError("Field cannot be None") try: DataAlignment.align([self.data], field) except: return False return True @typeCheck(str, object) def setMetadata(self, name, value): """Modify the value of the current metadata which name is specified with the provided value. :parameter name: the name of the metadata to edit :type name: :str :parameter value: the new value of the specified metadata :type value: object :raise TypeError if parameters are not valid and ValueError if the specified value is incompatible with the metadata. """ if not self.isValueForMetadataValid(name, value): raise ValueError("The value of metadata {0} is not valid.") self.__metadata[name] = value def isValueForMetadataValid(self, name, value): """Computes if the specified value is compatible for the provided name of metadata It should be redefined to support specifities of certain metadata :parameter name: the name of the metadata :type name: str :parameter value: the value of the metadata to check :type value: object :raise TypeError if parameters are not valid""" if name is None: raise TypeError("name cannot be none") return True def clearVisualizationFunctions(self): """Remove all the visualization functions attached to the current element""" while(len(self.__visualizationFunctions) > 0): self.__visualizationFunctions.pop() def priority(self): """Return the value that will be used to represent the current message when sorted with the others. :type: int """ return int(self.id) def __str__(self): """Returns a string that describes the message. :warning: This string should only considered for debuging and/or fast visualization. Do not rely on it since its format can often be modified. """ # Add visualization effects to the data functionTable = FunctionApplicationTable([self.data]) for function in self.visualizationFunctions: start = (function.start / 8) end = (function.end / 8) functionTable.applyFunction(function, start, end) tmpData = "".join(functionTable.getResult()) # Add header in front of the data HLS1 = "\033[0;32m" HLE1 = "\033[0;m" HLS2 = "\033[1;32m" HLE2 = "\033[1;m" header = HLS1 + "[{0} {1}{2}{3}->{4}{5}{6}]".format(self.date, HLE1 + HLS2, self.source, HLE2 + HLS1, HLE1 + HLS2, self.destination, HLE2 + HLS1) + HLE1 return "{0} {1}".format(header, repr(tmpData)) @property def id(self): """The unique identified of the message :type: UUID """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, _id): if _id is None: return TypeError("Id cannot be None") self.__id = _id @property def data(self): """The content of the message :type: :class:`object` """ return self.__data @data.setter def data(self, data): self.__data = data @property def date(self): """The date when the message was captured. The date must be encoded in the epoch format. :type:float :raise: a TypeError if date is not valid """ return self.__date @date.setter @typeCheck(float) def date(self, date): if date is None: raise TypeError("Date cannot be None") self.__date = date @property def source(self): """The name or type of the source which emitted the current message :type: str """ return self.__source @source.setter @typeCheck(str) def source(self, source): self.__source = source @property def destination(self): """The name or type of the destination which received the current message :type: str """ return self.__destination @destination.setter @typeCheck(str) def destination(self, destination): self.__destination = destination @property def metadata(self): """The metadata or properties of the message. :type: a dict<str, Object> """ return self.__metadata @metadata.setter @typeCheck(dict) def metadata(self, metadata): if metadata is None: return TypeError("Metadata cannot be None") for k in metadata.keys(): self.setMetadata(k, metadata[k]) @property def visualizationFunctions(self): """Sorted list of visualization function to attach on message. :type: a list of :class:`netzob.Common.Models.Vocabulary.Functions.VisualizationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__visualizationFunctions @visualizationFunctions.setter def visualizationFunctions(self, visualizationFunctions): self.clearVisualizationFunctions() self.visualizationFunctions.extend(visualizationFunctions) @property def session(self): """The session from which message comes from. :type: :class:`netzob.Common.Models.Vocabulary.Session.Session` """ return self.__session @session.setter def session(self, session): self.__session = session @typeCheck(int, str) def addSemanticTag(self, position, tag): """Attach the specific semantic tag the specified position of the data. :parameter position: the position on which the semantic tag is attached :type position: :class:`int` :parameter tag: the name of the tag :type tag: :class:`str` """ if position is None: raise TypeError("Position cannot be none") if tag is None: raise TypeError("Tag cannot be None") if position in self.semanticTags.keys(): tags = self.semanticTags[position] else: tags = [] tags.append(tag) self.semanticTags[position] = tags @property def semanticTags(self): """Position of identified semantic tags found in the current data. :type: :class:`dict` with keys is int (position) and values is a list of str """ return self.__semanticTags @semanticTags.setter @typeCheck(dict) def semanticTags(self, semanticTags): if semanticTags is None: self.__semanticTags = OrderedDict() # check for key, value in semanticTags.iteritems(): if not isinstance(key, int): raise TypeError("At least one key is not a valid int position") if not isinstance(value, list): raise TypeError("At least one value of the provided dict is not a list of string") for x in value: if not isinstance(x, str): raise TypeError("At least one value of the provided dict is not a list of string") self.__semanticTags = semanticTags
class AbstractField(AbstractMementoCreator, metaclass=abc.ABCMeta): """Represents all the different classes which participates in fields definitions of a message format.""" def __init__(self, name=None, meta = False): self.id = uuid.uuid4() self.name = name self.meta = meta self.description = "" self.__fields = TypedList(AbstractField) self.__parent = None self.__encodingFunctions = SortedTypedList(EncodingFunction) self.__visualizationFunctions = TypedList(VisualizationFunction) self.__transformationFunctions = TypedList(TransformationFunction) self._variable = None @typeCheck(bool, bool, bool) def getCells(self, encoded=True, styled=True, transposed=False): """Returns a matrix with a different line for each messages attached to the symbol of the current element. The matrix includes a different column for each leaf children of the current element. In each cell, the slices of messages once aligned. Attached :class:`EncodingFunction` can also be considered if parameter encoded is set to True. In addition, visualizationFunctions are also applied if parameter styled is set to True. If parameter Transposed is set to True, the matrix is built with rows for fields and columns for messages. >>> from netzob.all import * >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] >>> fh1 = Field(ASCII("hello "), name="hello") >>> fh2 = Field(Alt([ASCII("netzob"), ASCII("zoby"), ASCII("lapy"), ASCII("sygus")]), name="pseudo") >>> fheader = Field(name="header") >>> fheader.fields = [fh1, fh2] >>> fb1 = Field(ASCII(", what's up in "), name="whatsup") >>> fb2 = Field(["Paris", "Berlin", "New-York"], name="city") >>> fb3 = Field(" ?", name="end") >>> fbody = Field(name="body") >>> fbody.fields = [fb1, fb2, fb3] >>> symbol = Symbol([fheader, fbody], messages=messages) >>> print(symbol) hello | pseudo | whatsup | city | end -------- | -------- | ----------------- | ---------- | ---- 'hello ' | 'netzob' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'New-York' | ' ?' -------- | -------- | ----------------- | ---------- | ---- >>> fh1.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> fb2.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) hello | pseudo | whatsup | city | end -------------- | -------- | ----------------- | ------------------ | ---- '68656c6c6f20' | 'netzob' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'netzob' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'netzob' | ", what's up in " | '4e65772d596f726b' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '4e65772d596f726b' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '4e65772d596f726b' | ' ?' -------------- | -------- | ----------------- | ------------------ | ---- >>> print(fheader.getCells()) Field | Field -------------- | -------- '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'lapy' '68656c6c6f20' | 'lapy' '68656c6c6f20' | 'lapy' -------------- | -------- >>> print(fh1.getCells()) Field -------------- '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' -------------- >>> print(fh2.getCells()) Field -------- 'netzob' 'netzob' 'netzob' 'zoby' 'zoby' 'zoby' 'lapy' 'lapy' 'lapy' -------- >>> print(fbody.getCells()) Field | Field | Field ----------------- | ------------------ | ----- ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ----------------- | ------------------ | ----- >>> print(fb1.getCells()) Field ----------------- ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ----------------- >>> print(fb2.getCells()) Field ------------------ '5061726973' '4265726c696e' '4e65772d596f726b' '5061726973' '4265726c696e' '4e65772d596f726b' '5061726973' '4265726c696e' '4e65772d596f726b' ------------------ >>> print(fb3.getCells()) Field ----- ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ----- :keyword encoded: if set to True, encoding functions are applied on returned cells :type encoded: :class:`bool` :keyword styled: if set to True, visualization functions are applied on returned cells :type styled: :class:`bool` :keyword transposed: is set to True, the returned matrix is transposed (1 line for each field) :type transposed: :class:`bool` :return: a matrix representing the aligned messages following fields definitions. :rtype: a :class:`netzob.Common.Utils.MatrixList.MatrixList` :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages """ if len(self.messages) < 1: raise ValueError("This symbol does not contain any message.") # Fetch all the data to align data = [message.data for message in self.messages] # [DEBUG] set to false for debug only. A sequential alignment is more simple to debug useParallelAlignment = False if useParallelAlignment: # Execute a parallel alignment from netzob.Common.Utils.DataAlignment.ParallelDataAlignment import ParallelDataAlignment return ParallelDataAlignment.align(data, self, encoded=encoded) else: # Execute a sequential alignment from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment return DataAlignment.align(data, self, encoded=encoded) @typeCheck(bool, bool) def getValues(self, encoded=True, styled=True): """Returns all the values the current element can take following messages attached to the symbol of current element. Specific encodingFunctions can also be considered if parameter encoded is set to True. In addition, visualizationFunctions are also applied if parameter styled is set to True. >>> from netzob.all import * >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] >>> f1 = Field("hello ", name="hello") >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f3 = Field(", what's up in ", name="whatsup") >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f5 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages) >>> print(symbol) hello | pseudo | whatsup | city | end -------- | -------- | ----------------- | ---------- | ---- 'hello ' | 'netzob' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'New-York' | ' ?' -------- | -------- | ----------------- | ---------- | ---- >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) hello | pseudo | whatsup | city | end -------------- | -------------- | -------------------------------- | ------------------ | ------ '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' -------------- | -------------- | -------------------------------- | ------------------ | ------ >>> print(symbol.getValues()) [b'68656c6c6f206e65747a6f622c2077686174277320757020696e205061726973203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f207a6f62792c2077686174277320757020696e205061726973203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f206c6170792c2077686174277320757020696e205061726973203f', b'68656c6c6f206c6170792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206c6170792c2077686174277320757020696e204e65772d596f726b203f'] >>> print(f1.getValues()) [b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20'] >>> print(f2.getValues()) [b'6e65747a6f62', b'6e65747a6f62', b'6e65747a6f62', b'7a6f6279', b'7a6f6279', b'7a6f6279', b'6c617079', b'6c617079', b'6c617079'] >>> print(f3.getValues()) [b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20'] >>> print(f4.getValues()) [b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b'] >>> print(f5.getValues()) [b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f'] :keyword encoded: if set to True, encoding functions are applied on returned cells :type encoded: :class:`bool` :keyword styled: if set to True, visualization functions are applied on returned cells :type styled: :class:`bool` :return: a list detailling all the values current element takes. :rtype: a :class:`list` of :class:`str` :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages """ cells = self.getCells(encoded=encoded, styled=styled) values = [] for line in cells: values.append(b''.join(line)) return values @typeCheck(bool, bool) def getMessageCells(self, encoded=False, styled=False): """Computes and returns the alignment of each message belonging to the current field as proposed by getCells() method but indexed per message. >>> from netzob.all import * >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f2 = Field(", what's up in ", name="whatsup") >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f4 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> print(symbol) pseudo | whatsup | city | end -------- | ----------------- | -------- | ---- 'netzob' | ", what's up in " | 'Paris' | ' ?' 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'zoby' | ", what's up in " | 'Paris' | ' ?' 'zoby' | ", what's up in " | 'Berlin' | ' ?' -------- | ----------------- | -------- | ---- >>> messageCells = symbol.getMessageCells() >>> for message in symbol.messages: ... print(message.data, messageCells[message]) netzob, what's up in Paris ? [b'netzob', b", what's up in ", b'Paris', b' ?'] netzob, what's up in Berlin ? [b'netzob', b", what's up in ", b'Berlin', b' ?'] zoby, what's up in Paris ? [b'zoby', b", what's up in ", b'Paris', b' ?'] zoby, what's up in Berlin ? [b'zoby', b", what's up in ", b'Berlin', b' ?'] >>> [f.name for f in messageCells.fields] ['pseudo', 'whatsup', 'city', 'end'] :keyword encoded: if set to true, values are encoded :type encoded: :class:`bool` :keyword styled: if set to true, values are styled :type styled: :class:`bool` :return: a dict indexed by messages that denotes their cells :rtype: a :class:`dict` """ if encoded is None: raise TypeError("Encoded cannot be None") if styled is None: raise TypeError("Styled cannot be None") fieldCells = self.getCells(encoded=encoded, styled=styled) result = MessageCells() leaf_fields = self.getLeafFields() result.fields = leaf_fields for iMessage, message in enumerate(self.messages): result[message] = fieldCells[iMessage] return result @typeCheck(bool, bool) def getMessageValues(self, encoded=False, styled=False): """Computes and returns the alignment of each message belonging to the current field as proposed by getValues() method but indexed per message. >>> from netzob.all import * >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f2 = Field(", what's up in ", name="whatsup") >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f4 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> print(symbol) pseudo | whatsup | city | end -------- | ----------------- | -------- | ---- 'netzob' | ", what's up in " | 'Paris' | ' ?' 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'zoby' | ", what's up in " | 'Paris' | ' ?' 'zoby' | ", what's up in " | 'Berlin' | ' ?' -------- | ----------------- | -------- | ---- >>> messageValues = f3.getMessageValues() >>> for message in symbol.messages: ... print(message.data, messageValues[message]) netzob, what's up in Paris ? b'Paris' netzob, what's up in Berlin ? b'Berlin' zoby, what's up in Paris ? b'Paris' zoby, what's up in Berlin ? b'Berlin' :keyword encoded: if set to true, values are encoded :type encoded: :class:`bool` :keyword styled: if set to true, values are styled :type styled: :class:`bool` :return: a dict indexed by messages that denotes their values :rtype: a :class:`dict` """ if encoded is None: raise TypeError("Encoded cannot be None") if styled is None: raise TypeError("Styled cannot be None") result = OrderedDict() fieldValues = self.getValues(encoded=encoded, styled=styled) for iMessage, message in enumerate(self.messages): result[message] = fieldValues[iMessage] return result # def getMessagesWithValue(self, value): # """Computes and returns the messages that have a specified value # in the current field. # >>> from netzob.all import * # >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] # >>> f1 = Field("hello ", name="hello") # >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") # >>> f3 = Field(", what's up in ", name="whatsup") # >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city") # >>> f5 = Field(" ?", name="end") # >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages) # >>> print(symbol.specialize()) # >>> print(symbol) # hello | netzob | , what's up in | Paris | ? # hello | netzob | , what's up in | Berlin | ? # hello | netzob | , what's up in | New-York | ? # hello | zoby | , what's up in | Paris | ? # hello | zoby | , what's up in | Berlin | ? # hello | zoby | , what's up in | New-York | ? # hello | lapy | , what's up in | Paris | ? # hello | lapy | , what's up in | Berlin | ? # hello | lapy | , what's up in | New-York | ? # >>> lapySymbol = Symbol(messages=symbol.fields[1].getMessagesWithValue("lapy")) # >>> print(lapySymbol) # hello lapy, what's up in Paris ? # hello lapy, what's up in Berlin ? # hello lapy, what's up in New-York ? # >>> Format.splitStatic(lapySymbol) # >>> lapySymbol.encodingFunctions.add(TypeEncodingFunction(HexaString)) # >>> print(lapySymbol) # 68656c6c6f206c6170792c2077686174277320757020696e20 | 5061726973203f # 68656c6c6f206c6170792c2077686174277320757020696e20 | 4265726c696e203f # 68656c6c6f206c6170792c2077686174277320757020696e20 | 4e65772d596f726b203f # :parameter value: a Raw value # :type value: :class:`object` # :return: a list of messages # :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` # """ # if value is None: # raise TypeError("Value cannot be None") # fieldValues = self.getValues(encoded=False, styled=False) # result = [] # for i_message, message in enumerate(self.messages): # if fieldValues[i_message] == value: # result.append(message) # return result @abc.abstractmethod def specialize(self, mutator=None): """Specialize and generate a :class:`netzob.Model.Vocabulary.Messages.RawMessage` which content follows the fields definitions attached to current element. :keyword mutator: if set, the mutator will be used to mutate the fields definitions :type mutator: :class:`netzob.Model.Mutators.AbstractMutator` :return: a generated content represented with an hexastring :rtype: :class:`str` :raises: :class:`netzob.Model.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message """ return @staticmethod def abstract(data, fields): """Search in the fields/symbols the first one that can abstract the data. >>> from netzob.all import * >>> messages = ["{0}, what's up in {1} ?".format(pseudo, city) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1a = Field(name="name", domain="netzob") >>> f2a = Field(name="question", domain=", what's up in ") >>> f3a = Field(name="city", domain=Alt(["Paris", "Berlin"])) >>> f4a = Field(name="mark", domain=" ?") >>> s1 = Symbol([f1a, f2a, f3a, f4a], name="Symbol-netzob") >>> f1b = Field(name="name", domain="zoby") >>> f2b = Field(name="question", domain=", what's up in ") >>> f3b = Field(name="city", domain=Alt(["Paris", "Berlin"])) >>> f4b = Field(name="mark", domain=" ?") >>> s2 = Symbol([f1b, f2b, f3b, f4b], name="Symbol-zoby") >>> for m in messages: ... (abstractedSymbol, structured_data) = AbstractField.abstract(m, [s1, s2]) ... print(structured_data) ... print(abstractedSymbol.name) OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')]) Symbol-netzob OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')]) Symbol-netzob OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')]) Symbol-zoby OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')]) Symbol-zoby :parameter data: the data that should be abstracted in symbol :type data: :class:`str` :parameter fields: a list of fields/symbols targeted during the abstraction process :type fields: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField` :return: a field/symbol and the structured received message :rtype: a tuple (:class:`netzob.Model.Vocabulary.AbstractField`, dict) :raises: :class:`netzob.Model.Vocabulary.AbstractField.AbstractionException` if an error occurs while abstracting the data """ from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment for field in fields: try: # Try to align/parse the data with the current field alignedData = DataAlignment.align([data], field, encoded=False) # If it matches, we build a dict that contains, for each field, the associated value that was present in the message structured_data = OrderedDict() for fields_value in alignedData: for i, field_value in enumerate(fields_value): structured_data[alignedData.headers[i]] = field_value return (field, structured_data) except: pass from netzob.Model.Vocabulary.UnknownSymbol import UnknownSymbol from netzob.Model.Vocabulary.Messages.RawMessage import RawMessage unknown_symbol = UnknownSymbol(RawMessage(data)) structured_data = OrderedDict() logging.error("Impossible to abstract the message in one of the specified symbols, we create an unknown symbol for it: '%s'", unknown_symbol) return (unknown_symbol, structured_data) def getSymbol(self): """Computes the symbol to which this field is attached. To retrieve it, this method recursively call the parent of the current object until the root is found. If the last root is not a :class:`netzob.Model.Vocabulary.Symbol`, it raises an Exception. :returns: the symbol if available :type: :class:`netzob.Model.Vocabulary.Symbol` :raises: :class:`netzob.Model.Vocabulary.AbstractField.NoSymbolException` """ from netzob.Model.Vocabulary.Symbol import Symbol if isinstance(self, Symbol): return self elif self.hasParent(): return self.parent.getSymbol() else: raise NoSymbolException( "Impossible to retrieve the symbol attached to this element") def getLeafFields(self, depth=None, currentDepth=0, includePseudoFields=False): """Extract the leaf fields to consider regarding the specified depth >>> from netzob.all import * >>> field = Field("hello", name="F0") >>> print([f.name for f in field.getLeafFields()]) ['F0'] >>> field = Field(name="L0") >>> headerField = Field(name="L0_header") >>> payloadField = Field(name="L0_payload") >>> footerField = Field(name="L0_footer") >>> fieldL1 = Field(name="L1") >>> fieldL1_header = Field(name="L1_header") >>> fieldL1_payload = Field(name="L1_payload") >>> fieldL1.fields = [fieldL1_header, fieldL1_payload] >>> payloadField.fields = [fieldL1] >>> field.fields = [headerField, payloadField, footerField] >>> print([f.name for f in field.getLeafFields(depth=None)]) ['L0_header', 'L1_header', 'L1_payload', 'L0_footer'] >>> print([f.name for f in field.getLeafFields(depth=0)]) ['L0'] >>> print([f.name for f in field.getLeafFields(depth=1)]) ['L0_header', 'L0_payload', 'L0_footer'] >>> print([f.name for f in field.getLeafFields(depth=2)]) ['L0_header', 'L1', 'L0_footer'] :return: the list of leaf fields :rtype: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField.AbstractField`. """ if currentDepth is None: currentDepth = 0 if len(self.fields) == 0: return [self] if currentDepth == depth: return [self] leafFields = [] for fields in self.fields: # Handle case where the field is pseudo (meaning it does not procude concrete value) if fields.isPseudoField: if includePseudoFields: pass else: continue if fields is not None: leafFields.extend(fields.getLeafFields(depth, currentDepth + 1, includePseudoFields)) return leafFields def hasParent(self): """Computes if the current element has a parent. :returns: True if current element has a parent. :rtype: :class:`bool` """ return self.__parent is not None def clearFields(self): """Remove all the children attached to the current element""" while (len(self.__fields) > 0): self.__fields.pop() def clearEncodingFunctions(self): """Remove all the encoding functions attached to the current element""" self.__encodingFunctions = SortedTypedList(EncodingFunction) for child in self.fields: child.clearEncodingFunctions() def clearVisualizationFunctions(self): """Remove all the visualization functions attached to the current element""" while (len(self.__visualizationFunctions) > 0): self.__visualizationFunctions.pop() def clearTransformationFunctions(self): """Remove all the transformation functions attached to the current element""" while (len(self.__transformationFunctions) > 0): self.__transformationFunctions.pop() # Standard methods def __str__(self): result = self.getCells(encoded=True) if self.meta: for split_message in result: message1 = b'' for part in split_message: try: message1 += part except: pass first = True for message2 in self.messages: if message1 == message2.data and first: split_message.insert(0, message2.destination) split_message.insert(0, message2.source) try: split_message.insert(0, message2.session.name) session_present = True except: session_present = False # split_message.insert(0, str(message2.date)) first = False if not first: # Add IP, Timestamp, PCAP etc to matrix headers result.headers.insert(0, 'Destination') result.headers.insert(0, 'Source') if session_present: result.headers.insert(0, 'Session') # matrix.headers.insert(0, 'Time') return str(result) @typeCheck(int) def _str_debug(self, deepness=0): """Returns a string which denotes the current field definition using a tree display""" tab = ["|-- " for x in range(deepness)] tab.append(str(self.name)) lines = [''.join(tab)] from netzob.Model.Vocabulary.Field import Field if isinstance(self, Field): lines.append(self.domain._str_debug(deepness + 1)) for f in self.fields: lines.append(f._str_debug(deepness + 1)) return '\n'.join(lines) # PROPERTIES @property def id(self): """Unique identifier of the field. This value must be a unique UUID instance (generated with uuid.uuid4()). :type: :class:`uuid.UUID` :raises: :class:`TypeError`, :class:`ValueError` """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, id): if id is None: raise ValueError("id is Mandatory.") self.__id = id @property def name(self): """Public name (may not be unique), default value is None :type: :class:`str` :raises: :class:`TypeError` """ return self.__name @name.setter @typeCheck(str) def name(self, name): self.__name = name @property def meta(self): """Meta boolean to print metadata,default is False :type: :class:`bool` :raises: :class:`TypeError` """ return self.__meta @meta.setter @typeCheck(bool) def meta(self, meta): self.__meta = meta @property def description(self): """User description of the field. Default value is ''. :type: :class:`str` :raises: :class:`TypeError` """ return self.__description @description.setter @typeCheck(str) def description(self, description): self.__description = description @property def encodingFunctions(self): """Sorted typed list of encoding function to attach on field. .. note:: list implemented as a :class:`netzob.Common.Utils.TypedList.TypedList` :type: a list of :class:`netzob.Model.Vocabulary.Functions.EncodingFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__encodingFunctions @encodingFunctions.setter def encodingFunctions(self, encodingFunctions): self.clearEncodingFunctions() for encodingFunction in encodingFunctions: self.addEncodingFunction(encodingFunction) def addEncodingFunction(self, encodingFunction): self.encodingFunctions.add(encodingFunction) for child in self.fields: child.addEncodingFunction(encodingFunction) @property def visualizationFunctions(self): """Sorted list of visualization function to attach on field. :type: a list of :class:`netzob.Model.Vocabulary.Functions.VisualizationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__visualizationFunctions @visualizationFunctions.setter def visualizationFunctions(self, visualizationFunctions): self.clearVisualizationFunctions() self.visualizationFunctions.extend(visualizationFunctions) @property def transformationFunctions(self): """Sorted list of transformation function to attach on field. :type: a list of :class:`netzob.Model.Vocabulary.Functions.TransformationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__transformationFunctions @transformationFunctions.setter def transformationFunctions(self, transformationFunctions): self.clearTransformationFunctions() self.transformationFunctions.extend(transformationFunctions) @property def fields(self): """Sorted list of field fields.""" return self.__fields @fields.setter def fields(self, fields): from netzob.Model.Vocabulary.Field import Field # First it checks the specified children are abstractfiled if fields is not None: for c in fields: if not isinstance(c, Field): raise TypeError( "Cannot edit the fields because at least one specified element is not an AbstractField its a {0}.". format(type(c))) self.clearFields() if fields is not None: for c in fields: c.parent = self self.__fields.append(c) @property def parent(self): """The parent of this current element. If current element has no parent, its value is **None**. :type: a :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :raises: :class:`TypeError` """ return self.__parent @parent.setter def parent(self, parent): if not isinstance(parent, AbstractField): raise TypeError( "Specified parent must be an AbstractField and not an {0}". format(type(parent))) self.__parent = parent def storeInMemento(self): pass def restoreFromMemento(self, memento): pass
class Session(object): """A session includes messages exchanged in the same session. Messages are automaticaly sorted. Applicative data can be attached to sessions. >>> import time >>> from netzob.all import * >>> # we create 3 messages >>> msg1 = RawMessage("ACK", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:05", "%d %b %y %H:%M:%S"))) >>> msg2 = RawMessage("SYN", source="A", destination="B", date=time.mktime(time.strptime("9 Aug 13 10:45:01", "%d %b %y %H:%M:%S"))) >>> msg3 = RawMessage("SYN/ACK", source="B", destination="A", date=time.mktime(time.strptime("9 Aug 13 10:45:03", "%d %b %y %H:%M:%S"))) >>> session = Session([msg1, msg2, msg3]) >>> print(session.messages.values()[0].data) SYN >>> print(session.messages.values()[1].data) SYN/ACK >>> print(session.messages.values()[2].data) ACK """ def __init__(self, messages=None, _id=None, applicativeData=None, name="Session"): """ :parameter messages: the messages exchanged in the current session :type data: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` :parameter _id: the unique identifier of the session :type _id: :class:`uuid.UUID` :keyword applicativeData: a list of :class:`netzob.Model.Vocabulary.ApplicaticeData.ApplicativeData` """ self.__messages = SortedTypedList(AbstractMessage) self.__applicativeData = TypedList(ApplicativeData) if messages is None: messages = [] self.messages = messages if _id is None: _id = uuid.uuid4() self.id = _id if applicativeData is None: applicativeData = [] self.applicativeData = applicativeData self.name = name @property def id(self): """The unique identifier of the session. :type: :class:`uuid.UUID` """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, _id): if _id is None: raise TypeError("Id cannot be None") self.__id = _id @property def messages(self): """The messages exchanged in the current session. Messages are sorted. :type: a :class:`netzob.Common.Utils.TypedList.TypedList` of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` """ return self.__messages def clearMessages(self): """Delete all the messages attached to the current session""" for msg in list(self.__messages.values()): msg.session = None self.__messages.clear() @messages.setter def messages(self, messages): if messages is None: messages = [] # First it checks the specified messages are all AbstractMessages for msg in messages: if not isinstance(msg, AbstractMessage): raise TypeError( "Cannot add messages of type {0} in the session, only AbstractMessages are allowed.". format(type(msg))) self.clearMessages() for message in messages: self.__messages.add(message) message.session = self @property def applicativeData(self): """Applicative data attached to the current session. >>> from netzob.all import * >>> appData = ApplicativeData("test", Integer(20)) >>> session = Session(applicativeData=[appData]) >>> print(len(session.applicativeData)) 1 >>> appData2 = ApplicativeData("test2", ASCII("helloworld")) >>> session.applicativeData.append(appData2) >>> print(len(session.applicativeData)) 2 >>> print(session.applicativeData[0]) Applicative Data: test=Integer=20 ((8, 8))) >>> print(session.applicativeData[1]) Applicative Data: test2=ASCII=helloworld ((0, 80))) :type: a list of :class:`netzob.Model.Vocabulary.ApplicativeData.ApplicativeData`. """ return self.__applicativeData def clearApplicativeData(self): while (len(self.__applicativeData) > 0): self.__applicativeData.pop() @applicativeData.setter def applicativeData(self, applicativeData): for app in applicativeData: if not isinstance(app, ApplicativeData): raise TypeError( "Cannot add an applicative data with type {0}, only ApplicativeData accepted.". format(type(app))) self.clearApplicativeData() for app in applicativeData: self.applicativeData.append(app) @property def name(self): return self.__name @name.setter @typeCheck(str) def name(self, _name): if _name is None: raise TypeError("Name cannot be None") self.__name = _name def getEndpointsList(self): """Retrieve all the endpoints couples that are present in the session. >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="C") >>> session = Session([msg1, msg2, msg3]) >>> print(len(session.getEndpointsList())) 2 >>> print(session.getEndpointsList()) [('A', 'B'), ('A', 'C')] :return: a list containing couple of endpoints (src, dst). :rtype: a :class:`list` """ endpointsList = [] for message in list(self.messages.values()): src = message.source dst = message.destination endpoints1 = (src, dst) endpoints2 = (dst, src) if (not endpoints1 in endpointsList) and ( not endpoints2 in endpointsList): endpointsList.append(endpoints1) return endpointsList def getTrueSessions(self): """Retrieve the true sessions embedded in the current session. A session is here characterized by a uniq endpoints couple. TODO: a more precise solution would be to use flow reconstruction (as in TCP). >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="C") >>> session = Session([msg1, msg2, msg3]) >>> print(len(session.getTrueSessions())) 2 >>> for trueSession in session.getTrueSessions(): ... print(trueSession.name) Session: 'A' - 'B' Session: 'A' - 'C' :return: a list containing true sessions embedded in the current session. :rtype: a :class:`list` """ trueSessions = [] for endpoints in self.getEndpointsList(): trueSessionMessages = [] src = None dst = None for message in list(self.messages.values()): if message.source in endpoints and message.destination in endpoints: trueSessionMessages.append(message) if src is None: src = message.source if dst is None: dst = message.destination trueSession = Session( messages=trueSessionMessages, applicativeData=self.applicativeData, name="Session: '" + str(src) + "' - '" + str(dst) + "'") trueSessions.append(trueSession) return trueSessions def isTrueSession(self): """Tell if the current session is true. A session is said to be true if the communication flow pertain to a uniq applicative session between a couple of endpoints. >>> from netzob.all import * >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="B") >>> session = Session([msg1, msg2, msg3]) >>> print(session.isTrueSession()) True :return: a boolean telling if the current session is a true one (i.e. it corresponds to a uniq applicative session between two endpoints). :rtype: a :class:`bool` """ if len(self.getTrueSessions()) == 1: return True else: return False @typeCheck(list) def abstract(self, symbolList): """This method abstract each message of the current session into symbols according to a list of symbols given as parameter. >>> from netzob.all import * >>> symbolSYN = Symbol([Field(ASCII("SYN"))], name="Symbol_SYN") >>> symbolSYNACK = Symbol([Field(ASCII("SYN/ACK"))], name="Symbol_SYNACK") >>> symbolACK = Symbol([Field(ASCII("ACK"))], name="Symbol_ACK") >>> symbolList = [symbolSYN, symbolSYNACK, symbolACK] >>> msg1 = RawMessage("SYN", source="A", destination="B") >>> msg2 = RawMessage("SYN/ACK", source="B", destination="A") >>> msg3 = RawMessage("ACK", source="A", destination="B") >>> session = Session([msg1, msg2, msg3]) >>> if session.isTrueSession(): ... for src, dst, sym in session.abstract(symbolList): ... print(str(src) + " - " + str(dst) + " : " + str(sym.name)) A - B : Symbol_SYN B - A : Symbol_SYNACK A - B : Symbol_ACK :return: a list of tuples containing the following elements : (source, destination, symbol). :rtype: a :class:`list` """ abstractSession = [] if not self.isTrueSession(): self._logger.warn( "The current session cannot be abstracted as it not a true session (i.e. it may contain inner true sessions)." ) return abstractSession for message in list(self.messages.values()): (symbol, structured_message) = AbstractField.abstract(message.data, symbolList) abstractSession.append((message.source, message.destination, symbol)) return abstractSession
def __init__(self, children): super(NetzobAggregateRegex, self).__init__() self.__children = TypedList(NetzobRegex) self.children = children
class AbstractMessage(SortableObject): """Every message must inherits from this class""" def __init__(self, data, _id=None, session=None, date=None, source=None, destination=None): """ :parameter data: the content of the message :type data: a :class:`object` :parameter _id: the unique identifier of the message :type _id: :class:`uuid.UUID` :keyword session: the session in which the message was captures :type session: :class:`netzob.Model.Vocabulary.Session.Session` :parameter date: the timestamp of the message :type date: a :class:`int` :parameter source: the optional source address of the message :type source: a :class:`str` :parameter destination: the optional destination address of the message :type destination: a :class:`str` """ if data is None: data = '' self.data = data self.session = session if _id is None: _id = uuid.uuid4() self.id = _id if date is None: date = time.mktime(time.gmtime()) self.__date = date self.__source = source self.__destination = destination self.__visualizationFunctions = TypedList(VisualizationFunction) self.__metadata = OrderedDict() self.__semanticTags = OrderedDict() @typeCheck(AbstractField) def isValidForField(self, field): """Checks if the specified field can be used to parse the current message. It returns a boolean that indicates this. >>> from netzob.all import * >>> f0 = Field(Raw(nbBytes=4)) >>> f1 = Field(b", hello ", name="F1") >>> f2 = Field(Raw(nbBytes=(2,5)), name="F2") >>> symbol = Symbol([f0, f1, f2], name="Symbol") >>> m2 = RawMessage(b"Toto, hello you", source="server", destination="client") >>> m2.isValidForField(symbol) True >>> m1 = RawMessage(b"Toto, hello !", source="server", destination="client") >>> m1.isValidForField(symbol) False :parameter field: the current message will be parsed with this field :type field: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :return: a boolean indicating if the current message can be parsed with the specified field :rtype: :class:`bool` """ if field is None: raise TypeError("Field cannot be None") try: DataAlignment.align([self.data], field) except: return False return True @typeCheck(str, object) def setMetadata(self, name, value): """Modify the value of the current metadata which name is specified with the provided value. :parameter name: the name of the metadata to edit :type name: :str :parameter value: the new value of the specified metadata :type value: object :raise TypeError if parameters are not valid and ValueError if the specified value is incompatible with the metadata. """ if not self.isValueForMetadataValid(name, value): raise ValueError("The value of metadata {0} is not valid.") self.__metadata[name] = value def isValueForMetadataValid(self, name, value): """Computes if the specified value is compatible for the provided name of metadata It should be redefined to support specifities of certain metadata :parameter name: the name of the metadata :type name: str :parameter value: the value of the metadata to check :type value: object :raise TypeError if parameters are not valid""" if name is None: raise TypeError("name cannot be none") return True def clearVisualizationFunctions(self): """Remove all the visualization functions attached to the current element""" while (len(self.__visualizationFunctions) > 0): self.__visualizationFunctions.pop() def priority(self): """Return the value that will be used to represent the current message when sorted with the others. :type: int """ return int(self.id) def __str__(self): """Returns a string that describes the message. :warning: This string should only considered for debuging and/or fast visualization. Do not rely on it since its format can often be modified. """ # Add visualization effects to the data functionTable = FunctionApplicationTable([self.data]) for function in self.visualizationFunctions: start = (function.start / 8) end = (function.end / 8) functionTable.applyFunction(function, start, end) tmpData = b"".join(functionTable.getResult()).decode('utf-8') # Add header in front of the data HLS1 = "\033[0;32m" HLE1 = "\033[0;m" HLS2 = "\033[1;32m" HLE2 = "\033[1;m" header = HLS1 + "[{0} {1}{2}{3}->{4}{5}{6}]".format( self.date, HLE1 + HLS2, self.source, HLE2 + HLS1, HLE1 + HLS2, self.destination, HLE2 + HLS1) + HLE1 return "{0} {1}".format(header, repr(tmpData)) @property def id(self): """The unique identified of the message :type: UUID """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, _id): if _id is None: return TypeError("Id cannot be None") self.__id = _id @property def data(self): """The content of the message :type: :class:`object` """ return self.__data @data.setter def data(self, data): self.__data = data @property def date(self): """The date when the message was captured. The date must be encoded in the epoch format. :type:float :raise: a TypeError if date is not valid """ return self.__date @date.setter @typeCheck(float) def date(self, date): if date is None: raise TypeError("Date cannot be None") self.__date = date @property def source(self): """The name or type of the source which emitted the current message :type: str """ return self.__source @source.setter @typeCheck(str) def source(self, source): self.__source = source @property def destination(self): """The name or type of the destination which received the current message :type: str """ return self.__destination @destination.setter @typeCheck(str) def destination(self, destination): self.__destination = destination @property def metadata(self): """The metadata or properties of the message. :type: a dict<str, Object> """ return self.__metadata @metadata.setter @typeCheck(dict) def metadata(self, metadata): if metadata is None: return TypeError("Metadata cannot be None") for k in list(metadata.keys()): self.setMetadata(k, metadata[k]) @property def visualizationFunctions(self): """Sorted list of visualization function to attach on message. :type: a list of :class:`netzob.Model.Vocabulary.Functions.VisualizationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__visualizationFunctions @visualizationFunctions.setter def visualizationFunctions(self, visualizationFunctions): self.clearVisualizationFunctions() self.visualizationFunctions.extend(visualizationFunctions) @property def session(self): """The session from which message comes from. :type: :class:`netzob.Model.Vocabulary.Session.Session` """ return self.__session @session.setter def session(self, session): self.__session = session @typeCheck(int, str) def addSemanticTag(self, position, tag): """Attach the specific semantic tag the specified position of the data. :parameter position: the position on which the semantic tag is attached :type position: :class:`int` :parameter tag: the name of the tag :type tag: :class:`str` """ if position is None: raise TypeError("Position cannot be none") if tag is None: raise TypeError("Tag cannot be None") if position in list(self.semanticTags.keys()): tags = self.semanticTags[position] else: tags = [] tags.append(tag) self.semanticTags[position] = tags @property def semanticTags(self): """Position of identified semantic tags found in the current data. :type: :class:`dict` with keys is int (position) and values is a list of str """ return self.__semanticTags @semanticTags.setter @typeCheck(dict) def semanticTags(self, semanticTags): if semanticTags is None: self.__semanticTags = OrderedDict() # check for key, value in list(semanticTags.items()): if not isinstance(key, int): raise TypeError("At least one key is not a valid int position") if not isinstance(value, list): raise TypeError( "At least one value of the provided dict is not a list of string" ) for x in value: if not isinstance(x, str): raise TypeError( "At least one value of the provided dict is not a list of string" ) self.__semanticTags = semanticTags
class AbstractField(AbstractMementoCreator, metaclass=abc.ABCMeta): """Represents all the different classes which participates in fields definitions of a message format.""" def __init__(self, name=None, meta=False): self.id = uuid.uuid4() self.name = name self.meta = meta self.description = "" self.__fields = TypedList(AbstractField) self.__parent = None self.__encodingFunctions = SortedTypedList(EncodingFunction) self.__visualizationFunctions = TypedList(VisualizationFunction) self.__transformationFunctions = TypedList(TransformationFunction) self._variable = None @typeCheck(bool, bool, bool) def getCells(self, encoded=True, styled=True, transposed=False): """Returns a matrix with a different line for each messages attached to the symbol of the current element. The matrix includes a different column for each leaf children of the current element. In each cell, the slices of messages once aligned. Attached :class:`EncodingFunction` can also be considered if parameter encoded is set to True. In addition, visualizationFunctions are also applied if parameter styled is set to True. If parameter Transposed is set to True, the matrix is built with rows for fields and columns for messages. >>> from netzob.all import * >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] >>> fh1 = Field(ASCII("hello "), name="hello") >>> fh2 = Field(Alt([ASCII("netzob"), ASCII("zoby"), ASCII("lapy"), ASCII("sygus")]), name="pseudo") >>> fheader = Field(name="header") >>> fheader.fields = [fh1, fh2] >>> fb1 = Field(ASCII(", what's up in "), name="whatsup") >>> fb2 = Field(["Paris", "Berlin", "New-York"], name="city") >>> fb3 = Field(" ?", name="end") >>> fbody = Field(name="body") >>> fbody.fields = [fb1, fb2, fb3] >>> symbol = Symbol([fheader, fbody], messages=messages) >>> print(symbol) hello | pseudo | whatsup | city | end -------- | -------- | ----------------- | ---------- | ---- 'hello ' | 'netzob' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'New-York' | ' ?' -------- | -------- | ----------------- | ---------- | ---- >>> fh1.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> fb2.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) hello | pseudo | whatsup | city | end -------------- | -------- | ----------------- | ------------------ | ---- '68656c6c6f20' | 'netzob' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'netzob' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'netzob' | ", what's up in " | '4e65772d596f726b' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'zoby' | ", what's up in " | '4e65772d596f726b' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '5061726973' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '4265726c696e' | ' ?' '68656c6c6f20' | 'lapy' | ", what's up in " | '4e65772d596f726b' | ' ?' -------------- | -------- | ----------------- | ------------------ | ---- >>> print(fheader.getCells()) Field | Field -------------- | -------- '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'netzob' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'zoby' '68656c6c6f20' | 'lapy' '68656c6c6f20' | 'lapy' '68656c6c6f20' | 'lapy' -------------- | -------- >>> print(fh1.getCells()) Field -------------- '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' '68656c6c6f20' -------------- >>> print(fh2.getCells()) Field -------- 'netzob' 'netzob' 'netzob' 'zoby' 'zoby' 'zoby' 'lapy' 'lapy' 'lapy' -------- >>> print(fbody.getCells()) Field | Field | Field ----------------- | ------------------ | ----- ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ", what's up in " | '5061726973' | ' ?' ", what's up in " | '4265726c696e' | ' ?' ", what's up in " | '4e65772d596f726b' | ' ?' ----------------- | ------------------ | ----- >>> print(fb1.getCells()) Field ----------------- ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ", what's up in " ----------------- >>> print(fb2.getCells()) Field ------------------ '5061726973' '4265726c696e' '4e65772d596f726b' '5061726973' '4265726c696e' '4e65772d596f726b' '5061726973' '4265726c696e' '4e65772d596f726b' ------------------ >>> print(fb3.getCells()) Field ----- ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ' ?' ----- :keyword encoded: if set to True, encoding functions are applied on returned cells :type encoded: :class:`bool` :keyword styled: if set to True, visualization functions are applied on returned cells :type styled: :class:`bool` :keyword transposed: is set to True, the returned matrix is transposed (1 line for each field) :type transposed: :class:`bool` :return: a matrix representing the aligned messages following fields definitions. :rtype: a :class:`netzob.Common.Utils.MatrixList.MatrixList` :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages """ if len(self.messages) < 1: raise ValueError("This symbol does not contain any message.") # Fetch all the data to align data = [message.data for message in self.messages] # [DEBUG] set to false for debug only. A sequential alignment is more simple to debug useParallelAlignment = False if useParallelAlignment: # Execute a parallel alignment from netzob.Common.Utils.DataAlignment.ParallelDataAlignment import ParallelDataAlignment return ParallelDataAlignment.align(data, self, encoded=encoded) else: # Execute a sequential alignment from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment return DataAlignment.align(data, self, encoded=encoded) @typeCheck(bool, bool) def getValues(self, encoded=True, styled=True): """Returns all the values the current element can take following messages attached to the symbol of current element. Specific encodingFunctions can also be considered if parameter encoded is set to True. In addition, visualizationFunctions are also applied if parameter styled is set to True. >>> from netzob.all import * >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] >>> f1 = Field("hello ", name="hello") >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f3 = Field(", what's up in ", name="whatsup") >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f5 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages) >>> print(symbol) hello | pseudo | whatsup | city | end -------- | -------- | ----------------- | ---------- | ---- 'hello ' | 'netzob' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'netzob' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'zoby' | ", what's up in " | 'New-York' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Paris' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'Berlin' | ' ?' 'hello ' | 'lapy' | ", what's up in " | 'New-York' | ' ?' -------- | -------- | ----------------- | ---------- | ---- >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) hello | pseudo | whatsup | city | end -------------- | -------------- | -------------------------------- | ------------------ | ------ '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '6e65747a6f62' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '7a6f6279' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '5061726973' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '4265726c696e' | '203f' '68656c6c6f20' | '6c617079' | '2c2077686174277320757020696e20' | '4e65772d596f726b' | '203f' -------------- | -------------- | -------------------------------- | ------------------ | ------ >>> print(symbol.getValues()) [b'68656c6c6f206e65747a6f622c2077686174277320757020696e205061726973203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206e65747a6f622c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f207a6f62792c2077686174277320757020696e205061726973203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f207a6f62792c2077686174277320757020696e204e65772d596f726b203f', b'68656c6c6f206c6170792c2077686174277320757020696e205061726973203f', b'68656c6c6f206c6170792c2077686174277320757020696e204265726c696e203f', b'68656c6c6f206c6170792c2077686174277320757020696e204e65772d596f726b203f'] >>> print(f1.getValues()) [b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20', b'68656c6c6f20'] >>> print(f2.getValues()) [b'6e65747a6f62', b'6e65747a6f62', b'6e65747a6f62', b'7a6f6279', b'7a6f6279', b'7a6f6279', b'6c617079', b'6c617079', b'6c617079'] >>> print(f3.getValues()) [b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20', b'2c2077686174277320757020696e20'] >>> print(f4.getValues()) [b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b', b'5061726973', b'4265726c696e', b'4e65772d596f726b'] >>> print(f5.getValues()) [b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f', b'203f'] :keyword encoded: if set to True, encoding functions are applied on returned cells :type encoded: :class:`bool` :keyword styled: if set to True, visualization functions are applied on returned cells :type styled: :class:`bool` :return: a list detailling all the values current element takes. :rtype: a :class:`list` of :class:`str` :raises: :class:`netzob.Model.Vocabulary.AbstractField.AlignmentException` if an error occurs while aligning messages """ cells = self.getCells(encoded=encoded, styled=styled) values = [] for line in cells: values.append(b''.join(line)) return values @typeCheck(bool, bool) def getMessageCells(self, encoded=False, styled=False): """Computes and returns the alignment of each message belonging to the current field as proposed by getCells() method but indexed per message. >>> from netzob.all import * >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f2 = Field(", what's up in ", name="whatsup") >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f4 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> print(symbol) pseudo | whatsup | city | end -------- | ----------------- | -------- | ---- 'netzob' | ", what's up in " | 'Paris' | ' ?' 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'zoby' | ", what's up in " | 'Paris' | ' ?' 'zoby' | ", what's up in " | 'Berlin' | ' ?' -------- | ----------------- | -------- | ---- >>> messageCells = symbol.getMessageCells() >>> for message in symbol.messages: ... print(message.data, messageCells[message]) netzob, what's up in Paris ? [b'netzob', b", what's up in ", b'Paris', b' ?'] netzob, what's up in Berlin ? [b'netzob', b", what's up in ", b'Berlin', b' ?'] zoby, what's up in Paris ? [b'zoby', b", what's up in ", b'Paris', b' ?'] zoby, what's up in Berlin ? [b'zoby', b", what's up in ", b'Berlin', b' ?'] >>> [f.name for f in messageCells.fields] ['pseudo', 'whatsup', 'city', 'end'] :keyword encoded: if set to true, values are encoded :type encoded: :class:`bool` :keyword styled: if set to true, values are styled :type styled: :class:`bool` :return: a dict indexed by messages that denotes their cells :rtype: a :class:`dict` """ if encoded is None: raise TypeError("Encoded cannot be None") if styled is None: raise TypeError("Styled cannot be None") fieldCells = self.getCells(encoded=encoded, styled=styled) result = MessageCells() leaf_fields = self.getLeafFields() result.fields = leaf_fields for iMessage, message in enumerate(self.messages): result[message] = fieldCells[iMessage] return result @typeCheck(bool, bool) def getMessageValues(self, encoded=False, styled=False): """Computes and returns the alignment of each message belonging to the current field as proposed by getValues() method but indexed per message. >>> from netzob.all import * >>> messages = [RawMessage("{0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") >>> f2 = Field(", what's up in ", name="whatsup") >>> f3 = Field(["Paris", "Berlin", "New-York"], name="city") >>> f4 = Field(" ?", name="end") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> print(symbol) pseudo | whatsup | city | end -------- | ----------------- | -------- | ---- 'netzob' | ", what's up in " | 'Paris' | ' ?' 'netzob' | ", what's up in " | 'Berlin' | ' ?' 'zoby' | ", what's up in " | 'Paris' | ' ?' 'zoby' | ", what's up in " | 'Berlin' | ' ?' -------- | ----------------- | -------- | ---- >>> messageValues = f3.getMessageValues() >>> for message in symbol.messages: ... print(message.data, messageValues[message]) netzob, what's up in Paris ? b'Paris' netzob, what's up in Berlin ? b'Berlin' zoby, what's up in Paris ? b'Paris' zoby, what's up in Berlin ? b'Berlin' :keyword encoded: if set to true, values are encoded :type encoded: :class:`bool` :keyword styled: if set to true, values are styled :type styled: :class:`bool` :return: a dict indexed by messages that denotes their values :rtype: a :class:`dict` """ if encoded is None: raise TypeError("Encoded cannot be None") if styled is None: raise TypeError("Styled cannot be None") result = OrderedDict() fieldValues = self.getValues(encoded=encoded, styled=styled) for iMessage, message in enumerate(self.messages): result[message] = fieldValues[iMessage] return result # def getMessagesWithValue(self, value): # """Computes and returns the messages that have a specified value # in the current field. # >>> from netzob.all import * # >>> messages = [RawMessage("hello {0}, what's up in {1} ?".format(pseudo, city)) for pseudo in ['netzob', 'zoby', 'lapy'] for city in ['Paris', 'Berlin', 'New-York']] # >>> f1 = Field("hello ", name="hello") # >>> f2 = Field(["netzob", "zoby", "lapy", "sygus"], name="pseudo") # >>> f3 = Field(", what's up in ", name="whatsup") # >>> f4 = Field(["Paris", "Berlin", "New-York"], name="city") # >>> f5 = Field(" ?", name="end") # >>> symbol = Symbol([f1, f2, f3, f4, f5], messages=messages) # >>> print(symbol.specialize()) # >>> print(symbol) # hello | netzob | , what's up in | Paris | ? # hello | netzob | , what's up in | Berlin | ? # hello | netzob | , what's up in | New-York | ? # hello | zoby | , what's up in | Paris | ? # hello | zoby | , what's up in | Berlin | ? # hello | zoby | , what's up in | New-York | ? # hello | lapy | , what's up in | Paris | ? # hello | lapy | , what's up in | Berlin | ? # hello | lapy | , what's up in | New-York | ? # >>> lapySymbol = Symbol(messages=symbol.fields[1].getMessagesWithValue("lapy")) # >>> print(lapySymbol) # hello lapy, what's up in Paris ? # hello lapy, what's up in Berlin ? # hello lapy, what's up in New-York ? # >>> Format.splitStatic(lapySymbol) # >>> lapySymbol.encodingFunctions.add(TypeEncodingFunction(HexaString)) # >>> print(lapySymbol) # 68656c6c6f206c6170792c2077686174277320757020696e20 | 5061726973203f # 68656c6c6f206c6170792c2077686174277320757020696e20 | 4265726c696e203f # 68656c6c6f206c6170792c2077686174277320757020696e20 | 4e65772d596f726b203f # :parameter value: a Raw value # :type value: :class:`object` # :return: a list of messages # :rtype: a list of :class:`netzob.Model.Vocabulary.Messages.AbstractMessage.AbstractMessage` # """ # if value is None: # raise TypeError("Value cannot be None") # fieldValues = self.getValues(encoded=False, styled=False) # result = [] # for i_message, message in enumerate(self.messages): # if fieldValues[i_message] == value: # result.append(message) # return result @abc.abstractmethod def specialize(self, mutator=None): """Specialize and generate a :class:`netzob.Model.Vocabulary.Messages.RawMessage` which content follows the fields definitions attached to current element. :keyword mutator: if set, the mutator will be used to mutate the fields definitions :type mutator: :class:`netzob.Model.Mutators.AbstractMutator` :return: a generated content represented with an hexastring :rtype: :class:`str` :raises: :class:`netzob.Model.Vocabulary.AbstractField.GenerationException` if an error occurs while generating a message """ return @staticmethod def abstract(data, fields): """Search in the fields/symbols the first one that can abstract the data. >>> from netzob.all import * >>> messages = ["{0}, what's up in {1} ?".format(pseudo, city) for pseudo in ['netzob', 'zoby'] for city in ['Paris', 'Berlin']] >>> f1a = Field(name="name", domain="netzob") >>> f2a = Field(name="question", domain=", what's up in ") >>> f3a = Field(name="city", domain=Alt(["Paris", "Berlin"])) >>> f4a = Field(name="mark", domain=" ?") >>> s1 = Symbol([f1a, f2a, f3a, f4a], name="Symbol-netzob") >>> f1b = Field(name="name", domain="zoby") >>> f2b = Field(name="question", domain=", what's up in ") >>> f3b = Field(name="city", domain=Alt(["Paris", "Berlin"])) >>> f4b = Field(name="mark", domain=" ?") >>> s2 = Symbol([f1b, f2b, f3b, f4b], name="Symbol-zoby") >>> for m in messages: ... (abstractedSymbol, structured_data) = AbstractField.abstract(m, [s1, s2]) ... print(structured_data) ... print(abstractedSymbol.name) OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')]) Symbol-netzob OrderedDict([('name', b'netzob'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')]) Symbol-netzob OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Paris'), ('mark', b' ?')]) Symbol-zoby OrderedDict([('name', b'zoby'), ('question', b", what's up in "), ('city', b'Berlin'), ('mark', b' ?')]) Symbol-zoby :parameter data: the data that should be abstracted in symbol :type data: :class:`str` :parameter fields: a list of fields/symbols targeted during the abstraction process :type fields: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField` :return: a field/symbol and the structured received message :rtype: a tuple (:class:`netzob.Model.Vocabulary.AbstractField`, dict) :raises: :class:`netzob.Model.Vocabulary.AbstractField.AbstractionException` if an error occurs while abstracting the data """ from netzob.Common.Utils.DataAlignment.DataAlignment import DataAlignment for field in fields: try: # Try to align/parse the data with the current field alignedData = DataAlignment.align([data], field, encoded=False) # If it matches, we build a dict that contains, for each field, the associated value that was present in the message structured_data = OrderedDict() for fields_value in alignedData: for i, field_value in enumerate(fields_value): structured_data[alignedData.headers[i]] = field_value return (field, structured_data) except: pass from netzob.Model.Vocabulary.UnknownSymbol import UnknownSymbol from netzob.Model.Vocabulary.Messages.RawMessage import RawMessage unknown_symbol = UnknownSymbol(RawMessage(data)) structured_data = OrderedDict() logging.error( "Impossible to abstract the message in one of the specified symbols, we create an unknown symbol for it: '%s'", unknown_symbol) return (unknown_symbol, structured_data) def getSymbol(self): """Computes the symbol to which this field is attached. To retrieve it, this method recursively call the parent of the current object until the root is found. If the last root is not a :class:`netzob.Model.Vocabulary.Symbol`, it raises an Exception. :returns: the symbol if available :type: :class:`netzob.Model.Vocabulary.Symbol` :raises: :class:`netzob.Model.Vocabulary.AbstractField.NoSymbolException` """ from netzob.Model.Vocabulary.Symbol import Symbol if isinstance(self, Symbol): return self elif self.hasParent(): return self.parent.getSymbol() else: raise NoSymbolException( "Impossible to retrieve the symbol attached to this element") def getLeafFields(self, depth=None, currentDepth=0, includePseudoFields=False): """Extract the leaf fields to consider regarding the specified depth >>> from netzob.all import * >>> field = Field("hello", name="F0") >>> print([f.name for f in field.getLeafFields()]) ['F0'] >>> field = Field(name="L0") >>> headerField = Field(name="L0_header") >>> payloadField = Field(name="L0_payload") >>> footerField = Field(name="L0_footer") >>> fieldL1 = Field(name="L1") >>> fieldL1_header = Field(name="L1_header") >>> fieldL1_payload = Field(name="L1_payload") >>> fieldL1.fields = [fieldL1_header, fieldL1_payload] >>> payloadField.fields = [fieldL1] >>> field.fields = [headerField, payloadField, footerField] >>> print([f.name for f in field.getLeafFields(depth=None)]) ['L0_header', 'L1_header', 'L1_payload', 'L0_footer'] >>> print([f.name for f in field.getLeafFields(depth=0)]) ['L0'] >>> print([f.name for f in field.getLeafFields(depth=1)]) ['L0_header', 'L0_payload', 'L0_footer'] >>> print([f.name for f in field.getLeafFields(depth=2)]) ['L0_header', 'L1', 'L0_footer'] :return: the list of leaf fields :rtype: :class:`list` of :class:`netzob.Model.Vocabulary.AbstractField.AbstractField`. """ if currentDepth is None: currentDepth = 0 if len(self.fields) == 0: return [self] if currentDepth == depth: return [self] leafFields = [] for fields in self.fields: # Handle case where the field is pseudo (meaning it does not procude concrete value) if fields.isPseudoField: if includePseudoFields: pass else: continue if fields is not None: leafFields.extend( fields.getLeafFields(depth, currentDepth + 1, includePseudoFields)) return leafFields def hasParent(self): """Computes if the current element has a parent. :returns: True if current element has a parent. :rtype: :class:`bool` """ return self.__parent is not None def clearFields(self): """Remove all the children attached to the current element""" while (len(self.__fields) > 0): self.__fields.pop() def clearEncodingFunctions(self): """Remove all the encoding functions attached to the current element""" self.__encodingFunctions = SortedTypedList(EncodingFunction) for child in self.fields: child.clearEncodingFunctions() def clearVisualizationFunctions(self): """Remove all the visualization functions attached to the current element""" while (len(self.__visualizationFunctions) > 0): self.__visualizationFunctions.pop() def clearTransformationFunctions(self): """Remove all the transformation functions attached to the current element""" while (len(self.__transformationFunctions) > 0): self.__transformationFunctions.pop() # Standard methods def __str__(self): result = self.getCells(encoded=True) if self.meta: for split_message in result: message1 = b'' for part in split_message: try: message1 += part except: pass first = True for message2 in self.messages: if message1 == message2.data and first: split_message.insert(0, message2.destination) split_message.insert(0, message2.source) try: split_message.insert(0, message2.session.name) session_present = True except: session_present = False # split_message.insert(0, str(message2.date)) first = False if not first: # Add IP, Timestamp, PCAP etc to matrix headers result.headers.insert(0, 'Destination') result.headers.insert(0, 'Source') if session_present: result.headers.insert(0, 'Session') # matrix.headers.insert(0, 'Time') return str(result) @typeCheck(int) def _str_debug(self, deepness=0): """Returns a string which denotes the current field definition using a tree display""" tab = ["|-- " for x in range(deepness)] tab.append(str(self.name)) lines = [''.join(tab)] from netzob.Model.Vocabulary.Field import Field if isinstance(self, Field): lines.append(self.domain._str_debug(deepness + 1)) for f in self.fields: lines.append(f._str_debug(deepness + 1)) return '\n'.join(lines) # PROPERTIES @property def id(self): """Unique identifier of the field. This value must be a unique UUID instance (generated with uuid.uuid4()). :type: :class:`uuid.UUID` :raises: :class:`TypeError`, :class:`ValueError` """ return self.__id @id.setter @typeCheck(uuid.UUID) def id(self, id): if id is None: raise ValueError("id is Mandatory.") self.__id = id @property def name(self): """Public name (may not be unique), default value is None :type: :class:`str` :raises: :class:`TypeError` """ return self.__name @name.setter @typeCheck(str) def name(self, name): self.__name = name @property def meta(self): """Meta boolean to print metadata,default is False :type: :class:`bool` :raises: :class:`TypeError` """ return self.__meta @meta.setter @typeCheck(bool) def meta(self, meta): self.__meta = meta @property def description(self): """User description of the field. Default value is ''. :type: :class:`str` :raises: :class:`TypeError` """ return self.__description @description.setter @typeCheck(str) def description(self, description): self.__description = description @property def encodingFunctions(self): """Sorted typed list of encoding function to attach on field. .. note:: list implemented as a :class:`netzob.Common.Utils.TypedList.TypedList` :type: a list of :class:`netzob.Model.Vocabulary.Functions.EncodingFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__encodingFunctions @encodingFunctions.setter def encodingFunctions(self, encodingFunctions): self.clearEncodingFunctions() for encodingFunction in encodingFunctions: self.addEncodingFunction(encodingFunction) def addEncodingFunction(self, encodingFunction): self.encodingFunctions.add(encodingFunction) for child in self.fields: child.addEncodingFunction(encodingFunction) @property def visualizationFunctions(self): """Sorted list of visualization function to attach on field. :type: a list of :class:`netzob.Model.Vocabulary.Functions.VisualizationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__visualizationFunctions @visualizationFunctions.setter def visualizationFunctions(self, visualizationFunctions): self.clearVisualizationFunctions() self.visualizationFunctions.extend(visualizationFunctions) @property def transformationFunctions(self): """Sorted list of transformation function to attach on field. :type: a list of :class:`netzob.Model.Vocabulary.Functions.TransformationFunction` :raises: :class:`TypeError` .. warning:: Setting this value with a list copies its members and not the list itself. """ return self.__transformationFunctions @transformationFunctions.setter def transformationFunctions(self, transformationFunctions): self.clearTransformationFunctions() self.transformationFunctions.extend(transformationFunctions) @property def fields(self): """Sorted list of field fields.""" return self.__fields @fields.setter def fields(self, fields): from netzob.Model.Vocabulary.Field import Field # First it checks the specified children are abstractfiled if fields is not None: for c in fields: if not isinstance(c, Field): raise TypeError( "Cannot edit the fields because at least one specified element is not an AbstractField its a {0}." .format(type(c))) self.clearFields() if fields is not None: for c in fields: c.parent = self self.__fields.append(c) @property def parent(self): """The parent of this current element. If current element has no parent, its value is **None**. :type: a :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :raises: :class:`TypeError` """ return self.__parent @parent.setter def parent(self, parent): if not isinstance(parent, AbstractField): raise TypeError( "Specified parent must be an AbstractField and not an {0}". format(type(parent))) self.__parent = parent def storeInMemento(self): pass def restoreFromMemento(self, memento): pass