def get_params(ea): data_addr = None length = None inst = idautils.DecodePreviousInstruction(ea) if (inst != None) and (inst.get_canon_mnem() == "mov"): length = inst.Op2.value inst2 = idautils.DecodePreviousInstruction(inst.ea) if (inst2 != None) and (inst2.get_canon_mnem() == "mov"): inst3 = idautils.DecodePreviousInstruction(inst2.ea) if (inst3 != None) and (inst3.get_canon_mnem() == "lea"): length = inst.Op2.value data_addr = inst3.Op2.addr return data_addr, length
def get_first_function(ea): """ see above, but returns the first pushed value """ maybe_start = idc.get_func_attr(ea, idc.FUNCATTR_START) limit = 0 if maybe_start == idc.BADADDR: limit = 10 cur_ea = ea limit_count = 0 while cur_ea != idc.BADADDR: # are we over limit or up to the func start? limit_count += 1 limit_exceeded = (limit > 0 and limit_count > limit) too_far = (maybe_start != idc.BADADDR and cur_ea < maybe_start) if limit_exceeded or too_far: LOG.error( "Failed to find string walking backwards from {:08X}".format( ea)) return None prev_ins = idautils.DecodePreviousInstruction(cur_ea) prev_ea = prev_ins.ea # did we find it? if idc.GetMnem(prev_ea) == 'push': if idc.get_operand_type(prev_ea, 0) in [idc.o_mem, idc.o_imm]: # push offset found! pushed_addr = idc.GetOperandValue(prev_ea, 0) # it's not data, then probably good if idc.isCode(idc.GetFlags(pushed_addr)): return pushed_addr cur_ea = prev_ea
def ret_addr(ea): #we can't assume Thumb only, so we also keep ARM cases, just adjust addr in Thumb cases if (ea % 2) != 0: ea -= 1 ''' #calculating code segment ranges every time is wasteful code_segs = [] for s in idautils.Segments(): if idaapi.segtype(s) == idaapi.SEG_CODE: s_end = idc.GetSegmentAttr(s, SEGATTR_END) code_segs.append({"start" : s, "end" : s_end}) if not reduce(lambda x, y: x or y, map(lambda x: (x["start"] <= ea) and (x["end"] > ea), code_segs)): return False ''' #this is-in-function check is enough (segment check redundant) if we trust function ID'ing anyway. f_ea = idaapi.get_func(ea) if not f_ea: return False #Preceding or Previous? # Not necessarily all preceding will be a call to a ret instruction, # but "the" prev should be always the one. i = idautils.DecodePreviousInstruction(ea) if i and "BL" in idc.GetMnem(i.ea): return True return False
def parse_xrefs(xrefs): rsas = set() for xref in xrefs: name = None rsa = None ea = xref for i in range(0, 7): decoded_instruction = idautils.DecodePreviousInstruction(ea) xrefs_from = idautils.XrefsFrom(decoded_instruction.ea) for ref in xrefs_from: if ref.type == 1: s = idc.GetString(ref.to) if s: rsa = s elif ref.type == 21 and idc.GetDisasm(ref.to).find("cfstr") != -1: cfstr_xrefs = idautils.XrefsFrom(ref.to) for cfstr_xref in cfstr_xrefs: if cfstr_xref.type == 1: for cfstr_xref2 in idautils.XrefsFrom(cfstr_xref.to): s = idc.GetString(cfstr_xref2.to) if s and s.strip() != "": name = s if name and rsa: rsas.add((name, rsa)) ea = decoded_instruction.ea return [{"name": x[0].strip(), "rsa": x[1].strip()} for x in rsas]
def get_prev_push_addr(base_addr): global INSTR_DISTANCE_LIMITER prev_op = idautils.DecodePreviousInstruction(base_addr) retVal = idaapi.BADADDR # static value for idx in range(INSTR_DISTANCE_LIMITER): # some calls get rekt by asm segments if prev_op is None: break if is_push_mnem(prev_op.ea): retVal = prev_op.ea break prev_op = idautils.DecodePreviousInstruction(prev_op.ea) return retVal
def ret_addr(ea): #we can't assume Thumb only, so we also keep ARM cases, just adjust addr in Thumb cases if (ea % 2) != 0: ea -= 1 f_ea = idaapi.get_func(ea) if not f_ea: return False #Preceding or Previous? # Not necessarily all preceding will be a call to a ret instruction, # but "the" prev should be always the one. i = idautils.DecodePreviousInstruction(ea) if i and "BL" in idc.GetMnem(i.ea): return True return False
def get_first_string(ea): """ walk up and grab a string like this push offset aGamegetpotionc ; "GameGetPotionColorUint" lea ecx, [ebp+var_244] ; int call makestr push offset sub_435ADA lea eax, [ebp+var_244] mov ecx, esi push eax call register_global ; <---- ea """ maybe_start = idc.get_func_attr(ea, idc.FUNCATTR_START) limit = 0 if maybe_start == idc.BADADDR: limit = 10 cur_ea = ea limit_count = 0 while cur_ea != idc.BADADDR: # are we over limit or up to the func start? limit_count += 1 limit_exceeded = (limit > 0 and limit_count > limit) too_far = (maybe_start != idc.BADADDR and cur_ea < maybe_start) if limit_exceeded or too_far: LOG.error( "Failed to find string walking backwards from {:08X}".format( ea)) return None prev_ins = idautils.DecodePreviousInstruction(cur_ea) prev_ea = prev_ins.ea # did we find it? if idc.GetMnem(prev_ea) == 'push': if idc.get_operand_type(prev_ea, 0) in [idc.o_mem, idc.o_imm]: # push offset found! pushed_addr = idc.GetOperandValue(prev_ea, 0) if idc.isASCII(idc.GetFlags(pushed_addr)): s = idc.GetString(pushed_addr, -1, idc.ASCSTR_C) #LOG.debug("Read string {} from {:08X} from instruction at {:08X}".format(repr(s), pushed_addr, prev_ea)) return s cur_ea = prev_ea
def get_interface_param_addr(xref): """ Find the interface address looking for this instruction : lea r8, Interface """ # Check up to 3 instructions before next_addr = xref for i in range(3): inst = idautils.DecodePreviousInstruction(next_addr) if inst.get_canon_mnem() == 'call': break elif is_lea_r8_mem_inst(inst): return inst.Op2.addr next_addr = inst.ea # Check up to 3 instructions forward for addr in get_func_items_from_xref(xref)[:3]: inst = idautils.DecodeInstruction(addr) if inst.get_canon_mnem() == 'call': break elif is_lea_r8_mem_inst(inst): return inst.Op2.addr return None
def preceeding_call(ea): p = idautils.DecodePreviousInstruction(ea) if p and "BL" in idc.GetMnem(p.ea): return str(idc.GetOpnd(p.ea, 0)) else: return ""
def notify_ana(self, insn): ea = insn.ea bytes_left = chunk_start(ea) + chunk_size(ea) - ea bytecode = ida_bytes.get_bytes( insn.ea, bytes_left) # get remaining bytes in chunk try: instruction = EVMAsm.disassemble_one(bytecode) except Exception as e: print e return insn.size = instruction.size #initialize operands to voids operands = [insn[i] for i in xrange(1, 6)] for o in operands: o.type = o_void # set the instruction insn.itype = self.instruction_index.get(instruction._opcode, 0) #detect "CALLI" if instruction.name == "JUMPI": # TODO: check for 4 prev instructions where there is a DUP2 in the middle #decode 3 previous instructions prev_insns = [insn] for i in range(3): prev_insns.append( idautils.DecodePreviousInstruction(prev_insns[-1].ea)) prev_insns.pop(0) # remove orig # sanity check previous instructions for i in prev_insns: #print i.get_canon_mnem(), if i.ea == ida_idaapi.BADADDR: print 'ERROR' if (prev_insns[0].get_canon_mnem().startswith("PUSH2") and prev_insns[1].get_canon_mnem().startswith("EQ") and prev_insns[2].get_canon_mnem().startswith("PUSH4")): # switch it to a "CALLI" insn.itype = 1 # CALLI instruction index jump_target = self.get_operand( prev_insns[0][0]) # operand on the push2 insn[0].type = o_near insn[0].addr = jump_target function_hash = self.get_operand( prev_insns[2][0]) # operand on the push4 insn[1].type = o_imm insn[1].value = function_hash # set the target to be a function AutoMark(jump_target, AU_PROC) if instruction.has_operand: insn[0].type = o_idpspec0 # custom type insn[0].dtype = dt_byte32 insn[0].addr = ea + 1 # operand is located after opcode insn[0].specval = instruction.operand_size #o.specval = str(instruction.operand) # return the instruction size here return insn.size
def notify_emu(self, insn): feature = insn.get_canon_feature() #print "emulating", insn.get_canon_mnem(), hex(feature) mnemonic = insn.get_canon_mnem() if mnemonic == "CALLI": # operand 0 is the address # operand 1 is the function hash add_cref(insn.ea, insn.ea + insn.size, fl_JN) # false branch is following instruction addr = insn[0].addr add_cref(insn.ea, addr, fl_CN) # true branch is stored in operand index 0 ida_bytes.set_cmt(insn.ea, "JUMPI", True) hash_str = '0x%08x' % (insn[1].value) function_prototype = known_hashes.knownHashes.get(hash_str, '') label = '%s (%s)' % (function_prototype, hash_str) if not ida_lines.get_extra_cmt(addr, ida_lines.E_PREV + 0): # don't dup ida_lines.add_extra_cmt(addr, True, label) if function_prototype == '': function_prototype = 'func_%s' % (hash_str) set_name(addr, function_prototype, SN_NOCHECK) elif mnemonic == "JUMPI": # add ref to next instruction for false branch add_cref(insn.ea, insn.ea + insn.size, fl_JN) # maybe we have a simple puch prev_insn = idautils.DecodePreviousInstruction(insn.ea) if prev_insn: if prev_insn.get_canon_mnem().startswith("PUSH"): jump_addr = self.get_operand(prev_insn[0]) add_cref(insn.ea, jump_addr, fl_JN) elif mnemonic == "JUMP": prev_insn = idautils.DecodePreviousInstruction(insn.ea) if prev_insn: # TODO: implement stack modeling to resolve actual top value of stack if prev_insn.get_canon_mnem().startswith("PUSH"): jump_addr = self.get_operand(prev_insn[0]) #print "found jump to", hex(jump_addr) add_cref(insn.ea, jump_addr, fl_JN) # TODO: adjust function boundary to include all code #func = get_func(insn.ea) #if func: # print "appending new tail" # append_func_tail(func, jump_addr, BADADDR) flows = (feature & CF_STOP) == 0 if flows: add_cref(insn.ea, insn.ea + insn.size, fl_F) if may_trace_sp(): if flows: self.trace_sp(insn) else: idc.recalc_spd(insn.ea) return True
def trace_arg_bwd(ea, arg_num): ARCH = "ARM32" CALL_ARGS = {"ARM32": ["R0", "R1", "R2", "R3"]} args = CALL_ARGS[ARCH] if (len(args) <= arg_num): arg_into = "SP" arg_offs = ((arg_num - len(args))) * 4 else: arg_into = CALL_ARGS[ARCH][arg_num] arg_offs = 0 func = idaapi.get_func(ea) fc = idaapi.FlowChart(func) for block in fc: if block.startEA <= ea and block.endEA > ea: break #original sink arg_in = set([arg_into]) while (ea >= block.startEA): #print "0x%08x %s" % (ea, idc.GetDisasm(ea)) ############ BEGINNING OF TRACING ############ mnem = idc.GetMnem(ea) if mnem == "MOV": arg_to = idc.GetOpnd(ea, 0) arg_from = idc.GetOpnd(ea, 1) #propagate to new register if arg_to in arg_in: arg_in.add(arg_from) #note: if arg_from is in arg_in, but arg_to isn't, we don't add arg_to to the sinks, because we are going backwards, #so we know that's not the one that ended up being used. elif mnem == "LDR": arg_to = idc.GetOpnd(ea, 0) arg_from = idc.GetOpnd(ea, 1) if ARCH == "ARM32": if arg_to in arg_in: #now there should be a a DataRef here to a string. #we want the data reference that is of type 1 (Data_Offset), as oppossed to 1 (Data_Read) refs = [r for r in idautils.XrefsFrom(ea) if r.type == 1] if len(refs) == 1: #print "There is only one data offset reference from here, if it is a string we are done." for s in IDAStrings: if s.ea == refs[0].to: return str(s) elif mnem == "ADR" or mnem == "ADR.W": #print "ADR instruction!" arg_to = idc.GetOpnd(ea, 0) arg_from = idc.GetOpnd(ea, 1) if ARCH == "ARM32": if arg_to in arg_in: #now there should be a a DataRef here to a string. #we want the data reference that is of type 1 (Data_Offset), as oppossed to 1 (Data_Read) refs = [r for r in idautils.XrefsFrom(ea) if r.type == 1] if len(refs) == 1: #print "There is only one data offset reference from here, if it is a string we are done." for s in IDAStrings: if s.ea == refs[0].to: return str(s) elif mnem == "ADD": arg_to = idc.GetOpnd(ea, 0) arg_from = idc.GetOpnd(ea, 1) if ARCH == "ARM32": if arg_from == "PC" and arg_to in arg_in: #now there should be a a DataRef here to a string. if sum(1 for _ in idautils.DataRefsFrom(ea)) == 1: for ref in idautils.DataRefsFrom(ea): #get string at ref for s in IDAStrings: if s.ea == ref: return str(s) ############ END OF TRACING ############ if ea == block.startEA: #For some reason, block.preds() seems to be broken. I get 0 predecessors to every block. So for now, we limit to same block. #Also idaapi.decode_preceding_instruction is annoying, because if there are more than 1 preceding, it just shows the first one only. #So this is getting around the preds() not working. preds = [] for b in fc: for s in b.succs(): if s.startEA == block.startEA: #this is a predecessor block to us preds.append(b) if len(preds) == 1: #print "1 predecessor, continuing there" block = preds[0] i = idautils.DecodePreviousInstruction(block.endEA) ea = block.endEA - i.size else: #print "0 or multiple predecessor blocks, givin up." return "" else: i = idautils.DecodePreviousInstruction(ea) ea -= i.size return ""