def call_effects(self, addr, instr): assignblks, extra = super(IRADelModCallStack, self).call_effects(addr, instr) if use_ida_stack: stk_before = idc.get_spd(instr.offset) stk_after = idc.get_spd(instr.offset + instr.l) stk_diff = stk_after - stk_before print(hex(stk_diff)) call_assignblk = AssignBlock([ ExprAssign(self.ret_reg, ExprOp('call_func_ret', addr)), ExprAssign(self.sp, self.sp + ExprInt(stk_diff, self.sp.size)) ], instr) return [call_assignblk], [] else: if not dontmodstack: return assignblks, extra out = [] for assignblk in assignblks: dct = dict(assignblk) dct = { dst: src for (dst, src) in viewitems(dct) if dst != self.sp } out.append(AssignBlock(dct, assignblk.instr)) return out, extra
def call_effects(self, ad, instr): print(hex(instr.offset), instr) stk_before = idc.get_spd(instr.offset) stk_after = idc.get_spd(instr.offset + instr.l) stk_diff = stk_after - stk_before print(hex(stk_diff)) call_assignblk = AssignBlock([ ExprAssign(self.ret_reg, ExprOp('call_func_ret', ad)), ExprAssign(self.sp, self.sp + ExprInt(stk_diff, self.sp.size)) ], instr) return [call_assignblk], []
def get_operand_value_replacement(ea, pos, state): """ A replacement for Ida's idc.get_operand_value that handles displacements more reasonably :param ea: memory location :param pos: argument location example: add eax, ebx eax is position 0, ebx is position 1 :param state: the current stack pointer register family (usually sp) :return: computes a numerical replacement for an operand """ if is_displ(ea, pos): bit_size = 64 if is_64_bit() else 32 stack_reg = 'rsp' if bit_size == 64 else 'esp' cmd = idaapi.insn_t() idaapi.decode_insn(cmd, ea) offset = cmd.ops[pos].addr # Convert the offset to a signed value if offset & (1 << (bit_size - 1)): offset -= (1 << bit_size) if stack_reg in get_opnd_replacement(ea, pos): offset += idc.get_spd(ea) or 0 return offset else: return idc.get_operand_value(ea, pos)
def handle_call(self, state): """Updates the state of the stack string finding based on a call instruction""" stack_pointer = idc.get_spd(state.ea) next_ea = state.ea + idc.get_item_size(state.ea) stack_pointer_delta = idc.get_sp_delta(next_ea) if stack_pointer is not None and stack_pointer_delta is not None: next_reg = tracing.get_reg_fam(idc.print_operand(next_ea, POS_FIRST)) # Caller cleanup handling, vulnerable to instruction reordering though. if next_reg and "esp" in next_reg and "add" in idc.print_insn_mnem(next_ea).lower(): stack_pointer_delta += idc.get_sp_delta(next_ea + idc.get_item_size(next_ea)) for index in range(stack_pointer, stack_pointer + stack_pointer_delta): if index in state.stack: del state.stack[index]
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), ir_arch.sp.size) stk_off = m2_expr.ExprInt(self.ira.sp.size // 8 * arg_num, ir_arch.sp.size) element = m2_expr.ExprMem(self.mn.regs.regs_init[ir_arch.sp] + stk_high + stk_off, self.ira.sp.size) element = expr_simp(element) # Force stack unaliasing self.stk_unalias_force = True elif value: element = self.ira.arch.regs.all_regs_ids_byname.get(value, None) else: raise ValueError("Unknown element '%s'!" % value) return set([element])
def get_stack_strings(self, functions): """ Finds all the stack strings it can in the given functions. Parameters set globally: STRING_GAP_TOLERANCE - the gap allowed between string characters. MAX_CHARACTER_WIDTH - the maximum character size, in bytes ASCII - Whether character values must be 0-127 """ stack_strings = [] for func in functions: state = tracing.BranchingTraceState(func.start_ea) state.strs = set() states = [state] func_eas = [] ea = state.ea while ea < func.end_ea: func_eas.append(ea) ea += idc.get_item_size(ea) while states: state = states.pop() while state.ea < func.end_ea: try: func_eas.remove(state.ea) except: pass state.visited_eas.append(state.ea) mnemonic = idc.print_insn_mnem(state.ea) if mnemonic in IGNORED_MNEMONICS: pass elif "pop" in mnemonic: reg = tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_FIRST)) if reg: value = state.stack.get(idc.get_spd(state.ea), None) if value is not None: state.regs[reg[0]] = value else: self.clear_reg_if_needed(reg, state.regs) elif "push" in mnemonic: # bug where idc.get_spd was not correctly tracking the pointer, # this case also hasn't really been seen often as part of a stack string # self.set_stack(idc.get_spd(ea), ea, POS_FIRST, regs, stack) pass elif "mov" in mnemonic: self.handle_mov(state) elif ( ( "xor" in mnemonic and tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_FIRST)) == tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_SECOND)) ) or ("lea" in mnemonic and idc.print_operand(state.ea, POS_SECOND) == "[0]") or ( "sub" in mnemonic and tracing.get_opnd_replacement(state.ea, POS_FIRST) == tracing.get_opnd_replacement(state.ea, POS_SECOND) ) ): reg = tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_FIRST)) if reg: state.regs[reg[0]] = (0, state.ea) elif "loop" in mnemonic or "movsb" in mnemonic: state.regs["rcx"] = (0, state.ea) elif mnemonic in JUMPS: try: target = next(idautils.CodeRefsFrom(state.ea, 0)) except StopIteration: target = None if target and target not in state.visited_eas: if func.end_ea > target >= func.start_ea: state.visited_eas.append(target) new_state = tracing.BranchingTraceState(target, state) new_state.strs = state.strs states.append(new_state) else: self.report_strings(state.strs, state.stack) # Always follow an unconditional jump if mnemonic == "jmp": break elif ( "rep" in idc.GetDisasm(state.ea).split(" ")[0] and "scas" not in idc.GetDisasm(state.ea).split(" ")[1] ): self.report_strings(state.strs, state.stack) elif "lea" in mnemonic: self.handle_lea(state) elif "call" in mnemonic: self.handle_call(state) elif "ret" in mnemonic: break elif ( idc.get_operand_type(state.ea, POS_FIRST) == idc.o_reg ): # If we find a target register we were tracking, stop tracking it. self.clear_reg_if_needed( tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_FIRST)), state.regs ) state.ea += idc.get_item_size(state.ea) self.report_strings(state.strs, state.stack) if not states and func_eas: new_state = tracing.BranchingTraceState(func_eas[0]) new_state.strs = set() states.append(new_state) stack_strings.extend(state.strs) self.strings.update(stack_strings)
def launch_depgraph(): global graphs, comments, sol_nb, settings, addr, ir_arch, 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, ira = machine.mn, machine.dis_engine, machine.ira bs = bin_stream_ida() mdis = dis_engine(bs, dont_dis_nulstart_bloc=True) ir_arch = ira(mdis.loc_db) # Populate symbols with ida names for ad, name in idautils.Names(): if name is None: continue mdis.loc_db.add_location(name, ad) asmcfg = mdis.dis_multiblock(func.start_ea) # Generate IR ircfg = ir_arch.new_ircfg_from_asmcfg(asmcfg) # Get settings settings = depGraphSettingsForm(ir_arch, 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 = ir_arch.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), ir_arch.sp.size) fix_dct = {ir_arch.sp: mn.regs.regs_init[ir_arch.sp] + stk_high} new_assignblk = {} for dst, src in viewitems(assignblk): if fix_stack: src = src.replace_expr(fix_dct) if dst != ir_arch.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_key, irs) # Get dependency graphs dg = settings.depgraph graphs = dg.get(loc_key, elements, line_nb, set([ir_arch.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()