Source code for sulley.primitives.bit_field

import struct
from base import base

[docs]class bit_field (base): def __init__ (self, value, width, max_num=None, endian="<", format="binary", signed=False, full_range=False, fuzzable=True, name=None): """ The bit field primitive represents a number of variable length and is used to define all other integer types. Used to sub-class out words, qwords, bytes, and any other binary blocks. :type value: Integer :param value: Default integer value :type width: Integer :param width: Width of bit fields :type endian: Character :param endian: (Optional, def=LITTLE_ENDIAN) Endianess of the bit field (LITTLE_ENDIAN: <, BIG_ENDIAN: >) :type format: String :param format: (Optional, def=binary) Output format, "binary" or "ascii" :type signed: Boolean :param signed: (Optional, def=False) Make size signed vs. unsigned (applicable only with format="ascii") :type full_range: Boolean :param full_range: (Optional, def=False) If enabled the field mutates through *all* possible values. :type fuzzable: Boolean :param fuzzable: (Optional, def=True) Enable/disable fuzzing of this primitive :type name: String :param name: (Optional, def=None) Specifying a name gives you direct access to a primitive """ assert(type(value) is int or type(value) is long) assert(type(width) is int or type(value) is long) super(bit_field,self).__init__() self.value = self.original_value = value self.width = width self.max_num = max_num self.endian = endian self.format = format self.signed = signed self.full_range = full_range self.fuzzable = fuzzable self.name = name self.rendered = "" # rendered value self.fuzz_complete = False # flag if this primitive has been completely fuzzed self.fuzz_library = [] # library of fuzz heuristics self.mutant_index = 0 # current mutation number if self.max_num is None: self.max_num = self.to_decimal("1" * width) assert(type(self.max_num) is int or type(self.max_num) is long) # build the fuzz library. if self.full_range: # add all possible values. for i in xrange(0, self.max_num): self.fuzz_library.append(i) else: # try only "smart" values. self.add_integer_boundaries(0) self.add_integer_boundaries(self.max_num / 2) self.add_integer_boundaries(self.max_num / 3) self.add_integer_boundaries(self.max_num / 4) self.add_integer_boundaries(self.max_num / 8) self.add_integer_boundaries(self.max_num / 16) self.add_integer_boundaries(self.max_num / 32) self.add_integer_boundaries(self.max_num) # if the optional file '.fuzz_ints' is found, parse each line as a new entry for the fuzz library. try: fh = open(".fuzz_ints", "r") for fuzz_int in fh.readlines(): # convert the line into an integer, continue on failure. try: fuzz_int = long(fuzz_int, 16) except: continue if fuzz_int <= self.max_num: self.fuzz_library.append(fuzz_int) fh.close() except: pass
[docs] def add_integer_boundaries (self, integer): """ Add the supplied integer and border cases to the integer fuzz heuristics library. :type integer: Int :param integer: Integer to append to fuzz heuristics """ for i in xrange(-10, 10): case = integer + i # ensure the border case falls within the valid range for this field. if 0 <= case <= self.max_num: if case not in self.fuzz_library: self.fuzz_library.append(case)
[docs] def render (self): """ Render the primitive. """ # # binary formatting. # if self.format == "binary": bit_stream = "" rendered = "" # pad the bit stream to the next byte boundary. if self.width % 8 == 0: bit_stream += self.to_binary() else: bit_stream = "0" * (8 - (self.width % 8)) bit_stream += self.to_binary() # convert the bit stream from a string of bits into raw bytes. for i in xrange(len(bit_stream) / 8): chunk = bit_stream[8*i:8*i+8] rendered += struct.pack("B", self.to_decimal(chunk)) # if necessary, convert the endianess of the raw bytes. if self.endian == "<": rendered = list(rendered) rendered.reverse() rendered = "".join(rendered) self.rendered = rendered # # ascii formatting. # else: # if the sign flag is raised and we are dealing with a signed integer (first bit is 1). if self.signed and self.to_binary()[0] == "1": max_num = self.to_decimal("0" + "1" * (self.width - 1)) # chop off the sign bit. val = self.value & max_num # account for the fact that the negative scale works backwards. val = max_num - val # toss in the negative sign. self.rendered = "%d" % ~val # unsigned integer or positive signed integer. else: self.rendered = "%d" % self.value return self.rendered
[docs] def to_binary (self, number=None, bit_count=None): """ Convert a number to a binary string. :type number: Integer :param number: (Optional, def=self.value) Number to convert :type bit_count: Integer :param bit_count: (Optional, def=self.width) Width of bit string :rtype: String :returns: Bit string """ if number is None: number = self.value if bit_count is None: bit_count = self.width return "".join(map(lambda x:str((number >> x) & 1), range(bit_count -1, -1, -1)))
[docs] def to_decimal (self, binary): """ Convert a binary string to a decimal number. :type binary: String :param binary: Binary string :rtype: Integer :returns: Converted bit string """ return int(binary, 2)