def renameDword(self): proc_addr = self._import_table.item(self._import_table.currentRow(), 3).text() proc_name = str(self._import_table.item(self._import_table.currentRow(), 2).text()) renamed = 0 if proc_addr: try: proc_addr = int(proc_addr, 16) proc_bin_str = " ".join([x.encode("hex") for x in struct.pack("<I", proc_addr)]) next_dword = idc.FindBinary(idc.MinEA(), idc.SEARCH_DOWN|idc.SEARCH_NEXT, proc_bin_str) while next_dword != idc.BADADDR: log.debug("Trying to fix-up 0x{:08x}".format(next_dword)) # DWORDs can be "inaccessible" for many reasons and it requires "breaking up" the data blobs # and manually fixing them # Reason 1: In a dword array in an unknown section if idc.isUnknown(next_dword): idc.MakeUnkn(next_dword, idc.DOUNK_EXPAND) idc.MakeDword(next_dword) # Reason 2: In a dword array in a data section elif idc.isData(next_dword): hd = idc.ItemHead(next_dword) idc.MakeDword(hd) idc.MakeDword(next_dword) # Reason 3: In a dword array in a code section (validate via "dd <dword>,") elif idc.isCode(next_dword) and idc.GetDisasm(next_dword).startswith("dd "): hd = idc.ItemHead(next_dword) idc.MakeDword(hd) idc.MakeDword(next_dword) # Only perform if idc.Name(next_dword).startswith(("off_", "dword_")) or idc.Name(next_dword) == "": success = idc.MakeNameEx(next_dword, proc_name, idc.SN_NOWARN|idc.SN_NON_AUTO) i = 0 new_proc_name = proc_name while not success and i < 10: new_proc_name = "{}{}".format(proc_name, i) success = idc.MakeNameEx(next_dword, new_proc_name, idc.SN_NOWARN|idc.SN_NON_AUTO) i += 1 if success: renamed += 1 item = self._import_table.item(self._import_table.currentRow(), 5) item.setText("{}, {}".format(str(item.text()), new_proc_name)) log.debug("DWORD @ 0x{:08x} now has name {}".format(next_dword, new_proc_name)) else: log.error("Unable to auto-rename successfully, terminating search") break else: log.debug("Value at 0x{:08x} does not meet renaming requirements".format(next_dword)) next_dword = idc.FindBinary(next_dword+4, idc.SEARCH_DOWN|idc.SEARCH_NEXT, proc_bin_str) except Exception, e: log.error("Error encountered: {}".format(e)) log.debug("Renamed {:d} instances of {}".format(renamed, proc_name))
def find_binary_instruction_start(search_start_location, search_direction, target, min_location=idc.MinEA(), max_location=idc.MaxEA()): """ 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.MinEA()) max_location - The maximum EA to accept results for (default: idc.MaxEA()) Output: Returns the first matching location if found, otherwise idc.BADADDR """ target = target.upper() while search_start_location < max_location: ea = idc.FindBinary(search_start_location, search_direction, target) if (min_location <= ea < max_location and ea == idc.ItemHead(ea) and idaapi.get_many_bytes(ea, idc.ItemSize(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 lines(start=None, end=None, reverse=False, max_steps=None): """ Iterates through instructions within the start address and end addresses. :param start: Address of the starting instruction. (starts at beginning if not defined) :param end: Address of the end instruction. :param reverse: Iterates up if true. :param max_steps: If set, iteration will stop after the given number of steps. :yields: instructions addresses """ max_ea = idaapi.cvar.inf.maxEA min_ea = idaapi.cvar.inf.minEA # Normalize start and end addresses. if reverse: if start is None: start = max_ea - 1 if end is None: end = 0 start = max(start, end) else: if start is None: start = min_ea if end is None: end = max_ea + 1 start = min(start, end) func = idc.PrevHead if reverse else idc.NextHead ea = idc.ItemHead(start) while ea != idc.BADADDR: yield ea ea = func(ea, end)
def ida_make_function(location): ''' Description: From the first non-function byte, attempt to make a function. Input: location - The EA at which IDA should attempt to make a function. Output: True if it succeeded, False otherwise. ''' function_start = location ea = location while not (idaapi.get_func(ea) or idc.isAlign(idc.GetFlags(ea))): function_start = ea ea = idc.PrevHead(ea) function_start = _un_nop(function_start, idc.NextHead) if idc.MakeFunction(function_start): last_mnem = idc.GetMnem( idc.ItemHead(idaapi.get_func(function_start).endEA - 1)) if 'ret' not in last_mnem and 'jmp' not in last_mnem: idc.DelFunction(function_start) append_debug( 'Created a function at 0x%X, but there wasn\'t a jmp or ret at the end.' % function_start) return False else: append_debug('Created a function 0x%X.' % function_start) return True else: return False
def ida_get_disasm(self, address, fixup=False): def GetMnem(asm): sp = asm.find(' ') if (sp == -1): return asm return asm[:sp] if self.check_address(address) != 1: # not a valid address return '' # return if address is in the middle of instruction / data if address != idc.ItemHead(address): return '' asm = self.asm_normalize(idc.GetDisasm(address)) # for now, only support IDA syntax fixup for Intel CPU if not fixup or self.arch != KS_ARCH_X86: return asm # KS_ARCH_X86 mode # rebuild disasm code from IDA i = 0 mnem = GetMnem(asm) if mnem == '' or mnem in ('rep', 'repne', 'repe'): return asm opers = [] while GetOpType(address, i) > 0 and i < 6: t = GetOpType(address, i) o = GetOpnd(address, i) if t in (idc.o_mem, o_displ): parts = list(o.partition(':')) if parts[2] == '': parts[2] = parts[0] parts[0] = '' if '[' not in parts[2]: parts[2] = '[{}]'.format(parts[2]) o = ''.join(parts) if 'ptr ' not in o: dtyp_name = self.get_op_dtype_name(i) if dtyp_name != None: o = "{} ptr {}".format(dtyp_name, o) opers.append(o) i += 1 asm = mnem for o in opers: if o != '': asm = "{} {},".format(asm, o) asm = asm.strip(',') return asm
def _yara_callback(data): ''' Description: Generic yara callback. Input: As defined by YARA. See YARA's documentation for more info. Output: A list of tuples: (offset, identifier) where offsets are always item heads ''' if not data['matches']: return False for datum in data['strings']: if FROM_FILE: _YARA_MATCHES.append( (idc.ItemHead(idaapi.get_fileregion_ea(datum[0])), datum[1])) else: _YARA_MATCHES.append( (idc.ItemHead(datum[0] + SECTION_START), datum[1])) return yara.CALLBACK_CONTINUE
def _convert_address_to_function(func): """Convert an address that IDA has classified incorrectly into a proper function.""" # If everything goes wrong, we'll try to restore this function. orig = idc.FirstFuncFchunk(func) # If the address is not code, let's undefine whatever it is. if not idc.isCode(idc.GetFlags(func)): if not is_mapped(func): # Well, that's awkward. return False item = idc.ItemHead(func) itemend = idc.ItemEnd(func) if item != idc.BADADDR: _log(1, 'Undefining item {:#x} - {:#x}', item, itemend) idc.MakeUnkn(item, idc.DOUNK_EXPAND) idc.MakeCode(func) # Give IDA a chance to analyze the new code or else we won't be able to create a # function. idc.Wait() idc.AnalyseArea(item, itemend) else: # Just try removing the chunk from its current function. IDA can add it to another function # automatically, so make sure it's removed from all functions by doing it in loop until it # fails. for i in range(1024): if not idc.RemoveFchunk(func, func): break # Now try making a function. if idc.MakeFunction(func) != 0: return True # This is a stubborn chunk. Try recording the list of chunks, deleting the original function, # creating the new function, then re-creating the original function. if orig != idc.BADADDR: chunks = list(idautils.Chunks(orig)) if idc.DelFunction(orig) != 0: # Ok, now let's create the new function, and recreate the original. if idc.MakeFunction(func) != 0: if idc.MakeFunction(orig) != 0: # Ok, so we created the functions! Now, if any of the original chunks are not # contained in a function, we'll abort and undo. if all(idaapi.get_func(start) for start, end in chunks): return True # Try to undo the damage. for start, _ in chunks: idc.DelFunction(start) # Everything we've tried so far has failed. If there was originally a function, try to restore # it. if orig != idc.BADADDR: _log(0, 'Trying to restore original function {:#x}', orig) idc.MakeFunction(orig) return False
def item_off(ea): '''ea_t -> (int, int)''' head = idc.ItemHead(ea) end = idc.ItemEnd(head) ti = get_or_guess_tinfo(head) try: size = array_elem_size(ti) except NotArrayError: size = ti.get_size() diff = ea - head idx = diff / size rem = diff % size return (idx, rem)
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 startEA of the function to create function_end - The endEA 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.MakeFunction(function_start, function_end): append_debug('Created a function 0x%X - 0x%X.' % (function_start, function_end)) if require_term: last_mnem_ea = idc.ItemHead(idaapi.get_func(function_start).endEA - 1) last_mnem = idc.GetMnem(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 idaapi.get_many_bytes(last_mnem_ea, idc.ItemSize(last_mnem_ea)).encode('hex').upper() != end_mnem_bytes.upper()): idc.DelFunction(function_start) append_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).endEA: idc.AnalyzeArea(function_start, idaapi.get_func(function_start).endEA) return function_start, function_end else: idc.DelFunction(function_start) append_debug( 'Deleted function at 0x%X - the function didn\'t contain the target location.' % function_start) return else: append_debug( 'Tried to create a function 0x%X - 0x%X, but IDA wouldn\'t do it.' % (function_start, function_end)) else: append_debug('The end address was not greater than the start address!')
def _yara_callback(data): """ Description: Generic yara callback. Input: As defined by YARA. See YARA's documentation for more info. Output: A list of tuples: (offset, identifier) """ if not data['matches']: return False for datum in data['strings']: _YARA_MATCHES.append((idc.ItemHead(idaapi.get_fileregion_ea(datum[0])), datum[1])) return yara.CALLBACK_CONTINUE
def ida_get_item(self, address, hex_output=False): if self.check_address(address) != 1: # not a valid address return (None, 0) # return None if address is in the middle of instruction / data if address != idc.ItemHead(address): return (None, 0) len = idc.ItemSize(address) item = idaapi.get_many_bytes(address, len) if item is None: return (None, 0) if hex_output: item = to_hexstr(item) return (item, len)
def is_block_or_instruction_head(ea): """Returns `True` if `ea` looks like it's the beginning of an actual instruction.""" return is_internal_code(ea) and idc.ItemHead(ea) == ea
def resolve_opnd(target, val): '''ea_t -> str | int -> op_ret''' # XXX probably wrong head = idc.ItemHead(target) name = idc.Name(head) ti = get_or_guess_tinfo(head) return op_ret_for_ti(ti, name, target, val)
def name_of_head(ea): '''ea -> str''' return idc.Name(idc.ItemHead(ea))