def sourcechain(fn, *args, **kwds): # sentinel = kwds.get('types', set(('imm','phrase','addr','void'))) sentinel = kwds.get('types', set(('imm', 'addr', 'void'))) result = {} for ea, opi in source(*args): if not function.contains(fn, ea): continue opt = tuple(ins.op_type(ea, i) for i in opi) for i, t in zip(opi, opt): if t in sentinel: result.setdefault(ea, set()).add(i) elif t in ('reg', ): result.setdefault(ea, set()).add(i) r = ins.op_value(ea, i) for a, b in sourcechain(fn, ea, r): map(result.setdefault(a, set()).add, b) elif t in ('phrase', ): result.setdefault(ea, set()).add(i) _, (r1, r2, _) = ins.op_value(ea, i) for a, b in sourcechain( fn, ea, *tuple(r for r in (r1, r2) if r is not None)): map(result.setdefault(a, set()).add, b) elif t in ( 'imm', 'addr', ): result.setdefault(ea, set()).add(i) else: raise ValueError, (t, ea, i) continue continue return [(ea, result[ea]) for ea in sorted(result.keys())]
def search_func(addr, funcname, curr_addr_list=None): """ Given an address and function name, recursively look from the given address for calls to the given function name. Modifies the 'calls' function tag with the function name and True or False depending if the function contains the wanted function or not. """ if curr_addr_list == None: curr_addr_list = [addr] print("Addr: {}".format(addr)) curr_calls = fn.tag(addr).get('calls', {}) for ea in fn.iterate(addr): if funcname in db.disasm(ea): curr_calls[funcname] = True fn.tag(addr, 'calls', curr_calls) return True if not ins.isCall(ea): continue call = ins.op_value(ea, 0) # Don't cycle ourselves if call == addr: print("Ignoring recursive loop on function: {}".format(hex(addr))) continue # Try to know if the call operand is a valid address # Bail if not.. try: print(hex(call)) except: continue # Check if this function has been analyzed already # and return the result # Cannot return False, because that will exit the loop and not # continue to iterate over the rest of the instrutions in the function call_cache = fn.tag(call).get('calls', {}) print("Call cache: {}".format(call_cache)) if funcname in call_cache: if call_cache[funcname]: curr_calls[funcname] = call_cache[funcname] fn.tag(addr, 'calls', curr_calls) return True # Don't call functions we are currently looking into if call in curr_addr_list: continue # Only now ca curr_addr_list.append(call) search_func(call, funcname=funcname, curr_addr_list=curr_addr_list) curr_calls[funcname] = False fn.tag(addr, 'calls', curr_calls) return False
def check(ea): if 'mov' not in db.disasm(ea) or 'offset' not in db.disasm(ea): return if ins.ops_type(ea) != ['phrase', 'immediate']: return if ins.op_value(ea, 0).offset != 0: return print(hex(ea), db.disasm(ea), ins.ops_type())
def search_dyn_calls(addr, curr_addr_list=None, parents=None): dyn_call = namedtuple('dyn_call', ['call', 'parents']) hex = '{:x}'.format # print(hex(addr), curr_addr_list) if curr_addr_list == None: curr_addr_list = [addr] if parents == None: parents = [] calls = [] #print("Addr: {}".format(addr)) for ea in fn.iterate(addr): if not ins.isCall(ea): continue call = ins.op_value(ea, 0) # Don't cycle ourselves if call == addr: # print("Ignoring recursive loop on function: {}".format(hex(addr))) continue # Don't call functions we are currently looking into if call in curr_addr_list: continue """ .text:3F6E66AD 0B4 call ds:memmove Python>x = ins.op_value(h(), 0) Python>print(x.base) None .text:3F6E6208 008 call dword ptr [eax] Python>x.base <instruction.register.eax(0,dt_dword) 'eax' 0:+32> """ # we want non int/long for calls if isinstance(call, (int, long)) and not ins.op_type(ea, 0) in ('phrase'): # Only now call the function once curr_addr_list.append(call) new_parents = copy.deepcopy(parents) new_parents.append(ea) calls.extend( search_dyn_call(call, curr_addr_list=curr_addr_list, parents=new_parents)) elif isinstance( call, ins.intelop.OffsetBaseIndexScale) and call.base == None: # Ignore 'call ds:memmove' or 'call ds:atoi' # print("OffsetBase", call.offset, hex(ea)[2:-1], db.disasm(ea), parents) pass else: calls.append(dyn_call(ea, parents)) return calls
def uses_register(ea, regs): res = [(_instruction.op_type(ea, x), _instruction.op_value(ea, x), _instruction.op_state(ea, x)) for x in xrange(_instruction.ops_count(ea)) if _instruction.op_type(ea, x) in ('opt_reg', 'opt_phrase')] match = lambda r, regs: itertools.imap( _instruction.reg_t.byName(r).related, itertools.imap(_instruction.reg_t.byName, regs)) for t, p, st in res: if t == 'opt_reg' and any(match( p, regs)) and ('w' in st if write else True): return True if t == 'opt_phrase' and not write: _, (base, index, _) = p if (base and any(match(base, regs))) or (index and any( match(index, regs))): return True continue return False
def _search_func(addr, func, curr_addr_list=None): if curr_addr_list == None: curr_addr_list = [addr] for ea in fn.iterate(addr): func(ea) if not ins.isCall(ea): continue call = ins.op_value(ea, 0) # Don't cycle ourselves if call == addr: # print("Ignoring recursive loop on function: {}".format(hex(addr))) continue # Try to know if the call operand is a valid address # Bail if not.. if not isinstance(call, (int, long)): continue """ try: x = hex(call) except: continue """ # Don't call functions we are currently looking into if call in curr_addr_list: continue # Only now ca curr_addr_list.append(call) _search_func(call, func, curr_addr_list=curr_addr_list)
def makecall(ea=None, target=None): """Output the function call at `ea` and its arguments with the address they originated from. If `target` is specified, then assume that the instruction is calling `target` instead of the target address that the call is referencing. """ ea = current.address() if ea is None else ea if not func.contains(ea, ea): return None if database.config.bits() != 32: raise RuntimeError( "{:s}.makecall({!r}, {!r}) : Unable to determine arguments for {:s} due to {:d}-bit calling convention." .format(__name__, ea, target, database.disasm(ea), database.config.bits())) if target is None: # scan down until we find a call that references something chunk, = ((l, r) for l, r in func.chunks(ea) if l <= ea <= r) result = [] while (len(result) < 1) and ea < chunk[1]: # FIXME: it's probably not good to just scan for a call if not database.instruction(ea).startswith('call '): ea = database.next(ea) continue result = database.cxdown(ea) if len(result) == 0: raise TypeError( "{:s}.makecall({!r}, {!r}) : Unable to determine number of arguments." .format(__name__, ea, target)) if len(result) != 1: raise ValueError( "{:s}.makecall({!r}, {!r}) : An invalid number of targets was returned for the call at {:#x}. The call targets that were returned are {!r}." .format(__name__, ea, result)) fn, = result else: fn = target try: result = [] for offset, name, size in func.arguments(fn): left = database.address.prevstack( ea, offset + database.config.bits() / 8) # FIXME: if left is not an assignment or a push, find last assignment result.append((name, left)) except internal.exceptions.OutOfBoundsError: raise internal.exceptions.OutOfBoundserror( "{:s}.makecall({!r}, {!r}) : Unable to get arguments for target function." .format(__name__, ea, target)) # FIXME: replace these crazy list comprehensions with something more comprehensible. # result = ["{:s}={:s}".format(name, instruction.op_repr(ea, 0)) for name, ea in result] result = [ "({:#x}){:s}={:s}".format( ea, name, ':'.join( instruction.op_repr( database.address.prevreg( ea, instruction.op_value(ea, 0), write=1), n) for n in instruction.ops_read( database.address.prevreg( ea, instruction.op_value(ea, 0), write=1))) if instruction.op_type(ea, 0) == 'reg' else instruction.op_repr( ea, 0)) for name, ea in result ] try: return "{:s}({:s})".format( internal.declaration.demangle(func.name(func.by_address(fn))), ','.join(result)) except: pass return "{:s}({:s})".format( internal.declaration.demangle(database.name(fn)), ','.join(result))
def makecall(ea=None, target=None): ea = current.address() if ea is None else ea if not function.contains(ea, ea): return None if database.config.bits() != 32: raise RuntimeError( "{:s}.makecall({!r},{!r}) : Unable to determine arguments for {:s} due to {:d}-bit calling convention." .format(__name__, ea, target, database.disasm(ea), database.config.bits())) if target is None: # scan down until we find a call that references something chunk, = ((l, r) for l, r in function.chunks(ea) if l <= ea <= r) result = [] while (len(result) < 1) and ea < chunk[1]: # FIXME: it's probably not good to just scan for a call if not database.instruction(ea).startswith('call '): ea = database.next(ea) continue result = database.cxdown(ea) if len(result) == 0: raise TypeError( "{:s}.makecall({!r},{!r}) : Unable to determine number of arguments" .format(__name__, ea, target)) if len(result) != 1: raise ValueError( "{:s}.makecall({!r},{!r}) : Too many targets for call at {:x} : {!r}" .format(__name__, ea, result)) fn, = result else: fn = target try: result = [] for offset, name, size in function.arguments(fn): left, _ = function.stack_window( ea, offset + database.config.bits() / 8) # FIXME: if left is not an assignment or a push, find last assignment result.append((name, left)) except LookupError: raise LookupError( "{:s}.makecall({!r},{!r}) : Unable to get arguments for target function" .format(__name__, ea, target)) # FIXME: replace these crazy list comprehensions with something more comprehensible. # result = ["{:s}={:s}".format(name,ins.op_repr(ea, 0)) for name,ea in result] result = [ "({:x}){:s}={:s}".format( ea, name, ':'.join( ins.op_repr( database.address.prevreg(ea, ins.op_value(ea, 0), write=1), n) for n in ins.ops_read( database.address.prevreg(ea, ins.op_value(ea, 0), write=1)) ) if ins.op_type(ea, 0) == 'reg' else ins.op_repr(ea, 0)) for name, ea in result ] try: return "{:s}({:s})".format( internal.declaration.demangle( function.name(function.by_address(fn))), ','.join(result)) except: pass return "{:s}({:s})".format( internal.declaration.demangle(database.name(fn)), ','.join(result))
def search_malloc(addr, funcname='malloc', curr_addr_list=None): # print(hex(addr), curr_addr_list) if curr_addr_list == None: curr_addr_list = [addr] print("Addr: {}".format(addr)) curr_calls = fn.tag(addr).get('calls', {}) for ea in fn.iterate(addr): if not ins.isCall(ea): continue if ins.op_type(0) == 'reg': """ Handle this case - malloc(100) mov ebp, ds:malloc push 100 call ebp """ if funcname in db.disasm(ea): # Push before malloc "should" be within 20 instructions """ Handle this case - malloc(100) push 100 mov eax, 10 mov ebx, 20 call malloc """ search_addr = db.prev(ea) for _ in range(20): if ins.mnem(search_addr) == 'push': break search_addr = db.prev(search_addr) print("FOUND PUSH FOR MALLOC: {}".format(hex(search_addr))) malloc_value = ins.op_value(search_addr, 0) if isinstance(malloc_value, (int, long)): curr_calls[funcname] = malloc_value else: curr_calls[funcname] = 'variable' fn.tag(addr, 'calls', curr_calls) return True call = ins.op_value(ea, 0) # Don't cycle ourselves if call == addr: print("Ignoring recursive loop on function: {}".format(hex(addr))) continue # Try to know if the call operand is a valid address # Bail if not.. try: print(hex(call)) except: continue # Check if this function has been analyzed already # and return the result # Cannot return False, because that will exit the loop and not # continue to iterate over the rest of the instrutions in the function call_cache = fn.tag(call).get('calls', {}) print("Call cache: {}".format(call_cache)) if funcname in call_cache: if call_cache[funcname]: curr_calls[funcname] = call_cache[funcname] fn.tag(addr, 'calls', curr_calls) return True # Don't call functions we are currently looking into if call in curr_addr_list: continue # Only now ca curr_addr_list.append(call) search_malloc(call, funcname=funcname, curr_addr_list=curr_addr_list) curr_calls[funcname] = False fn.tag(addr, 'calls', curr_calls) return False
def makecall(ea=None, target=None): """Output the function call at `ea` and its arguments with the address they originated from. If `target` is specified, then assume that the instruction is calling `target` instead of the target address that the call is referencing. """ ea = current.address() if ea is None else ea if not func.contains(ea, ea): return None if database.config.bits() != 32: raise RuntimeError("{:s}.makecall({!r}, {!r}) : Unable to determine arguments for {:s} due to {:d}-bit calling convention.".format(__name__, ea, target, database.disasm(ea), database.config.bits())) if target is None: # scan down until we find a call that references something chunk, = ((l, r) for l, r in func.chunks(ea) if l <= ea <= r) result = [] while (len(result) < 1) and ea < chunk[1]: # FIXME: it's probably not good to just scan for a call if not database.instruction(ea).startswith('call '): ea = database.next(ea) continue result = database.cxdown(ea) if len(result) == 0: raise TypeError("{:s}.makecall({!r}, {!r}) : Unable to determine number of arguments.".format(__name__, ea, target)) if len(result) != 1: raise ValueError("{:s}.makecall({!r}, {!r}) : An invalid number of targets was returned for the call at {:#x}. The call targets that were returned are {!r}.".format(__name__, ea, result)) fn, = result else: fn = target try: result = [] for offset, name, size in func.arguments(fn): left = database.address.prevstack(ea, offset+database.config.bits()/8) # FIXME: if left is not an assignment or a push, find last assignment result.append((name, left)) except internal.exceptions.OutOfBoundsError: raise internal.exceptions.OutOfBoundserror("{:s}.makecall({!r}, {!r}) : Unable to get arguments for target function.".format(__name__, ea, target)) # FIXME: replace these crazy list comprehensions with something more comprehensible. # result = ["{:s}={:s}".format(name, instruction.op_repr(ea, 0)) for name, ea in result] result = ["({:#x}){:s}={:s}".format(ea, name, ':'.join(instruction.op_repr(database.address.prevreg(ea, instruction.op_value(ea, 0), write=1), n) for n in instruction.ops_read(database.address.prevreg(ea, instruction.op_value(ea, 0), write=1))) if instruction.op_type(ea, 0) == 'reg' else instruction.op_repr(ea, 0)) for name, ea in result] try: return "{:s}({:s})".format(internal.declaration.demangle(func.name(func.by_address(fn))), ','.join(result)) except: pass return "{:s}({:s})".format(internal.declaration.demangle(database.name(fn)), ','.join(result))