def is_func_ptr(offset: int) -> bool: """Returns true if the given offset is a function pointer.""" # As a first check, simply see if the offset is the start of a function. func = ida_funcs.get_func(offset) if func and func.start_ea == offset: return True # Sometimes we will get a really strange issue where the IDA disassember has set a type for an # address that should not have been set during our course of emulation. # Therefore, before attempting to use get_function_data() to test if it's a function pointer, # first see if guess_type() will return None while is_loaded() is true. # If it doesn't, we know that it shouldn't be a function pointer. # (plus it saves on time) # TODO: Determine if we could have false negatives. # - this caused a false negatives, so I added the check if offset is the start of a function. try: if idc.is_loaded(offset) and not idc.guess_type(offset): return False except TypeError: return False try: get_function_data(offset) return True except RuntimeError: return False except Exception as e: # If we get any other type of exception raise a more friendly error message. raise FunctionTracingError( "Failed to retrieve function data from {!r}: {}".format(offset, e))
def _get_tif_with_guess_type(address: int) -> Optional[ida_typeinf.tinfo_t]: """ Attempt to get the tinfo_t object for the function using the "guess_type" function. :raises: RuntimeError on failure. :returns: tinfo_t object on success. """ guessed_type = idc.guess_type(address) if guessed_type is None: return None func_name = idc.get_func_name(address) if func_name is None: return None # Documentation states the type must be ';' terminated, also the function name must be inserted guessed_type = re.sub(r"\(", f" {func_name}(", f"{guessed_type};") set_type_result = idc.SetType(address, guessed_type) if not set_type_result: logger.warning( f"Failed to SetType for function at 0x{address:X} with guessed type {guessed_type!r}" ) tif = ida_typeinf.tinfo_t() if not ida_nalt.get_tinfo(tif, address): return None return tif
def _get_function_tif_with_guess_type(offset): """ Attempt to get the tinfo_t object of a function using the "guess_type" function. :param offset: Offset of function. :raises: RuntimeError on failure. :returns: tinfo_t object on success. """ tif = ida_typeinf.tinfo_t() guessed_type = idc.guess_type(offset) if guessed_type is None: raise RuntimeError( "Failed to guess function type for offset 0x{:X}".format(offset)) func_name = idc.get_func_name(offset) if func_name is None: raise RuntimeError( "Failed to get function name for offset 0x{:X}".format(offset)) # Documentation states the type must be ';' terminated, also the function name must be inserted guessed_type = re.sub(r"\(", " {}(".format(func_name), "{};".format(guessed_type)) set_type_result = idc.SetType(offset, guessed_type) if not set_type_result: logger.warning( "Failed to SetType for function at 0x{:X} with guessed type {!r}". format(offset, guessed_type)) # Try one more time to get the tinfo_t object if not ida_nalt.get_tinfo(tif, offset): raise RuntimeError( "Failed to obtain tinfo_t object for offset 0x{:X}".format(offset)) return tif
def guess_dummy_type(self, interface_ea): if self.dummy: if is_struct(get_full_flags(interface_ea)): return t = guess_type(interface_ea) if t is not None: next(self.members()).type = t
def guess_strtype(self): """ Property which allow to return the prototype of the function guessed by IDA. :return str: The guess prototype of the function. """ return idc.guess_type(self.ea)
def _propagate_virtual_method_type_for_method(classinfo, class_vindex, vmethod): """Propagate the type of a class's virtual method to the vtable struct.""" if not idau.is_function_start(vmethod): _log(2, 'Not a function start: {:x}', vmethod) return False vmethod_type = idc.guess_type(vmethod) if not vmethod_type: _log(2, 'No guessed type: {:x}', vmethod) return False vmethod_ptr_type = symbol.convert_function_type_to_function_pointer_type(vmethod_type) if not vmethod_ptr_type: _log(2, 'Could not convert to function pointer type: {:x}', vmethod) return False vmethods_sid = idau.struct_open(classinfo.classname + '::vmethods') vmethod_offset = class_vindex * idau.WORD_SIZE vmethod_mid = idc.get_member_id(vmethods_sid, vmethod_offset) if not bool(idc.SetType(vmethod_mid, vmethod_ptr_type)): _log(2, 'Could not set vmethod field type: {:x}, {}, {}', vmethod, classinfo.classname, class_vindex) return False return True
def _is_func_ptr(self, offset): """Returns true if the given offset is a function pointer.""" # Sometimes we will get a really strange issue where the IDA disassember has set a type for an # address that should not have been set during our course of emulation. # Therefore, before attempting to use get_function_data() to test if it's a function pointer, # first see if guess_type() will return None while is_loaded() is true. # If it doesn't, we know that it shouldn't be a function pointer. # (plus it saves on time) # TODO: Determine if we could have false negatives. if idc.is_loaded(offset) and not idc.guess_type(offset): return False try: utils.get_function_data(offset) return True except RuntimeError: return False except Exception as e: # If we get any other type of exception raise a more friendly error message. raise FunctionTracingError( 'Failed to retrieve function data from {!r}: {}'.format( offset, e), ip=self.ip)
def get_function_data(offset): """ Obtain a idaapi.func_type_data_t object for the function with the provided start EA. :param int offset: start EA of function :return: idaapi.func_type_data_t object :raise RuntimeError: if func_type_data_t object cannot be obtained """ global _func_types tif = ida_typeinf.tinfo_t() try: func_type = idc.get_type(offset) except TypeError: raise RuntimeError('Not a valid offset: {!r}'.format(offset)) # First see if it's a type we already set before. if func_type and offset in _func_types: ida_nalt.get_tinfo(tif, offset) else: # Otherwise, try to use the Hexrays decompiler to determine function signature. # (It's better than IDA's guess_type) try: # This requires Hexrays decompiler, load it and make sure it's available before continuing. if not idaapi.init_hexrays_plugin(): idc.load_and_run_plugin( "hexrays", 0) or idc.load_and_run_plugin("hexx64", 0) if not idaapi.init_hexrays_plugin(): raise RuntimeError('Unable to load Hexrays decompiler.') # Pull type from decompiled C code. try: decompiled = idaapi.decompile(offset) except idaapi.DecompilationFailure: decompiled = None if decompiled is None: raise RuntimeError( "Cannot decompile function at 0x{:X}".format(offset)) decompiled.get_func_type(tif) # Save type for next time. format = decompiled.print_dcl() # The 2's remove the unknown bytes always found at the start and end. idc.SetType(offset, "{};".format(format[2:-2])) # If we fail, resort to using guess_type+ except RuntimeError: if func_type: # If IDA's disassembler set it already, go with that. ida_nalt.get_tinfo(tif, offset) else: # Otherwise try to pull it from guess_type() guessed_type = idc.guess_type(offset) if guessed_type is None: raise RuntimeError( "failed to guess function type for offset 0x{:X}". format(offset)) func_name = idc.get_func_name(offset) if func_name is None: raise RuntimeError( "failed to get function name for offset 0x{:X}".format( offset)) # Documentation states the type must be ';' terminated, also the function name must be inserted guessed_type = re.sub("\(", " {}(".format(func_name), "{};".format(guessed_type)) idc.SetType(offset, guessed_type) # Try one more time to get the tinfo_t object if not ida_nalt.get_tinfo(tif, offset): raise RuntimeError( "failed to obtain tinfo_t object for offset 0x{:X}". format(offset)) funcdata = ida_typeinf.func_type_data_t() if not tif.get_func_details(funcdata): raise RuntimeError( "failed to obtain func_type_data_t object for offset 0x{:X}". format(offset)) # record that we have processed this function before. _func_types.add(offset) return funcdata
def get_function_data(offset, operand: Operand = None): """ Obtain a idaapi.func_type_data_t object for the function with the provided start EA. :param int offset: start EA of function :param operand: operand containing function address in it's value. This can be provided when function is dynamically generated at runtime. (e.g. call eax) :return: idaapi.func_type_data_t object :raise RuntimeError: if func_type_data_t object cannot be obtained """ global _func_types tif = ida_typeinf.tinfo_t() try: func_type = idc.get_type(offset) except TypeError: raise RuntimeError("Not a valid offset: {!r}".format(offset)) # First see if it's a type we already set before. if func_type and offset in _func_types: ida_nalt.get_tinfo(tif, offset) else: # Otherwise, try to use the Hexrays decompiler to determine function signature. # (It's better than IDA's guess_type) try: # This requires Hexrays decompiler, load it and make sure it's available before continuing. if not idaapi.init_hexrays_plugin(): idc.load_and_run_plugin("hexrays", 0) or idc.load_and_run_plugin("hexx64", 0) if not idaapi.init_hexrays_plugin(): raise RuntimeError("Unable to load Hexrays decompiler.") # Pull type from decompiled C code. try: decompiled = idaapi.decompile(offset) except idaapi.DecompilationFailure: decompiled = None if decompiled is None: raise RuntimeError("Cannot decompile function at 0x{:X}".format(offset)) decompiled.get_func_type(tif) # Save type for next time. fmt = decompiled.print_dcl() fmt = "".join(c for c in fmt if c in string.printable and c not in ("\t", "!")) # The 2's remove the unknown bytes always found at the start and end. set_type_result = idc.SetType(offset, "{};".format(fmt)) if not set_type_result: logger.warning("Failed to SetType for function at 0x{:X} with decompiler type {!r}".format(offset, fmt)) # If we fail, resort to using guess_type+ except RuntimeError: if func_type: # If IDA's disassembler set it already, go with that. ida_nalt.get_tinfo(tif, offset) else: # Otherwise try to pull it from guess_type() guessed_type = idc.guess_type(offset) if guessed_type is None: raise RuntimeError("failed to guess function type for offset 0x{:X}".format(offset)) func_name = idc.get_func_name(offset) if func_name is None: raise RuntimeError("failed to get function name for offset 0x{:X}".format(offset)) # Documentation states the type must be ';' terminated, also the function name must be inserted guessed_type = re.sub(r"\(", " {}(".format(func_name), "{};".format(guessed_type)) set_type_result = idc.SetType(offset, guessed_type) if not set_type_result: logger.warning( "Failed to SetType for function at 0x{:X} with guessed type {!r}".format(offset, guessed_type) ) # Try one more time to get the tinfo_t object if not ida_nalt.get_tinfo(tif, offset): raise RuntimeError("failed to obtain tinfo_t object for offset 0x{:X}".format(offset)) funcdata = ida_typeinf.func_type_data_t() success = tif.get_func_details(funcdata) if success: # record that we have processed this function before. (and that we can grab it from the offset) _func_types.add(offset) return funcdata # If we have still failed, we have one more trick under our sleeve. # Try to pull the type information from the operand of the call instruction. # This could be set if the function has been dynamically created. if operand: tif = operand._tif success = tif.get_func_details(funcdata) if success: return funcdata raise RuntimeError("failed to obtain func_type_data_t object for offset 0x{:X}".format(offset))