def test_getRef(self): model.keepRef(1, int, 0xcafecafe) model.keepRef(2, float, 0xcafecafe) self.assertEquals(model.getRef(int, 0xcafecafe), 1) self.assertEquals(model.getRef(float, 0xcafecafe), 2) self.assertIsNone(model.getRef(str, 0xcafecafe)) self.assertIsNone(model.getRef(str, 0xdeadbeef)) self.assertIsNone(model.getRef(int, 0xdeadbeef))
def test_getRef(self): model.keepRef(1, int, 0xcafecafe) model.keepRef(2, float, 0xcafecafe) self.assertEquals( model.getRef(int,0xcafecafe), 1) self.assertEquals( model.getRef(float,0xcafecafe), 2) self.assertIsNone( model.getRef(str,0xcafecafe)) self.assertIsNone( model.getRef(str,0xdeadbeef)) self.assertIsNone( model.getRef(int,0xdeadbeef))
def iterateListField(self, mappings, fieldname, sentinels=[]): ''' start from the field and iterate a list. does not return self.''' structType, offset = self._getListFieldInfo(fieldname) # @ of the field headAddr = self._orig_address_ + utils.offsetof( type(self), fieldname) #log.info('Ignore headAddress self.%s at 0x%0.8x'%(fieldname, headAddr)) head = getattr(self, fieldname) if not hasattr(head, '_iterateList'): raise ValueError('Not an iterable field. Probably not declared as a list.') from haystack import model # need my cache done = [s for s in sentinels]+[headAddr] for entry in head._iterateList(mappings): # DO NOT think HEAD is a valid entry - FIXME if entry in done: continue # save it done.append(entry) # @ of the struct, entry is not null, head._iterateList garantizes it. link = entry + offset #log.info('Read %s at 0x%0.8x instead of 0x%0.8x'%(fieldname, link, entry)) # use cache if possible, avoid loops. st = model.getRef( structType, link) #st._orig_addr_ = link if st: yield st else: raise ValueError('the structure has not been loaded, please use loadMembers.') raise StopIteration
def loadMembers(self, mappings, maxDepth): if not LoadableMembersStructure.loadMembers(self, mappings, maxDepth): return False #log.debug('evp app_data attr_obj_address=0x%lx'%(self.evp.app_data) ) #log.debug('evp cipher_data attr_obj_address=0x%lx'%(self.evp.cipher_data) ) ##none cipher = model.getRef( Cipher, getaddress(self.cipher) ) ciphername = cipher.name.toString() # cast evp.app_data into a valid struct if ciphername in self.cipherContexts: # evp.cipher.nid should be 0 struct = self.cipherContexts[ciphername] if (struct is None): log.warning("Unsupported cipher %s"%(ciphername)) return True attr_obj_address = self.evp.app_data memoryMap = is_valid_address_value( attr_obj_address, mappings, struct) log.debug( "CipherContext CAST app_data into : %s "%( struct) ) if not memoryMap: log.warning('On second toughts, app_data seems to be at an invalid address. That should not happen (often).') log.warning('%s addr:0x%lx size:0x%lx addr+size:0x%lx '%(is_valid_address_value( attr_obj_address, mappings), attr_obj_address, ctypes.sizeof(struct), attr_obj_address+ctypes.sizeof(struct))) return False # DEBUG kill it # read the void * and keep a ref st = memoryMap.readStruct(attr_obj_address, struct ) model.keepRef(st, struct, attr_obj_address) # yeah... no. "self.evp.app_data = xx" means SEGFAULT. evp_app_data = ctypes.c_void_p(ctypes.addressof(st)) log.debug('Copied 0x%lx into app_data (0x%lx)'%(attr_obj_address, evp_app_data.value) ) log.debug('LOADED app_data as %s from 0x%lx (%s) into 0x%lx'%(struct, attr_obj_address, is_valid_address_value(attr_obj_address,mappings,struct), evp_app_data.value)) log.debug('\t\t---------\n%s\t\t---------'%(st.toString() ) ) else: log.debug("Unknown cipher %s, can't load a data struct for the EVP_CIPHER_CTX->app_data"%(ciphername)) return True
def _attrToString(self,attr,field,attrtype,prefix, depth=-1): s='' if isStructType(attrtype): s=prefix+'"%s": {\t%s%s},\n'%(field, attr.toString(prefix+'\t', depth-1),prefix ) elif isFunctionType(attrtype): s=prefix+'"%s": 0x%lx, #(FIELD NOT LOADED: function type)\n'%(field, getaddress(attr) ) # only print address in target space elif isBasicTypeArray(attr): ## array of something else than int s=prefix+'"%s": b%s,\n'%(field, repr(array2bytes(attr)) ) elif isArrayType(attrtype): ## array of something else than int/byte # go through each elements, we hardly can make a array out of that... s=prefix+'"%s" :{'%(field) eltyp=type(attr[0]) for i in range(0,len(attr)): s+=self._attrToString( attr[i], i, eltyp, '') s+='},\n' elif isPointerType(attrtype): if not bool(attr) : s=prefix+'"%s": 0x%lx,\n'%(field, getaddress(attr) ) # only print address/null elif isVoidPointerType(attrtype) : s=prefix+'"%s": 0x%lx, #(FELD NOT LOADED: void pointer) \n'%(field, attr ) # only print address/null elif not is_address_local(attr) : s=prefix+'"%s": 0x%lx, #(FIELD NOT LOADED)\n'%(field, getaddress(attr) ) # only print address in target space else: # we can read the pointers contents # if isBasicType(attr.contents): ? # if isArrayType(attr.contents): ? _attrType = get_subtype(attrtype) contents = getRef(_attrType, getaddress(attr)) if type(self) == type(contents): s=prefix+'"%s": { #(0x%lx) -> %s\n%s},\n'%(field, getaddress(attr), _attrType, prefix) # use struct printer elif isStructType(type(contents)): # do not enter in lists s=prefix+'"%s": { #(0x%lx) -> %s%s},\n'%(field, getaddress(attr), contents.toString(prefix+'\t', depth-1),prefix) # use struct printer elif isPointerType(type(contents)): s=prefix+'"%s": { #(0x%lx) -> %s%s},\n'%(field, getaddress(attr), self._attrToString(contents, None, None, prefix+'\t'), prefix ) # use struct printer else: s=prefix+'"%s": { #(0x%lx) -> %s\n%s},\n'%(field, getaddress(attr), contents, prefix) # use struct printer elif isCStringPointer(attrtype): s=prefix+'"%s": "%s" , #(CString)\n'%(field, getRef(CString, getaddress(attr.ptr)) ) elif isBasicType(attrtype): # basic, ctypes.* !Structure/pointer % CFunctionPointer? s=prefix+'"%s": %s, \n'%(field, repr(attr) ) elif isUnionType(attrtype): # UNION s=prefix+'"%s": { # UNION DEFAULT repr\t%s%s},\n'%(field, attr.toString(prefix+'\t', depth-1),prefix ) else: # wtf ? s=prefix+'"%s": %s, # Unknown/bug DEFAULT repr\n'%(field, repr(attr) ) return s
def _loadListEntries(self, fieldname, mappings, maxDepth): ''' we need to load the pointed entry as a valid struct at the right offset, and parse it. When does it stop following FLink/BLink ? sentinel is headAddr only ''' structType, offset = self._getListFieldInfo(fieldname) # DO NOT think HEAD is a valid entry. # if its a ListEntries, self has already been loaded anyway. headAddr = self._orig_address_ + utils.offsetof(type(self), fieldname) head = getattr(self, fieldname) for entry in head._iterateList(mappings): # DO NOT think HEAD is a valid entry if entry == headAddr: continue link = entry + offset log.debug('got a element of list at %s 0x%x/0x%x offset:%d' % (fieldname, entry, link, offset)) # use cache if possible, avoid loops. #XXX from haystack import model ref = model.getRef(structType, link) if ref: # struct has already been loaded, bail out log.debug("%s loading from references cache %s/0x%lx" % (fieldname, structType, link)) continue # do not reload else: # OFFSET read, specific to a LIST ENTRY model memoryMap = utils.is_valid_address_value( link, mappings, structType) if memoryMap is False: log.error( 'error while validating address 0x%x type:%s @end:0x%x' % (link, structType.__name__, link + ctypes.sizeof(structType))) log.error('self : %s , fieldname : %s' % (self.__class__.__name__, fieldname)) raise ValueError( 'error while validating address 0x%x type:%s @end:0x%x' % (link, structType.__name__, link + ctypes.sizeof(structType))) st = memoryMap.readStruct( link, structType) # point at the right offset st._orig_addr_ = link model.keepRef(st, structType, link) log.debug("keepRef %s.%s @%x" % (structType, fieldname, link)) # load the list entry structure members if not st.loadMembers(mappings, maxDepth - 1): log.error('Error while loading members on %s' % (self.__class__.__name__)) print st raise ValueError('error while loading members') return True
def getEvpAppData(self): cipher = model.getRef( Cipher, getaddress(self.cipher) ) ciphername = cipher.name.toString() if ciphername in self.cipherContexts: struct = self.cipherContexts[ciphername] if(struct is None): log.warning("Unsupported cipher %s"%(ciphername)) log.warning("%s"%(cipher.toString())) return None log.debug('CAST evp.app_data Into %s'%(struct)) attr_obj_address = self.evp.app_data st = model.getRef(struct, attr_obj_address) #st = struct.from_address(attr) log.debug('app_data value is : 0x%lx'%(attr_obj_address)) log.debug(st.toString()) return st return None
def list_head_loadRealMembers(self, mappings, maxDepth, attrtype, listHeadName, addresses): ''' Copy ->next and ->prev target structure's memeory space. attach prev and next correctly. @param attrtype the target structure type @param listHeadName the member name of the list_head in the target structure @param addresses original pointers for prev and next ''' attrname = listHeadName addr_prev,addr_next = addresses null = list_head() for listWay, addr in [('prev',addr_prev),('next',addr_next)]: attr = getattr(self,listWay) if addr is None or not bool(attr): attr.contents = null continue # do not load unvalid address #print listHeadName,listWay, hex(addr) #elif not is_address_local(attr) : # continue # coul cache = model.getRef(attrtype, addr ) # is the next/prev Item in cache if cache: # get the offset into the buffer and associate the .list_head->{next,prev} to it attr.contents = gen.list_head.from_address(ctypes.addressof( getattr(cache, attrname)) ) #loadMember is done log.debug("assigned &%s.%s in self "%(attrname,listWay )) # DO NOT recurse continue log.debug('re-loading %s.%s.%s from 0x%x'%(attrtype,attrname,listWay, addr)) memoryMap = is_valid_address_value( addr, mappings, attrtype) if(not memoryMap): # big BUG Badaboum, why did pointer changed validity/value ? log.warning("%s.%s %s not loadable 0x%lx but VALID "%(attrname,listWay, attr, addr )) attr.contents = null continue else: log.debug("self.%s.%s -> 0x%lx (is_valid_address_value: %s)"%(attrname,listWay, addr, memoryMap )) # save the total struct to local memspace nextItem = memoryMap.readStruct(addr, attrtype ) #if not nextItem.isValid(mappings): # log.warning('%s.%s (%s) is INVALID'%(attrname,listWay, attrtype)) # return False log.debug("%s.%s is loaded: '%s'"%(attrname,listWay, nextItem )) # save the ref and load the task model.keepRef( nextItem, attrtype, addr) # get the offset into the buffer and associate the .tasks->next to it attr.contents = gen.list_head.from_address(ctypes.addressof( getattr(nextItem, attrname) )) #loadMember is done log.debug("assigned &%s.%s in self "%(attrname,listWay )) # recursive validation checks on new struct if not bool(attr): log.warning('Member %s.%s is null after copy: %s'%(attrname, listWay ,attr)) attr.contents = null else: # recursive loading - model revalidation if not nextItem.loadMembers(mappings, maxDepth-1): return False continue return True
def task_struct_toPyObject(self): my_class=getattr(sys.modules[self.__class__.__module__],"%s_py"%(self.__class__.__name__) ) #keep ref cache = model.getRef(my_class, ctypes.addressof(self) ) if cache: return cache obj=model.LoadableMembers.toPyObject(self) # change list_head by task_struct obj.tasks.next = self.getTasksNext().toPyObject() obj.tasks.prev = self.getTasksPrev().toPyObject()
def EVP_CIPHER_CTX_toPyObject(self): d=super(EVP_CIPHER_CTX,self).toPyObject() log.debug('Cast a EVP_CIPHER_CTX into PyObj') # cast app_data or cipher_data to right struct if bool(self.cipher_data): cipher = model.getRef( evp_cipher_st, getaddress(self.cipher) ) struct = getCipherDataType( cipher.nid) if struct is not None: # CAST c_void_p to struct d.cipher_data = struct.from_address(self.cipher_data).toPyObject() return d
def task_struct_toPyObject(self): my_class = getattr(sys.modules[self.__class__.__module__], "%s_py" % (self.__class__.__name__)) #keep ref cache = model.getRef(my_class, ctypes.addressof(self)) if cache: return cache obj = model.LoadableMembers.toPyObject(self) # change list_head by task_struct obj.tasks.next = self.getTasksNext().toPyObject() obj.tasks.prev = self.getTasksPrev().toPyObject()
def _loadListEntries(self, fieldname, mappings, maxDepth): ''' we need to load the pointed entry as a valid struct at the right offset, and parse it. When does it stop following FLink/BLink ? sentinel is headAddr only ''' structType, offset = self._getListFieldInfo(fieldname) # DO NOT think HEAD is a valid entry. # if its a ListEntries, self has already been loaded anyway. headAddr = self._orig_address_ + utils.offsetof( type(self), fieldname) head = getattr(self, fieldname) for entry in head._iterateList(mappings): # DO NOT think HEAD is a valid entry if entry == headAddr: continue link = entry + offset log.debug('got a element of list at %s 0x%x/0x%x offset:%d'%(fieldname, entry, link, offset)) # use cache if possible, avoid loops. #XXX from haystack import model ref = model.getRef( structType, link) if ref: # struct has already been loaded, bail out log.debug("%s loading from references cache %s/0x%lx"%(fieldname, structType, link )) continue # do not reload else: # OFFSET read, specific to a LIST ENTRY model memoryMap = utils.is_valid_address_value( link, mappings, structType) if memoryMap is False: log.error('error while validating address 0x%x type:%s @end:0x%x'%(link, structType.__name__, link+ctypes.sizeof(structType)) ) log.error('self : %s , fieldname : %s'%(self.__class__.__name__, fieldname)) raise ValueError('error while validating address 0x%x type:%s @end:0x%x'%(link, structType.__name__, link+ctypes.sizeof(structType)) ) st = memoryMap.readStruct( link, structType) # point at the right offset st._orig_addr_ = link model.keepRef(st, structType, link) log.debug("keepRef %s.%s @%x"%(structType, fieldname, link )) # load the list entry structure members if not st.loadMembers(mappings, maxDepth-1): log.error('Error while loading members on %s'%(self.__class__.__name__)) print st raise ValueError('error while loading members') return True
def EVP_CIPHER_CTX_loadMembers(self, mappings, maxDepth): if not super(EVP_CIPHER_CTX,self).loadMembers(mappings, maxDepth): return False log.debug('trying to load cipher_data Structs.') ''' if bool(cipher) and bool(self.cipher.nid) and is_valid_address(cipher_data): memcopy( self.cipher_data, cipher_data_addr, self.cipher.ctx_size) # cast possible on cipher.nid -> cipherType ''' cipher = model.getRef( evp_cipher_st, getaddress(self.cipher) ) if cipher.nid == 0: # NID_undef, not openssl doing log.info('The cipher is home made - the cipher context data should be application dependant (app_data)') return True struct = getCipherDataType( cipher.nid) log.debug('cipher type is %s - loading %s'%( getCipherName(cipher.nid), struct )) if(struct is None): log.warning("Unsupported cipher %s"%(cipher.nid)) return True # c_void_p is a basic type. attr_obj_address = self.cipher_data memoryMap = is_valid_address_value( attr_obj_address, mappings, struct) log.debug( "cipher_data CAST into : %s "%(struct) ) if not memoryMap: log.warning('in CTX On second toughts, cipher_data seems to be at an invalid address. That should not happen (often).') log.warning('%s addr:0x%lx size:0x%lx addr+size:0x%lx '%(is_valid_address_value( attr_obj_address, mappings), attr_obj_address, ctypes.sizeof(struct), attr_obj_address+ctypes.sizeof(struct))) return True #ok st = memoryMap.readStruct(attr_obj_address, struct ) model.keepRef(st, struct, attr_obj_address) self.cipher_data = ctypes.c_void_p(ctypes.addressof(st)) ###print 'self.cipher_data in loadmembers',self.cipher_data # check debug attr=getattr(self, 'cipher_data') log.debug('Copied 0x%lx into %s (0x%lx)'%(ctypes.addressof(st), 'cipher_data', attr)) log.debug('LOADED cipher_data as %s from 0x%lx (%s) into 0x%lx'%(struct, attr_obj_address, is_valid_address_value(attr_obj_address, mappings, struct), attr )) log.debug('\t\t---------\n%s\t\t---------'%st.toString()) return True
def _HEAP_SEGMENT_loadMember(self, attr, attrname, attrtype, mappings, maxDepth): #log.debug('_loadMember attrname : %s'%(attrname)) if attrname == 'LastValidEntry': # isPointerType code. _attrType = model.get_subtype(attrtype) attr_obj_address = utils.getaddress(attr) #### memoryMap = utils.is_valid_address(attr, mappings, _attrType) if (not memoryMap): log.debug("LastValidEntry out of mapping - 0x%lx - ignore " % (attr_obj_address)) return True from haystack.model import getRef, keepRef, delRef # TODO CLEAN ref = 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 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. ### 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 keepRef(contents, _attrType, attr_obj_address) #log.debug("%s %s loaded memcopy from 0x%lx to 0x%lx"%(attrname, attr, attr_obj_address, (utils.getaddress(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 if not contents.loadMembers(mappings, maxDepth): log.debug('member %s was not loaded' % (attrname)) #invalidate the cache ref. delRef(_attrType, attr_obj_address) return False return True else: return super(_HEAP_SEGMENT, self)._loadMember(attr, attrname, attrtype, mappings, maxDepth)
def toPyObject(self): ''' Returns a Plain Old python object as a perfect copy of this ctypes object. array would be lists, pointers, inner structures, and circular reference should be handled nicely. ''' # get self class. #log.debug("%s %s %s_py"%(self.__class__.__module__, sys.modules[self.__class__.__module__], self.__class__.__name__) ) my_class = getattr(sys.modules[self.__class__.__module__],"%s_py"%(self.__class__.__name__) ) my_self = my_class() #keep ref if hasRef(my_class, ctypes.addressof(self) ): return getRef(my_class, ctypes.addressof(self) ) # we are saving us in a partially resolved state, to keep from loops. keepRef(my_self, my_class, ctypes.addressof(self) ) for field,typ in self.getFields(): attr = getattr(self,field) member = self._attrToPyObject(attr,field,typ) setattr(my_self, field, member) # save the original type (me) and the field setattr(my_self, '_ctype_', type(self)) return my_self
def _HEAP_SEGMENT_loadMember(self, attr, attrname, attrtype, mappings, maxDepth): # log.debug('_loadMember attrname : %s'%(attrname)) if attrname == "LastValidEntry": # isPointerType code. _attrType = model.get_subtype(attrtype) attr_obj_address = utils.getaddress(attr) #### memoryMap = utils.is_valid_address(attr, mappings, _attrType) if not memoryMap: log.debug("LastValidEntry out of mapping - 0x%lx - ignore " % (attr_obj_address)) return True from haystack.model import getRef, keepRef, delRef # TODO CLEAN ref = 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 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. ### 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 keepRef(contents, _attrType, attr_obj_address) # log.debug("%s %s loaded memcopy from 0x%lx to 0x%lx"%(attrname, attr, attr_obj_address, (utils.getaddress(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 if not contents.loadMembers(mappings, maxDepth): log.debug("member %s was not loaded" % (attrname)) # invalidate the cache ref. delRef(_attrType, attr_obj_address) return False return True else: return super(_HEAP_SEGMENT, self)._loadMember(attr, attrname, attrtype, mappings, maxDepth)
def iterateListField(self, mappings, fieldname, sentinels=[]): ''' start from the field and iterate a list. does not return self.''' structType, offset = self._getListFieldInfo(fieldname) # @ of the field headAddr = self._orig_address_ + utils.offsetof(type(self), fieldname) #log.info('Ignore headAddress self.%s at 0x%0.8x'%(fieldname, headAddr)) head = getattr(self, fieldname) if not hasattr(head, '_iterateList'): raise ValueError( 'Not an iterable field. Probably not declared as a list.') from haystack import model # need my cache done = [s for s in sentinels] + [headAddr] for entry in head._iterateList(mappings): # DO NOT think HEAD is a valid entry - FIXME if entry in done: continue # save it done.append(entry) # @ of the struct, entry is not null, head._iterateList garantizes it. link = entry + offset #log.info('Read %s at 0x%0.8x instead of 0x%0.8x'%(fieldname, link, entry)) # use cache if possible, avoid loops. st = model.getRef(structType, link) #st._orig_addr_ = link if st: yield st else: raise ValueError( 'the structure has not been loaded, please use loadMembers.' ) raise StopIteration
def _attrToPyObject(self,attr,field,attrtype): if isStructType(attrtype): obj=attr.toPyObject() elif isUnionType(attrtype): obj=attr.toPyObject() elif isBasicTypeArray(attr): ## array of basic types obj=array2bytes(attr) elif isArrayType(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 isFunctionType(attrtype): obj = repr(attr) elif isCStringPointer(attrtype): #obj = getRef(CString, getaddress(attr)).toString() obj = attr.toString() elif isPointerType(attrtype): if isVoidPointerType(attrtype): log.error('Void pointer - %s'%(field)) obj='Void Pointer' elif isBasicTypeArray(attr): log.error('basic Type array - %s'%(field)) obj='BasicType array' elif not bool(attr) : obj=(None,None) else: # get the cached Value of the LP. _subtype = get_subtype(attrtype) cache = getRef(_subtype, getaddress(attr) ) if cache is not None: obj = self._attrToPyObject(cache, field, _subtype ) elif isPointerBasicType(attrtype): log.error('Pointer to Basic type - %s'%(field)) obj = 'Pointer to Basic type' else: log.error('LP structure for field:%s %s/%s not in cache %x'%(field, attrtype, get_subtype(attrtype), getaddress(attr) ) ) #raise ValueError('LP structure for %s not in cache %s,%x'%(field, get_subtype(attrtype), getaddress(attr) ) ) return (None,None) # ####### any pointer should be in cache # contents=attr.contents # will SEGFAULT # if isStructType(type(contents)) : # attr_py_class = getattr(sys.modules[contents.__class__.__module__],"%s_py"%(contents.__class__.__name__) ) # cache = getRef(attr_py_class, getaddress(attr) ) # if cache: # return cache # #else: # # log.error('any LP struct should be in cache.') # # raise ValueError('LP structure not in cache %s'%(attr_py_class)) # obj=contents.toPyObject() # elif isPointerType(type(contents)): # obj=self._attrToPyObject(contents,None,None) # else: # pointer vers autre chose, le repr() est le seul choix. # #obj=repr(contents) # obj=contents elif isBasicType(attrtype) and isCTypes(attr): obj = attr.value elif isinstance(attr, numbers.Number): obj = attr else: log.error('toPyObj default to return attr %s'%( type(attr) )) obj = attr return obj
def list_head_loadRealMembers(self, mappings, maxDepth, attrtype, listHeadName, addresses): ''' Copy ->next and ->prev target structure's memeory space. attach prev and next correctly. @param attrtype the target structure type @param listHeadName the member name of the list_head in the target structure @param addresses original pointers for prev and next ''' attrname = listHeadName addr_prev, addr_next = addresses null = list_head() for listWay, addr in [('prev', addr_prev), ('next', addr_next)]: attr = getattr(self, listWay) if addr is None or not bool(attr): attr.contents = null continue # do not load unvalid address #print listHeadName,listWay, hex(addr) #elif not is_address_local(attr) : # continue # coul cache = model.getRef(attrtype, addr) # is the next/prev Item in cache if cache: # get the offset into the buffer and associate the .list_head->{next,prev} to it attr.contents = gen.list_head.from_address( ctypes.addressof(getattr(cache, attrname))) #loadMember is done log.debug("assigned &%s.%s in self " % (attrname, listWay)) # DO NOT recurse continue log.debug('re-loading %s.%s.%s from 0x%x' % (attrtype, attrname, listWay, addr)) memoryMap = is_valid_address_value(addr, mappings, attrtype) if (not memoryMap): # big BUG Badaboum, why did pointer changed validity/value ? log.warning("%s.%s %s not loadable 0x%lx but VALID " % (attrname, listWay, attr, addr)) attr.contents = null continue else: log.debug("self.%s.%s -> 0x%lx (is_valid_address_value: %s)" % (attrname, listWay, addr, memoryMap)) # save the total struct to local memspace nextItem = memoryMap.readStruct(addr, attrtype) #if not nextItem.isValid(mappings): # log.warning('%s.%s (%s) is INVALID'%(attrname,listWay, attrtype)) # return False log.debug("%s.%s is loaded: '%s'" % (attrname, listWay, nextItem)) # save the ref and load the task model.keepRef(nextItem, attrtype, addr) # get the offset into the buffer and associate the .tasks->next to it attr.contents = gen.list_head.from_address( ctypes.addressof(getattr(nextItem, attrname))) #loadMember is done log.debug("assigned &%s.%s in self " % (attrname, listWay)) # recursive validation checks on new struct if not bool(attr): log.warning('Member %s.%s is null after copy: %s' % (attrname, listWay, attr)) attr.contents = null else: # recursive loading - model revalidation if not nextItem.loadMembers(mappings, maxDepth - 1): return False continue return True
def _HEAP_getSegmentList(self, mappings): """ list all heap entries attached to one Heap structure. Heap.SegmentList points to the _LIST_ENTRY of Heap.Segment. +0x10 Iteration on SegmentList should return Segment. Offset is needed and provided through _listHead_. FIXME: offset could/should be automated by declaring child _LIST_ENTRY fieldname (SegmentListEntry) listmodel.py:164 BUT iteration should stop on sentinel values. Internal algoritm of iterateListField will not use the root LIST_Entry address as a valid object. It will use it as a sentinel. If there is another sentinel... bad luck.... Heap.Segment.Entry is a valid Heap_entry, addr+size*8 should point to FirstValidEntry. """ allocated = list() free = list() # log.debug('%s'%(self)) for segment in self.iterateListField(mappings, "SegmentList"): # for segment_addr in self.SegmentList._iterateList( mappings): segment_addr = segment._orig_addr_ first_addr = utils.getaddress(segment.FirstEntry) last_addr = utils.getaddress(segment.LastValidEntry) log.debug( "Heap.Segment: 0x%0.8x FirstEntry: 0x%0.8x LastValidEntry: 0x%0.8x" % (segment_addr, first_addr, last_addr) ) skiplist = dict() for ucr in segment.iterateListField(mappings, "UCRSegmentList"): ucr_addr = ucr._orig_addr_ if ucr_addr is None: log.error("None in _orig_addr_") seg_addr = utils.getaddress(ucr.SegmentEntry.FLink) if ucr.Address is None: log.error("None in ucr.Address") try: log.debug( "Heap.Segment.UCRSegmentList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (ucr_addr, ucr.Address, ucr.Size * 8) ) except TypeError, e: import code code.interact(local=locals()) # log.debug("%s"%(ucr.SegmentEntry)) # TODO - CHECK FOR CONSISTENCY ? more heapdebug than haystack debug skiplist[ucr.Address] = ucr.Size * 8 # # obviously not usable, first entry sits on heap header. # chunk_header = segment.Entry # if self.EncodeFlagMask: #heap.EncodeFlagMask # log.debug('EncodeFlagMask is set on the HEAP. decoding is needed.') # chunk_header = _HEAP_ENTRY_decode(chunk_header, self) # chunk_addr = segment._orig_addr_ + utils.offsetof(_HEAP_SEGMENT, 'Entry') # log.debug('Heap.Segment.Entry: 0x%0.8x\n%s'%( chunk_addr, chunk_header)) # print segment from haystack.model import getRef # TODO CLEAN chunk_addr = first_addr while chunk_addr < last_addr: if chunk_addr in skiplist: size = skiplist[chunk_addr] log.debug("Skipping 0x%0.8x - skip %0.5x bytes to 0x%0.8x" % (chunk_addr, size, chunk_addr + size)) chunk_addr += size continue chunk_header = getRef(_HEAP_ENTRY, chunk_addr) if chunk_header is None: # force read it chunk_header = _get_chunk(mappings, self, chunk_addr) if self.EncodeFlagMask: # heap.EncodeFlagMask chunk_header = _HEAP_ENTRY_decode(chunk_header, self) # log.debug('\t\tEntry: 0x%0.8x\n%s'%( chunk_addr, chunk_header)) if (chunk_header.Flags & 1) == 1: log.debug("Chunk 0x%0.8x is in use size: %0.5x" % (chunk_addr, chunk_header.Size * 8)) allocated.append((chunk_addr, chunk_header.Size * 8)) else: log.debug("Chunk 0x%0.8x is FREE" % (chunk_addr)) free.append((chunk_addr, chunk_header.Size * 8)) pass chunk_addr += chunk_header.Size * 8
def _loadMember(self,attr,attrname,attrtype,mappings, maxDepth): # skip static basic 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 isStructType(attrtype) or isUnionType(attrtype): # DEBUG TEST offset = 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 if not attr.loadMembers(mappings, maxDepth+1): log.debug("st: %s %s not valid, erreur while loading inner struct "%(attrname,attrtype) ) return False log.debug("st: %s %s inner struct LOADED "%(attrname,attrtype) ) return True elif isBasicTypeArray(attr): return True if isArrayType(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 if isCStringPointer(attrtype) : # can't use basic c_char_p because we can't load in foreign memory attr_obj_address = getaddress(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 = is_valid_address_value(attr_obj_address, mappings) if not memoryMap : log.warning('Error on addr while fetching a CString. should not happen') return False MAX_SIZE=255 ref = getRef(CString,attr_obj_address) if ref: log.debug("%s %s loading from references cache %s/0x%lx"%(attrname,attr,CString,attr_obj_address )) return True log.debug("%s %s is defined as a CString, loading from 0x%lx is_valid_address %s"%( attrname,attr,attr_obj_address, is_valid_address(attr,mappings) )) txt,full = memoryMap.readCString(attr_obj_address, MAX_SIZE ) if not full: log.warning('buffer size was too small for this CString') # that will SEGFAULT attr.string = txt - instead keepRef to String keepRef( txt, CString, attr_obj_address) log.debug('kept CString ref for "%s" at @%x'%(txt, attr_obj_address)) return True elif isPointerType(attrtype): # not functionType, it's not loadable _attrType = get_subtype(attrtype) attr_obj_address=getaddress(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 = is_valid_address( attr, mappings, _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 = 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 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, (getaddress(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 if not contents.loadMembers(mappings, maxDepth): log.debug('member %s was not loaded'%(attrname)) #invalidate the cache ref. delRef( _attrType, attr_obj_address) return False return True #TATAFN return True
def getIV(self): #return pointer2bytes(model.getRef(ctypes.Array, getaddress(self.iv)), self.block_size) return model.array2bytes(model.getRef( model.get_subtype(self.iv), getaddress(self.iv)) )
def _HEAP_getSegmentList(self, mappings): ''' list all heap entries attached to one Heap structure. Heap.SegmentList points to the _LIST_ENTRY of Heap.Segment. +0x10 Iteration on SegmentList should return Segment. Offset is needed and provided through _listHead_. FIXME: offset could/should be automated by declaring child _LIST_ENTRY fieldname (SegmentListEntry) listmodel.py:164 BUT iteration should stop on sentinel values. Internal algoritm of iterateListField will not use the root LIST_Entry address as a valid object. It will use it as a sentinel. If there is another sentinel... bad luck.... Heap.Segment.Entry is a valid Heap_entry, addr+size*8 should point to FirstValidEntry. ''' allocated = list() free = list() #log.debug('%s'%(self)) for segment in self.iterateListField(mappings, 'SegmentList'): #for segment_addr in self.SegmentList._iterateList( mappings): segment_addr = segment._orig_addr_ first_addr = utils.getaddress(segment.FirstEntry) last_addr = utils.getaddress(segment.LastValidEntry) log.debug( 'Heap.Segment: 0x%0.8x FirstEntry: 0x%0.8x LastValidEntry: 0x%0.8x' % (segment_addr, first_addr, last_addr)) skiplist = dict() for ucr in segment.iterateListField(mappings, 'UCRSegmentList'): ucr_addr = ucr._orig_addr_ if ucr_addr is None: log.error('None in _orig_addr_') seg_addr = utils.getaddress(ucr.SegmentEntry.FLink) if ucr.Address is None: log.error('None in ucr.Address') try: log.debug( "Heap.Segment.UCRSegmentList: 0x%0.8x addr: 0x%0.8x size: 0x%0.5x" % (ucr_addr, ucr.Address, ucr.Size * 8)) except TypeError, e: import code code.interact(local=locals()) #log.debug("%s"%(ucr.SegmentEntry)) # TODO - CHECK FOR CONSISTENCY ? more heapdebug than haystack debug skiplist[ucr.Address] = ucr.Size * 8 # # obviously not usable, first entry sits on heap header. #chunk_header = segment.Entry #if self.EncodeFlagMask: #heap.EncodeFlagMask # log.debug('EncodeFlagMask is set on the HEAP. decoding is needed.') # chunk_header = _HEAP_ENTRY_decode(chunk_header, self) #chunk_addr = segment._orig_addr_ + utils.offsetof(_HEAP_SEGMENT, 'Entry') #log.debug('Heap.Segment.Entry: 0x%0.8x\n%s'%( chunk_addr, chunk_header)) #print segment from haystack.model import getRef # TODO CLEAN chunk_addr = first_addr while (chunk_addr < last_addr): if chunk_addr in skiplist: size = skiplist[chunk_addr] log.debug('Skipping 0x%0.8x - skip %0.5x bytes to 0x%0.8x' % (chunk_addr, size, chunk_addr + size)) chunk_addr += size continue chunk_header = getRef(_HEAP_ENTRY, chunk_addr) if chunk_header is None: # force read it chunk_header = _get_chunk(mappings, self, chunk_addr) if self.EncodeFlagMask: #heap.EncodeFlagMask chunk_header = _HEAP_ENTRY_decode(chunk_header, self) #log.debug('\t\tEntry: 0x%0.8x\n%s'%( chunk_addr, chunk_header)) if ((chunk_header.Flags & 1) == 1): log.debug('Chunk 0x%0.8x is in use size: %0.5x' % (chunk_addr, chunk_header.Size * 8)) allocated.append((chunk_addr, chunk_header.Size * 8)) else: log.debug('Chunk 0x%0.8x is FREE' % (chunk_addr)) free.append((chunk_addr, chunk_header.Size * 8)) pass chunk_addr += chunk_header.Size * 8
def getKey(self): #return pointer2bytes(self.key,self.key_len) return model.array2bytes( model.getRef( model.get_subtype(self.key), getaddress(self.key)) )
elif isBasicTypeArray(attr): try: s+='%s (@0x%lx) : %s\n'%(field,ctypes.addressof(attr), repr(array2bytes(attr)) ) except IndexError,e: log.error( 'error while reading %s %s'%(repr(attr),type(attr)) ) elif isArrayType(attrtype): ## array of something else than int s+='%s (@0x%lx) :['%(field, ctypes.addressof(attr),)+','.join(["%s"%(val) for val in attr ])+'],\n' continue elif isCStringPointer(attrtype): if not bool(attr) : s+='%s (@0x%lx) : 0x%lx\n'%(field,ctypes.addressof(attr), getaddress(attr.ptr) ) # only print address/null elif not is_address_local(attr) : s=prefix+'"%s": 0x%lx, #(FIELD NOT LOADED)\n'%(field, getaddress(attr) ) # only print address in target space else: s+='%s (@0x%lx) : %s (CString) \n'%(field,ctypes.addressof(attr), getRef(CString, getaddress(attr.ptr))) elif isPointerType(attrtype) and not isVoidPointerType(attrtype): # bug with CString if not bool(attr) : s+='%s (@0x%lx) : 0x%lx\n'%(field, ctypes.addressof(attr), getaddress(attr) ) # only print address/null elif not is_address_local(attr) : s+='%s (@0x%lx) : 0x%lx (FIELD NOT LOADED)\n'%(field, ctypes.addressof(attr), getaddress(attr) ) # only print address in target space else: _attrType=get_subtype(attrtype) contents = getRef(_attrType, getaddress(attr)) if type(self) == type(contents): # do not recurse in lists s+='%s (@0x%lx) : (0x%lx) -> {%s}\n'%(field, ctypes.addressof(attr), getaddress(attr), repr(contents) ) # use struct printer else: s+='%s (@0x%lx) : (0x%lx) -> {%s}\n'%(field, ctypes.addressof(attr), getaddress(attr), contents) # use struct printer elif type(attr) is long or type(attr) is int: s+='%s : %s\n'%(field, hex(attr) ) else: