Exemple #1
0
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))
Exemple #2
0
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
Exemple #3
0
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
Exemple #5
0
    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)
Exemple #6
0
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
Exemple #7
0
 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)
Exemple #8
0
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
Exemple #9
0
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))