def mergeFields(self, field1, field2): """Merge specified fields. >>> import binascii >>> from netzob.all import * >>> samples = ["00ff2f000000", "000010000000", "00fe1f000000"] >>> messages = [RawMessage(data=binascii.unhexlify(sample)) for sample in samples] >>> f1 = Field(Raw(nbBytes=1), name="f1") >>> f2 = Field(Raw(nbBytes=2), name="f2") >>> f3 = Field(Raw(nbBytes=2), name="f3") >>> f4 = Field(Raw(nbBytes=1), name="f4") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) f1 | f2 | f3 | f4 ---- | ------ | ------ | ---- '00' | 'ff2f' | '0000' | '00' '00' | '0010' | '0000' | '00' '00' | 'fe1f' | '0000' | '00' ---- | ------ | ------ | ---- >>> fo = FieldOperations() >>> fo.mergeFields(f2, f3) >>> print(symbol) f1 | Merge | f4 ---- | ---------- | ---- '00' | 'ff2f0000' | '00' '00' | '00100000' | '00' '00' | 'fe1f0000' | '00' ---- | ---------- | ---- >>> fo.mergeFields(symbol.fields[0], symbol.fields[1]) >>> print(symbol) Merge | f4 ------------ | ---- '00ff2f0000' | '00' '0000100000' | '00' '00fe1f0000' | '00' ------------ | ---- >>> fo.mergeFields(symbol.fields[0], symbol.fields[1]) >>> print(symbol) Merge -------------- '00ff2f000000' '000010000000' '00fe1f000000' -------------- :param field1: the left field to merge :type field1: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :param field2: the right field to merge :type field2: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :raise Exception if something bad happens """ if field1 is None or field2 is None: raise TypeError("Fields cannot be None") if field1 == field2: raise ValueError("Cannot merge a unique field (field1 == field2)") self._logger.debug("Merging field {0} with field {1}".format(field1.name, field2.name)) if field1.parent is not field2.parent: raise ValueError("Specified fields don't have the same parent, only fields with same parents can be merged.") # retrieve indexes of specified fields iField1 = None iField2 = None for iField, field in enumerate(field1.parent.fields): if field == field1: iField1 = iField elif field == field2: iField2 = iField if iField1 is None: raise ValueError("Cannot retrieve position of field1 in its parent fields") if iField2 is None: raise ValueError("Cannot retrieve position of field2 in its parent fields") if iField2 != iField1 + 1: raise ValueError("Field1 must be directly on the left of field2 (iField1={0}, iField2={1})".format(iField1, iField2)) # build a new field domain newDomain = Agg([field1.domain, field2.domain]) newField = Field(domain=newDomain, name="Merge") newField.encodingFunctions = list(field1.encodingFunctions.values()) parent = field1.parent before = parent.fields[:iField1] after = parent.fields[iField2 + 1:] parent.fields = before + [newField] + after
def _blendFields(field1, field2): """ Completely blend two fields without using Agg. This requires that the two fields are of the same domain and its parameters. The returned new field still needs to be placed into the symbol. >>> from netzob.all import * >>> samples = [ b'\\x00\\xff/BPz', b'\\x00\\x00 CQ~', b'\\x00\\xff/Gf/' ] >>> messages = [RawMessage(data=sample) for sample in samples] >>> f1 = Field(Data(Integer(unitSize=AbstractType.UNITSIZE_8))) >>> f2 = Field(Raw(nbBytes=2)) >>> f3 = Field(Raw(nbBytes=3)) >>> symbol = Symbol([f1, f2, f3], messages=messages) >>> print(f2.domain.dataType.size) (16, 16) >>> print(f3.domain.dataType.size) (24, 24) >>> nf = FieldOperations._blendFields(f2,f3) >>> print(nf.domain.currentValue) None >>> print(nf.domain.dataType.endianness) big >>> print(nf.domain.dataType.size) (40, 40) >>> nf2 = FieldOperations._blendFields(f1,f2) Traceback (most recent call last): ... NotImplementedError: The datatype Integer is not yet supported. :param field1: the left field :param field2: the right field :return: a combination of field1 and field2 :raises TypeError: if domains are not compatible :raises NotImplementedError: for datatypes which still need to be implemented """ for d in [field1.domain, field2.domain]: if len(d._AbstractVariable__boundedVariables) > 0 \ or len(d._AbstractVariable__fathers) > 0 \ or len(d._AbstractVariable__tokenChoppedIndexes) > 0: raise TypeError( "Blending does not support __boundedVariables, __fathers, or __tokenChoppedIndexes." ) if not isinstance(d.dataType, Raw): raise NotImplementedError( "The datatype {} is not yet supported.".format( d.dataType.typeName)) if not field1.domain.svas == field2.domain.svas: raise TypeError( "The SVAS-values of both fields to merge are not the same.") if not field1.domain.dataType.endianness == field2.domain.dataType.endianness: raise TypeError( "The endianness of both fields to merge are not the same.") if not field1.domain.dataType.sign == field2.domain.dataType.sign: raise TypeError( "The signedness of both fields to merge are not the same.") if not field1.domain.dataType.unitSize == field2.domain.dataType.unitSize: raise TypeError( "The unitSize of both fields to merge are not the same.") # '_ASCII__nbChars': (None, None) newDomain = field1.domain.__class__(field1.domain.dataType.__class__()) newDomain.svas = field1.domain.svas newDomain.dataType.endianness = field1.domain.dataType.endianness newDomain.dataType.sign = field1.domain.dataType.sign newDomain.dataType.unitSize = field1.domain.dataType.unitSize newDomain.dataType.size = field1.domain.dataType.size minsizes = [] maxsizes = [] for f in [field1, field2]: if not f.domain.dataType.size is None: sizes = [] for s in f.domain.dataType.size: if s is not None: sizes.append(s) minsizes.append(min(sizes)) maxsizes.append(max(sizes)) newDomain.dataType.size = (sum(minsizes), sum(maxsizes)) newField = Field(domain=newDomain, name="Merge") newField.encodingFunctions = list(field1.encodingFunctions.values()) # Position the new field in correct positions with correct dataType size if field1.domain.currentValue is None or field2.domain.currentValue is None: newField.domain.currentValue = None else: newField.domain.currentValue = (field1.domain.currentValue + field2.domain.currentValue) return newField
def split(field, delimiter): """Split a field (or symbol) with a specific delimiter. The delimiter can be passed either as an ASCII, a Raw, an HexaString, or any objects that inherit from AbstractType. >>> from netzob.all import * >>> samples = [b"aaaaff000000ff10", b"bbff110010ff00000011", b"ccccccccfffe1f000000ff12"] >>> messages = [RawMessage(data=sample) for sample in samples] >>> symbol = Symbol(messages=messages[:3]) >>> Format.splitDelimiter(symbol, ASCII("ff")) >>> print(symbol) Field-0 | Field-sep-6666 | Field-2 | Field-sep-6666 | Field-4 ---------- | -------------- | ------------ | -------------- | ---------- 'aaaa' | 'ff' | '000000' | 'ff' | '10' 'bb' | 'ff' | '110010' | 'ff' | '00000011' 'cccccccc' | 'ff' | 'fe1f000000' | 'ff' | '12' ---------- | -------------- | ------------ | -------------- | ---------- >>> samples = [b"434d446964656e74696679230400000066726564", b"5245536964656e74696679230000000000000000", b"434d44696e666f2300000000", b"524553696e666f230000000004000000696e666f", b"434d4473746174732300000000", b"52455373746174732300000000050000007374617473", b"434d4461757468656e7469667923090000006d7950617373776421", b"52455361757468656e74696679230000000000000000", b"434d44656e6372797074230a00000031323334353674657374", b"524553656e637279707423000000000a00000073707176777436273136", b"434d4464656372797074230a00000073707176777436273136", b"5245536465637279707423000000000a00000031323334353674657374", b"434d446279652300000000", b"524553627965230000000000000000", b"434d446964656e746966792307000000526f626572746f", b"5245536964656e74696679230000000000000000", b"434d44696e666f2300000000", b"524553696e666f230000000004000000696e666f", b"434d4473746174732300000000", b"52455373746174732300000000050000007374617473", b"434d4461757468656e74696679230a000000615374726f6e67507764", b"52455361757468656e74696679230000000000000000", b"434d44656e63727970742306000000616263646566", b"524553656e6372797074230000000006000000232021262724", b"434d44646563727970742306000000232021262724", b"52455364656372797074230000000006000000616263646566", b"434d446279652300000000", b"524553627965230000000000000000"] >>> messages = [RawMessage(data=TypeConverter.convert(sample, HexaString, Raw)) for sample in samples] >>> symbol = Symbol(messages=messages) >>> symbol.encodingFunctions.add(TypeEncodingFunction(ASCII)) # Change visualization to hexastring >>> Format.splitDelimiter(symbol, ASCII("#")) >>> print(symbol) Field-0 | Field-sep-23 | Field-2 | Field-sep-23 | Field-4 --------------- | ------------ | -------------------- | ------------ | ------- 'CMDidentify' | '#' | '....fred' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....myPasswd!' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....123456test' | '' | '' 'RESencrypt' | '#' | "........spqvwt6'16" | '' | '' 'CMDdecrypt' | '#' | "....spqvwt6'16" | '' | '' 'RESdecrypt' | '#' | '........123456test' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' 'CMDidentify' | '#' | '....Roberto' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....aStrongPwd' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....abcdef' | '' | '' 'RESencrypt' | '#' | '........' | '#' | " !&'$" 'CMDdecrypt' | '#' | '....' | '#' | " !&'$" 'RESdecrypt' | '#' | '........abcdef' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' --------------- | ------------ | -------------------- | ------------ | ------- >>> print(symbol.fields[0]._str_debug()) Field-0 |-- Alt |-- Data (Raw=b'CMDidentify' ((0, 88))) |-- Data (Raw=b'RESidentify' ((0, 88))) |-- Data (Raw=b'CMDinfo' ((0, 56))) |-- Data (Raw=b'RESinfo' ((0, 56))) |-- Data (Raw=b'CMDstats' ((0, 64))) |-- Data (Raw=b'RESstats' ((0, 64))) |-- Data (Raw=b'CMDauthentify' ((0, 104))) |-- Data (Raw=b'RESauthentify' ((0, 104))) |-- Data (Raw=b'CMDencrypt' ((0, 80))) |-- Data (Raw=b'RESencrypt' ((0, 80))) |-- Data (Raw=b'CMDdecrypt' ((0, 80))) |-- Data (Raw=b'RESdecrypt' ((0, 80))) |-- Data (Raw=b'CMDbye' ((0, 48))) |-- Data (Raw=b'RESbye' ((0, 48))) Below is another example of the FieldSplitDelimiter usage: it splits fields based on a Raw string. >>> from netzob.all import * >>> samples = [b"\\x01\\x02\\x03\\xff\\x04\\x05\\xff\\x06\\x07", b"\\x01\\x02\\xff\\x03\\x04\\x05\\x06\\xff\\x07", b"\\x01\\xff\\x02\\x03\\x04\\x05\\x06"] >>> messages = [RawMessage(data=sample) for sample in samples] >>> symbol = Symbol(messages=messages) >>> Format.splitDelimiter(symbol, Raw(b"\\xff")) >>> print(symbol) Field-0 | Field-sep-ff | Field-2 | Field-sep-ff | Field-4 -------------- | ------------ | ---------------------- | ------------ | ---------- '\\x01\\x02\\x03' | b'\\xff' | '\\x04\\x05' | b'\\xff' | '\\x06\\x07' '\\x01\\x02' | b'\\xff' | '\\x03\\x04\\x05\\x06' | b'\\xff' | '\\x07' '\\x01' | b'\\xff' | '\\x02\\x03\\x04\\x05\\x06' | '' | '' -------------- | ------------ | ---------------------- | ------------ | ---------- :param field : the field to consider when spliting :type: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :param delimiter : the delimiter used to split messages of the field :type: :class:`netzob.Model.Vocabulary.Types.AbstractType.AbstractType` """ if delimiter is None: raise TypeError("Delimiter cannot be None.") if field is None: raise TypeError("Field cannot be None.") if len(field.messages) < 1: raise ValueError( "The associated symbol does not contain any message.") # Find message substrings after applying delimiter splittedMessages = [] for cell in field.getValues(encoded=False, styled=False): splittedMessage = cell.split(delimiter.value.tobytes()) splittedMessages.append(splittedMessage) import itertools # Inverse the array, so that columns contains observed values for each field splittedMessages = list(itertools.zip_longest(*splittedMessages)) # If the delimiter does not create splitted fields if len(splittedMessages) <= 1: return # Else, we add (2*len(splittedMessages)-1) fields newFields = [] iField = -1 for i in range(len(splittedMessages)): iField += 1 fieldDomain = list() # temporary set that hosts all the observed values to prevent useless duplicate ones observedValues = set() has_inserted_empty_value = False isEmptyField = True # To avoid adding an empty field for v in splittedMessages[i]: if v != "" and v is not None: isEmptyField = False if v not in observedValues: fieldDomain.append(Raw(v)) observedValues.add(v) else: if not has_inserted_empty_value: fieldDomain.append(Raw(nbBytes=0)) has_inserted_empty_value = True if not isEmptyField: newField = Field( domain=DomainFactory.normalizeDomain(fieldDomain), name="Field-" + str(iField)) newField.encodingFunctions = list( field.encodingFunctions.values()) newFields.append(newField) iField += 1 str_delimiter = TypeConverter.convert(delimiter.value, BitArray, HexaString).decode('utf-8') fieldName = "Field-sep-{}".format(str_delimiter) newFields.append( Field(domain=Alt([delimiter, Raw(nbBytes=0)]), name=fieldName)) newFields.pop() # Reset the field from netzob.Inference.Vocabulary.Format import Format Format.resetFormat(field) # Create a field for each entry field.fields = newFields
def split(field, delimiter): """Split a field (or symbol) with a specific delimiter. The delimiter can be passed either as an ASCII, a Raw, an HexaString, or any objects that inherit from AbstractType. >>> from netzob.all import * >>> samples = [b"aaaaff000000ff10", b"bbff110010ff00000011", b"ccccccccfffe1f000000ff12"] >>> messages = [RawMessage(data=sample) for sample in samples] >>> symbol = Symbol(messages=messages[:3]) >>> Format.splitDelimiter(symbol, ASCII("ff")) >>> print(symbol) Field-0 | Field-sep-6666 | Field-2 | Field-sep-6666 | Field-4 ---------- | -------------- | ------------ | -------------- | ---------- 'aaaa' | 'ff' | '000000' | 'ff' | '10' 'bb' | 'ff' | '110010' | 'ff' | '00000011' 'cccccccc' | 'ff' | 'fe1f000000' | 'ff' | '12' ---------- | -------------- | ------------ | -------------- | ---------- >>> samples = [b"434d446964656e74696679230400000066726564", b"5245536964656e74696679230000000000000000", b"434d44696e666f2300000000", b"524553696e666f230000000004000000696e666f", b"434d4473746174732300000000", b"52455373746174732300000000050000007374617473", b"434d4461757468656e7469667923090000006d7950617373776421", b"52455361757468656e74696679230000000000000000", b"434d44656e6372797074230a00000031323334353674657374", b"524553656e637279707423000000000a00000073707176777436273136", b"434d4464656372797074230a00000073707176777436273136", b"5245536465637279707423000000000a00000031323334353674657374", b"434d446279652300000000", b"524553627965230000000000000000", b"434d446964656e746966792307000000526f626572746f", b"5245536964656e74696679230000000000000000", b"434d44696e666f2300000000", b"524553696e666f230000000004000000696e666f", b"434d4473746174732300000000", b"52455373746174732300000000050000007374617473", b"434d4461757468656e74696679230a000000615374726f6e67507764", b"52455361757468656e74696679230000000000000000", b"434d44656e63727970742306000000616263646566", b"524553656e6372797074230000000006000000232021262724", b"434d44646563727970742306000000232021262724", b"52455364656372797074230000000006000000616263646566", b"434d446279652300000000", b"524553627965230000000000000000"] >>> messages = [RawMessage(data=TypeConverter.convert(sample, HexaString, Raw)) for sample in samples] >>> symbol = Symbol(messages=messages) >>> symbol.encodingFunctions.add(TypeEncodingFunction(ASCII)) # Change visualization to hexastring >>> Format.splitDelimiter(symbol, ASCII("#")) >>> print(symbol) Field-0 | Field-sep-23 | Field-2 | Field-sep-23 | Field-4 --------------- | ------------ | -------------------- | ------------ | ------- 'CMDidentify' | '#' | '....fred' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....myPasswd!' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....123456test' | '' | '' 'RESencrypt' | '#' | "........spqvwt6'16" | '' | '' 'CMDdecrypt' | '#' | "....spqvwt6'16" | '' | '' 'RESdecrypt' | '#' | '........123456test' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' 'CMDidentify' | '#' | '....Roberto' | '' | '' 'RESidentify' | '#' | '........' | '' | '' 'CMDinfo' | '#' | '....' | '' | '' 'RESinfo' | '#' | '........info' | '' | '' 'CMDstats' | '#' | '....' | '' | '' 'RESstats' | '#' | '........stats' | '' | '' 'CMDauthentify' | '#' | '....aStrongPwd' | '' | '' 'RESauthentify' | '#' | '........' | '' | '' 'CMDencrypt' | '#' | '....abcdef' | '' | '' 'RESencrypt' | '#' | '........' | '#' | " !&'$" 'CMDdecrypt' | '#' | '....' | '#' | " !&'$" 'RESdecrypt' | '#' | '........abcdef' | '' | '' 'CMDbye' | '#' | '....' | '' | '' 'RESbye' | '#' | '........' | '' | '' --------------- | ------------ | -------------------- | ------------ | ------- >>> print(symbol.fields[0]._str_debug()) Field-0 |-- Alt |-- Data (Raw=b'CMDidentify' ((0, 88))) |-- Data (Raw=b'RESidentify' ((0, 88))) |-- Data (Raw=b'CMDinfo' ((0, 56))) |-- Data (Raw=b'RESinfo' ((0, 56))) |-- Data (Raw=b'CMDstats' ((0, 64))) |-- Data (Raw=b'RESstats' ((0, 64))) |-- Data (Raw=b'CMDauthentify' ((0, 104))) |-- Data (Raw=b'RESauthentify' ((0, 104))) |-- Data (Raw=b'CMDencrypt' ((0, 80))) |-- Data (Raw=b'RESencrypt' ((0, 80))) |-- Data (Raw=b'CMDdecrypt' ((0, 80))) |-- Data (Raw=b'RESdecrypt' ((0, 80))) |-- Data (Raw=b'CMDbye' ((0, 48))) |-- Data (Raw=b'RESbye' ((0, 48))) Below is another example of the FieldSplitDelimiter usage: it splits fields based on a Raw string. >>> from netzob.all import * >>> samples = [b"\\x01\\x02\\x03\\xff\\x04\\x05\\xff\\x06\\x07", b"\\x01\\x02\\xff\\x03\\x04\\x05\\x06\\xff\\x07", b"\\x01\\xff\\x02\\x03\\x04\\x05\\x06"] >>> messages = [RawMessage(data=sample) for sample in samples] >>> symbol = Symbol(messages=messages) >>> Format.splitDelimiter(symbol, Raw(b"\\xff")) >>> print(symbol) Field-0 | Field-sep-ff | Field-2 | Field-sep-ff | Field-4 -------------- | ------------ | ---------------------- | ------------ | ---------- '\\x01\\x02\\x03' | b'\\xff' | '\\x04\\x05' | b'\\xff' | '\\x06\\x07' '\\x01\\x02' | b'\\xff' | '\\x03\\x04\\x05\\x06' | b'\\xff' | '\\x07' '\\x01' | b'\\xff' | '\\x02\\x03\\x04\\x05\\x06' | '' | '' -------------- | ------------ | ---------------------- | ------------ | ---------- :param field : the field to consider when spliting :type: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :param delimiter : the delimiter used to split messages of the field :type: :class:`netzob.Model.Types.AbstractType.AbstractType` """ if delimiter is None: raise TypeError("Delimiter cannot be None.") if field is None: raise TypeError("Field cannot be None.") if len(field.messages) < 1: raise ValueError("The associated symbol does not contain any message.") # Find message substrings after applying delimiter splittedMessages = [] for cell in field.getValues(encoded=False, styled=False): splittedMessage = cell.split(delimiter.value.tobytes()) splittedMessages.append(splittedMessage) import itertools # Inverse the array, so that columns contains observed values for each field splittedMessages = list(itertools.zip_longest(*splittedMessages)) # If the delimiter does not create splitted fields if len(splittedMessages) <= 1: return # Else, we add (2*len(splittedMessages)-1) fields newFields = [] iField = -1 for i in range(len(splittedMessages)): iField += 1 fieldDomain = list() # temporary set that hosts all the observed values to prevent useless duplicate ones observedValues = set() has_inserted_empty_value = False isEmptyField = True # To avoid adding an empty field for v in splittedMessages[i]: if v != "" and v is not None: isEmptyField = False if v not in observedValues: fieldDomain.append(Raw(v)) observedValues.add(v) else: if not has_inserted_empty_value: fieldDomain.append(Raw(nbBytes=0)) has_inserted_empty_value = True if not isEmptyField: newField = Field(domain=DomainFactory.normalizeDomain(fieldDomain), name="Field-"+str(iField)) newField.encodingFunctions = list(field.encodingFunctions.values()) newFields.append(newField) iField += 1 str_delimiter = TypeConverter.convert(delimiter.value, BitArray, HexaString).decode('utf-8') fieldName = "Field-sep-{}".format(str_delimiter) newFields.append(Field(domain=Alt([delimiter, Raw(nbBytes=0)]), name=fieldName)) newFields.pop() # Reset the field from netzob.Inference.Vocabulary.Format import Format Format.resetFormat(field) # Create a field for each entry field.fields = newFields
def mergeFields(self, field1, field2): """Merge specified fields. >>> import binascii >>> from netzob.all import * >>> samples = ["00ff2f000000", "000010000000", "00fe1f000000"] >>> messages = [RawMessage(data=binascii.unhexlify(sample)) for sample in samples] >>> f1 = Field(Raw(nbBytes=1), name="f1") >>> f2 = Field(Raw(nbBytes=2), name="f2") >>> f3 = Field(Raw(nbBytes=2), name="f3") >>> f4 = Field(Raw(nbBytes=1), name="f4") >>> symbol = Symbol([f1, f2, f3, f4], messages=messages) >>> symbol.addEncodingFunction(TypeEncodingFunction(HexaString)) >>> print(symbol) f1 | f2 | f3 | f4 ---- | ------ | ------ | ---- '00' | 'ff2f' | '0000' | '00' '00' | '0010' | '0000' | '00' '00' | 'fe1f' | '0000' | '00' ---- | ------ | ------ | ---- >>> fo = FieldOperations() >>> fo.mergeFields(f2, f3) >>> print(symbol) f1 | Merge | f4 ---- | ---------- | ---- '00' | 'ff2f0000' | '00' '00' | '00100000' | '00' '00' | 'fe1f0000' | '00' ---- | ---------- | ---- >>> fo.mergeFields(symbol.fields[0], symbol.fields[1]) >>> print(symbol) Merge | f4 ------------ | ---- '00ff2f0000' | '00' '0000100000' | '00' '00fe1f0000' | '00' ------------ | ---- >>> fo.mergeFields(symbol.fields[0], symbol.fields[1]) >>> print(symbol) Merge -------------- '00ff2f000000' '000010000000' '00fe1f000000' -------------- :param field1: the left field to merge :type field1: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :param field2: the right field to merge :type field2: :class:`netzob.Model.Vocabulary.AbstractField.AbstractField` :raise Exception if something bad happens """ if field1 is None or field2 is None: raise TypeError("Fields cannot be None") if field1 == field2: raise ValueError("Cannot merge a unique field (field1 == field2)") self._logger.debug("Merging field {0} with field {1}".format( field1.name, field2.name)) if field1.parent is not field2.parent: raise ValueError( "Specified fields don't have the same parent, only fields with same parents can be merged." ) # retrieve indexes of specified fields iField1 = None iField2 = None for iField, field in enumerate(field1.parent.fields): if field == field1: iField1 = iField elif field == field2: iField2 = iField if iField1 is None: raise ValueError( "Cannot retrieve position of field1 in its parent fields") if iField2 is None: raise ValueError( "Cannot retrieve position of field2 in its parent fields") if iField2 != iField1 + 1: raise ValueError( "Field1 must be directly on the left of field2 (iField1={0}, iField2={1})". format(iField1, iField2)) # build a new field domain newDomain = Agg([field1.domain, field2.domain]) newField = Field(domain=newDomain, name="Merge") newField.encodingFunctions = list(field1.encodingFunctions.values()) parent = field1.parent before = parent.fields[:iField1] after = parent.fields[iField2 + 1:] parent.fields = before + [newField] + after