Source code for boofuzz.blocks.size

from functools import wraps

from .. import helpers, primitives
from ..fuzzable import Fuzzable


def _may_recurse(f):
    @wraps(f)
    def safe_recurse(self, *args, **kwargs):
        self._recursion_flag = True
        result = f(self, *args, **kwargs)
        self._recursion_flag = False
        return result

    return safe_recurse


[docs]class Size(Fuzzable): """ Size 用于计算某个 Block 的大小。 示例如下: :: Block("S7COMM", children=( Bytes("Protocol ID", default_value=h2b(s7comm.protocol_id), size=1, fuzzable=False), Bytes("ROSCTR", default_value=h2b(s7comm.rosctr), size=1, fuzzable=False), Bytes("Reserved", default_value=h2b(s7comm.reserved), size=2, fuzzable=False), Bytes("PDU Reference", default_value=h2b(s7comm.pdu_reference), size=2, fuzzable=False), Bytes("Parameter Length", default_value=h2b(s7comm.parameter_length), size=2, fuzzable=False), Bytes("Data Length", default_value=h2b(s7comm.data_length), size=2, fuzzable=False), Bytes("Parameter", default_value=h2b(s7comm.parameter), fuzzable=False), Bytes("Data", default_value=h2b(s7comm.data), fuzzable=False) )), Size(name="test",block_name="S7COMM", fuzzable=False, length=2,output_format="ascii") # 实际使用时注意输出格式 如上代码用一个名为 test 的 Size 计算一个名为 S7COMM 的 Block 的大小,并在模糊测试时将计算结果附加在该 block 之后。 使用时需要注意输出的格式是 ascii 格式还是 binary 格式,同时也要注意端序以及该 Size 本身是否也要算入长度中去。 Create a sizer block bound to the block with the specified name. Size blocks that size their own parent or grandparent are allowed. :type name: str, optional :param name: Name, for referencing later. Names should always be provided, but if not, a default name will be given, defaults to None :type block_name: str, optional :param block_name: Name of block to apply sizer to. :type request: boofuzz.Request, optional :param request: Request this block belongs to. :type offset: int, optional :param offset: Offset for calculated size value, defaults to 0 :type length: int, optional :param length: Length of sizer, defaults to 4 :type endian: chr, optional :param endian: Endianness of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >), defaults to LITTLE_ENDIAN :type output_format: str, optional :param output_format: Output format, "binary" or "ascii", defaults to binary :type inclusive: bool, optional :param inclusive: Should the sizer count its own length? Defaults to False :type signed: bool, optional :param signed: Make size signed vs. unsigned (applicable only with format="ascii"), defaults to False :type math: def, optional :param math: Apply the mathematical op defined in this function to the size, defaults to None :type fuzzable: bool, optional :param fuzzable: Enable/disable fuzzing of this block, defaults to true """ def __init__( self, name=None, block_name=None, request=None, offset=0, length=4, endian="<", output_format="binary", inclusive=False, signed=False, math=None, *args, **kwargs ): super(Size, self).__init__(name=name, default_value=None, *args, **kwargs) self.block_name = block_name self.request = request self.offset = offset self.length = length self.endian = endian self.format = output_format self.inclusive = inclusive self.signed = signed self.math = math self.bit_field = primitives.BitField( name="innerBitField", default_value=0, width=self.length * 8, endian=self.endian, output_format=self.format, signed=self.signed, ) self._rendered = b"" self._fuzz_complete = False if not self.math: self.math = lambda x: x # Set the recursion flag before calling a method that may cause a recursive loop. self._recursion_flag = False def mutations(self, default_value): for mutation in self.bit_field.mutations(None): yield mutation def num_mutations(self, default_value): """ Wrap the num_mutations routine of the internal bit_field primitive. :param default_value: :rtype: int :return: Number of mutated forms this primitive can take. """ return self.bit_field.get_num_mutations() def encode(self, value, mutation_context): if value is None: # default if self._recursion_flag: return self._get_dummy_value() else: return helpers.str_to_bytes( self._length_to_bytes(self._calculated_length(mutation_context=mutation_context)) ) else: return self.bit_field.encode(value=value, mutation_context=mutation_context) def _get_dummy_value(self): return self.length * b"\x00" def _calculated_length(self, mutation_context): return ( self.offset + self._inclusive_length_of_self + self._length_of_target_block(mutation_context=mutation_context) ) def _length_to_bytes(self, length): return primitives.BitField._render_int( value=self.math(length), output_format=self.format, bit_width=self.length * 8, endian=self.endian, signed=self.signed, ) @property def _inclusive_length_of_self(self): """Return length of self or zero if inclusive flag is False.""" if self.inclusive: return self.length else: return 0 @_may_recurse def _length_of_target_block(self, mutation_context): """Return length of target block, including mutations if mutation applies.""" if self.request is not None and self.block_name is not None: target_block = self.request.resolve_name(self.context_path, self.block_name) return len(target_block.render(mutation_context=mutation_context)) else: return 0 @property @_may_recurse def _original_length_of_target_block(self): """Return length of target block, including mutations if it is currently mutated.""" if self.request is not None and self.block_name is not None: target_block = self.request.resolve_name(self.context_path, self.block_name) length = len(target_block.original_value) return length else: return 0 def __repr__(self): return "<%s %s>" % (self.__class__.__name__, self._name) def __len__(self): return self.length