def getSize(self, withPool=False): """ Computes the size of the function the first time this is called, and caches that computation for later Parsed Comment commands: <endpool> specifies the last element in the pool. That element's size is included in the pool. to specify a function has no pool at all, put the comment command at its last instruction. :param withPool: (bool) somewhat of a heuristic. Computes the pool size as simply the amount of bytes since the function's code portion finished (endEA) until a new code head is detected :return: Returns the size of the Function in bytes: EndEA - StartEA (if no pool selected, otherwise + pool) """ if not withPool: return self.func.end_ea - self.func.start_ea head = self.func.end_ea # check if the function is set to have no pool instSize = self.isThumb() and 2 or 4 endCmt = idc.Comment(self.func.end_ea - instSize) if endCmt and '<endpool>' in endCmt: return self.func.end_ea - self.func.start_ea while not idc.isCode(idc.GetFlags(head)): # manual pool computation, trust and assume that this is the last element in the pool! if idc.Comment(head) and '<endpool>' in idc.Comment(head): head += idc.get_item_size(head) break # advance to next data element head += idc.get_item_size(head) return head - self.func.start_ea
def extract_info_from_IDA(self): prots = ida_bytes.get_qword(self.ea + 0x10) if prots: count = ida_bytes.get_qword(prots) entrysize = 0x8 p_ea = prots + 8 for i in range(count): proto_ea = idc.get_qword(p_ea) self.prots.append(proto_ea) p_ea += entrysize type_info = ida_bytes.get_qword(self.ea + 0x48) for idx in range(0, 4): # 0: inst_meths # 1: class_meths # 2: opt_inst_meths # 3: opt_class_meths meth_list = ida_bytes.get_qword(self.ea + 0x18 + idx * 8) if meth_list: entrysize = ida_bytes.get_dword(meth_list) count = ida_bytes.get_dword(meth_list + 4) ea = meth_list + 8 for i in range(0, count): sel = idc.get_bytes(idc.Qword(ea), idc.get_item_size(idc.Qword(ea)) - 1) meth_type = idc.get_bytes(idc.Qword(type_info), idc.get_item_size(idc.Qword(type_info)) - 1) self.meths[sel] = meth_type ea += entrysize type_info += 8
def lazy_read_selection(): # HTC - Ignore the byte at end address sel, start, end = idaapi.read_range_selection(None) if not sel: if idc.get_item_size(idc.get_screen_ea()): start = idc.get_screen_ea() end = start + idc.get_item_size(start) sel = True if not sel: start = idaapi.BADADDR end = idaapi.BADADDR else: rFrm = RangeForm(start, end) rFrm.Compile() ok = rFrm.Execute() if ok == 1: # OK start = rFrm.intStartEA.value end = start + rFrm.intSize.value else: # Cancel sel = False rFrm.Free() # Ensure we have at least one byte if end <= start: sel = False return sel, start, end
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 lazy_read_selection(): # HTC - Ignore the byte at end address sel, start, end = idaapi.read_range_selection(None) if not sel: if idc.get_item_size(idc.get_screen_ea()): start = idc.get_screen_ea() end = start + idc.get_item_size(start) sel = True if not sel: start = idaapi.BADADDR end = idaapi.BADADDR return sel, start, end
def _getDataDisasm(self): """ You cannot get array data using getdisasm. The disassembly has to be extracted differently. This identifies the data in question, and gets its disassembly :return: the disasssembly of the data item """ # First, do the easy cases that just work with GetDisasm flags = idc.GetFlags(self.ea) # TODO: change this so it accounts for arrays also if idc.is_enum0(flags): return self._filterComments(idc.GetDisasm(self.ea)) if idc.is_data(flags) and ( idc.is_byte(flags) and idc.get_item_size(self.ea) == 1 or idc.is_word(flags) and idc.get_item_size(self.ea) == 2 or idc.is_dword(flags) and idc.get_item_size(self.ea) == 4): # normal case where an int is not misread as a reference content = self.getContent() if self.getXRefsFrom()[1] and self.isPointer(content): disasm = idc.GetDisasm(self.ea) contentData = Data(content) # If it's a struct member, replace it with its hex, but keep the information if '.' in disasm and (';' not in disasm or '.' in disasm[:disasm.index(';')]): disasm = 'DCD %s+0x%X // %s' % (contentData.getName(), content - contentData.ea, disasm[len('DCD '):]) elif ida_bytes.is_manual(flags, 0): # Manual forms put in IDA, just grab it. (This is for cases where computations are applied to data) disasm = idc.GetDisasm(self.ea) else: # build the disassembly: this is for none-pointer symbols found in IDA (ex: word_0) if idc.is_byte(flags): op = 'DCB' elif idc.is_word(flags): op = 'DCW' else: op = 'DCD' disasm = op + ' ' + '0x%X' % content return self._filterComments(disasm) else: # The weird case... an array. I don't know why it's weird. IDA doesn't like it! # It is assumed this is an array, but the type is unknown. Imply type based on disasm of first line! # analysis on the array is based on the very first line disasm = idc.GetDisasm(self.ea) if ';' in disasm: disasm = disasm[:disasm.index(';')] firstLineSplitDisasm = list(filter(None, re.split('[ ,]', disasm))) dataType = firstLineSplitDisasm[0] return self._getArrDisasm(len(firstLineSplitDisasm) - 1, dataType)
def findMostUsedLabels(start_ea, end_ea, count, notModified=False, disp=True): # type: (int, int, int, bool, bool) -> list[int] """ Scans through all labels in the given range and counts the ones with the highest amount of references :param start_ea: start of the range :param end_ea: end of the range :param count: :param notModified: :param disp: :return: """ xrefs = [] if count <= 0: count = 1 for i in range(count): xrefs.append((0, 0)) ea = start_ea while ea < end_ea: if not idc.get_name(ea): ea += idc.get_item_size(ea) continue if notModified: name = Data.Data(ea).getName() if not ('_' in name and name[name.rindex('_'):] == ('_%X' % ea)): continue currXrefs = Data.Data(ea).getXRefsTo() numXrefs = len(currXrefs[0]) + len(currXrefs[1]) # add if more than least in the list and sort if numXrefs > xrefs[0][1]: xrefs[0] = (ea, numXrefs) xrefs = sorted(xrefs, key=lambda tup: tup[1]) ea += idc.get_item_size(ea) # reverse to display most common first xrefs = sorted(xrefs, key=lambda tup: tup[1], reverse=True) if disp: for ea, xrefCount in xrefs: print('%07x <%s>: %d' % (ea, Data.Data(ea).getName(), xrefCount)) output = [] for ea, xrefCount in xrefs: output.append(ea) return output
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) ida_bytes.create_data(ea, ida_bytes.FF_BYTE, 1, ida_idaapi.BADADDR) idc.make_array(ea, size)
def find_binary_instruction_start( search_start_location, search_direction, target, min_location=idc.get_inf_attr(idc.INF_MIN_EA), max_location=idc.get_inf_attr(idc.INF_MAX_EA)): """ Description: Given a starting location, target, and direction, find an instruction starting with the target bytes. Input: search_start_location - The EA to start searching at search_direction - either idc.SEARCH_UP or idc.SEARCH_DOWN target - The target as space separated bytes (i.e. '55' for 'push ebp') min_location - The minimum EA to accept results for (default: idc.get_inf_attr(idc.INF_MIN_EA)) max_location - The maximum EA to accept results for (default: idc.get_inf_attr(idc.INF_MAX_EA)) Output: Returns the first matching location if found, otherwise idc.BADADDR """ target = target.upper() while search_start_location < max_location: ea = idc.find_binary(search_start_location, search_direction, target) if (min_location <= ea < max_location and ea == idc.get_item_head(ea) and idc.get_bytes( ea, idc.get_item_size(ea)).encode('hex').upper().startswith( target.replace(' ', ''))): return ea else: search_start_location = ea + (1 if search_direction == idc.SEARCH_DOWN else -1) return idc.BADADDR
def do_unpatch_call(va_callsite): size = idc.get_item_size(va_callsite) ida_xref.del_cref(va_callsite, fva_stub, 0) cmt = idc.get_cmt(va_callsite, 0) newcmt = cmt # Remove automated comments if newcmt.startswith(g_patched_call_cmt): newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt.find('\n') == -1: newcmt = '' else: newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt.startswith(g_cmt_pointed): if newcmt.find('\n') == -1: newcmt = '' else: newcmt = newcmt[newcmt.find('\n') + 1:] if newcmt != cmt: idc.set_cmt(va_callsite, newcmt, 0) if idc.get_operand_type(va_callsite, 0) == ida_ua.o_mem: patch_import(va_callsite, idc.BADADDR) elif idc.get_operand_type(va_callsite, 0) == ida_ua.o_reg: va_imp = self._get_imp_for_register_call(va_callsite) if va_imp: patch_pointer_width(va_imp, idc.BADADDR) else: revert_patch(va_callsite, size)
def finish_populating_widget_popup(self, form, popup): form_type = idaapi.get_widget_type(form) if form_type == idaapi.BWN_DISASM or form_type == idaapi.BWN_DUMP: idaapi.attach_action_to_popup(form, popup, ACTION_PASTE, None) idaapi.attach_action_to_popup(form, popup, ACTION_DUMPER, None) idaapi.attach_action_to_popup(form, popup, ACTION_JMP, None) t0, t1, view = idaapi.twinpos_t(), idaapi.twinpos_t( ), idaapi.get_current_viewer() if idaapi.read_selection( view, t0, t1) or idc.get_item_size(idc.get_screen_ea()) > 1: idaapi.attach_action_to_popup(form, popup, ACTION_XORDATA, None) idaapi.attach_action_to_popup(form, popup, ACTION_FILLNOP, None) for action in ACTION_CONVERT: idaapi.attach_action_to_popup(form, popup, action, "Convert/") if form_type == idaapi.BWN_DISASM and (ARCH, BITS) in [ (idaapi.PLFM_386, 32), (idaapi.PLFM_386, 64), (idaapi.PLFM_ARM, 32), ]: idaapi.attach_action_to_popup(form, popup, ACTION_SCANVUL, None)
def set_jit_info(self, method_id, start): end = self.get_func_end(start) if (end < start or end - start > self.jit_max_size): return method = next((x for x in self.as3dump if x["id"] == method_id), None) if (method is None): return stackvars = self.get_stack_vars(start, end) save_eip = self.get_save_eip(method, stackvars) ea = start while (ea < end): if ("ebp" in idc.print_operand(ea, 0) and idc.get_operand_type(ea, 1) == idc.o_imm): op0 = idc.get_operand_value(ea, 0) op1 = idc.get_operand_value(ea, 1) if (op0 == save_eip): idc.set_cmt(ea, method["instructions"][op1], 0) ea += idc.get_item_size(ea)
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) idc.MakeByte(ea) idc.MakeArray(ea, size)
def get_data_symbols(): idata_seg_selector = idc.selector_by_name(".bss") idata_seg_startea = idc.get_segm_by_sel(idata_seg_selector) idata_seg_endea = idc.get_segm_end(idata_seg_startea) for seg_ea in range(idata_seg_startea, idata_seg_endea): if idc.get_name(seg_ea): address = format(seg_ea, 'x') name = ".global"+"_"+idc.get_name(seg_ea) size = idc.get_item_size(seg_ea) dtype = ida_bytes.get_data_elsize(seg_ea, idc.get_full_flags(seg_ea)) if size == dtype == 8: ownertype = "pointer" elif size == dtype != 8: ownertype = "scalar" elif size > dtype: ownertype = "array" else: continue for xref in idautils.XrefsTo(seg_ea): # print(format(xref.frm, 'x')) function = idc.get_func_name(xref.frm) # print(function) if function not in functions: continue # print(function) if str(function) not in instruction_map: instruction_map[str(function)] = {} instruction_map[str(function)][format(xref.frm, 'x')]=name,ownertype # print("Found a cross reference {}: from {:x} to variable {}".format(xref, xref.frm, name)) metadata[".global"].append({"owner":name,"datatype":ownertype, "address":address, "size":size})
def make_array(ea, size): if ea != idc.BADADDR and ea != 0: flags = idc.get_full_flags(ea) if not idc.isByte(flags) or idc.get_item_size(ea) != 1: idc.del_items(ea, idc.DOUNK_SIMPLE, 1) idc.MakeByte(ea) idc.MakeArray(ea, size)
def guessFuncSig(func_ea): # type: (int) -> (list[str], list[str]) """ Guesses the signature of the current function based on the input registers it uses (R0-R3) and based on the output registers it may return. (most likely R0, but sometimes multiple registers are returned) It also checks for whether the zero flag have been written to by the function without being used, then it would return the zero flag. :param func_ea: the linear address of the function to analyze :return: the list of types for the input parameters, and for the returns. 'zf' can be included, in the return types list. """ # input register parameters -- r0 through r3. If they are identified as an input, this will store # a string of their type. (States -- None: No param found yet. '': Parameter. But Unknown Type. paramTypes = [None, None, None, None] retType = None zf = False # flags updatedRegs = 0b0000 # if the register is updated, its flag is set # make sure to recognize push/pop like patterns. A register can be saved to be used later. savedRegs = 0b0000 # This flag is cleared whenever R0 is updated. # It is set whenever R0 is used. This indicated whether the return is used or not usedRet = False # the returns of calls are remembered since their type can match with the current function callRets = None func = Function.Function(func_ea) ea = func.func_ea while ea < func.func_ea + func.getSize(): insn = Instruction.Insn(ea) if insn.size != 0: # parse destination and source registers, if any if insn.ops[0].type == idaapi.o_reg: destReg = insn.ops[0].reg # now parse all source registers sourceRegisters = insn.getSourceRegisters() # if a destReg is R0~R3, set its flag raise (NotImplemented()) # update return status to know whether the register is used after being set at the end of the function # traverse function calls if parameters weren't identified yet if insn.ops[0].type in [idaapi.o_far, idaapi.o_near ] and None in paramTypes: callParams, callRets = guessFuncSig(insn.ops[0].addr) # deduce input parameters for this function from input parameters for the called function for i in range(len(callParams)): if not updatedRegs & i: # register is passed in as input to callee! Register it as an input of this function too! raise (NotImplemented()) # handle the push/pop register saving pattern if insn.itype == idaapi.NN_push: raise (NotImplemented()) if insn.itype == idaapi.NN_pop: raise (NotImplemented()) ea += insn.size else: ea += idc.get_item_size(ea)
def handle_test(ea, state): """ If a test of a register against itself occurs and the next instruction is a jnz, then the register can be set to zero (code is followed linearly, jumps are ignored), unless the next instruction is a jmp. :param ea: instruction location :param state: the current TraceState """ if idc.get_operand_type(ea, POS_FIRST) == idc.o_reg and idc.get_operand_type(ea, POS_SECOND) == idc.o_reg: op1 = get_opnd_replacement(ea, POS_FIRST) op2 = get_opnd_replacement(ea, POS_SECOND) next_ea = ea + idc.get_item_size(ea) if op1 == op2 and idc.print_insn_mnem(next_ea) == 'jnz': next_ea += idc.get_item_size(next_ea) if not idc.print_insn_mnem(next_ea).startswith('j'): state.set_reg_value(op1, 0, ea)
def getSize(self, withPool=False): """ Computes the size of the file the first time this is called, and caches that computation for later :param withPool: (bool) If end_ea is a function, compute its size including its pool if it has any :return: Returns the size of the file in bytes: EndEA + itemSize - StartEA """ if isFunction(self.end_ea): return self.end_ea + Function(self.end_ea).getSize(withPool=withPool) - self.start_ea return (self.end_ea + idc.get_item_size(self.end_ea) - self.start_ea)
def nop(): """Nops-out the current instruction and advance the cursor to the next instruction.""" ea = idaapi.get_screen_ea() num_bytes = idc.get_item_size(ea) for i in range(num_bytes): ida_bytes.patch_byte(ea, 0x90) ea += 1 ida_kernwin.refresh_idaview_anyway() ida_kernwin.jumpto(ea)
def __get_instruction_bytes_wildcarded(pattern, addr, instr_type, op1_type, op2_type): """Replaces bytes related to memory addresses with wildcards. TODO: To be replaced by ida_idp.ph_calcrel() Args: pattern: current buffer containing the bytes of the current instruction. addr: the address of the current instruction to be wildcarded instr_type: type of the current instruction op1_type: type of the first operand op2_type: type of the second operand Returns: String: hex-encoded representation of the bytes obtained at addr where all the operands that refers to memmory addresses are wildcarded. """ type_calls = frozenset( [idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni]) type_jumps = frozenset( [idaapi.NN_jmp, idaapi.NN_jmpfi, idaapi.NN_jmpni]) inst_prefix = binascii.hexlify(idc.get_bytes(addr, 1)).decode('utf-8') drefs = [x for x in idautils.DataRefsFrom(addr)] logging.debug('[VTGREP] Wildcarding: %s', idc.generate_disasm_line(addr, 0)) # Known 2 bytes opcodes if inst_prefix in ('0f', 'f2', 'f3'): pattern = binascii.hexlify(idc.get_bytes(addr, 2)).decode('utf-8') inst_num_bytes = 2 # CALLs or JUMPs using 2 bytes opcodes elif inst_prefix == 'ff' and (instr_type in type_jumps or instr_type in type_calls): pattern = binascii.hexlify(idc.get_bytes(addr, 2)).decode('utf-8') inst_num_bytes = 2 # A PUSH instruction using an inmediate value (mem offset) elif (inst_prefix == 'ff' and drefs and (op1_type == idaapi.o_imm or op2_type == idaapi.o_imm)): pattern = binascii.hexlify(idc.get_bytes(addr, 2)).decode('utf-8') inst_num_bytes = 2 # No prefix is used else: pattern = inst_prefix inst_num_bytes = 1 pattern += ' ' + '??' * (idc.get_item_size(addr) - inst_num_bytes) + ' ' return pattern
def find_function_ends_near(location, end_mnem_bytes=None): """ Description: Identifies the nearest possible function ends before the next function or Align for each end mnem. Input: location - The EA to search after end_mnem_bytes - Try to end functions on a particular instruction Instructions are entered as space separated bytes (i.e. 'C2' for 'retn') The specified pattern will be used first, then the defaults will be used If no pattern is specified, the defaults will be used, which prefers 'retn' Output: ends - A list of function end EAs sorted: end_mnem_bytes, retn, jmp """ # foreach target bytes: # step instructions down # if instruction matches the target bytes, add to output list # then move on to the next target bytes # if we hit a function or an align, quit # return ends in the order # end_nmem_bytes # retn # jmp # others, sorted ascending max_location = None ea = location while max_location is None: ea = idc.next_head(ea) if idaapi.get_func(ea) or idc.is_align(idc.get_full_flags(ea)): max_location = ea elif ea == idc.BADADDR: max_location = idaapi.getseg(location).end_ea max_location = min(max_location, idaapi.getseg(location).end_ea) targets = ['C2', 'C3', 'E9', 'EA', 'EB'] if end_mnem_bytes: targets.insert(0, end_mnem_bytes) ends = {} for target in targets: ea = find_binary_instruction_start(location, idc.SEARCH_DOWN, target, max_location=max_location) if ea <= max_location: ends[target] = ea return [ end + idc.get_item_size(end) for end in (([ ends.get(end_mnem_bytes, None), ends.get('C2', None), ends.get('C3', None) ]) + sorted(ends.get(target, None) for target in targets[-3:])) if end ]
def __init__(self, ea): # type: (int) -> None """ :param ea: effective address of the data item :raise InvalidDataException: if the current item is not data (isCode) """ # determine actual EA of the dataitem by analyzing its size. Going back in EA should increase the size # if we're in the middle of an array, or of the item. If it remains unchanged, or decreases, then we exited # the item. size = 0 ranLoop = False while idc.get_item_size(ea) > size: ranLoop = True size = idc.get_item_size(ea) ea -= 1 if ranLoop: # we just hit a failure condition, so the previous one is the correct one! ea += 1 self.ea = ea
def parse_vtable(ea, typename): os = get_os() if os == OS_Linux: ea += 8 funcs = [] while ea != idc.BADADDR: eatemp = ea offs = idc.get_wide_dword(ea) # if ida_bytes.is_unknown(ida_bytes.get_full_flags(ea)): # break size = idc.get_item_size( ea ) # This is bad abd abadbadbadbabdbabdad but there's no other choice here if size != 4: # This looks like it might be a bug with IDA # Random points of a vtable are getting turned into unknown data if size != 1: break s = "".join([ "%02x" % idc.get_wide_byte(ea + i) for i in range(3, -1, -1) ]) #.replace("0x", "") if not s.lower().startswith("ffff"): ea = ida_bytes.next_not_tail(ea) continue offs = int(s, 16) ea += 3 name = idc.get_name(offs, ida_name.GN_VISIBLE) if name: if os == OS_Linux: if not (name.startswith("_Z") or name.startswith("__cxa")) or name.startswith("_ZTV"): break # If we've exceeded past this vtable elif name.startswith("??"): break else: if os == OS_Win: break # dd -offsettothis # This is even worseworsoewewrosorooese s = "%02x" % offs if not s.lower().startswith("ffff"): ea = ida_bytes.next_not_tail(ea) continue name = (1 << 32) - int(offs) funcs.append(name) ea = ida_bytes.next_not_tail(ea) return funcs, eatemp
def try_make_function(function_start, function_end=idc.BADADDR, target_location=None, require_term=True, end_mnem_bytes=None): """ Description: Given a function location, attempt to create a function. If function creation fails, delete any partially created functions. If function creation succeeds, ensure all of the function's bytes are analyzed as code. Input: function_start - The start_ea of the function to create function_end - The end_ea of the function to create. IDA will calculate if not provided. target_location - If provided, fail function creation if it does not include this EA require_term - If provided, fail function creation if the last instruction is not a ret or jmp end_mnem_bytes - If provided, fail function creation if the last instruction is not the provided bytes Instructions are entered as space separated bytes (i.e. '55' for 'push ebp') Output: Returns a tuple (function_start, function_end) for the created function if successful, None otherwise """ if function_start <= function_end: if idc.add_func(function_start, function_end): logger.debug('Created a function 0x%X - 0x%X.' % (function_start, function_end)) if require_term: last_mnem_ea = idc.get_item_head( idaapi.get_func(function_start).end_ea - 1) last_mnem = idc.print_insn_mnem(last_mnem_ea) if (end_mnem_bytes is None and 'ret' not in last_mnem and 'jmp' not in last_mnem) or \ (end_mnem_bytes and idc.get_bytes(last_mnem_ea, idc.get_item_size(last_mnem_ea)).encode('hex').upper() != end_mnem_bytes.upper()): idc.del_func(function_start) logger.debug( 'Deleted function at 0x%X - the function didn\'t end with the correct mnem/bytes.' % function_start) return if target_location is not None: if function_start <= target_location < idaapi.get_func( function_start).end_ea: idc.plan_and_wait(function_start, idaapi.get_func(function_start).end_ea) return function_start, function_end else: idc.del_func(function_start) logger.debug( 'Deleted function at 0x%X - the function didn\'t contain the target location.' % function_start) return else: logger.debug( 'Tried to create a function 0x%X - 0x%X, but IDA wouldn\'t do it.' % (function_start, function_end)) else: logger.debug('The end address was not greater than the start address!')
def _emit_fnbytes(emit_instr_cb, header, footer, indent, fva=None, warn=True): """Emit function bytes in a format defined by the callback and headers/footers provided. Warns if any instruction operands are not consistent with position-independent code, in which case the user may need to templatize the position-dependent portions. """ fva = fva or idc.here() fva = idc.get_func_attr(fva, idc.FUNCATTR_START) va_end = idc.get_func_attr(fva, idc.FUNCATTR_END) # Operand types observed in position-independent code: optypes_position_independent = set([ ida_ua.o_reg, # 1: General Register (al,ax,es,ds...) ida_ua.o_phrase, # 3: Base + Index ida_ua.o_displ, # 4: Base + Index + Displacement ida_ua.o_imm, # 5: Immediate ida_ua.o_near, # 7: Immediate Near Address ]) # Notably missing because I want to note and handle these if/as they are # encountered: # ida_ua.o_idpspec0 = 8: FPP register # ida_ua.o_idpspec1 = 9: 386 control register # ida_ua.o_idpspec2 = 10: 386 debug register # ida_ua.o_idpspec3 = 11: 386 trace register va = fva nm = idc.get_name(fva) optypes_found = set() s = header.format(name=nm) while va not in (va_end, idc.BADADDR): size = idc.get_item_size(va) the_bytes = idc.get_bytes(va, size) for i in range(0, 8): optype = idc.get_operand_type(va, i) if optype: optypes_found.add(optype) s += indent + emit_instr_cb(va, the_bytes, size) va = idc.next_head(va) s += footer position_dependent = optypes_found - optypes_position_independent if position_dependent: msg = ('This code may have position-dependent operands (optype %s)' % (', '.join([str(o) for o in position_dependent]))) if warn: Warning(msg) else: logger.warn(msg) return s
def extract_info_from_IDA(self): for xref in idautils.XrefsTo(self.ea): frm = xref.frm if idc.SegName(frm) == '__objc_classrefs': self.classref = frm if idc.SegName(frm) == '__objc_superrefs': self.superref = frm base_ivars = ida_bytes.get_qword(self.info + 0x30) if base_ivars and idc.SegName(base_ivars) == '__objc_const': entrysize = ida_bytes.get_dword(base_ivars) count = ida_bytes.get_dword(base_ivars + 4) ea = base_ivars + 8 for i in range(count): offset = ida_bytes.get_dword(idc.get_qword(ea)) _type = idc.get_bytes(idc.Qword(ea + 0X10), idc.get_item_size(idc.Qword(ea + 0X10)) - 1) _name = idc.get_bytes(idc.Qword(ea + 0X08), idc.get_item_size(idc.Qword(ea + 0X08)) - 1) # self.ivars[offset] = _type self.ivars[_name] = _type ea += entrysize base_props = ida_bytes.get_qword(self.info + 0x40) if base_props and idc.SegName(base_props) == '__objc_const': entrysize = ida_bytes.get_dword(base_props) count = ida_bytes.get_dword(base_props + 4) ea = base_props + 8 for i in range(count): _type = idc.get_bytes(idc.Qword(ea + 0X08), idc.get_item_size(idc.Qword(ea + 0X08)) - 1) _name = idc.get_bytes(idc.Qword(ea), idc.get_item_size(idc.Qword(ea)) - 1) self.props[_name] = _type ea += entrysize base_prots = ida_bytes.get_qword(self.info + 0x28) if base_prots and idc.SegName(base_prots) == '__objc_const': count = ida_bytes.get_qword(base_prots) entrysize = 0x8 p_ea = base_prots + 8 for i in range(count): proto_ea = idc.get_qword(p_ea) self.prots.append(proto_ea) Protocol.add_implementing_class(proto_ea, self.ea) p_ea += entrysize
def get_relevant_infos(self, relevant): find_start = False infos = {} h = idautils.Heads(relevant.startEA, relevant.endEA) last = None for i in h: mnem = idc.GetMnem(i) if (mnem == "tst.w") or (mnem == "TST.W"): find_start = True infos["fix_start"] = (i + idc.get_item_size(i)) # infos["fix_start_inst_len"] = elif find_start == True and (mnem.startswith("IT ") or mnem.startswith("it ")): infos["fix_end"] = (i + idc.get_item_size(i) + idc.get_item_size(i + idc.get_item_size(i))) infos["conf_type"] = mnem[3:] find_start = False last = i infos["last"] = (last) return infos
def __init__(self, ea): self.ea = ea # ea: objc_data address self.info = ida_bytes.get_qword(ea + 0x20) self.superclass = ida_bytes.get_qword( ida_bytes.get_qword(ea) + 0x08) # idc.Name(self.superclass): _OBJC_METACLASS_$_UIView self.name = idc.get_bytes(idc.Qword(self.info + 0x18), idc.get_item_size(idc.Qword(self.info + 0x18)) - 1) self.classref = None self.superref = None self.prots = [] self.ivars = dict() self.props = dict()
def finish_populating_widget_popup(self, form, popup): form_type = idaapi.get_widget_type(form) if form_type == idaapi.BWN_DISASM or form_type == idaapi.BWN_DUMP: t0, t1, view = idaapi.twinpos_t(), idaapi.twinpos_t( ), idaapi.get_current_viewer() if idaapi.read_selection(view, t0, t1) \ or idc.get_item_size(idc.get_screen_ea()) > 1: idaapi.attach_action_to_popup(form, popup, GOLANG_FUNC, None) idaapi.attach_action_to_popup(form, popup, GOLANG_STRING, None) idaapi.attach_action_to_popup(form, popup, RENAME_POINTER, None)
def is_method_a_getter(self, sel, f=None): base_props = ida_bytes.get_qword(self.info + 0x40) if not base_props: return False entrysize = ida_bytes.get_dword(base_props) count = ida_bytes.get_dword(base_props + 4) p_ea = base_props + 8 for i in range(count): p_name = idc.get_bytes(idc.Qword(p_ea), idc.get_item_size(idc.Qword(p_ea)) - 1) if p_name == sel: return True p_ea += entrysize return False
def eFunc(self, address=None, retAddr=None, args=[]): if address == None: address = here() func = get_func(address) if retAddr == None: refs = [ref.frm for ref in XrefsTo(func.start_ea, 0)] if len(refs) != 0: retAddr = refs[0] + get_item_size(refs[0]) else: print("Please offer the return address.") return self._emulate(func.start_ea, retAddr, args) res = self.curUC.reg_read(self.REG_RES) return res
def get_func_end(self, start): if (idc.add_func(start)): return idc.find_func_end(start) ea = start while (idc.get_wide_byte(ea) != 0xCC): idc.create_insn(ea) ea += idc.get_item_size(ea) if (ea - start > self.jit_max_size): return 0 return ea
def _stop_looking_for_xrefs(ea): """This is a heuristic to decide whether or not we should stop looking for cross-references. It is relevant to IDA structs, where IDA will treat structs and everything in them as one single 'thing', and so all xrefs embedded within a struct will actually be associated with the first EA of the struct. So if we're in a struct or something like it, and the item size is bigger than the address size, then we will assume it's actually in a struct.""" if is_external_segment(ea): return False if is_code(ea): return False addr_size = get_address_size_in_bytes() item_size = idc.get_item_size(ea) return item_size > addr_size
def get_stack_vars(self, start, end): stackvars = {} ea = start while (ea < end): if ("ebp" in idc.print_operand(ea, 0) and idc.get_operand_type(ea, 1) == idc.o_imm): op0 = idc.get_operand_value(ea, 0) op1 = idc.get_operand_value(ea, 1) if (op0 in stackvars): stackvars[op0]["values"].append(op1) else: stackvars[op0] = {"values": [], "hits": 0} ea += idc.get_item_size(ea) return stackvars