import struct import binascii class v_enum(object): def __init__(self): object.__setattr__(self, '_vs_reverseMap', {}) def __setattr__(self, name, value): '''if duplicate values, last one wins!''' self._vs_reverseMap[value] = name return object.__setattr__(self, name, value) def vsReverseMapping(self, val, default=None): '''Maps an integer to its name. Returns default if not found''' return self._vs_reverseMap.get(val, default) class v_bitmask(object): def __init__(self): object.__setattr__(self, "_vs_reverseMap", {}) def __setattr__(self, name, value): self._vs_reverseMap[value] = name return object.__setattr__(self, name, value) def vsReverseMapping(self, val, default=None): '''Returns a list of names where apply the AND of the mask and val is non-zero''' return [v for k, v in self._vs_reverseMap.items() if (val & k) != 0] class v_base(object): def __init__(self, bigend=False): self._vs_meta = {} self._vs_bigend = bigend def vsGetMeta(self, name, defval=None): return self._vs_meta.get(name, defval) def vsSetMeta(self, name, value): self._vs_meta[name] = value def vsGetEndian(self): return self._vs_bigend def vsSetEndian(self, bigend): self._vs_bigend = bigend # Sub-classes (primitive base, or VStruct must have these def vsParse(self, bytes): return NotImplemented def vsCalculate(self): pass def vsIsPrim(self): return NotImplemented def vsGetTypeName(self): return NotImplemented class v_prim(v_base): def __init__(self, bigend=False): v_base.__init__(self, bigend) # Used by base len(),vsGetFormat, etc... self._vs_value = None self._vs_length = None self._vs_fmt = None self._vs_align = None def vsIsPrim(self): return True def vsGetTypeName(self): return self.__class__.__name__ def vsParse(self, bytes, offset=0): ''' Parser for primitives which assumes we are calling parse directly. ''' return NotImplemented def vsParseFd(self, fd): # Most primitives should be able to simply use this... fbytes = fd.read(self._vs_length) if len(fbytes) != self._vs_length: raise Exception('Not enough data in fd!') self.vsParse(fbytes) def vsEmit(self): ''' Return the actual bytes which represent this field ''' return NotImplemented def vsGetValue(self): ''' Get the type specific value for this field. (Used by the structure dereference method to return a python native for the field by name) ''' return self._vs_value def vsSetValue(self, value): ''' Set the type specific value for this field. ''' self._vs_value = value def vsSetLength(self, size): ''' Set the length of this primitive type. This may be used to dynamically update the length of string fields, etc... ''' return NotImplemented def __repr__(self): return repr(self.vsGetValue()) def __len__(self): return self._vs_length def __str__(self): return str(self.vsGetValue()) def __hash__(self): return hash(self.vsGetValue()) num_fmts = { # big endian unsigned (True, 1): '>B', (True, 2): '>H', (True, 4): '>I', (True, 8): '>Q', # little endian unsigned (False, 1): 'b', (True, 2): '>h', (True, 4): '>i', (True, 8): '>q', # little endian signed (False, 1): '> (i*8)) & 0xff) if self._vs_bigend: r.reverse() return bytes(r) def vsSetValue(self, value): self._vs_value = value & self.maxval def __int__(self): return int(self._vs_value) def __repr__(self): v = self.vsGetValue() if self._vs_enum is not None: return str(self._vs_enum.vsReverseMapping(v, default=hex(v))) return "0x%x (%d)" % (v, v) ################################################################## # Implement the number API def __add__(self, other): return int(self) + int(other) def __sub__(self, other): return int(self) - int(other) def __mul__(self, other): return int(self) * int(other) def __div__(self, other): return int(self) / int(other) def __floordiv__(self, other): return int(self) // int(other) def __mod__(self, other): return int(self) % int(other) def __divmod__(self, other): return divmod(int(self), int(other)) def __pow__(self, other, modulo=None): return pow(int(self), int(other), modulo) def __lshift__(self, other): return int(self) << int(other) def __rshift__(self, other): return int(self) >> int(other) def __and__(self, other): return int(self) & int(other) def __xor__(self, other): return int(self) ^ int(other) def __or__(self, other): return int(self) | int(other) def __ge__(self, other): return int(self) >= int(other) def __le__(self, other): return int(self) <= int(other) def __gt__(self, other): return int(self) > int(other) def __lt__(self, other): return int(self) < int(other) def __eq__(self, other): return int(self) == int(other) # Operator swapped variants def __radd__(self, other): return int(other) + int(self) def __rsub__(self, other): return int(other) - int(self) def __rmul__(self, other): return int(other) * int(self) def __rdiv__(self, other): return int(other) / int(self) def __rfloordiv__(self, other): return int(other) // int(self) def __rmod__(self, other): return int(other) % int(self) def __rdivmod__(self, other): return divmod(int(other), int(self)) def __rpow__(self, other, modulo=None): return pow(int(other), int(self), modulo) def __rlshift__(self, other): return int(other) << int(self) def __rrshift__(self, other): return int(other) >> int(self) def __rand__(self, other): return int(other) & int(self) def __rxor__(self, other): return int(other) ^ int(self) def __ror__(self, other): return int(other) | int(self) # Inplace variants def __iadd__(self, other): self.vsSetValue(self+other) return self def __isub__(self, other): self.vsSetValue(self - other) return self def __imul__(self, other): self.vsSetValue(self*other) return self def __idiv__(self, other): self.vsSetValue(self/other) return self def __ifloordiv__(self, other): self.vsSetValue(self // other) return self def __imod__(self, other): self.vsSetValue(self % other) return self def __ipow__(self, other, modulo=None): self.vsSetValue(pow(self, other, modulo)) return self def __ilshift__(self, other): self.vsSetValue(self << other) return self def __irshift__(self, other): self.vsSetValue(self >> other) return self def __iand__(self, other): self.vsSetValue(self & other) return self def __ixor__(self, other): self.vsSetValue(self ^ other) return self def __ior__(self, other): self.vsSetValue(self | other) return self # operator helpers def __neg__(self): return -(int(self)) def __pos__(self): return +(int(self)) def __abs__(self): return abs(int(self)) def __invert__(self): return ~(int(self)) # index use helper def __index__(self): return int(self) # Print helpers def __hex__(self): return hex(int(self)) def __oct__(self): return oct(int(self)) class v_snumber(v_number): _vs_length = 1 def __init__(self, value=0, bigend=False, enum=None): smaxval = (2**((8 * self._vs_length)-1)) - 1 self.smask = smaxval + 1 v_number.__init__(self, value=value, bigend=bigend, enum=enum) self._vs_fmt = signed_fmts.get((bigend, self._vs_length)) def vsSetValue(self, value): value = value & self.maxval if value & self.smask: value = value - self.maxval - 1 self._vs_value = int(value) # TODO class v_uint8(v_number): _vs_builder = True _vs_length = 1 class v_uint16(v_number): _vs_builder = True _vs_length = 2 class v_uint24(v_number): _vs_builder = True _vs_length = 3 class v_uint32(v_number): _vs_builder = True _vs_length = 4 class v_uint64(v_number): _vs_builder = True _vs_length = 8 class v_int8(v_snumber): _vs_builder = True _vs_length = 1 class v_int16(v_snumber): _vs_builder = True _vs_length = 2 class v_int24(v_snumber): _vs_builder = True _vs_length = 3 class v_int32(v_snumber): _vs_builder = True _vs_length = 4 class v_int64(v_snumber): _vs_builder = True _vs_length = 8 pointersize = struct.calcsize('P') class v_size_t(v_number): _vs_builder = True _vs_length = pointersize def __repr__(self): return "0x%.8x" % self._vs_value class v_ptr(v_size_t): pass class v_ptr32(v_ptr): _vs_builder = True _vs_length = 4 class v_ptr64(v_ptr): _vs_builder = True _vs_length = 8 float_fmts = { (True,4): '>f', (True,8): '>d', (False,4): '> (i*8)) & 0xff) ) if self._vs_bigend: r.reverse() return b''.join(r) def __int__(self): return int(self._vs_value) def __float__(self): return float(self._vs_value) def __add__(self, other): return float(self) + float(other) def __sub__(self, other): return float(self) - float(other) def __mul__(self, other): return float(self) * float(other) def __div__(self, other): return float(self) / float(other) def __floordiv__(self, other): return float(self) // float(other) def __mod__(self, other): return float(self) % float(other) def __divmod__(self, other): return divmod(float(self), float(other)) def __pow__(self, other, modulo=None): return pow(float(self), float(other), modulo) def __lshift__(self, other): return float(self) << float(other) def __rshift__(self, other): return float(self) >> float(other) def __and__(self, other): return float(self) & float(other) def __xor__(self, other): return float(self) ^ float(other) def __or__(self, other): return float(self) | float(other) # Operator swapped variants def __radd__(self, other): return float(other) + float(self) def __rsub__(self, other): return float(other) - float(self) def __rmul__(self, other): return float(other) * float(self) def __rdiv__(self, other): return float(other) / float(self) def __rfloordiv__(self, other): return float(other) // float(self) def __rmod__(self, other): return float(other) % float(self) def __rdivmod__(self, other): return divmod(float(other), float(self)) def __rpow__(self, other, modulo=None): return pow(float(other), float(self), modulo) def __rlshift__(self, other): return float(other) << float(self) def __rrshift__(self, other): return float(other) >> float(self) def __rand__(self, other): return float(other) & float(self) def __rxor__(self, other): return float(other) ^ float(self) def __ror__(self, other): return float(other) | float(self) # Inplace variants def __iadd__(self, other): self.vsSetValue(self+other); return self def __isub__(self, other): self.vsSetValue(self - other); return self def __imul__(self, other): self.vsSetValue(self*other); return self def __idiv__(self, other): self.vsSetValue(self/other); return self def __ifloordiv__(self, other): self.vsSetValue(self // other); return self def __imod__(self, other): self.vsSetValue(self % other); return self def __ipow__(self, other, modulo=None): self.vsSetValue(pow(self, other, modulo)); return self def __ilshift__(self, other): self.vsSetValue(self << other); return self def __irshift__(self, other): self.vsSetValue(self >> other); return self def __iand__(self, other): self.vsSetValue(self & other); return self def __ixor__(self, other): self.vsSetValue(self ^ other); return self def __ior__(self, other): self.vsSetValue(self | other); return self # operator helpers def __neg__(self): return -(float(self)) def __pos__(self): return +(float(self)) def __abs__(self): return abs(float(self)) def __invert__(self): return ~(float(self)) # index use helper def __index__(self): return float(self) # Print helpers def __hex__(self): return hex(float(self)) def __oct__(self): return oct(float(self)) class v_double(v_float): _vs_length = 8 class v_bytes(v_prim): ''' v_bytes is used for fixed width byte fields. ''' _vs_builder = True def __init__(self, size=0, vbytes=None): v_prim.__init__(self) if vbytes is None: vbytes = b'\x00' * size self._vs_length = len(vbytes) self._vs_value = vbytes self._vs_align = 1 self._vs_fmt = '%ds' % self._vs_length def vsSetValue(self, val): if len(val) != self._vs_length: raise Exception('v_bytes field set to wrong length!') self._vs_value = val def vsParse(self, fbytes, offset=0): offend = offset + self._vs_length self._vs_value = fbytes[offset : offend] return offend def vsEmit(self): return self._vs_value def vsSetLength(self, size): size = int(size) self._vs_length = size self._vs_fmt = '%ds' % size # Either chop or expand my string... b = self._vs_value[:size] self._vs_value = b.ljust(size, b'\x00') def __repr__(self): return binascii.hexlify(self._vs_value).decode('utf-8') class v_str(v_prim): ''' A string placeholder class which will automagically return up to a null terminator (and will keep it's size by null padding when assigned to) ''' _vs_builder = True def __init__(self, size=4, val=b''): v_prim.__init__(self) self._vs_length = size self._vs_fmt = '%ds' % size self._vs_value = val.ljust(size, b'\x00') self._vs_align = 1 def vsParse(self, fbytes, offset=0): offend = offset + self._vs_length self._vs_value = fbytes[offset : offend] return offend def vsEmit(self): return self._vs_value def vsGetValue(self): s = self._vs_value.split(b'\x00')[0].decode('utf-8') return s def vsSetValue(self, val): if type(val) is str: val = val.encode('utf-8') self._vs_value = val.ljust(self._vs_length, b'\x00') def vsSetLength(self, size): size = int(size) self._vs_length = size self._vs_fmt = '%ds' % size # Either chop or expand my string... b = self._vs_value[:size] self._vs_value = b.ljust(size, b'\x00') class v_zstr(v_prim): ''' A string placeholder class which will automagically return up to a null terminator dynamically. ''' _vs_builder = True def __init__(self, val=b'', align=1): v_prim.__init__(self) self._vs_align = align self.vsParse(val + b'\x00') def vsParse(self, fbytes, offset=0): nulloff = fbytes.find(b'\x00', offset) if nulloff == -1: raise Exception('v_zstr found no NULL terminator!') # align things correctly diff = self._vs_align - ((nulloff-offset) % self._vs_align) self._vs_align_pad = diff nulloff += diff self._vs_value = fbytes[offset : nulloff] self._vs_length = len(self._vs_value) return nulloff def vsParseFd(self, fd): ret = b'' while not ret.endswith(b'\x00'): y = fd.read(1) if not y: raise Exception('v_zstr file ended before NULL') ret += y return self.vsParse(ret) def vsEmit(self): return self._vs_value def vsGetValue(self): return self._vs_value[:-self._vs_align_pad].decode('utf-8') def vsSetValue(self, val): # FIXME: just call vsParse? length = len(val) diff = self._vs_align - (length % self._vs_align) self._vs_value = val + b'\x00'*(diff) self._vs_length = len(self._vs_value) self._vs_align_pad = diff def vsSetLength(self, size): raise Exception('Cannot vsSetLength on v_zstr! (its dynamic)') class v_wstr(v_str): ''' Unicode variant of the above string class NOTE: the size paramater is in WCHARs! ''' _vs_builder = True def __init__(self, size=4, encode='utf-16le', val=b''): v_str.__init__(self) b = val.ljust(size, b'\x00') self._vs_length = len(b) self._vs_value = b self._vs_encode = encode self._vs_align = 2 def vsParse(self, fbytes, offset=0): offend = offset + self._vs_length self._vs_value = fbytes[offset : offend] return offend def vsEmit(self): return self._vs_value def vsSetValue(self, val): rbytes = val.encode(self._vs_encode) self._vs_value = rbytes.ljust(len(self), b'\x00') def vsGetValue(self): s = self._vs_value.decode(self._vs_encode, errors='replace') return s.split("\x00")[0] class v_zwstr(v_str): ''' Unicode variant of the above zstring class ''' _vs_builder = True def __init__(self, encode='utf-16le', val=b'', align=2): v_str.__init__(self) self._vs_encode = encode self._vs_align = align self.vsParse(val + b'\x00\x00') def vsParse(self, fbytes, offset=0): nulloff = offset while nulloff < len(fbytes): nulloff = fbytes.find(b'\x00\x00', nulloff) if nulloff == -1: raise Exception('v_wzstr found no NULL terminator!') #make sure that this is acutally the null word by checking if # the alignment is correct if ((nulloff-offset) % self._vs_align) == 0: #yes, found it break #advance nulloff by 1 & keep looking nulloff += 1 # align things correctly diff = self._vs_align - ((nulloff-offset) % self._vs_align) self._vs_align_pad = diff nulloff += diff self._vs_value = fbytes[offset : nulloff] self._vs_length = len(self._vs_value) return nulloff #offend = offset + self._vs_length #self._vs_value = fbytes[offset : offend] #return offend def vsEmit(self): return self._vs_value def vsGetValue(self): cstr = self._vs_value.decode(self._vs_encode, errors='replace') return cstr.split('\x00')[0] def vsSetValue(self, val): #rbytes = val.encode(self._vs_encode) #self._vs_value = rbytes.ljust(len(self), '\x00') # FIXME: just call vsParse? rbytes = val.encode(self._vs_encode) length = len(rbytes) diff = self._vs_align - (length % self._vs_align) self._vs_value = rbytes + b'\x00' * (diff) self._vs_value = rbytes.ljust(len(self), b'\x00') self._vs_length = len(self._vs_value) self._vs_align_pad = diff def vsSetLength(self, size): raise Exception('Cannot vsSetLength on v_zwstr! (its dynamic)') class GUID(v_prim): _vs_builder = True def __init__(self, guidstr=None): """ Construct a new GUID primitive. You may specify a GUID string in the constructor to populate initial values. """ v_prim.__init__(self) self._vs_length = 16 self._vs_value = '\x00' * 16 self._vs_fmt = '16s' self._guid_fields = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) if guidstr is not None: self._parseGuidStr(guidstr) def vsParse(self, fbytes, offset=0): offend = offset + self._vs_length self._guid_fields = struct.unpack("IHH8B', bytes) def vsSetValue(self, guidstr): self._parseGuidStr(guidstr) def vsGetValue(self): return repr(self) def __repr__(self): base = "{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}" return base % self._guid_fields