def nameDispatch(address): '''Name the dispatch function at the specified address in quicktime.qts''' try: start, end = function.getRange(address) except ValueError: print '%x making a function' % address function.make(address) start, end = function.getRange(address) try: ea = FindLastAssignment(address, 'eax') code = getDispatchCode(ea) except ValueError: print '%08x - Unable to find dispatch code' % address return function.setName(start, 'dispatch_%08x' % code) function.tag(start, 'code', code) function.tag(start, 'group', 'dispatch') try: function.tag(start, 'realname', __quicktime.qt_fv_list[code]) except KeyError: pass try: function.tag(start, 'address', resolveDispatcher(code), repeatable=True) except: pass
def nameDispatch(address): '''Name the dispatch function at the specified address in quicktime.qts''' try: start, end = function.getRange(address) except ValueError: print '%x making a function'% address function.make(address) start, end = function.getRange(address) try: ea = FindLastAssignment(address, 'eax') code = getDispatchCode(ea) except ValueError: print '%08x - Unable to find dispatch code'% address return function.setName(start, 'dispatch_%08x'% code) function.tag(start, 'code', code) function.tag(start, 'group', 'dispatch') try: function.tag(start, 'realname', __quicktime.qt_fv_list[code]) except KeyError: pass try: function.tag(start, 'address', resolveDispatcher(code), repeatable=True) except: pass
def tag_popularity(self, context=None): import function import idautils import idc import symath.graph.algorithms as algorithms funcs = set(idautils.Functions()) rv = {} for fa in funcs: f = idc.GetTrueName(fa) p = algorithms.popularity(self, node=f, context=context) function.tag(fa, 'popularity', p) rv[fa] = p return rv
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 __select(q): for x in functions(): x = function.top(x) if q.has(function.tag(x)): yield x continue return
def select(*tags, **boolean): '''Fetch all the functions containing the specified tags within it's declaration''' boolean = dict((k, set(v) if type(v) is tuple else set((v, ))) for k, v in boolean.viewitems()) if tags: boolean.setdefault( 'And', set(boolean.get( 'And', set())).union(set(tags) if len(tags) > 1 else set(tags, ))) if not boolean: for ea in functions(): res = tag(ea) if res: yield ea, res return for ea in functions(): res, d = {}, function.tag(ea) Or = boolean.get('Or', set()) res.update((k, v) for k, v in d.iteritems() if k in Or) And = boolean.get('And', set()) if And: if And.intersection(d.viewkeys()) == And: res.update((k, v) for k, v in d.iteritems() if k in And) else: continue if res: yield ea, res return
def select(*tags, **boolean): '''Fetch all the functions containing the specified tags within it's declaration''' boolean = dict((k,set(v) if type(v) is tuple else set((v,))) for k,v in boolean.viewitems()) if tags: boolean.setdefault('And', set(boolean.get('And',set())).union(set(tags) if len(tags) > 1 else set(tags,))) if not boolean: for ea in functions(): res = tag(ea) if res: yield ea, res return for ea in functions(): res,d = {},function.tag(ea) Or = boolean.get('Or', set()) res.update((k,v) for k,v in d.iteritems() if k in Or) And = boolean.get('And', set()) if And: if And.intersection(d.viewkeys()) == And: res.update((k,v) for k,v in d.iteritems() if k in And) else: continue if res: yield ea,res return
def del_func(pfn): global State if State != state.ready: return # convert all contents into globals for l, r in function.chunks(pfn): for ea in database.address.iterate(l, r): for k in database.tag(ea): internal.comment.contents.dec( ea, k, target=interface.range.start(pfn)) internal.comment.globals.inc(ea, k) logging.debug( u"{:s}.del_func({:#x}) : Exchanging (increasing) refcount for global tag {!s} and (decreasing) refcount for contents tag {!s}." .format(__name__, interface.range.start(pfn), utils.string.repr(k), utils.string.repr(k))) continue continue # remove all function tags for k in function.tag(interface.range.start(pfn)): internal.comment.globals.dec(interface.range.start(pfn), k) logging.debug( u"{:s}.del_func({:#x}) : Removing (global) tag {!s} from function." .format(__name__, interface.range.start(pfn), utils.string.repr(k))) return
def colormarks(color=0x7f007f): '''Iterate through all database marks and tag+color their address''' # tag and color f = set() for ea, m in database.marks(): database.tag(ea, 'mark', m) database.color(ea, color) try: f.add(function.top(ea)) except (LookupError, ValueError): pass # tag the functions too for ea in list(f): m = function.marks(ea) function.tag(ea, 'marks', [ea for ea, _ in m]) return
def colormarks(color=0x7f007f): '''Iterate through all database marks and tag+color their address''' # tag and color f = set() for ea,m in database.marks(): database.tag(ea, 'mark', m) database.color(ea, color) try: f.add(function.top(ea)) except ValueError: pass continue # tag the functions too for ea in list(f): m = function.marks(ea) function.tag(ea, 'marks', [ea for ea,_ in m]) return
def tag_signed(): import function import idc import idautils fns = set(idautils.Functions()) rv = {} for f in fns: fg = FunctionGraph(f) for n in fg.nodes: m = idc.GetMnem(n).lower() if m in ('jl', 'jle', 'jg', 'jge', 'js'): function.tag(f, 'signed arithmetic', True) rv[f] = True break return rv
def prioritize(ruleset=DEFAULT_RULESET, context=None): ruleset = list(ruleset) # make all weights explicit for i in range(len(ruleset)): if type(ruleset[i]) != type((1,1)): print 'using default weight of 1.0 for %s' % (ruleset[i],) ruleset[i] = (ruleset[i], 1.0) numerator = {} denominator = {} zeros = {} fns = set(idautils.Functions()) for f in fns: numerator[f] = 0.0 denominator[f] = 0.0 zeros[f] = False for (rule,weight) in ruleset: d = rule(context) for f in fns: if f not in d: continue if d[f] == 0: zeros[f] = True else: try: numerator[f] += symath.desymbolic(weight) * math.log(symath.desymbolic(d[f])) denominator[f] += symath.desymbolic(weight) except: print "could not float/parse either %s or %s" % (weight, d[f]) raise for f in fns: if denominator[f] != 0.0 and not zeros[f]: numerator[f] = math.exp(numerator[f] / denominator[f]) else: numerator[f] = 0.0 function.tag(f, 'priority', numerator[f]) return numerator
def tag_cyclomatic_complexity(): import function import idc import idautils import symath.graph.signatures as sigs fns = set(idautils.Functions()) rv = {} for f in fns: fg = FunctionGraph(f) #c = fg.cyclomatic_complexity() c = sigs.complexity(fg)[1] if c < 0: c = 0 function.tag(fg.start_addr, 'cyclomatic complexity', c) rv[f] = c return rv
def do_functions(): addr, tags = {}, {} t = len(list(db.functions())) for i, ea in enumerate(db.functions()): print "{:x} : fetching function {:d} of {:d}".format(ea, i, t) res = fn.tag(ea) #res.pop('name', None) for k, v in res.iteritems(): addr[ea] = addr.get(ea, 0) + 1 tags[k] = tags.get(k, 0) + 1 continue return addr, tags
def tag_recursive(self): import function import idautils import idc funcs = set(idautils.Functions()) rv = {} for fa in funcs: f = idc.GetTrueName(fa) tags = [] for stack in self.stackwalk(f): if self.connectedQ(stack[-1], f): tags.append(stack) if len(tags) > 0: function.tag(idc.LocByName(f), 'recursive', tags) rv[fa] = len(tags) return rv
def tag_distance(self, function_name, direction='incoming'): import function import idc import idautils f = idc.LocByName(function_name) rv = {} fns = set(idautils.Functions()) for (n,l) in self.walk(function_name, direction=direction): if l == 0: continue loc = idc.LocByName(n) if loc in fns: function.tag(loc, '%s distance %s' % (direction, function_name), l) rv[loc] = l for fn in fns: if fn not in rv: rv[fn] = 0.0 return rv
def globals(): '''Yields all the global tags.''' ea, sentinel = db.range() while ea < sentinel: f = idaapi.get_func(ea) if f: t = fn.tag(ea) if t: yield ea, t ea = f.endEA continue t = db.tag(ea) if t: yield ea, t ea = db.a.next(ea) return
def tag_aggregate_complexity(): import function import idc import idautils import callgraph import symath.graph.signatures as sigs cg = callgraph.CallGraph(includeImports=False) graphs = {} _reversed = {} rv = {} fns = set(idautils.Functions()) cc = {} for f in fns: graphs[f] = FunctionGraph(f) _reversed[f] = FunctionGraph._tag_val(f, 'reversed') != None if _reversed[f]: cg.strip_edges_to(idc.GetTrueName(f)) for i in fns: ac = symbolic(0) for j,l in cg.walk(idc.GetTrueName(i), direction='outgoing'): loc = idc.LocByName(j) if loc not in graphs: continue if loc not in cc: cc[loc] = sigs.complexity(graphs[loc])[1] ac += cc[loc] if cc[loc] > 0 else 0 ac = ac.simplify() function.tag(i, 'aggregate complexity', ac) rv[i] = ac return rv
def colormarks(color=0x7f007f): """Walk through the current list of marks whilst coloring them with the specified `color`. Each mark's address is tagged with its description, and if the address belongs to a function, the function is also tagged with the address of the marks that it contains. """ # tag and color f = set() for ea, m in database.marks(): database.tag(ea, 'mark', m) if database.color(ea) is None: database.color(ea, color) try: f.add(func.top(ea)) except internal.exceptions.FunctionNotFoundError: pass continue # tag the functions too for ea in list(f): m = func.marks(ea) func.tag(ea, 'marks', [ea for ea, _ in m]) return
def tag_xors(): import function import idc import idautils fns = set(idautils.Functions()) rv = {} for f in fns: #print 'analyzing %x for xor loops' % (f) fg = FunctionGraph(f) rv[f] = False for n in fg.nodes: m = idc.GetMnem(n) if m != 'xor': continue op0 = idc.GetOpnd(n,0) op1 = idc.GetOpnd(n,1) if op0 != op1 and n in algorithms.find_cylic_nodes(fg,f): function.tag(f, 'xor in loop', True) rv[f] = True break return rv
def del_func(pfn): global State if State != state.ready: return # convert all contents into globals for (l,r) in function.chunks(pfn): for ea in database.iterate(l, r): for k in database.tag(ea): internal.comment.contents.dec(ea, k, target=pfn.startEA) internal.comment.globals.inc(ea, k) continue continue # remove all function tags for k in function.tag(pfn.startEA): internal.comment.globals.dec(pfn.startEA, k) return
def analyze_vtables(): vtables = set(locate_vtables()) fns = set(idautils.Functions()) for vt in vtables: # create a new class sname = "class_%x" % (vt) sid = idc.AddStruc(-1, sname) if sid == idc.BADADDR: sid = idc.GetStrucIdByName(sname) # create a struc for the vtable vname = "vtable_%x" % (vt) vid = idc.AddStruc(-1, vname) vfuncs = set() if vid == idc.BADADDR: vid = idc.GetStrucIdByName(vname) for i in range(0xffffff): target = idc.Dword(vt + (4 * i)) if target not in fns: break if i > 0: # if there is a data ref, then it's probably the start of a new vtable xrefs = set(idautils.DataRefsTo(vt + (4 * i))) if len(xrefs) > 0: continue idc.AddStrucMember(vid, 'vfunc_%x' % (i), -1, idc.FF_DWRD, -1, 4) # setup the target function function.tag(target, 'virtual table', hex(vt)) vfuncs.add(target) # add a vtable to the new class idc.AddStrucMember(sid, 'vtable', 0, idc.FF_DWRD, -1, 4) # tag functions that reference the vtable as either a constructor or destructor # in order to do decide which, take advantage of a C++ thing, destructors will be # virtual, and constructors will not xrefs = idautils.DataRefsTo(vt) for r in xrefs: try: if xrefs in vfuncs: function.tag(function.top(r), 'destructor', 'guessed from vtable (%x) analysis' % (vt)) else: function.tag(function.top(r), 'constructor', 'guessed from vtable (%x) analysis' % (vt)) except: pass
def del_func(pfn): global State if State != state.ready: return # convert all contents into globals for l, r in function.chunks(pfn): for ea in database.address.iterate(l, r): for k in database.tag(ea): internal.comment.contents.dec(ea, k, target=pfn.startEA) internal.comment.globals.inc(ea, k) logging.debug("{:s}.del_func({:#x}) : Decreasing refcount for contents tag {!r} and increasing refcount for globals tag {!r}".format(__name__, pfn.startEA, k, k)) continue continue # remove all function tags for k in function.tag(pfn.startEA): internal.comment.globals.dec(pfn.startEA, k) logging.debug("{:s}.del_func({:#x}) : Removing function (global) tag {!r}".format(__name__, pfn.startEA, k)) return
def fetch_globals_functions(): """Fetch the reference count for the global tags (function) in the database. Returns the tuple `(address, tags)` where the `address` and `tags` fields are both dictionaries containing the reference count for the addresses and tag names. """ addr, tags = {}, {} t = len(list(db.functions())) for i, ea in enumerate(db.functions()): ui.navigation.auto(ea) six.print_(u"globals: fetching tag from function {:#x} : {:d} of {:d}".format(ea, i, t), file=output) res = func.tag(ea) #res.pop('name', None) for k, v in six.iteritems(res): addr[ea] = addr.get(ea, 0) + 1 tags[k] = tags.get(k, 0) + 1 continue return addr, tags
def fetch_globals_functions(): """Fetch the reference count for the global tags (function) in the database. Returns the tuple `(address, tags)` where the `address` and `tags` fields are both dictionaries containing the reference count for the addresses and tag names. """ addr, tags = {}, {} t = len(list(db.functions())) for i, ea in enumerate(db.functions()): ui.navigation.auto(ea) print >> output, "globals: fetching tag from function {:#x} : {:d} of {:d}".format( ea, i, t) res = func.tag(ea) #res.pop('name', None) for k, v in six.iteritems(res): addr[ea] = addr.get(ea, 0) + 1 tags[k] = tags.get(k, 0) + 1 continue return addr, tags
async def rand_tag(ctx, *, arg): embed = tag(arg) msg = await ctx.send(embed=embed) await msg.add_reaction('👍') await msg.add_reaction('👎') await msg.add_reaction('❌')
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 apply_dyn_calls(dyn_calls, delete=False): hex = '{:x}'.format for dyn_call in dyn_calls: print(dyn_call) for i, p in enumerate(dyn_call.parents): print(i, hex(p)) top = func.top(p) if 'dynamic_call' not in func.tag(top): fn.tag(top, 'dynamic_call', set()) if delete: fn.tag(top, 'dynamic_call', None) continue curr_tag = fn.tag(top, 'dynamic_call') print(type(curr_tag), hex(top)) try: curr_tag.add(dyn_call.parents[i + 1]) except IndexError: curr_tag.add(dyn_call.call) fn.tag(top, 'dynamic_call', curr_tag) # Be sure to tag the actual function containing the dynamic call top = fn.top(dyn_call.call) if delete: if 'dynamic_call' in fn.tag(top): fn.tag(top, 'dynamic_call', None) if 'dynamic_call' in fn.tag(dyn_call.call): fn.tag(dyn_call.call, 'dynamic_call', None) continue if 'dynamic_call' not in fn.tag(top): fn.tag(top, 'dynamic_call', set()) curr_tag = fn.tag(top, 'dynamic_call') curr_tag.add(dyn_call.call) fn.tag(top, 'dynamic_call', curr_tag) db.tag(dyn_call.call, 'dynamic_call', 'here')
def _tag_val(addr, tagname, default=None): import function try: return function.tag(addr, tagname) except: return default
def has(ea): d = function.tag(ea) for k, v in where.iteritems(): if k not in d or (v is not None and v != d[k]): return False return True