def get_pointee_address(obj): """ Returns the address of the struct pointed by the obj, or null if invalid. :param obj: a pointer. """ import ctypes # check for homebrew POINTER if hasattr(obj, '_sub_addr_'): # print 'obj._sub_addr_', hex(obj._sub_addr_) return obj._sub_addr_ elif isinstance(obj, int) or isinstance(obj, long): # basictype pointers are created as int. return obj elif not bool(obj): return 0 elif ctypes.is_function_type(type(obj)): return ctypes.cast(obj, ctypes.c_void_p).value elif ctypes.is_pointer_type(type(obj)): return ctypes.cast(obj, ctypes.c_void_p).value # check for null pointers # if bool(obj): if not hasattr(obj, 'contents'): return 0 # print '** NOT MY HAYSTACK POINTER' return ctypes.addressof(obj.contents) else: return 0
def __str__(self): """Print the direct members values. Never tries to recurse.""" import ctypes if hasattr(self, '_orig_address_'): s = "# <%s at @%x>\n" % ( self.__class__.__name__, self._orig_address_) else: s = "# <%s at @???>\n" % (self.__class__.__name__) # we need to ensure _mappings_ is defined in all children. for field, attrtype in self.getFields(): attr = getattr(self, field) if ctypes.is_basic_type(attrtype): # basic type, ctypes or python s += '%s : %s, \n' % (field, repr(attr)) elif (ctypes.is_struct_type(attrtype) or ctypes.is_union_type(attrtype)): # you can print a inner struct content s += '%s (@0x%lx) : {\t%s}\n' % (field, ctypes.addressof(attr), attr) elif ctypes.is_function_type(attrtype): # only print address in target space s += '%s (@0x%lx) : 0x%lx (FIELD NOT LOADED: function type)\n' % ( field, ctypes.addressof(attr), utils.get_pointee_address(attr)) elif ctypes.is_array_of_basic_type(attrtype): try: s += '%s (@0x%lx) : %s\n' % (field, ctypes.addressof(attr), repr(utils.array2bytes(attr))) except IndexError as e: log.error('error while reading %s %s' % (repr(attr), type(attr))) # FIXME elif ctypes.is_array_type(attrtype): # array of something else than int s += '%s (@0x%lx) :[' % (field, ctypes.addressof(attr)) s += ','.join(["%s" % (val) for val in attr]) s += '],\n' elif ctypes.is_cstring_type(attrtype): # only print address/null s += '%s (@0x%lx) : 0x%lx\n' % (field, ctypes.addressof(attr), utils.get_pointee_address(attr.ptr)) elif ctypes.is_pointer_type(attrtype): # and # not ctypes.is_pointer_to_void_type(attrtype)): # do not recurse. if attr is None: attr = 0 s += '%s (@????) : 0x0\n' % (field) else: s += '%s (@0x%lx) : 0x%lx\n' % (field, ctypes.addressof(attr), utils.get_pointee_address(attr)) elif (isinstance(attr, long)) or (isinstance(attr, int)): s += '%s : %s\n' % (field, hex(attr)) else: s += '%s : %s\n' % (field, repr(attr)) return s
def check_varname_for_type(varname, structType): done = [] st = structType from haystack import model import ctypes for v in varname: if not hasattr(st, v): fields = ["%s: %s" % (n, t) for n, t in st.getFields()] log.error( '(%s.)%s does not exists in type %s\n\t%s' % ('.'.join(done), v, st, '\n\t'.join(fields))) return False st = st.getFieldType(v) if ctypes.is_pointer_type(st): # accept pointers st = model.get_subtype(st) done.append(v) return True
def toString(cls): import ctypes fieldsStrings = [] for attrname, attrtyp in cls.getFields(): # model if ctypes.is_pointer_type(attrtyp) and not ctypes.is_pointer_to_void_type(attrtyp): fieldsStrings.append("(%s, ctypes.POINTER(%s) ),\n" % (attrname, attrtyp._type_.__name__)) else: # pointers not in the heap. fieldsStrings.append("(%s, %s ),\n" % (attrname, attrtyp.__name__)) fieldsString = "[ \n%s ]" % ("".join(fieldsStrings)) info = "size:%d" % (ctypes.sizeof(cls)) ctypes_def = """ class %s(ctypes.Structure): # %s _fields_ = %s """ % ( cls.__name__, info, fieldsString, ) return ctypes_def
def declare_double_linked_list_type(structType, forward, backward): """Declares a list iterator on structType, as used by ListModel. declare_double_linked_list_type(struct_A, 'next', 'previous') C code example: struct Entry { struct Entry * next; struct Entry * previous; } The effect will be the current structType will NOT be validated, or loaded by basicmodel. No member, pointers members will be loaded. But next and previous elements can be iterated upon with _iterateList, at what point, address validation of both forward and backward pointer occurs before loading of pointee. """ import ctypes # test existence flinkType = getattr(structType, forward) blinkType = getattr(structType, backward) d = dict(structType.getFields()) flinkType = d[forward] blinkType = d[backward] if not ctypes.is_pointer_type(flinkType): raise TypeError("The %s field is not a pointer." % (forward)) if not ctypes.is_pointer_type(blinkType): raise TypeError("The %s field is not a pointer." % (backward)) def iterateList(self, mappings): """ iterate forward, then backward, until null or duplicate """ done = [0] obj = self # print 'going forward ' for fieldname in [forward, backward]: link = getattr(obj, fieldname) addr = utils.get_pointee_address(link) # print fieldname,addr,hex(addr) log.debug("iterateList got a <%s>/0x%x" % (link.__class__.__name__, addr)) nb = 0 while addr not in done: # print '%x %s'%(addr, addr in done) done.append(addr) memoryMap = mappings.is_valid_address_value(addr, structType) if memoryMap == False: log.error("ValueError: 'the link of this linked list has a bad value'") raise StopIteration st = memoryMap.readStruct(addr, structType) st._orig_address_ = addr mappings.keepRef(st, structType, addr) log.debug("keepRefx2 %s.%s: @%x" % (structType.__name__, fieldname, addr)) yield addr # next link = getattr(st, fieldname) addr = utils.get_pointee_address(link) # print 'going backward after %x'%(addr) raise StopIteration def loadMembers(self, mappings, depth): # voluntary blockade of loadMembers. # we do not want to validate members or pointees of this struct. log.debug("- <%s> loadMembers return TRUE" % (structType.__name__)) return True def attrToPyObject(self, attr, field, attrtype): if field in [forward, backward]: # TODO we could maybe put a pointer to parent struct ? # or add a hidden field in list struct representation so that the # python user can get the parent python object easily. return "# double_linked_list_type - contents not loaded" else: return self._attrToPyObject2(attr, field, attrtype) # set iterator on the list structure structType._iterateList = iterateList # structType.loadMembers = loadMembers # FIXME DEBUG WHY? # be nicer on printouts # structType._attrToPyObject2 = structType._attrToPyObject # structType._attrToPyObject = attrToPyObject log.debug("%s has been fitted with a list iterator self._iterateList(mappings)" % (structType)) return
def _attrToPyObject(self, attr, field, attrtype): import ctypes if ctypes.is_basic_type(attrtype): if ctypes.is_basic_ctype(type(attr)): obj = attr.value else: obj = attr elif ctypes.is_struct_type(attrtype) or ctypes.is_union_type(attrtype): attr._mappings_ = self.mappings obj = self.parse(attr) elif ctypes.is_array_of_basic_type(attrtype): # return a list of int, float, or a char[] to str obj = utils.ctypes_to_python_array(attr) elif ctypes.is_array_type(attrtype): # array of something else than int/byte obj = [] eltyp = type(attr[0]) for i in range(0, len(attr)): obj.append(self._attrToPyObject(attr[i], i, eltyp)) elif ctypes.is_cstring_type(attrtype): obj = self.mappings.getRef( ctypes.CString, utils.get_pointee_address( attr.ptr)) elif ctypes.is_function_type(attrtype): obj = repr(attr) elif ctypes.is_pointer_type(attrtype): # get the cached Value of the LP. _subtype = get_subtype(attrtype) _address = utils.get_pointee_address(attr) if _address == 0: # Null pointer obj = None elif ctypes.is_pointer_to_void_type(attrtype): # TODO: make a prototype for c_void_p loading # void types a rereturned as None obj = None elif ctypes.is_array_of_basic_type(attrtype): log.error('basic Type array - %s' % (field)) obj = 'BasicType array' else: # get the cached Value of the LP. _subtype = get_subtype(attrtype) cache = self.mappings.getRef(_subtype, _address) if cache is not None: # struct, union... obj = self._attrToPyObject(cache, field, _subtype) else: # you got here because your pointer is not loaded: # did you ignore it in expectedValues ? # is it in the middle of a struct ? # is that a linked list ? # is it a invalid instance ? log.debug('Pointer for field:%s %s/%s not in cache ' '0x%x' % (field, attrtype, get_subtype(attrtype), _address)) return (None, None) elif isinstance(attr, numbers.Number): # case for int, long. But needs to be after c_void_p pointers case obj = attr else: log.error('toPyObj default to return attr %s' % (type(attr))) obj = attr return obj
def _loadMember(self, attr, attrname, attrtype, mappings, maxDepth): ctypes = self._mappings_.config.ctypes # skip static void_p data members if not self._isLoadableMember(attr, attrname, attrtype): log.debug("%s %s not loadable bool(attr) = %s" % (attrname, attrtype, bool(attr))) return True # load it, fields are valid elif ctypes.is_struct_type(attrtype) or ctypes.is_union_type(attrtype): # its an embedded record. Bytes are already loaded. offset = utils.offsetof(type(self), attrname) log.debug('st: %s %s is STRUCT at @%x' % (attrname, attrtype, self._orig_address_ + offset)) # TODO pydoc for impl. attr._orig_address_ = self._orig_address_ + offset attr._mappings = mappings if not attr.loadMembers(mappings, maxDepth - 1): log.debug( "st: %s %s not valid, error while loading inner struct" % (attrname, attrtype)) return False log.debug("st: %s %s inner struct LOADED " % (attrname, attrtype)) return True elif ctypes.is_array_of_basic_type(attrtype): return True elif ctypes.is_array_type(attrtype): log.debug('a: %s is arraytype %s recurse load' % (attrname, repr(attr))) attrLen = len(attr) if attrLen == 0: return True elType = type(attr[0]) for i in range(0, attrLen): # FIXME BUG DOES NOT WORK # offsetof("%s[%d]") is called, and %s exists, not %s[%d] # if not self._loadMember(attr[i], "%s[%d]"%(attrname,i), # elType, mappings, maxDepth): if not self._loadMember( attr[i], attrname, elType, mappings, maxDepth): return False return True # we have PointerType here . Basic or complex # exception cases elif ctypes.is_function_type(attrtype): pass # FIXME elif ctypes.is_cstring_type(attrtype): # can't use basic c_char_p because we can't load in foreign memory # FIXME, you need to keep a ref to this ctring if # your want _mappings_ to exists # or just mandate mappings in toString attr_obj_address = utils.get_pointee_address(attr.ptr) if not bool(attr_obj_address): log.debug('%s %s is a CString, the pointer is null (validation ' 'must have occurred earlier)' % (attrname, attr)) return True memoryMap = mappings.is_valid_address_value(attr_obj_address) if not memoryMap: log.warning('Error on addr while fetching a CString.' 'should not happen') return False ref = mappings.getRef(ctypes.CString, attr_obj_address) if ref: log.debug("%s %s loading from references cache %s/0x%lx" % (attrname, attr, ctypes.CString, attr_obj_address)) return True max_size = min( self.MAX_CSTRING_SIZE, memoryMap.end - attr_obj_address) log.debug('%s %s is defined as a CString, loading %d bytes from 0x%lx ' 'is_valid_address %s' % (attrname, attr, max_size, attr_obj_address, mappings.is_valid_address_value(attr_obj_address))) txt, truncated = memoryMap.readCString(attr_obj_address, max_size) if truncated: log.warning( 'buffer size was too small for this CString: %d' % (max_size)) # that will SEGFAULT attr.string = txt - instead keepRef to String mappings.keepRef(txt, ctypes.CString, attr_obj_address) log.debug( 'kept CString ref for "%s" at @%x' % (txt, attr_obj_address)) return True # not functionType, it's not loadable elif ctypes.is_pointer_type(attrtype): _attrType = get_subtype(attrtype) attr_obj_address = utils.get_pointee_address(attr) #### # memcpy and save objet ref + pointer in attr # we know the field is considered valid, so if it's not in # memory_space, we can ignore it memoryMap = mappings.is_valid_address(attr, _attrType) if(not memoryMap): # big BUG Badaboum, why did pointer changed validity/value ? log.warning( "%s %s not loadable 0x%lx but VALID " % (attrname, attr, attr_obj_address)) return True ref = mappings.getRef(_attrType, attr_obj_address) if ref: log.debug( "%s %s loading from references cache %s/0x%lx" % (attrname, attr, _attrType, attr_obj_address)) # DO NOT CHANGE STUFF SOUPID attr.contents = ref. attr.contents # will SEGFAULT return True log.debug( "%s %s loading from 0x%lx (is_valid_address: %s)" % (attrname, attr, attr_obj_address, memoryMap)) # Read the struct in memory and make a copy to play with. # DO NOT COPY THE STRUCT, we have a working readStruct for that... # ERRROR # attr.contents=_attrType.from_buffer_copy(memoryMap.readStruct(attr_obj_address, # _attrType )) contents = memoryMap.readStruct(attr_obj_address, _attrType) # save that validated and loaded ref and original addr so we dont # need to recopy it later mappings.keepRef(contents, _attrType, attr_obj_address) log.debug( "keepRef %s.%s @%x" % (_attrType, attrname, attr_obj_address)) log.debug( "%s %s loaded memcopy from 0x%lx to 0x%lx" % (attrname, attr, attr_obj_address, (utils.get_pointee_address(attr)))) # recursive validation checks on new struct if not bool(attr): log.warning( 'Member %s is null after copy: %s' % (attrname, attr)) return True # go and load the pointed struct members recursively subtype = utils.get_subtype(attrtype) if (ctypes.is_basic_type(subtype) or ctypes.is_array_of_basic_type(subtype)): # do nothing return True elif (ctypes.is_array_type(subtype) or ctypes.is_pointer_type(subtype)): return self._loadMember( contents, 'pointee', subtype, mappings, maxDepth - 1) if not contents.loadMembers(mappings, maxDepth - 1): log.debug('member %s was not loaded' % (attrname)) # invalidate the cache ref. mappings.delRef(_attrType, attr_obj_address) return False return True # TATAFN return True
def _isValidAttr(self, attr, attrname, attrtype, mappings): """ Validation of a single member """ import ctypes # a) log.debug('valid: %s, %s' % (attrname, attrtype)) if ctypes.is_basic_type(attrtype): if attrname in self.expectedValues: if attr not in self.expectedValues[attrname]: log.debug( 'basicType: %s %s %s bad value not in self.expectedValues[attrname]:' % (attrname, attrtype, repr(attr))) return False log.debug( 'basicType: %s %s %s ok' % (attrname, attrtype, repr(attr))) return True # b) elif ctypes.is_struct_type(attrtype) or ctypes.is_union_type(attrtype): # do i need to load it first ? becaus it should be memcopied with # the super().. if not attr.isValid(mappings): log.debug( 'structType: %s %s %s isValid FALSE' % (attrname, attrtype, repr(attr))) return False log.debug( 'structType: %s %s %s isValid TRUE' % (attrname, attrtype, repr(attr))) return True # c) elif ctypes.is_array_of_basic_type(attrtype): if attrname in self.expectedValues: if attr not in self.expectedValues[attrname]: log.debug( 'basicArray: %s %s %s - bad value not in self.expectedValues[attrname]:' % (attrname, attrtype, type(attr))) return False log.debug( 'basicArray: %s is arraytype %s we decided it was valid', attrname, type(attr)) return True # d) elif ctypes.is_array_type(attrtype): log.debug('array: %s is arraytype %s recurse validate' % (attrname, repr(attr))) attrLen = len(attr) if attrLen == 0: return True elType = type(attr[0]) for i in range(0, attrLen): # FIXME BUG DOES NOT WORK - offsetof("%s[%d]") is called, # and %s exists, not %s[%d] if not self._isValidAttr(attr[i], "%s[%d]" % (attrname, i), elType, mappings): return False return True # e) elif ctypes.is_cstring_type(attrtype): myaddress = utils.get_pointee_address(attr.ptr) if attrname in self.expectedValues: # test if NULL is an option if not bool(myaddress): if not ((None in self.expectedValues[attrname]) or (0 in self.expectedValues[attrname])): log.debug('str: %s %s %s isNULL - NOT EXPECTED' % ( attrname, attrtype, repr(attr))) return False log.debug('str: %s %s %s isNULL - OK' % (attrname, attrtype, repr(attr))) # e.1) return True if (myaddress != 0 and not mappings.is_valid_address_value(myaddress)): log.debug('str: %s %s %s 0x%lx INVALID' % (attrname, attrtype, repr(attr), myaddress)) # e.2) return False log.debug('str: %s %s %s is at 0x%lx OK' % (attrname, attrtype, repr(attr), myaddress)) # e.3) return True # f) elif ctypes.is_pointer_type(attrtype): myaddress = utils.get_pointee_address(attr) if attrname in self.expectedValues: # test if NULL is an option log.debug('ctypes.is_pointer_type: bool(attr):%s attr:%s' % ( bool(attr), attr)) if not bool(myaddress): if not ((None in self.expectedValues[attrname]) or (0 in self.expectedValues[attrname])): log.debug('ptr: %s %s %s isNULL - NOT EXPECTED' % ( attrname, attrtype, repr(attr))) # f.1) expectedValues specifies NULL to be invalid return False log.debug('ptr: %s %s %s isNULL - OK' % (attrname, attrtype, repr(attr))) # f.2) expectedValues specifies NULL to be valid return True _attrType = None if (ctypes.is_pointer_to_void_type(attrtype) or ctypes.is_function_type(attrtype)): log.debug( 'Its a simple type. Checking mappings only. attr=%s' % (attr)) if (myaddress != 0 and not mappings.is_valid_address_value(myaddress)): log.debug('voidptr: %s %s %s 0x%lx INVALID simple pointer' % ( attrname, attrtype, repr(attr), myaddress)) # f.3) address must be valid, no type requirement return False else: # test valid address mapping _attrType = get_subtype(attrtype) if (myaddress != 0 and not mappings.is_valid_address(attr, _attrType)): log.debug('ptr: %s %s %s 0x%lx INVALID' % (attrname, attrtype, repr(attr), utils.get_pointee_address(attr))) # f.4) its a pointer, but not valid in our mappings for this # pointee type. return False log.debug('ptr: name:%s repr:%s address:0x%lx OK' % (attrname, repr(attr), utils.get_pointee_address(attr))) # f.5) null is accepted by default return True # g) log.error('What type are You ?: %s/%s' % (attrname, attrtype)) return True