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 loadMembers(self, mappings, maxDepth): if not LoadableMembersStructure.loadMembers(self, mappings, maxDepth): return False # Load and memcopy key and iv log.debug('Enc Memcopying a Key with %d bytes'%self.key_len) attr_obj_address = getaddress(self.key) log.debug('got key @%x '%(attr_obj_address)) memoryMap = is_valid_address_value( attr_obj_address, mappings) # DEBUG - I do question the buffer_copy. log.debug('memoryMap is %s - \nmake array '%(memoryMap)) array=(ctypes.c_ubyte*self.key_len).from_buffer_copy(memoryMap.readArray(attr_obj_address, ctypes.c_ubyte, self.key_len)) # save key as bitstream ##key_contents = ctypes.c_ubyte.from_buffer(array) key_contents = array log.debug('keep ref ') model.keepRef(key_contents, model.get_subtype(self.key), attr_obj_address) log.debug('Enc Memcopying a IV with %d bytes'%( self.block_size) ) attr_obj_address=getaddress(self.iv) memoryMap = is_valid_address_value( attr_obj_address, mappings) log.debug('make array ') array=(ctypes.c_ubyte*self.block_size).from_buffer_copy(memoryMap.readArray(attr_obj_address, ctypes.c_ubyte,self.block_size)) # save iv contents as bitstream ##iv_contents = ctypes.c_ubyte.from_buffer(array) iv_contents = array log.debug('keep ref') model.keepRef(iv_contents, model.get_subtype(self.iv), attr_obj_address) log.debug('ENC KEY(%d bytes) and IV(%d bytes) acquired'%(self.key_len,self.block_size)) return True
def getNextChunk(self, mappings, orig_addr): ## do next_chunk mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError next_addr = orig_addr + self.real_size() # check if its in mappings if not model.is_valid_address_value(next_addr, mappings): return None,None next_chunk = mmap.readStruct(next_addr, malloc_chunk ) model.keepRef( next_chunk, malloc_chunk, next_addr) return next_chunk, next_addr
def check_prev_inuse(self, mappings, orig_addr): ''' if flags busy, inuse == true ''' prev_addr = self.prev_addr(orig_addr) mmap = model.is_valid_address_value(prev_addr, mappings) if not mmap: return 0 prev = mmap.readStruct(prev_addr, malloc_chunk) return prev.flags & FLAGS.HEAP_ENTRY_BUSY
def check_prev_inuse(self, mappings, orig_addr): ''' if flags busy, inuse == true ''' prev_addr = self.prev_addr(orig_addr) mmap = model.is_valid_address_value(prev_addr, mappings) if not mmap: return 0 prev = mmap.readStruct( prev_addr, malloc_chunk ) return prev.flags & FLAGS.HEAP_ENTRY_BUSY
def loadMembers(self, mappings, maxDepth): if not LoadableMembers.loadMembers(self, mappings, maxDepth): return False # Load and memcopy key and iv log.debug('Memcopying a Key with %d bytes'%self.key_len) attr_obj_address=getaddress(self.key) memoryMap = is_valid_address_value( attr_obj_address, mappings) array=(ctypes.c_ubyte*self.key_len).from_buffer_copy(memoryMap.readArray(attr_obj_address, ctypes.c_ubyte, self.key_len)) self.key.contents=ctypes.c_ubyte.from_buffer(array) log.debug('Memcopying a IV with %d bytes'%( self.block_size) ) attr_obj_address=getaddress(self.iv) memoryMap = is_valid_address_value( attr_obj_address, mappings) array=(ctypes.c_ubyte*self.block_size).from_buffer_copy(memoryMap.readArray(attr_obj_address, ctypes.c_ubyte,self.block_size)) self.iv.contents=ctypes.c_ubyte.from_buffer(array) log.debug('ENC KEY(%d bytes) and IV(%d bytes) acquired'%(self.key_len,self.block_size)) return True
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 loadMembers(self, mappings, maxDepth): if not LoadableMembers.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 #log.debug('cipher app_data attr_obj_address=0x%lx'%(getaddress(self.cipher.contents.cipher_data)) ) # cast evp.app_data into a valid struct if self.cipher.contents.name.string in self.cipherContexts: struct,fieldname=self.cipherContexts[self.cipher.contents.name.string] if(struct is None): log.warning("Unsupported cipher %s"%(self.cipher.contents.name.string)) return True attr=getattr(self.evp,fieldname) #attr_obj_address=getaddress(attr) or attr # c_void_p is a basic type. attr_obj_address = attr #print attr memoryMap = is_valid_address_value( attr_obj_address, mappings, struct) log.debug( "CipherContext CAST %s into : %s "%(fieldname, struct) ) if not memoryMap: log.warning('On second toughts, %s seems to be at an invalid address. That should not happen (often).'%(fieldname)) 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))) log.warning('%s : %s'%(fieldname, attr)) return False # DEBUG kill it #st=struct.from_buffer_copy(memoryMap.readStruct(attr_obj_address, struct ) ) # XXX CAST do not copy buffer when casting, sinon on perds des bytes ## attr.contents=(type(attr.contents)).from_buffer(st) ### c_void_p -> allocate local_mem and change value ### XXX dangling pointer ? #attr.value = ctypes.addressof(st) #setattr(self.evp, fieldname, ctypes.c_void_p(ctypes.addressof(st)) ) st=memoryMap.readStruct(attr_obj_address, struct ) model.keepRef(st) setattr(self.evp, fieldname, ctypes.c_void_p(ctypes.addressof(st)) ) attr=getattr(self.evp,fieldname) log.debug('Copied 0x%lx into %s (0x%lx)'%(ctypes.addressof(st), fieldname, attr)) log.debug('LOADED app_data evp.%s as %s from 0x%lx (%s) into 0x%lx'%(fieldname,struct, attr_obj_address, is_valid_address_value(attr_obj_address,mappings,struct), attr )) log.debug('\t\t---------\n%s\t\t---------'%st.toString()) else: log.warning("Unknown cipher %s, can't load a data struct for the EVP_CIPHER_CTX->app_data"%(self.cipher.contents.name.string)) return True
def getNextChunk(self, mappings, orig_addr): mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError if self.size > 0: next_addr = self.next_addr(orig_addr) next_chunk = mmap.readStruct(next_addr, malloc_chunk) model.keepRef(next_chunk, malloc_chunk, next_addr) return next_chunk, next_addr return None, None
def getPrevChunk(self, mappings, orig_addr): mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError if self.prev_size > 0: prev_addr = self.prev_addr(orig_addr) prev_chunk = mmap.readStruct(prev_addr, malloc_chunk) model.keepRef(prev_chunk, malloc_chunk, prev_addr) return prev_chunk, prev_addr return None, None
def getPrevChunk(self, mappings, orig_addr): mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError if self.prev_size > 0 : prev_addr = self.prev_addr(orig_addr) prev_chunk = mmap.readStruct(prev_addr, malloc_chunk ) model.keepRef( prev_chunk, malloc_chunk, prev_addr) return prev_chunk, prev_addr return None, None
def getNextChunk(self, mappings, orig_addr): mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError if self.size > 0 : next_addr = self.next_addr(orig_addr) next_chunk = mmap.readStruct(next_addr, malloc_chunk ) model.keepRef( next_chunk, malloc_chunk, next_addr) return next_chunk, next_addr return None, None
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 check_inuse(self, mappings, orig_addr): '''extract p's inuse bit doesnt not work on the top one ''' next_addr = self.next_addr(orig_addr) + Config.WORDSIZE mmap = model.is_valid_address_value(next_addr, mappings) if not mmap: return 0 #raise ValueError() next_size = mmap.readWord( next_addr) #print 'next_size',next_size, '%x'%next_addr return next_size & PREV_INUSE
def loadMembers(self, mappings, maxDepth): if not LoadableMembers.loadMembers(self, mappings, maxDepth): return False # Load and memcopy key log.debug('Memcopying a Key with %d bytes'%self.key_len) attr_obj_address=getaddress(self.key) memoryMap = is_valid_address_value( attr_obj_address, mappings) array=(ctypes.c_ubyte*self.key_len).from_buffer_copy(memoryMap.readArray(attr_obj_address, ctypes.c_ubyte, self.key_len)) self.key.contents=ctypes.c_ubyte.from_buffer(array) log.debug('unmac_ctx has been nulled and ignored. its not often used by any ssh impl. Not useful for us anyway.') log.debug('MAC KEY(%d bytes) acquired'%(self.key_len)) return True
def getPrevChunk(self, mappings, orig_addr): ## do prev_chunk if self.check_prev_inuse(): raise TypeError('Previous chunk is in use. can read its size.') mmap = model.is_valid_address_value(orig_addr, mappings) if not mmap: raise ValueError if self.prev_size > 0 : prev_addr = orig_addr - self.prev_size prev_chunk = mmap.readStruct(prev_addr, malloc_chunk ) model.keepRef( prev_chunk, malloc_chunk, prev_addr) return prev_chunk, prev_addr return None, None
def refresh(args): """ Default function for the refresh command line option. Try to map a Structure from a specific offset in memory. Returns it in pickled or text format. See the command line --help . """ log.debug(args) addr=int(args.addr,16) structType=getKlass(args.structName) mappings = MemoryMapper(args).getMappings() finder = StructFinder(mappings) memoryMap = model.is_valid_address_value(addr, finder.mappings) if not memoryMap: log.error("the address is not accessible in the memoryMap") raise ValueError("the address is not accessible in the memoryMap") instance,validated = finder.loadAt( memoryMap , addr, structType) ## if args.interactive: import code code.interact(local=locals()) if validated: if args.human: print '( %s, %s )'%(instance.toString(),validated) else: d=(instance.toPyObject(),validated) if model.findCtypesInPyObj(d[0]): log.error('=========************======= CTYPES STILL IN pyOBJ !!!! ') if args.json: #jsoned print json.dumps(ret) else: #pickled print pickle.dumps(d) else: if args.human: #Unloaded datastruct, printing safe __str__ print '( %s, %s )'%(instance,validated) else: d=None if args.json: #jsoned print json.dumps(ret) else: #pickled print pickle.dumps(d) return instance,validated
def refresh(args): """ Default function for the refresh command line option. Try to map a Structure from a specific offset in memory. Returns it in pickled or text format. See the command line --help . """ log.debug(args) addr = int(args.addr, 16) structType = getKlass(args.structName) mappings = MemoryMapper(args).getMappings() finder = StructFinder(mappings) memoryMap = model.is_valid_address_value(addr, finder.mappings) if not memoryMap: log.error("the address is not accessible in the memoryMap") raise ValueError("the address is not accessible in the memoryMap") instance, validated = finder.loadAt(memoryMap, addr, structType) ## if args.interactive: import code code.interact(local=locals()) if validated: if args.human: print '( %s, %s )' % (instance.toString(), validated) else: d = (instance.toPyObject(), validated) if model.findCtypesInPyObj(d[0]): log.error( '=========************======= CTYPES STILL IN pyOBJ !!!! ') if args.json: #jsoned print json.dumps(ret) else: #pickled print pickle.dumps(d) else: if args.human: #Unloaded datastruct, printing safe __str__ print '( %s, %s )' % (instance, validated) else: d = None if args.json: #jsoned print json.dumps(ret) else: #pickled print pickle.dumps(d) return instance, validated
def list_head_getOffsets(self, mappings, attrtype, listHeadName): ''' get the prev and next structure's real start addresses. we need a real attrtype and the list_head attrname to calculate the offset @param attrtype the target structure type @param listHeadName the member name in that target structure ''' names = ['prev','next'] ret = list() for name in names: addr = getaddress(getattr(self, name)) log.debug( '0x%x %s.%s'%(addr, listHeadName, name) ) if addr < 1 or not is_valid_address_value(addr, mappings, attrtype) : addr = None else: addr -= offsetof(attrtype, listHeadName) ret.append(addr) return (ret[0],ret[1])#(addr_prev,addr_next)
def list_head_getOffsets(self, mappings, attrtype, listHeadName): ''' get the prev and next structure's real start addresses. we need a real attrtype and the list_head attrname to calculate the offset @param attrtype the target structure type @param listHeadName the member name in that target structure ''' names = ['prev', 'next'] ret = list() for name in names: addr = getaddress(getattr(self, name)) log.debug('0x%x %s.%s' % (addr, listHeadName, name)) if addr < 1 or not is_valid_address_value(addr, mappings, attrtype): addr = None else: addr -= offsetof(attrtype, listHeadName) ret.append(addr) return (ret[0], ret[1]) #(addr_prev,addr_next)
def BIGNUM_loadMembers(self, mappings, maxDepth): ''' #self._d = process.readArray(attr_obj_address, ctypes.c_ulong, self.top) ## or #ulong_array= (ctypes.c_ulong * self.top) ''' if not self.isValid(mappings): log.debug('BigNUm tries to load members when its not validated') return False # Load and memcopy d / BN_ULONG * attr_obj_address=getaddress(self.d) if not bool(self.d): log.debug('BIGNUM has a Null pointer d') return True memoryMap = is_valid_address_value( attr_obj_address, mappings) contents=(BN_ULONG*self.top).from_buffer_copy(memoryMap.readArray(attr_obj_address, BN_ULONG, self.top)) log.debug('contents acquired %d'%ctypes.sizeof(contents)) self.d.contents=BN_ULONG.from_address(ctypes.addressof(contents)) self.d=ctypes.cast(contents, ctypes.POINTER(BN_ULONG) ) return True
def read_memory(self, addr, size): mmap = model.is_valid_address_value(addr, self.mappings) if not mmap: raise RuntimeError return mmap.readBytes(addr, size)
def read_memory(self, addr, size): mmap = model.is_valid_address_value(addr, self.mappings) if not mmap : raise RuntimeError return mmap.readBytes(addr, size)
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