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 _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