def readFunction(self, f, discard=True): name = idc.GetFunctionName(f) func = idaapi.get_func(f) flow = idaapi.FlowChart(func) size = func.endEA - func.startEA if discard: # Unnamed function, ignore it... if name.startswith("sub_") or name.startswith( "j_") or name.startswith("unknown"): return False # Already recognized runtime's function flags = idc.GetFunctionFlags(f) if flags & idc.FUNC_LIB or flags == -1: return False nodes = 0 edges = 0 points = 0 instructions = 0 mnems = [] dones = {} for block in flow: nodes += 1 indegree = 0 outdegree = 0 for succ_block in block.succs(): edges += 1 indegree += 1 if not dones.has_key(succ_block.id): dones[succ_block] = 1 for x in list( idautils.Heads(succ_block.startEA, succ_block.endEA)): instructions += 1 mnems.append(idc.GetMnem(x)) for pred_block in block.preds(): edges += 1 outdegree += 1 if not dones.has_key(succ_block.id): dones[succ_block] = 1 for x in list( idautils.Heads(succ_block.startEA, succ_block.endEA)): instructions += 1 mnems.append(idc.GetMnem(x)) if indegree > 0: points += indegree if outdegree > 0: points += outdegree if nodes > 1 and instructions > 5 and edges > 1: #myexport_print("Exporter: Current function 0x%08x %s" % (f, name)) return (name, nodes, edges, points, size, instructions, mnems) return False
def find_all_ioctls(): """ From the currently selected address attempts to traverse all blocks inside the current function to find all immediate values which are used for a comparison/sub immediately before a jz. Returns a list of address, second operand pairs. """ ioctls = [] # Find the currently selected function and get a list of all of it's basic blocks addr = idc.ScreenEA() f = idaapi.get_func(addr) fc = idaapi.FlowChart(f, flags=idaapi.FC_PREDS) for block in fc: # grab the last two instructions in the block last_inst = idc.PrevHead(block.endEA) penultimate_inst = idc.PrevHead(last_inst) # If the penultimate instruction is cmp or sub against an immediate value immediatly preceding a 'jz' # then it's a decent guess that it's an IOCTL code (if this is a disptach function) if idc.GetMnem(penultimate_inst) in ['cmp', 'sub'] and idc.GetOpType( penultimate_inst, 1) == 5: if idc.GetMnem(last_inst) == 'jz': ioctl_tracker.add_ioctl(penultimate_inst) for inst in ioctl_tracker.ioctl_locs: value = get_operand_value(inst) ioctls.append((inst, value)) return ioctls
def TraceApiCall(code): print "operand 0 is "+idc.GetOpnd(code,0) print "operand 1 is "+idc.GetOpnd(code,1) print "operand 2 is "+idc.GetOpnd(code,2) reg = idc.GetOpnd(code,0) ### search down to find caller it cannt deal with such situation: ### addi r10, r10, VOS_sprintf@l ### b addr ### in the above code, the trace should follow addr to find the right call ### func_end = idc.GetFunctionAttr(code, idc.FUNCATTR_END) instruct = "mtlr "+reg while(code < func_end): code = idc.FindCode(code, SEARCH_DOWN|SEARCH_NEXT) ### search "mtlr r10" if(("mtlr"==idc.GetMnem(code)) and (idc.GetOpnd(code,1) == reg)): print idc.GetOpnd(code,1) print "Get the instruct! "+ idc.GetDisasm(code) while(code < func_end): code = idc.FindCode(code, SEARCH_DOWN|SEARCH_NEXT) if("blrl" in idc.GetDisasm(code)): print "api call " + idc.GetDisasm(code)+" from ",hex(code) print "mnem "+idc.GetMnem(code) return code
def calBasicBlockFeature_gemini(block): numericNum = 0 stringNum = 0 transferNum = 0 callNum = 0 InstrNum = 0 arithNum = 0 logicNum = 0 curEA = block.startEA while curEA <= block.endEA: # 数值常量 , 字符常量的数量 numer, stri = calConstantNumber(curEA) numericNum = numericNum + numer stringNum = stringNum + stri # 转移指令的数量 if idc.GetMnem(curEA) in config_for_feature.Gemini_allTransferInstr: transferNum = transferNum + 1 # 调用的数量 if idc.GetMnem(curEA) == 'call': callNum = callNum + 1 # 指令的数量 InstrNum = InstrNum + 1 # 算术指令的数量 if idc.GetMnem(curEA) in config_for_feature.Gemini_arithmeticInstr: arithNum = arithNum + 1 # 逻辑指令 if idc.GetMnem(curEA) in config_for_feature.Gemini_logicInstr: logicNum = logicNum + 1 curEA = idc.NextHead(curEA, block.endEA) fea_str = str(numericNum) + "," + str(stringNum) + "," + str( transferNum) + "," + str(callNum) + "," + str(InstrNum) + "," + str( arithNum) + "," + str(logicNum) + "," return fea_str
def jumps_to(f, t): f_ea = idc.LocByName(f) t_ea = idc.LocByName(t) for start, end in idautils.Chunks(f_ea): ea = start while (ea < end): i = idautils.DecodeInstruction(ea) #Functions have data chunks in them, these are offsets and dwords. skipping 4 ahead seems like a safe choice. if type(i) == type(None): ea += 4 continue #detect if instruction is a jump to t_ea m = idc.GetMnem(ea) if idc.GetMnem(ea) == "LDR" and idc.GetOpnd( ea, 0) == "PC" and t in idc.GetOpnd(ea, 1): return True elif idc.GetMnem(ea) == "BX" and idc.GetOpnd(ea, 0) == t: return True try: ea += i.size except: print "0x%08x" % ea print "DecodeInstruction failed!" print i.size return False
def check_operand(opnd, call_arguments, heads): if is_argument(opnd, call_arguments): return True for head in heads: if is_jump(head): return False if is_return_value(opnd) and idc.GetMnem( head) == 'call' and ida_funcs.func_does_return(head): return check_eax(head, call_arguments) if idc.GetMnem(head) == 'call' and is_used(head, opnd): return False if opnd != idc.GetOpnd(head, 0): continue if should_check_second_opnd(head): if is_number(idc.GetOpnd(head, 1)): return is_argument(idc.GetOpnd(head, 1), call_arguments) opnd = idc.GetOpnd(head, 1) elif (not is_number(idc.GetOpnd(head, 1))) and check_next_opnd( head, call_arguments): return True if is_argument(opnd, call_arguments): return True return False
def get_args(addr): """ Retreives the passed arguments to the decryption function. We are only interested in the key and offset to the encrypted string. addr: (int) Address at which the decryption function was called. Returns: key: (int) The key used to decrypt the string. enc_str: (list) Byte array of encrypted string. ins_addr: (int) Address at which the encrypted byte array is referenced. """ found = False foundstr = False foundkey = False while not found: addr = idc.PrevHead(addr) if idc.GetMnem(addr) == "mov" and "r8d" in idc.GetOpnd(addr,0): #print "[+] Found key: 0x%08x at 0x%016x" % (idc.GetOperandValue(addr,1)& 0xffffffff, addr) key = idc.GetOperandValue(addr,1) & 0xffffffff foundkey = True if idc.GetMnem(addr) == "lea" and "rdx" in idc.GetOpnd(addr,0): #print "[+] Found str: 0x%016x at 0x%016x" % (idc.GetOperandValue(addr,1), addr) enc_str_addr = idc.GetOperandValue(addr,1) enc_str = get_encoded_string(enc_str_addr) ins_addr = addr foundstr = True if foundkey and foundstr: found = True return key, enc_str, ins_addr
def revise_syscall(rename=False): if not rename: print( 'Change the function name with `CGCHeler.revise_syscall(True)`.' ) # visit all instructions start_ea, end_ea = utils.get_seg_range('.text') eax = -1 ip = start_ea while ip < end_ea and ip != idaapi.BADADDR: if 'int' in idc.GetMnem(ip) and '80h' == idc.GetOpnd(ip, 0): if eax != -1: # fix comment and function name print('{}: {}'.format(hex(ip), syscall_table[eax])) idc.MakeComm(ip, 'CGC syscall: {}'.format(syscall_table[eax])) if rename: print('Change {} to {}'.format(idc.GetFunctionName(ip), syscall_table[eax])) idc.MakeName( idc.GetFunctionAttr(ip, idc.FUNCATTR_START), syscall_table[eax]) elif 'mov' in idc.GetMnem(ip) and 'eax' == idc.GetOpnd( ip, 0) and 5 == idc.GetOpType(ip, 1): value = idc.GetOpnd(ip, 1) if re.search('^[0-9]+$', value) != None: eax = int(value) if eax > 7 or eax < 1: eax = -1 ip = idc.NextHead(ip)
def find_vm_codes(dispatcher_ea): mnem = idc.GetMnem(dispatcher_ea) if mnem != "pusha": print "dispatcher_ea: 0x%08x, bad mnem: %s" (dispatcher_ea, mnem) assert (False) refs = idautils.CodeRefsTo(dispatcher_ea, 1) refs = list(x for x in refs) print "vms found:", len(refs) for ref in refs: print hex(ref) vms = [] for ref in refs: #push offset #jmp dispatcher push_ea = prev_head(ref) mnem = idc.GetMnem(push_ea) if mnem != "push": print "push_ea:", hex(push_ea) print "unexpected mnem:", mnem assert (False) op = idc.GetOpnd(push_ea, 0) op = str2int(op) vms.append((push_ea, op)) return vms
def add_bp_to_virtual_calls(cur_addr, end): while cur_addr < end: if cur_addr == idc.BADADDR: break elif idc.GetMnem(cur_addr) == 'call' or idc.GetMnem(cur_addr) == 'BLR': if True in [idc.GetOpnd(cur_addr, 0).find(reg) != -1 for reg in REGISTERS]: # idc.GetOpnd(cur_addr, 0) in REGISTERS: cond, bp_address = vtableAddress.write_vtable2file(cur_addr) if cond != '': bp_vtable = AddBP.add(bp_address, cond) cur_addr = idc.NextHead(cur_addr)
def add_xrefs(addr, end=idc.BADADDR): """ https://github.com/xyzz/vita-ida-physdump/blob/master/vita_phys_dump.py Searches for MOV / MOVT pair, probably separated by few instructions, and adds xrefs to things that look like addresses """ while addr < end and addr != BADADDR: addr = idc.NextHead(addr) if idc.GetMnem(addr) in ["MOV", "MOVW"]: reg = idc.GetOpnd(addr, 0) if idc.GetOpnd(addr, 1)[0] != "#": continue val = idc.GetOperandValue(addr, 1) found = False next_addr = addr for x in range(16): next_addr = idc.NextHead(next_addr) if idc.GetMnem(next_addr) in ["B", "BX"]: break # TODO: we could handle a lot more situations if we follow branches, but it's getting complicated # if there's a function call and our register is scratch, it will probably get corrupted, bail out if idc.GetMnem(next_addr) in ["BL", "BLX"] and reg in [ "R0", "R1", "R2", "R3" ]: break # if we see a MOVT, do the match! if idc.GetMnem(next_addr) in ["MOVT", "MOVT.W"] and idc.GetOpnd( next_addr, 0) == reg: if idc.GetOpnd(next_addr, 1)[0] == "#": found = True val += idc.GetOperandValue(next_addr, 1) * (2**16) break # if we see something other than MOVT doing something to the register, bail out if idc.GetOpnd(next_addr, 0) == reg or idc.GetOpnd( next_addr, 1) == reg: break if val & 0xFFFF0000 == 0: continue if found: # pair of MOV/MOVT try: idc.OpOffEx(addr, 1, idc.REF_LOW16, val, 0, 0) idc.OpOffEx(next_addr, 1, idc.REF_HIGH16, val, 0, 0) except: print "Failed xref @ %x next_addr %x val %x" % ( addr, next_addr, val) else: # a single MOV instruction try: idc.OpOff(addr, 1, 0) except: print "Failed xref at addr %x" % (addr)
def as_ret_or_arg(self): """ as ret value :return: """ for xref in idautils.XrefsTo(self.class_ref): if idc.SegName(xref.frm) == '__text': if idc.GetMnem(xref.frm) == 'ADRP': pass elif 'LDR' in idc.GetMnem(xref.frm): # ctx = CTX(xref.frm, PT(idc.GetOpnd(xref.frm, 0), xref.frm)) # if ctx.find_call(rec='x8', sel='alloc'): self.add_occurrences(xref.frm, 'ret_or_arg')
def as_ivar(self): """ When object was referenced as ivar, whether get or set, load or store, the object must exist in this context. :return: """ if self.class_name in self.bin_data['ivars2']: for ivar in self.bin_data['ivars2'][self.class_name]: for xref in idautils.XrefsTo(ivar): if idc.SegName(xref.frm) == '__text': if idc.GetMnem(xref.frm) == 'ADRP': pass elif 'LDR' in idc.GetMnem(xref.frm): # PT(idc.GetOpnd(xref.frm, 0), xref.frm) # ctx = CTX(xref.frm, PT(idc.GetOpnd(xref.frm, 0), xref.frm)) self.add_occurrences(xref.frm, 'ivar')
def get_con2_var_or_num(i_cnt, cur_addr): """ :param i_cnt: the register of the virtual call :param cur_addr: the current address in the memory :return: "success" string and the address of the vtable's location. if it fails it sends the reason and -1 """ start_addr = idc.GetFunctionAttr(cur_addr, idc.FUNCATTR_START) virt_call_addr = cur_addr cur_addr = idc.PrevHead(cur_addr) dct_arch = get_arch_dct() if dct_arch == -1: return 'Wrong Architechture', "-1", cur_addr while cur_addr >= start_addr: if idc.GetMnem(cur_addr)[:3] == dct_arch["opcode"] and idc.GetOpnd( cur_addr, 0) == i_cnt: # TODO lea ? opnd2 = idc.GetOpnd(cur_addr, 1) place = opnd2.find(dct_arch["separator"]) if place != -1: # if the function is not the first in the vtable register = opnd2[opnd2.find('[') + 1:place] if opnd2.find('*') == -1: offset = opnd2[place + dct_arch["val_offset"]:opnd2.find(']')] else: offset = "*" return register, offset, cur_addr else: offset = "0" if opnd2.find(']') != -1: register = opnd2[opnd2.find('[') + 1:opnd2.find(']')] else: register = opnd2 return register, offset, cur_addr elif idc.GetMnem(cur_addr)[:4] == "call": intr_func_name = idc.GetOpnd(cur_addr, 0) # In case the code has CFG -> ignores the function call before the virtual calls if "guard_check_icall_fptr" not in intr_func_name: if "nullsub" not in intr_func_name: # intr_func_name = idc.Demangle(intr_func_name, idc.GetLongPrm(idc.INF_SHORT_DN)) print( "Warning! At address 0x%08x: The vtable assignment might be in another function (Maybe %s)," " could not place BP." % (virt_call_addr, intr_func_name)) cur_addr = start_addr cur_addr = idc.PrevHead(cur_addr) return "out of the function", "-1", cur_addr return '', 0, cur_addr
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 _does_instruction_match(self, ea, instruction, regex=False): i = 0 op_cnt = 0 op_ok_cnt = 0 match = False ins_size = idaapi.decode_insn(ea) mnem = idc.GetMnem(ea) if (not instruction.mnem) or (instruction.mnem == mnem) or ( regex and re.match(instruction.mnem, mnem)): for operand in instruction.operands: if operand: op_cnt += 1 op = idc.GetOpnd(ea, i) if regex: if re.match(operand, op): op_ok_cnt += 1 elif operand == op: op_ok_cnt += 1 i += 1 if op_cnt == op_ok_cnt: match = True return match
def DelAllConditionBpt(): ea = 0x2C13000 while ea < 0x357D000: mnem = idc.GetMnem(ea) if mnem == 'jmp' or mnem == 'retn': idc.DelBpt(ea) ea = idc.NextHead(ea)
def PrintMsrTable(self, msr_code, function_ea, inst_ea): mnemonic = idc.GetMnem(inst_ea) call_addr = self.GetJumpAddr(inst_ea, function_ea) function_name = idc.GetFunctionName(function_ea) + '+' + call_addr dwSize = 30 - len(function_name) delimeter = " " * dwSize if (msr_code == None): msr_code_hex = 'Not imm value' else: msr_code_hex = self.NormalizeHexValue(msr_code) if (msr_code == None): msr_name = msr_code_hex else: msr_name = msr_list.get(int(msr_code_hex, 16)) idc.MakeComm(inst_ea, '{}({})'.format(mnemonic, msr_name)) idc.SetColor(inst_ea, idc.CIC_ITEM, 0xf8abef) msr_name_delimeter = (" " * (15 - len(msr_code_hex))) print '{}{}| {} | {} {} | {}'.format(function_name, delimeter, mnemonic, msr_code_hex, msr_name_delimeter, msr_name)
def parse_register_value(register, start_addr, end_addr): print "start find register: %s from 0x%x to 0x%x" % (register, start_addr, end_addr) _addr = start_addr while _addr > end_addr: _addr = idc.PrevHead(_addr) _opnd_first = idc.GetOpnd(_addr, 0) if _opnd_first == register: _op_code = idc.GetMnem(_addr) second_opnd_type = idc.GetOpType(_addr, 1) print 'find register( 0x%x ) -> %s, type1: %d, type2: %d' % ( _addr, idc.GetDisasm(_addr), second_opnd_type, idc.GetOpType(_addr, 2)) if _op_code.startswith('LDR'): return parse_opnd_value(_addr, end_addr, 1) elif _op_code.startswith('ADD'): if (idc.GetOpType(_addr, 2) == 0): if idc.GetOpnd(_addr, 1) == 'PC': # todo: check last _op => LDR R6, =(_GLOBAL_OFFSET_TABLE_ - 0xAEB6) return 0x00021DB4 else: return parse_register_value( register, _addr, end_addr) + parse_opnd_value( _addr, end_addr, 1) else: return parse_opnd_value(_addr, end_addr, 1) + parse_opnd_value( _addr, end_addr, 2) elif _op_code.startswith('MOV'): return parse_register_value(idc.GetOpnd(_addr, 1), _addr, end_addr)
def _find_system_calls(self, start_ea, end_ea): system_calls = [] system_load = MIPSInstruction("la", "$t9", "system") stack_arg_zero = MIPSInstruction("addiu", "$a0", "$sp") for xref in idautils.XrefsTo(idc.LocByName('system')): ea = xref.frm if ea >= start_ea and ea <= end_ea and idc.GetMnem(ea)[0] in [ 'j', 'b' ]: a0_ea = self._find_next_instruction_ea(ea + self.INSIZE, stack_arg_zero, ea + self.INSIZE) if a0_ea == idc.BADADDR: a0_ea = self._find_prev_instruction_ea( ea, stack_arg_zero, ea - (self.SEARCH_DEPTH * self.INSIZE)) if a0_ea != idc.BADADDR: control_ea = self._find_prev_instruction_ea( ea - self.INSIZE, system_load, ea - (self.SEARCH_DEPTH * self.INSIZE)) if control_ea != idc.BADADDR: system_calls.append( ROPGadget(self._get_instruction(control_ea), self._get_instruction(ea), self._get_instruction(a0_ea), description="System call", base=self.base)) ea += self.INSIZE else: break return system_calls
def find_interesting_xors(self): next_xor = idc.FindText(idc.MinEA(), idc.SEARCH_DOWN | idc.SEARCH_NEXT, 0, 0, "xor") while next_xor != idc.BADADDR: if idc.GetOpnd(next_xor, 0) != idc.GetOpnd(next_xor, 1): entry = { "func": "", "addr": next_xor, "loop": False, "disasm": idc.GetDisasm(next_xor) } func = idaapi.get_func(next_xor) if func: entry["func"] = idaapi.get_name(idc.BADADDR, func.startEA) heads = idautils.Heads(next_xor, func.endEA) lxors = [] for head in heads: if idc.GetMnem(head).startswith('j'): jmp_addr = idc.GetOperandValue(head, 0) if jmp_addr < next_xor and jmp_addr > func.startEA: entry["loop"] = True break self._interesting_xors.append(entry) next_xor = idc.FindText(idc.NextHead(next_xor), idc.SEARCH_DOWN | idc.SEARCH_NEXT, 0, 0, "xor")
def find_value(self, func_addr, instr_addr, register): """ Attempts to resolve the value of the given register at the given address. If the value cannot be resolved, None is returned. """ reg_num = idaapi.ph_get_regnames().index(register) # go back from the current instruction to the start of the function for instr_addr in list(instructions(func_addr, instr_addr))[::-1]: # look for instrucations that move a value into the desired register mnemonic = idc.GetMnem(instr_addr) if mnemonic == 'mov': op1_type = idc.get_operand_type(instr_addr, 0) op1_value = idc.get_operand_value(instr_addr, 0) if op1_type == idc.o_reg and op1_value == reg_num: op2_type = idc.get_operand_type(instr_addr, 1) op2_value = idc.get_operand_value(instr_addr, 1) # if this instruction sets the register to an immediate value if op2_type == idc.o_imm: # return that value return op2_value else: # it is not an immediate value, so we say we cannot # resolve the value return None # we did not find an allocation of the register, # so we return None to indicate that return None
def decide(self, func_addr): for instr_addr in func_instructions(func_addr): mnemonic = idc.GetMnem(instr_addr) if self.is_x64_syscall(mnemonic) or self.is_x86_syscall( mnemonic, instr_addr): self.log('0x{:x} has a syscall instruction at 0x{:x}'.format( func_addr, instr_addr)) num = self.find_value(func_addr, instr_addr, 'ax') elif self.is_libc_syscall(mnemonic, instr_addr): self.log('0x{:x} has a libc syscall call at 0x{:x}'.format( func_addr, instr_addr)) num = self.find_value(func_addr, instr_addr, 'di') else: continue # if the syscall number could not be determined or it is # one of those we are looking for, return True if num is None: self.log( '0x{:x} has a syscall at 0x{:x} but we could not find the number so we include it' .format(func_addr, instr_addr)) return True elif num in self.syscalls: self.log( '0x{:x} has a syscall at 0x{:x} that we are looking for ({})' .format(func_addr, instr_addr, num)) return True else: self.log( '0x{:x} has a syscall at 0x{:x} but it is NOT what are looking for ({})' .format(func_addr, instr_addr, num)) return False
def ext_instruction(file_name, addr_start, addr_end): name_fun = GetFunctionName(addr_start) row = '' for addr in Heads(addr_start, addr_end): ins = '' thisOperand = idc.GetMnem(addr) oPtype1 = idc.GetOpType(addr, 0) oPtype2 = idc.GetOpType(addr, 1) # assemblydata = parametertype(oPtype1)+' '+parametertype(oPtype2) if (oPtype1 == 1 or oPtype1 == 4): oPtype1 = idc.GetOpnd(addr, 0) if (oPtype2 == 1 or oPtype2 == 4): oPtype2 = idc.GetOpnd(addr, 1) if thisOperand == "call": call_fun_name = GetOpnd(addr, 0) keyInstr = LocByName(call_fun_name) fflags = idc.get_func_flags(keyInstr) if (fflags & idc.FUNC_LIB) or (fflags & idc.FUNC_THUNK): ins = thisOperand + '_' + idc.GetOpnd(addr, 0) + '_0' row = row + ' ' + ins continue ins = str(thisOperand)+'_'+tran(str(oPtype1)) + \ '_'+tran(str(oPtype2)) row = row + ' ' + ins return row
def SetAllConditionBpt(): ea = 0x2C13000 while ea < 0x357D000: mnem = idc.GetMnem(ea) if mnem == 'retn': idc.add_bpt(ea) ea = idc.NextHead(ea)
def norm_func1(head): inst = '' + idc.GetMnem(head) num_operands = op_count(head) for i in range(num_operands): type_op = idc.GetOpType(head, i) if type_op == idc.o_void: break elif type_op == idc.o_mem: inst += ' [MEM]' elif type_op == idc.o_imm: val = idc.GetOperandValue(head, i) #if -int(5000) <= val <= int(5000): if -int(500) <= val <= int(500): inst += ' ' + str(hex(val)) else: inst += ' HIMM' else: inst += ' ' + idc.GetOpnd(head, i) if num_operands > 1: inst += ',' if ',' in inst: inst = inst[:-1] inst = inst.replace(' ', '_') return str(inst)
def jump_branches(self, ea): """ if this instruction is a jump, yield the destination(s) of the jump, of which there may be more than one. only literal destinations (i.e. addresses without dereferences) are yielded. """ mnem = idc.GetMnem(ea) insn = idautils.DecodeInstruction(ea) if mnem in self.unconditional_jumps: if insn.Op1.type == idaapi.o_near: if insn.Op1.addr > self.signed_limit: dest = -((self.max_int + 1) - op.addr) else: dest = insn.Op1.addr yield dest elif mnem in self.conditional_jumps: dest = insn.Op1.addr yield dest dest = ea + insn.size yield dest return
def _split_basic_block(start_ea, end_ea): """ IDA Pro's ``idaapi.Flowchart`` does not consider function calls as basic block boundaries. This function takes an address range and splits the basic blocks found within that range so that function calls are considered basic block boundaries. """ split_bbs = [] func_name = idc.GetFunctionName(start_ea) demangled_name = idc.Demangle(func_name, idc.GetLongPrm(idc.INF_SHORT_DN)) if demangled_name: func_name = demangled_name bb_start_addr = start_ea block = idautils.Heads(start_ea, end_ea) for inst in block: mnem = idc.GetMnem(inst) if mnem == 'call' and inst != end_ea: split_bbs.append( dict(start_addr=bb_start_addr, end_addr=idc.NextHead(inst, end_ea + 1) - 1, function=func_name)) bb_start_addr = idc.NextHead(inst, end_ea + 1) if bb_start_addr < end_ea: split_bbs.append( dict(start_addr=bb_start_addr, end_addr=end_ea - 1, function=func_name)) return split_bbs
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 run(self): self.logger.info('Starting up') try: here = idc.here() self.logger.info('Using ea: 0x%08x', here) if not idc.GetMnem(here).startswith('call'): self.logger.info( 'Not running at a call instruction. Bailing out now') return if idc.GetOpType(here, 0) == idc.o_near: self.logger.info( "Cannot (or shouldn't) run when call optype is o_near") return dlg = ApplyCalleeTypeWidget() oldTo = idaapi.set_script_timeout(0) res = dlg.exec_() idaapi.set_script_timeout(oldTo) if res == QtWidgets.QDialog.Accepted: self.logger.debug('Dialog accepted. Input type: %d', dlg.inputType) else: self.logger.debug('Dialog rejected') return tinfo = None #check user input type if dlg.inputType == dlg.USER_TYPE: decl = self.convertUserType(str(dlg.getUserText())) tinfo = self.getUserDeclType(decl) elif dlg.inputType == dlg.STANDARD_TYPE: tinfo = self.getBuiltinGlobalType() elif dlg.inputType == dlg.LOCAL_TYPE: tinfo = self.getLocalType() else: self.logger.info('Bad user input type') return if tinfo is None: self.logger.debug('Bailing due to null tinfo') return #self.logger.info('Deserialize result: %r', ret) #not 100% sure if i need to explicitly convert from func to funcptr - seemed # to pretty much work without this, but doing it just to be sure if not tinfo.is_funcptr(): self.logger.debug('Converting to func pointer') tinfo.create_ptr(tinfo) typename = idaapi.print_tinfo('', 0, 0, idaapi.PRTYPE_1LINE, tinfo, '', '') self.logger.info('Applying tinfo: "%s"', str(typename)) #both applying callee type & setting op type -> not sure if both are needed? # set op type causes change in hexrays decompilation # apply callee type updates ida's stack analysis ret = idaapi.apply_callee_tinfo(here, tinfo) ret = idaapi.set_op_tinfo2(here, 0, tinfo) self.logger.debug('set_op_tinfo2 result: %r', ret) except Exception, err: self.logger.exception("Exception caught: %s", str(err))