from __future__ import print_function from builtins import map from builtins import range import os import tempfile from future.utils import viewitems, viewvalues import idautils import idc import ida_funcs import ida_kernwin from miasm.core.bin_stream_ida import bin_stream_ida from miasm.core.asmblock import * from miasm.expression import expression as m2_expr from miasm.core.locationdb import LocationDB from miasm.expression.simplifications import expr_simp from miasm.analysis.depgraph import DependencyGraph from miasm.ir.ir import AssignBlock, IRBlock from utils import guess_machine class depGraphSettingsForm(ida_kernwin.Form): def __init__(self, lifter_model_call, ircfg, mn): self.lifter_model_call = lifter_model_call self.ircfg = ircfg self.mn = mn self.stk_args = {'ARG%d' % i:i for i in range(10)} self.stk_unalias_force = False self.address = idc.get_screen_ea() cur_block = None for loc_key in ircfg.getby_offset(self.address): block = ircfg.get_block(loc_key) offset = self.ircfg.loc_db.get_location_offset(block.loc_key) if offset is not None: # Only one block non-generated assert cur_block is None cur_block = block assert cur_block is not None line_nb = None for line_nb, assignblk in enumerate(cur_block): if assignblk.instr.offset == self.address: break assert line_nb is not None cur_loc_key = str(cur_block.loc_key) loc_keys = sorted(map(str, ircfg.blocks)) regs = sorted(lifter_model_call.arch.regs.all_regs_ids_byname) regs += list(self.stk_args) reg_default = regs[0] for i in range(10): opnd = idc.print_operand(self.address, i).upper() if opnd in regs: reg_default = opnd break ida_kernwin.Form.__init__(self, r"""BUTTON YES* Launch BUTTON CANCEL NONE Dependency Graph Settings Track the element: {cMode}> INFO: To track stack argument number n, use "ARGn" Method to use: {cMethod}> """, { 'cbReg': ida_kernwin.Form.DropdownListControl( items=regs, readonly=False, selval=reg_default), 'cMode': ida_kernwin.Form.RadGroupControl( ("rBeforeLine", "rAfterLine", "rEndBlock")), 'cMethod': ida_kernwin.Form.ChkGroupControl( ("rNoMem", "rNoCall", "rImplicit", "rUnaliasStack")), 'iLineNb': ida_kernwin.Form.NumericInput( tp=ida_kernwin.Form.FT_RAWHEX, value=line_nb), 'cbBBL': ida_kernwin.Form.DropdownListControl( items=loc_keys, readonly=False, selval=cur_loc_key), 'cColor': ida_kernwin.Form.ColorInput(value=0xc0c020), }) self.Compile() @property def loc_key(self): value = self.cbBBL.value for real_loc_key in self.ircfg.blocks: if str(real_loc_key) == value: return real_loc_key raise ValueError("Bad loc_key") @property def line_nb(self): value = self.iLineNb.value mode = self.cMode.value if mode == 0: return value elif mode == 1: return value + 1 else: return len(self.ircfg.blocks[self.loc_key]) @property def elements(self): value = self.cbReg.value if value in self.stk_args: line = self.ircfg.blocks[self.loc_key][self.line_nb].instr arg_num = self.stk_args[value] stk_high = m2_expr.ExprInt(idc.get_spd(line.offset), lifter.sp.size) stk_off = m2_expr.ExprInt(self.lifter_model_call.sp.size // 8 * arg_num, lifter.sp.size) element = m2_expr.ExprMem(self.mn.regs.regs_init[lifter.sp] + stk_high + stk_off, self.lifter_model_call.sp.size) element = expr_simp(element) # Force stack unaliasing self.stk_unalias_force = True elif value: element = self.lifter_model_call.arch.regs.all_regs_ids_byname.get(value, None) else: raise ValueError("Unknown element '%s'!" % value) return set([element]) @property def depgraph(self): value = self.cMethod.value return DependencyGraph(self.ircfg, implicit=value & 4, follow_mem=value & 1, follow_call=value & 2) @property def unalias_stack(self): return self.cMethod.value & 8 or self.stk_unalias_force @property def color(self): return self.cColor.value def clean_lines(): "Remove previous comments" global comments for offset in comments: idc.set_color(offset, idc.CIC_ITEM, 0xffffff) idc.set_cmt(offset, "", 0) comments = {} def treat_element(): "Display an element" global graphs, comments, sol_nb, settings, addr, lifter, ircfg try: graph = next(graphs) except StopIteration: comments = {} print("Done: %d solutions" % (sol_nb)) return sol_nb += 1 print("Get graph number %02d" % sol_nb) filename = os.path.join(tempfile.gettempdir(), "solution_0x%08x_%02d.dot" % (addr, sol_nb)) print("Dump the graph to %s" % filename) open(filename, "w").write(graph.graph.dot()) for node in graph.relevant_nodes: try: offset = ircfg.blocks[node.loc_key][node.line_nb].instr.offset except IndexError: print("Unable to highlight %s" % node) continue comments[offset] = comments.get(offset, []) + [node.element] idc.set_color(offset, idc.CIC_ITEM, settings.color) if graph.has_loop: print('Graph has dependency loop: symbolic execution is inexact') else: print("Possible value: %s" % next(iter(viewvalues(graph.emul(lifter))))) for offset, elements in viewitems(comments): idc.set_cmt(offset, ", ".join(map(str, elements)), 0) def next_element(): "Display the next element" clean_lines() treat_element() def launch_depgraph(): global graphs, comments, sol_nb, settings, addr, lifter, ircfg # Get the current function addr = idc.get_screen_ea() func = ida_funcs.get_func(addr) # Init machine = guess_machine(addr=func.start_ea) mn, dis_engine, lifter_model_call = machine.mn, machine.dis_engine, machine.lifter_model_call bs = bin_stream_ida() loc_db = LocationDB() mdis = dis_engine(bs, loc_db=loc_db, dont_dis_nulstart_bloc=True) lifter = lifter_model_call(loc_db) # Populate symbols with ida names for ad, name in idautils.Names(): if name is None: continue loc_db.add_location(name, ad) asmcfg = mdis.dis_multiblock(func.start_ea) # Generate IR ircfg = lifter.new_ircfg_from_asmcfg(asmcfg) # Get settings settings = depGraphSettingsForm(lifter, ircfg, mn) settings.Execute() loc_key, elements, line_nb = settings.loc_key, settings.elements, settings.line_nb # Simplify assignments for irb in list(viewvalues(ircfg.blocks)): irs = [] offset = loc_db.get_location_offset(irb.loc_key) fix_stack = offset is not None and settings.unalias_stack for assignblk in irb: if fix_stack: stk_high = m2_expr.ExprInt(idc.get_spd(assignblk.instr.offset), lifter.sp.size) fix_dct = {lifter.sp: mn.regs.regs_init[lifter.sp] + stk_high} new_assignblk = {} for dst, src in viewitems(assignblk): if fix_stack: src = src.replace_expr(fix_dct) if dst != lifter.sp: dst = dst.replace_expr(fix_dct) dst, src = expr_simp(dst), expr_simp(src) new_assignblk[dst] = src irs.append(AssignBlock(new_assignblk, instr=assignblk.instr)) ircfg.blocks[irb.loc_key] = IRBlock(irb.loc_db, irb.loc_key, irs) # Get dependency graphs dg = settings.depgraph graphs = dg.get(loc_key, elements, line_nb, set([loc_db.get_offset_location(func.start_ea)])) # Display the result comments = {} sol_nb = 0 # Register and launch ida_kernwin.add_hotkey("Shift-N", next_element) treat_element() if __name__ == "__main__": launch_depgraph()