def getNamedFunc(name): func = sark.Function(name=name) if func: # if func just jump to another, return jump target. for line in func.lines: for xref in line.xrefs_from: func = sark.Function(xref.to) break break return func
def build_exports_cfg(bb_list): base = idaapi.get_imagebase() _first_ = bb_list[0].start + base fn = sark.Function(_first_) opti_bb_list = [(_first_, demangle_name(fn.name))] for bb in bb_list: va = bb.start + base try: fn = sark.Function(va) fn_name = demangle_name(fn.name) if fn_name != opti_bb_list[-1][1]: opti_bb_list.append((va, demangle_name(fn.name))) except: opti_bb_list.append((va, "_unknown_")) exported_apis = get_dll_export_entries().values() # start cfg building root = cfg_fn_node(0) cur_node = root i = 0 for entry in opti_bb_list: va, name = entry if cur_node.have_call_to(va): cur_node = cfg_fn_node(va, name, cur_node) else: # two_options? call to this 'va' in parent or it is a indirect call we don't have caller info, check_node = cur_node while check_node != root: check_node = check_node.parent if check_node.have_call_to(va): cur_node = cfg_fn_node(va, name, check_node) break elif check_node.name == name: cur_node = check_node break if check_node == root: if name in exported_apis: cur_node = cfg_fn_node(va, name, root) else: print "[*] indirect call unlinked %d %08x %s %s" % ( i, va, name, cur_node.name) i += 1 return root
def get_control_flow_graph(imagebase, bb_collection_gen, verbose=False): exported_apis_addresses = get_dll_export_entries().keys() root = fn_node() cur_fn = root last_bb, last_size = 0, 0 for bb in bb_collection_gen: va = bb.start + imagebase fn = sark.Function(va) if fn.startEA == va: note = "" last_asm_code = last_bb > 0 and sark.Line( last_bb + last_size - 1).disasm or "call _external_" if "ret" in last_asm_code: if cur_fn.is_bb_part_of_fn(va): cur_fn.add_bb(va) continue while cur_fn.parent and not cur_fn.has_call_to(va): cur_fn = cur_fn.parent if cur_fn == root: if not (va in exported_apis_addresses): note = "[_unlinked_] " cur_fn = fn_node(fn, cur_fn) cur_fn.note = note else: if cur_fn.is_bb_part_of_fn(va): cur_fn.add_bb(va) else: while cur_fn.parent and not cur_fn.is_bb_part_of_fn(va): cur_fn = cur_fn.parent cur_fn.add_bb(va) last_bb, last_size = va, bb.size return root
def record(self, line): """Record the given code / data line. Args: line (line): (sark) code line """ is_illegal = not self._analyzer.isLegalInsn(line) self._unknown_count += line.size if line.is_unknown else 0 self._illegal_count += line.size if is_illegal else 0 # don't count functions that contain illegal instructions if (not is_illegal) and (not line.is_unknown): try: self._current_function = sark.Function(line.startEA) if line.startEA == self._current_function.startEA: self._seen_function_start = line.startEA # Time to check if this is a contained function. # Note: could be one liner functions, so use "if" and not "elif" if line.endEA == self._current_function.endEA and self._current_function.startEA == self._seen_function_start: self._contains_functions = True except Exception: self._current_function = None else: self._current_function = None # now check for an alignment if self._measure_align: if self._analyzer.isAlignment(line): self._align_start = True elif self._align_start: self._align_metric = CodeMetric(self._analyzer, line.startEA) self._align_metric.start(line) self._align_start = False elif self._align_metric is not None: self._align_metric.record(line)
def show_current_function_meaningful(): try: function = sark.Function(idc.here()) show_meaningful_in_function(function) except sark.exceptions.SarkNoFunction: idaapi.msg("[FunctionStrings] No function at 0x{:08X}.\n".format(idc.here()))
def fix_noret_func(func=None): """Fix function that was created as noret func""" if not func: func = sark.Function() if func.is_noret: new_flags = func.flags ^ idaapi.FUNC_NORET idc.set_func_flags(func.ea, new_flags)
def run(): try: current_function = sark.Function() except sark.exceptions.SarkNoFunction: log("Cannot xref registers outside of functions.") return #register_name = idaapi.get_highlighted_identifier() # TypeError: in method 'get_highlight', argument 1 of type 'TWidget *' register_name = idaapi.get_highlighted_identifier(idaapi.get_current_tform()) # ida7.0 must give TWidget* try: register_id = get_register_identifier(register_name) except sark.exceptions.SarkInvalidRegisterName: log("Highlight a register to xref") return choose = RegisterReferencesView(current_function.name, register_name) for line in current_function.lines: if has_register_reference(line.insn, register_id): choose.add_xref(line.ea) choose.show()
def is_bb_part_of_fn(self, bb_va): if self.fn: if bb_va >= self.fn.startEA and bb_va <= self.fn.endEA: return True va_fn = sark.Function(bb_va) if va_fn.startEA == self.fn.startEA: return True
def _get_best_name(ea): try: return sark.Function(ea).demangled except exceptions.SarkNoFunction: name = idc.GetTrueName(ea) if name: return name return '0x{:X}'.format(ea)
def is_import_or_lib_func(ea): """ Is ea part of an imported function or a known library? @param ea: any ea within the function scope @return: True if function is either imported or a known library function. """ return sark.Function(ea).flags & (idaapi.FUNC_LIB | idaapi.FUNC_THUNK)
def hint_function(ea): try: function = sark.Function(ea) except sark.exceptions.SarkNoFunction: return base_name = get_base_name(function) function.lines.next().comments.posterior = 'Base name: {}'.format( base_name)
def run(self): # All the colors that signify unreachable blocks unreachable_cols = [ col_t2ida(self.plugin.col_unreachable), col_t2ida(self.plugin.col_invariant), col_t2ida(self.plugin.col_contextual) ] # helper lambdas cb_in_list = lambda cb, l: any(cb.startEA == x.startEA for x in l) not_colored_unreach = lambda cb: not cb.color or cb.color not in unreachable_cols # Get the current function in IDA f = sark.Function() # Find all yellow, cyan and purple blocks (i.e. already determined unreachable) unreachable_blocks = get_all_blocks_of_color(f, unreachable_cols) # Start with all white successors of the initial blocks possibly_unreachable = [] for cb in unreachable_blocks: for succ in filter( not_colored_unreach, filter(lambda succ: succ.startEA > cb.startEA, cb.succs())): possibly_unreachable.append(succ) while len(possibly_unreachable) > 0: # Pop one from the queue of possibly unreachable blocks cb = possibly_unreachable.pop() if not not_colored_unreach(cb): continue # If all our predecessors are unreachable, we're unreachable preds_no_backedges = filter(lambda pred: pred.startEA < cb.startEA, cb.preds()) if all( cb_in_list(pred, unreachable_blocks) for pred in preds_no_backedges): self.log_signal.emit( "all preds unreachable, changing color of {:x}\n".format( cb.startEA)) #cb.color = 0xFF00FF unreachable_blocks.append(cb) # Continue looking at our successors for succ in filter( not_colored_unreach, filter(lambda succ: succ.startEA > cb.startEA, cb.succs())): possibly_unreachable.append(succ) # Return results to GUI thread unreachable_addrs = map(lambda b: b.startEA, unreachable_blocks) self.log_signal.emit( "Unreachable blocks: {}".format(unreachable_addrs)) self.result_signal.emit(unreachable_addrs)
def main(): main_function = sark.Function(name='main') print(main_function.comments) dump_attrs(main_function.comments) for line in main_function.lines: print(line.comments) dump_attrs(line.comments)
def analyzeFunctionBlock(self, block_ea): """Return pairs indicating function calls (or fptr refs) from the lines in the basic block instance. Args: block_ea (int): basic block ea Return Value: (ordered) list of tuples: [<address of function ref (src), referenced address of the function (dest)>, ] """ function_calls = [] try: func_start = sark.Function(block_ea).start_ea block_lines = sark.CodeBlock(block_ea).lines except Exception: return function_calls # scan each of the lines for line in block_lines: instr_pos = line.ea call_candidates = set() # Data Refs (strings, fptrs) for ref in line.drefs_from: # Check for a string (finds un-analyzed strings too) str_const = self.disas.stringAt(ref) if str_const is not None and len(str_const) >= MIN_STR_SIZE: continue # Check for an fptr try: call_candidates.add(sark.Function(ref).start_ea) except sark.exceptions.SarkNoFunction: continue # Check for a function call for cref in line.crefs_from: try: if (cref == func_start and line.insn.is_call ) or sark.Function(cref).start_ea != func_start: call_candidates.add(sark.Function(cref).start_ea) except sark.exceptions.SarkNoFunction: continue # handle each ref for ref in call_candidates: # record the call function_calls.append((instr_pos, sark.Function(ref).start_ea)) # return the result return function_calls
def findMetadataCacheInitialize(): def checkTarget(func): #check write to global values' count. hitcount = 0 for xref in func.xrefs_from: segment = idaapi.getseg(xref.to) if idaapi.get_visible_segm_name(segment) == '.bss' and repr( xref.type) == "Data_Write": hitcount += 1 if hitcount >= 8 and hitcount < 12: return 1 return 0 # find addr of "global-metadata.dat" global_metadata = None s = idaapi.string_info_t() for i in range(0, idaapi.get_strlist_qty()): idaapi.get_strlist_item(s, i) if idaapi.get_ascii_contents(s.ea, s.length, s.type) == "global-metadata.dat": global_metadata = s.ea break # xref of "global-metadata.dat" for xref in sark.Line(global_metadata).xrefs_to: if sark.Function.is_function(xref.frm): target_func = sark.Function(xref.frm) if checkTarget(target_func): # print "find MetadataCache::Initialize at", hex(int(target_func.startEA)) idc.set_name(target_func.startEA, "MetadataCache_Initialize", SN_NOWARN | SN_NOCHECK) return else: for txref in target_func.xrefs_to: if sark.Function.is_function(txref.frm): caller = sark.Function(txref.frm) if checkTarget(caller): # print "find MetadataCache::Initialize at", hex(int(caller.startEA)) idc.set_name(caller.startEA, "MetadataCache_Initialize", SN_NOWARN | SN_NOCHECK) return print "can't find MetadataCache_Initialize"
def _get_call_ea(ea): func_eas = [] for xref in sark.Line(ea).xrefs_from: #if it is not a code xref, skip if xref.iscode == False: continue #if we reference somewhere outside the func - it is a call if sark.Function(xref.to).start_ea != sark.Function(ea).start_ea: func_eas += [xref.to] num_refs = len(func_eas) if num_refs == 0: return None elif num_refs == 1: return func_eas[0] else: # weird - expected only one xref outside the func. print "Err: < %x > Found more than one reference outside the func. Isn't supposed to happen." % ea return None
def __init__(self, ea, iatEA=None, library_name=None): """ Ctor """ self.logger = logging.getLogger(__name__) self.ea = ea # Effective Address of the function self.iatEA = iatEA # If imported function, the address in the IAT try: function = sark.Function(ea) except sark.exceptions.SarkNoFunction: raise DIE.Lib.DIE_Exceptions.DieNoFunction( "No Function at 0x%08X" % (ea, )) self.funcName = get_function_name(function.ea) self.func_start = function.startEA self.func_end = function.endEA self.proto_ea = self.getFuncProtoAdr() # Address of function prototype self.typeInfo = idaapi.tinfo_t() # Function type info self.funcInfo = idaapi.func_type_data_t() # Function info self.argNum = 0 # Number of input arguments self.args = [] # Function argument list self.retArg = None # Return argument self.library_name = library_name # If library function, name of containing library self.isLibFunc = False if self.iatEA: self.isLibFunc = True # Is this a library function elif sark.Function(ea).flags & (idaapi.FUNC_LIB | idaapi.FUNC_THUNK): self.isLibFunc = True try: self.getArguments() except Exception as ex: self.logger.error( "Failed to get function arguments for function %s: %s", self.funcName, ex)
def highlight_calls_in_function(ea): highlighted_lines = set() for line in sark.Function(ea).lines: if not line.insn.is_call: continue # Refrain from painting over someone else... if line.color is None: line.color = HIGHLIGHT_COLOR highlighted_lines.add(line.ea) return highlighted_lines
def does_fn_have_call_to(src_va, dst_va): fn = None if src_va in sark_fn_cache: fn = sark_fn_cache[src_va] else: fn = sark.Function(src_va) sark_fn_cache[src_va] = fn for xref in fn.xrefs_from: if xref.to == dst_va: return True return False
def _get_min_block_ranges(rgs, reg): if len(rgs) == 0: return rgs debug('Ranges before:') for s, e in rgs: debug('\t%x:%x' % (s, e)) rgs_d = {} rgs_new = [] rgs_new2 = [] #assumption - rgs is sorted s_first_blk, _ = rgs[0] func_start = sark.Function(s_first_blk).start_ea #if range between ranges (or between function start and first range) doesn't contain the reg - add it as range curr_s = func_start for s_blk, e_blk in rgs: dontextend = False for l in sark.lines(curr_s, s_blk): if (oregami_gen.proc_ops.is_load(l.ea, reg) or oregami_gen.proc_ops.is_store(l.ea, reg) ): #reg was used in line - range cannot include this dontextend = True break if not dontextend: rgs_new += [(curr_s, s_blk)] rgs_new += [(s_blk, e_blk)] curr_s = e_blk #if ranges are right after each other - make them one range while len(rgs_new) > 0: #print rgs_new s_blk, e_blk = rgs_new[0] rgs_new = rgs_new[1:] #while next ranges are consecutive, eat them up while len(rgs_new) > 0: s_blk2, e_blk2 = rgs_new[0] if e_blk != s_blk2: break e_blk = e_blk2 rgs_new = rgs_new[1:] rgs_new2 += [(s_blk, e_blk)] debug('Ranges after:') for s, e in rgs_new2: debug('\t%x:%x' % (s, e)) return rgs_new2
def btn_branch_opaque_clicked(self): self.btn_branch_opaque.setEnabled(False) # Only have it look at the currently selected code-block addr_trace = [get_current_codeblock().startEA] f = IDAFunctionCodeBlocks(sark.Function()) self.spawn_worker_thread( OpaquePredicateFinder(self.plugin, f, addr_trace, self.checkbox_all_symbolic.isChecked()), result_signal=self.opaque_results )
def show_highlighted_function_strings(): identifier = idaapi.get_highlighted_identifier() if not identifier: return try: function = sark.Function(name=identifier) show_function_strings(function) except sark.exceptions.SarkNoFunction: idaapi.msg("[FunctionStrings] {!r} is not a function.\n".format(identifier))
def searchIslands(self, func_ea, range_start, range_end): """Search a given function for "Islands" from a specific code range. Args: func_ea (int): effective address of the wanted function range_start (int): effective address of the start of the island range range_end (int): effective address of the end of the island range Return Value: Ordered list of code blocks for the found island, or None if found nothing """ island_guess = None func = sark.Function(func_ea) flow = idaapi.FlowChart(func.func_t) for block in flow: if range_start <= block.start_ea and block.end_ea <= range_end: if island_guess is None or block.start_ea < island_guess.start_ea: island_guess = block # quit if found nothing if island_guess is None: return None # make sure that the island is indeed an island, and not a well known function if sark.Function( island_guess.start_ea).start_ea == island_guess.start_ea: return None # find the contained flow, that island_guess is the start of island_blocks = [] candidate_list = [island_guess] while len(candidate_list) != 0: new_candidate_list = [] for candidate_block in candidate_list: if candidate_block in island_blocks: continue island_blocks.append(candidate_block) new_candidate_list += [ s for s in candidate_block.succs() if range_start <= s.start_ea and s.end_ea <= range_end ] candidate_list = new_candidate_list # return the results return island_blocks
def start(self, line): """Start the measurement for the code region. Args: line (line): (sark) code line """ try: self._starting_function = sark.Function(line.startEA) except Exception: self._starting_function = None # now record this line self.record(line)
def isFuncStart(self, ea): """Check if the given effective address is the start of a known function. Args: ea (int): effective address to be checked Return Value: True iff the given address is the start of a known function """ try: return ea == sark.Function(ea).start_ea except sark.exceptions.SarkNoFunction: return False
def findGetTypeInfoFromTypeIndex(): global GetTypeInfoFromTypeIndex finit = getNamedFunc("MetadataCache_Initialize") for xref in sark.Line(s_TypeInfoTable).xrefs_to: if sark.Function.is_function(xref.frm): func = sark.Function(xref.frm) if func.startEA == finit.startEA: continue # print "find findGetTypeInfoFromTypeIndex at", hex(int(func.startEA)) idc.set_name(func.startEA, "GetTypeInfoFromTypeIndex", SN_NOWARN | SN_NOCHECK) GetTypeInfoFromTypeIndex = int(func.startEA) return
def btn_opaque_clicked(self): self.btn_opaque.setEnabled(False) # Retrieve the addr_trace from colored code-blocks f = IDAFunctionCodeBlocks(sark.Function()) cbs = get_all_blocks_of_color(f, col_t2ida(self.plugin.col_visited)) addr_trace = [cb.startEA for cb in cbs] # Spawn the worker thread self.spawn_worker_thread( OpaquePredicateFinder(self.plugin, f, addr_trace, self.checkbox_all_symbolic.isChecked()), result_signal=self.opaque_results )
def _make_function_ea_item(self, function_context): """ Build a tree item for a function_ea node (level-1) @param function_context: a dbFunction_Context object @return: QStandradItemModel item for the function context """ calling_function_start = None with ignored(sark.exceptions.SarkNoFunction): calling_function_start = sark.Function( function_context.calling_ea).startEA if calling_function_start is not None: call_offset = function_context.calling_ea - calling_function_start func_ea_txt = "%s+%s" % (function_context.calling_func_name, hex(call_offset)) else: func_ea_txt = "[%s]:%s" % (function_context.calling_func_name, hex(function_context.calling_ea)) item_func_context_ea = QtGui.QStandardItem(func_ea_txt) item_func_context_ea.setEditable(False) item_func_context_ea.setData(hex(function_context.calling_ea), role=QtCore.Qt.ToolTipRole) item_func_context_ea.setData(function_context, role=DIE.UI.FunctionContext_Role) item_func_context_ea.setData( id(function_context), role=DIE.UI.ContextId_Role) # Used for module look-ups item_func_is_indirect = QtGui.QStandardItem() item_func_is_indirect.setEditable(False) if function_context.is_indirect: item_func_is_indirect.setIcon(self.die_icons.icon_v) item_func_is_new = QtGui.QStandardItem() item_func_is_new.setEditable(False) if function_context.is_new_func: item_func_is_new.setIcon(self.die_icons.icon_v) item_list = [ item_func_context_ea, QtGui.QStandardItem(), item_func_is_indirect, item_func_is_new, QtGui.QStandardItem(), QtGui.QStandardItem(), QtGui.QStandardItem(), QtGui.QStandardItem(), QtGui.QStandardItem(), QtGui.QStandardItem() ] return item_list
def sort_by_xrefs(functions): """ Sort by the number of Xrefs to the fucntion @param functions: List of db_DataTypes.dbFunction objects @return: a sorted list of db_DataTypes.dbFunction objects by Xref count. """ xref_counts = [] for f in functions: try: xref_counts.append((f, (len(list(sark.Function(ea=f.function_start).xrefs_to))))) except sark.exceptions.SarkNoFunction: pass sorted_funcs = sorted(xref_counts, key=lambda x: x[1], reverse=True) return [count[0] for count in sorted_funcs]
def isFuncEnd(self, ea): """Check if the given effective address is the end of a known function. Args: ea (int): effective address to be checked Return Value: True iff the given address is the end of a known function """ prev_line = sark.Line(ea).prev try: return ea == sark.Function(prev_line.start_ea).end_ea except sark.exceptions.SarkNoFunction: return False