def __process_function_typeinfo(self, info, func): tinfo = ida_typeinf.tinfo_t() func_type_data = ida_typeinf.func_type_data_t() if ida_pro.IDA_SDK_VERSION >= 740: ida_typeinf.guess_tinfo(tinfo,func.start_ea) else: ida_typeinf.guess_tinfo(func.start_ea,tinfo) tinfo.get_func_details(func_type_data) #calling convention info['calling_convention'] = self.__describe_callingconvention(func_type_data.cc) func_type_data.rettype #return tpye info['return_type'] = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type_data.rettype, '', '') #arguments arguments = list() for funcarg in func_type_data: arginfo = { 'name' : funcarg.name, 'type' : ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, funcarg.type, '', ''), 'argument_location' : self.__describe_argloc(funcarg.argloc.atype()) } arguments.append(arginfo) info['arguments'] = arguments
def __process_function_typeinfo(self, info, func): tinfo = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tinfo,func.start_ea) func_type_data = ida_typeinf.func_type_data_t() tinfo.get_func_details(func_type_data) #calling convention info['calling_convention'] = self.__describe_callingconvention(func_type_data.cc) info['memory_model_code'] = self.__describe_memorymodel_code(func_type_data.cc) info['memory_model_data'] = self.__describe_memorymodel_data(func_type_data.cc) #return type info['return_type'] = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type_data.rettype, '', '') #arguments arguments = list() for funcarg in func_type_data: arginfo = { 'name' : funcarg.name, 'type' : ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, funcarg.type, '', ''), 'argument_location' : self.__describe_argloc(funcarg.argloc.atype()) } arguments.append(arginfo) info['arguments'] = arguments
def apply_jni_func_sig(): """ Apply the standard JNIEnv* and jobject signature to a function. """ print("Function: {}".format(idc.get_func_name(here()))) func = ida_hexrays.decompile(here()) func_type = func.type funcdata = ida_typeinf.func_type_data_t() func_type.get_func_details(funcdata) jnienv = ida_typeinf.tinfo_t() jnienv.get_named_type(ida_typeinf.get_idati(), "JNIEnv") jnienv_ptr = ida_typeinf.tinfo_t() jnienv_ptr.create_ptr(jnienv) jobject = ida_typeinf.tinfo_t() jobject.get_named_type(ida_typeinf.get_idati(), "jobject") funcdata[0].type = jnienv_ptr funcdata[0].name = "env" funcdata[1].type = jobject funcdata[1].name = "thiz" new_tinfo = ida_typeinf.tinfo_t() new_tinfo.create_func(funcdata) ida_typeinf.apply_tinfo(here(), new_tinfo, ida_typeinf.TINFO_DEFINITE)
def __get_type_data(self, ea): tinfo = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tinfo, ea) func_type_data = ida_typeinf.func_type_data_t() tinfo.get_func_details(func_type_data) return func_type_data
def _is_func_type(ea): """Determines if data item at address is a function type.""" try: idc.get_type(ea) except TypeError: return False tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, ea) func_type_data = ida_typeinf.func_type_data_t() return bool(tif.get_func_details(func_type_data))
def _ida_func_type_data(self): """ Internal property which allow to get the ``func_type_data_t`` for this type function. :return: the ``ida_typeinf.func_type_data_t`` for this object. """ ftd = func_type_data_t() if not self._tinfo.get_func_details(ftd): raise BipError( "Unable to get function details for function {}".format( self.name)) return ftd
def _is_func_type(ea): """Determines if data item at address is a function type.""" try: idc.get_type(ea) except TypeError: return False tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, ea) func_type_data = ida_typeinf.func_type_data_t() # In IDA 7.6, imported functions are now function pointers. # To handle this, check if we need to pull out a pointed object first if tif.is_funcptr(): tif = tif.get_pointed_object() return bool(tif.get_func_details(func_type_data))
def declaration(self, decl): """ Changes the declaration of the function internally. """ # Ensure ends with ';' if not decl.endswith(";"): decl += ";" tif = ida_typeinf.tinfo_t() til = ida_typeinf.get_idati() func_type_data = ida_typeinf.func_type_data_t() ida_typeinf.parse_decl(tif, til, decl, ida_typeinf.PT_SIL) tif.get_func_details(func_type_data) self._tif = tif self._func_type_data = func_type_data
def processFunctionTypeinfo(function): tinfo = ida_typeinf.tinfo_t() func_type_data = ida_typeinf.func_type_data_t() tinfo.get_named_type ida_typeinf.guess_tinfo(function['start_ea'], tinfo) tinfo.get_func_details(func_type_data) #calling convention function['calling_convention'] = describe_callingconvention( func_type_data.cc) func_type_data.rettype #return tpye function['return_type'] = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type_data.rettype, '', '') #arguments arguments = list() for funcarg in func_type_data: arginfo = { 'name': funcarg.name, 'type': ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, funcarg.type, '', ''), 'argument_location': describe_argloc(funcarg.argloc.atype()) } arguments.append(arginfo) function['arguments'] = arguments
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: ida_typeinf.func_type_data_t object, ida_typeinf.tinfo_t object :raise RuntimeError: if func_type_data_t object cannot be obtained """ global _func_types tif = None 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: tif = ida_typeinf.tinfo_t() 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: tif = _get_function_tif_with_hex_rays(offset) # If we fail, resort to using guess_type+ except RuntimeError: if func_type: # If IDA's disassembler set it already, go with that. tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, offset) else: try: tif = _get_function_tif_with_guess_type(offset) except RuntimeError: # Don't allow to fail if we could pull from operand. pass if tif: funcdata = ida_typeinf.func_type_data_t() # In IDA 7.6, imported functions are now function pointers. # To handle this, check if we need to pull out a pointed object first if tif.is_funcptr(): tif = tif.get_pointed_object() 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, tif # 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 funcdata = ida_typeinf.func_type_data_t() success = tif.get_func_details(funcdata) if success: return funcdata, tif raise RuntimeError( "failed to obtain func_type_data_t object for offset 0x{:X}".format( offset))
def get_function_impl(self, address): """Given an address, return a `Function` instance or raise an `InvalidFunctionException` exception.""" arch = self._arch os = self._os pfn = ida_funcs.get_func(address) if not pfn: pfn = ida_funcs.get_prev_func(address) seg_ref = [None] seg = find_segment_containing_ea(address, seg_ref) # Check this function. if not pfn or not seg: raise InvalidFunctionException( "No function defined at or containing address {:x}".format(address) ) elif ( not ida_funcs.func_contains(pfn, address) and not _is_extern_seg(seg) and not is_imported_table_seg(seg) ): raise InvalidFunctionException( "No function defined at or containing address {:x}".format(address) ) # Reset to the start of the function, and get the type of the function. address = pfn.start_ea tif = ida_typeinf.tinfo_t() if not ida_nalt.get_tinfo(tif, address): if ida_typeinf.GUESS_FUNC_OK != ida_typeinf.guess_tinfo(tif, address): raise InvalidFunctionException( "Can't guess type information for function at address {:x}".format( address ) ) if not tif.is_func(): raise InvalidFunctionException( "Type information at address {:x} is not a function: {}".format( address, tif.dstr() ) ) ftd = ida_typeinf.func_type_data_t() if not tif.get_func_details(ftd): raise InvalidFunctionException( "Could not get function details for function at address {:x}".format( address ) ) # Make sure we can handle the basic signature of the function. This might # not be the final signature that we go with, but it's a good way to make # sure we can handle the relevant types. try: func_type = _get_type(tif, TYPE_CONTEXT_FUNCTION) except UnhandledTypeException as e: raise InvalidFunctionException( "Could not assign type to function at address {:x}: {}".format( address, str(e) ) ) # Get the calling convention. The CC might override `is_variadic`, e.g. how # old style C functions declared as `foo()` actually imply `foo(...)`. cc, is_variadic = _get_calling_convention(arch, os, ftd) if is_variadic: func_type.set_is_variadic() # Go look into each of the parameters and their types. Each parameter may # refer to multiple locations, so we want to split each of those locations # into unique i = 0 max_i = ftd.size() param_list = [] while i < max_i: funcarg = ftd[i] i += 1 arg_type = _get_type(funcarg.type, TYPE_CONTEXT_PARAMETER) arg_type_str = arg_type.serialize(arch, {}) j = len(param_list) _expand_locations(arch, pfn, arg_type, funcarg.argloc, param_list) # If we have a parameter name, then give a name to each of the expanded # locations associated with this parameter. if funcarg.name: if (j + 1) == len(param_list): param_list[-1].set_name(funcarg.name) else: k = j while k < len(param_list): param_list[-1].set_name("{}_{}".format(funcarg.name, k - j)) k += 1 # Build up the list of return values. ret_list = [] ret_type = _get_type(ftd.rettype, TYPE_CONTEXT_RETURN) if not isinstance(ret_type, VoidType): _expand_locations(arch, pfn, ret_type, ftd.retloc, ret_list) func = IDAFunction( arch, address, param_list, ret_list, pfn, ftd.is_noret(), func_type, cc ) self.add_symbol(address, _function_name(address)) return func
def get_function(self, address): """Given an address, return a `Function` instance or raise an `InvalidFunctionException` exception.""" arch = self._arch pfn = ida_funcs.get_func(address) if not pfn: pfn = ida_funcs.get_prev_func(address) # Check this function. if not pfn or not ida_funcs.func_contains(pfn, address): raise InvalidFunctionException( "No function defined at or containing address {:x}".format( address)) # Reset to the start of the function, and get the type of the function. address = pfn.start_ea if address in self._functions: return self._functions[address] tif = ida_typeinf.tinfo_t() if not ida_nalt.get_tinfo(tif, address): ida_typeinf.guess_tinfo(tif, address) if not tif.is_func(): raise InvalidFunctionException( "Type information at address {:x} is not a function: {}". format(address, tif.dstr())) ftd = ida_typeinf.func_type_data_t() if not tif.get_func_details(ftd): raise InvalidFunctionException( "Could not get function details for function at address {:x}". format(address)) # Make sure we can handle the basic signature of the function. This might # not be the final signature that we go with, but it's a good way to make # sure we can handle the relevant types. try: func_type = get_type(tif) except UnhandledTypeException as e: raise InvalidFunctionException( "Could not assign type to function at address {:x}: {}".format( address, str(e))) # Go look into each of the parameters and their types. Each parameter may # refer to multiple locations, so we want to split each of those locations # into unique i = 0 max_i = ftd.size() param_list = [] while i < max_i: funcarg = ftd[i] i += 1 arg_type = get_type(funcarg.type) arg_type_str = arg_type.serialize(arch, {}) j = len(param_list) _expand_locations(arch, pfn, arg_type, funcarg.argloc, param_list) # If we have a parameter name, then give a name to each of the expanded # locations associated with this parameter. if funcarg.name: if (j + 1) == len(param_list): param_list[-1].set_name(funcarg.name) else: k = j while k < len(param_list): param_list[-1].set_name("{}_{}".format( funcarg.name, k - j)) k += 1 # Build up the list of return values. ret_list = [] ret_type = get_type(ftd.rettype) if not isinstance(ret_type, VoidType): _expand_locations(arch, pfn, ret_type, ftd.retloc, ret_list) func = IDAFunction(arch, address, param_list, ret_list, pfn) self._functions[address] = func return func
si = None print "Starting analysis" current_index = 0 for ea in idautils.Functions(): flags = idc.GetFunctionFlags(ea) func_name = idc.get_func_name(ea) if (current_index % 1000 == 0): print "Processing function %d" % current_index if flags & FUNC_THUNK and not func_name.startswith( "sub_") and not func_name.startswith( "j__ZdlPv") and not "null" in func_name: # Revert weird designations # could also use ida_funcs.set_func_name_if_jumpfunc(ea, None) func_name = func_name.replace("j_", "") funcdata = ida_typeinf.func_type_data_t() tinfo = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tinfo, ea) tinfo.get_func_details(funcdata) if (flags & FUNC_NORET): retcode = '' else: retcode = 'N' mcsema_def = ("%s %d C %s" % (func_name, funcdata.size(), retcode)).strip() sdk_funcs_file.write(mcsema_def + '\n') if func_name.endswith("_1"): func_name = func_name.replace("_1", "") if func_name.endswith("_0"): func_name = func_name.replace("_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
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))
def get_func_type_info(address: int, operand: Tuple[int, int] = None) -> Tuple[ida_typeinf.func_type_data_t, ida_typeinf.tinfo_t]: """ Obtain a idaapi.func_type_data_t object for the function with the provided start address. :param address: start address of the function :param operand: Optional address and index pair for an operand containing the function address in its value. This can be provided when function is dynamically generated at runtime. (e.g. call eax) :return: ida_typeinf.func_type_data_t object, ida_typeinf.tinfo_t object :raise RuntimeError: if func_type_data_t object cannot be obtained """ func_type = idc.get_type(address) # First see if it's a type we already set before. if func_type and address in _seen_func_types: tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, address) # Otherwise, try to use the Hexrays decompiler to determine function signature. # (It's better than IDA's guess_type) else: # First try to get type information from the decompiled code produced # by the Hex Rays plugin. tif = _get_tif_with_hex_rays(address) if not tif: # Otherwise, if IDA's disassembler set it already, go with that. if func_type: tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, address) # Finally, see if we can obtain it with guess_type() else: tif = _get_tif_with_guess_type(address) if tif: func_type_data = ida_typeinf.func_type_data_t() # In IDA 7.6, imported functions are now function pointers. # To handle this, check if we need to pull out a pointed object first if tif.is_funcptr(): tif = tif.get_pointed_object() success = tif.get_func_details(func_type_data) if success: # record that we have processed this function before. (and that we can grab it from the offset) _seen_func_types.add(address) return func_type_data, tif # 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 = ida_typeinf.tinfo_t() ida_nalt.get_op_tinfo(tif, operand.address, operand.index) func_type_data = ida_typeinf.func_type_data_t() success = tif.get_func_details(func_type_data) if success: return func_type_data, tif raise RuntimeError(f"Failed to obtain func_type_data_t object for offset 0x{address:X}")