def handle_mov(self, state): """Updates the state of the stack string finding based on a mov instruction""" op1 = tracing.get_opnd_replacement(state.ea, POS_FIRST) if "[" in op1: offset = tracing.get_operand_value_replacement(state.ea, POS_FIRST, state) self.set_stack(offset, state.ea, POS_SECOND, state) else: reg = tracing.get_reg_fam(op1) type_ = idc.get_operand_type(state.ea, POS_SECOND) if reg: if type_ != idc.o_phrase and type_ != idc.o_displ: if type_ == idc.o_reg: reg2 = tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_SECOND)) if reg2 and reg2[0] in state.regs: val = state.regs[reg2[0]][0] else: val = None else: val = idc.get_operand_value(state.ea, POS_SECOND) if val is not None: state.regs[reg[0]] = (val, state.ea) else: offset = tracing.get_operand_value_replacement(state.ea, POS_SECOND, state) value = state.stack.get(offset, None) if value is not None: state.regs[reg[0]] = value else: self.clear_reg_if_needed(reg, state.regs)
def handle_lea(self, state): """Updates the state of the stack string finding based on an lea instruction""" if idc.get_operand_type(state.ea, POS_SECOND) == idc.o_reg: source_reg = tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_SECOND)) if source_reg and source_reg[0] in state.regs: value = state.regs[source_reg[0]] else: value = None else: value = tracing.get_operand_value_replacement(state.ea, POS_SECOND, state) if value is not None and value in state.stack: self.report_strings(state.strs, state.stack) self.clear_reg_if_needed(tracing.get_reg_fam(tracing.get_opnd_replacement(state.ea, POS_FIRST)), state.regs)
def set_stack(self, offset, ea, pos, state): """ Sets the stack dictionary, at the given offset, to contain the value at the given position at the given ea, performing a lookup in the register dictionary if needed. """ fill = False if idc.get_operand_type(ea, pos) == idc.o_imm: val = idc.get_operand_value(ea, pos) state.stack[offset] = (val, ea) fill = True else: reg = tracing.get_reg_fam(tracing.get_opnd_replacement(ea, pos)) if reg and reg[0] in state.regs: val = state.regs[reg[0]][0] state.stack[offset] = (state.regs[reg[0]][0], ea) fill = True if fill: for i in range(0, tracing.get_byte_size_of_operand(ea, pos)): state.stack[offset + i] = (val & 0xFF, ea) val //= 256
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)