def cfunc_type(self): """ Get the function's return type tinfo_t object. """ tif = ida_typeinf.tinfo_t() result = self.get_func_type(tif) if not result: return return tif
def __init__(self, cpu_context, start_ea): self._cpu_context = cpu_context self.start_ea = start_ea # TODO: Possibly move the get_function_data work into this class? self._func_type_data = utils.get_function_data(self.start_ea) tif = ida_typeinf.tinfo_t() ida_nalt.get_tinfo(tif, self.start_ea) self._tif = tif
def ChangeVariableType(func_ea, lvar, tif): lsi = ida_hexrays.lvar_saved_info_t() lsi.ll = lvar lsi.type = ida_typeinf.tinfo_t(tif) if not ida_hexrays.modify_user_lvar_info(func_ea, ida_hexrays.MLI_TYPE, lsi): ida_kernwin.warning("Could not modify lvar type for %s" % lvar.name) return False return True
def _get_tinfo_copy(self): """ Return a copy of the ida type (``tinfo_t``) represented by this object. This is an internal function which is used as an helper for setting the types of an element from a :class:`BipType` :return: A copy of the ``tinfo_t`` represented by this object. """ return tinfo_t(self._tinfo)
def force_variable(ea, type, name): t = ida_typeinf.tinfo_t() ida_typeinf.parse_decl(t, None, '{} a;'.format(type), 0) ida_bytes.del_items( ea, ida_bytes.DELIT_EXPAND | ida_bytes.DELIT_DELNAMES | ida_bytes.DELIT_NOCMT, t.get_size()) ida_name.set_name(ea, name, ida_name.SN_CHECK) idc.apply_type(ea, idc.parse_decl('{} a;'.format(type), 0), idc.TINFO_DEFINITE)
def get_func_type(f_start): tif = ida_typeinf.tinfo_t() idaapi.get_tinfo2(f_start, tif) funcdata = idaapi.func_type_data_t() got_data = tif.get_func_details(funcdata) if got_data: return funcdata else: return None
def _get_tinfo(dct): type, fields, fldcmts = dct type = Event.encode_bytes(type) fields = Event.encode_bytes(fields) fldcmts = Event.encode_bytes(fldcmts) type_ = ida_typeinf.tinfo_t() if type is not None: type_.deserialize(None, type, fields, fldcmts) return type_
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 local_types_changed(self): local_types = [] ti = ida_typeinf.get_idati() for ordinal in range(1, ida_typeinf.get_ordinal_qty(ti)): name = ida_typeinf.get_numbered_type_name(ti, ordinal) type_info = ida_typeinf.tinfo_t() type_info.get_numbered_type(ti, ordinal) ret = type_info.serialize() local_types.append((ordinal, name, ret)) self._send_packet(evt.LocalTypesChangedEvent(local_types)) return 0
def type(self, value): """ Sets FunctionArg to a new type. NOTE: Setting the type here has no affect on the FunctionSignature object this came from. """ is_ptr = value.endswith("*") value = value.strip(" *") # Create new tinfo object of type. tif = ida_typeinf.tinfo_t() tif.get_named_type(ida_typeinf.get_idati(), value) # If a pointer, create another tinfo object that is the pointer of the first. if is_ptr: tif2 = ida_typeinf.tinfo_t() tif2.create_ptr(tif) tif = tif2 self._funcarg_obj.type = tif
def _get_tinfo(dct): type, fields, fldcmts, parsed_list = dct # type = Event.encode_bytes(type) fields = Event.encode_bytes(fields) fldcmts = Event.encode_bytes(fldcmts) type = None if parsed_list is None else GetTypeString( pickle.loads(Event.encode_bytes(parsed_list))) type_ = ida_typeinf.tinfo_t() if type is not None: type_.deserialize(None, type, fields, fldcmts) return type_
def GetTypeSignature(apiName): # Look up the prototype by name from the main TIL o = ida_typeinf.get_named_type(None, apiName, ida_typeinf.NTF_SYMU) # Found? if o is not None: code, type_str, fields_str, cmt, field_cmts, sclass, value = o # Create a tinfo_t by deserializing the data returned above t = ida_typeinf.tinfo_t() if t.deserialize(None, type_str, fields_str, field_cmts): # And change the prototype into a function pointer ptrType = ida_typeinf.tinfo_t() ptrType.create_ptr(t) return ptrType # On any failure, return None return None
def create_struct(name, struct_def, ordinal=0, overwrite=True): if overwrite and ordinal: print "overwriting: {}".format(name) tif = ida_typeinf.tinfo_t() # Prepare a container for the type til = ida_typeinf.get_idati() # Get the "Local types" types library name = ida_typeinf.parse_decl( tif, til, struct_def, ida_typeinf.PT_SIL) # Parse the declaration, return its # name and fill the tinfo_t object # for storing it res = tif.set_numbered_type(til, ordinal, ida_typeinf.NTF_REPLACE, name) # ...and store it else: print "Creating new struct - {}".format(name) # Helpful for debugging #print "<start>{}<end>".format(struct_def) tif = ida_typeinf.tinfo_t() # Prepare a container for the type til = ida_typeinf.get_idati() # Get the "Local types" types library name = ida_typeinf.parse_decl( tif, til, struct_def, ida_typeinf.PT_SIL) # Parse the declaration, return its # name and fill the tinfo_t object if not name: print "Failed to create structure" return False ordinal = ida_typeinf.alloc_type_ordinal( til) # Make room in the "Local types" library, # for storing it res = tif.set_numbered_type(til, ordinal, 0, name) # ...and store it if res == -1: print "Failed to create structure" return False ida_typeinf.import_type(idaapi.cvar.idati, -1, name) return True
def data_type_size(self) -> int: """The data type size, defaults to 1 if unknown""" if self.is_stack: tif = ida_typeinf.tinfo_t() success = ida_struct.get_member_tinfo(tif, self._member) if not success: # Sometimes IDA will fail to get member type information for unknown reasons. # In these cases, set the type size ourselves if it is obvious or default to 1. return self.SIZE_MAP.get(self._data_type_enum, 1) return tif.get_size() else: return ida_bytes.get_data_elsize(self.addr, self._data_type_enum)
def __process_types(self): localtypes = [] ti_lib_obj = ida_typeinf.get_idati() ti_lib_count = ida_typeinf.get_ordinal_qty(ti_lib_obj) for ti_ordinal in range(1, ti_lib_count + 1): ti_info = ida_typeinf.tinfo_t() if ti_info.get_numbered_type(ti_lib_obj, ti_ordinal): localtypes.append(self.__process_types_tinfo(ti_info)) return localtypes
def local_types_changed(self): local_types = [] for ordinal in range(1, ida_typeinf.get_ordinal_qty(None)): ret = ida_typeinf.idc_get_local_type_raw(ordinal) if ret is not None: type_str, fields_str = ret type_name = ida_typeinf.get_numbered_type_name( ida_typeinf.cvar.idati, ordinal ) cur_ti = ida_typeinf.tinfo_t() cur_ti.deserialize( ida_typeinf.cvar.idati, type_str, fields_str ) type_serialized = cur_ti.serialize() local_types.append( ( ordinal, type_serialized[0], type_serialized[1], type_name, ) ) else: local_types.append(None) if self.last_local_type is None: self.last_local_type = local_types sent_types = local_types else: def differ_local_types(types1, types2): # [(i, types1, types2), ...] ret_types = [] for i in range(max([len(types1), len(types2)])): if i >= len(types1): ret_types.append((i, None, types2[i])) elif i >= len(types2): ret_types.append((i, types1[i], None)) else: if types1[i] != types2[i]: ret_types.append((i, types1[i], types2[i])) return ret_types diff = differ_local_types(self.last_local_type, local_types) self.last_local_type = local_types if len(diff) == 1 and diff[0][2] is None: return 0 elif len(diff) == 0: return 0 sent_types = [t[2] for t in diff] self._send_packet(evt.LocalTypesChangedEvent(sent_types)) return 0
def _get_tif_with_hex_rays(address: int) -> Optional[ida_typeinf.tinfo_t]: """ Attempt to get the tinfo_t object for the function using the Hex-Rays decompiler plugin. :raises: RuntimeError on failure. :returns: tinfo_t object on success. """ decompiled = decompiled_code(address) if not decompiled: return None tif = ida_typeinf.tinfo_t() decompiled.get_func_type(tif) return tif
def type_for_name(name): ret = ida_typeinf.get_named_type(None, name, 0) if not ret: return None type_str = ret[1] field_str = ret[2] t = ida_typeinf.tinfo_t() t.deserialize(None, type_str, field_str) typeinfo = str(t) typeinfo = typeinfo.replace("__stdcall", "(__stdcall*)") #print 'procdure type:', typeinfo return typeinfo
def force_array(ea, type, name, count=None): t = ida_typeinf.tinfo_t() ida_typeinf.parse_decl(t, None, '{} a;'.format(type), 0) ida_bytes.del_items( ea, ida_bytes.DELIT_EXPAND | ida_bytes.DELIT_DELNAMES | ida_bytes.DELIT_NOCMT, t.get_size() * (1 if count is None else count)) ida_name.set_name(ea, name, ida_name.SN_CHECK) idc.apply_type( ea, idc.parse_decl( '{} a[{}];'.format(type, '' if count is None else str(count)), 0), idc.TINFO_DEFINITE)
def deserialize_tinfo(py_type): """@param py_type: tuple(type, fields) """ # tif.deserialize(None, xtype, None) is fine # tif.deserialize(None, None, fields) returns None # tif.deserialize(None, None, None) crashes IDA (tested on IDA7.0 and IDA7.5 SP3) if py_type is None: return None xtype, fields = py_type if xtype is None: return None tif = ida_typeinf.tinfo_t() if not tif.deserialize(None, xtype, fields): return None return tif
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 test_biptype05(): # empty, partial and void ti = tinfo_t() assert BTypeEmpty.is_handling_type(ti) assert BipType._get_class_bip_type(ti) == BTypeEmpty assert BipType.from_tinfo_no_copy(ti).str == '?' assert BipType.from_tinfo_no_copy(ti).size is None # partial ti = tinfo_t() ti.create_simple_type(0x11) assert BTypeEmpty.is_handling_type(ti) == False assert BTypePartial.is_handling_type(ti) assert BipType._get_class_bip_type(ti) == BTypePartial assert BipType.from_tinfo_no_copy(ti).str == '_BYTE' assert BipType.from_tinfo_no_copy(ti).size == 1 # void ti = tinfo_t() ti.create_simple_type(0x1) assert BTypeEmpty.is_handling_type(ti) == False assert BTypePartial.is_handling_type(ti) == False assert BTypeVoid.is_handling_type(ti) assert BipType._get_class_bip_type(ti) == BTypeVoid assert BipType.from_tinfo_no_copy(ti).str == 'void'
def _parse_c_name(self): if self.tif is not None: raise RuntimeError("May not call _parse_c_name twice") # use ida_typeinf.parse_decl to obtain a tinfo_t object tif = ida_typeinf.tinfo_t() decl = "{} x;".format(self.c_name) ida_typeinf.parse_decl(tif, None, decl, ida_typeinf.PT_TYP) if tif.empty(): raise RuntimeError("Could not parse type '{}'".format(self.c_name)) self.tif = tif self
def _get_function_tif_with_hex_rays(offset): """ Attempt to get the tinfo_t object of a function using the Hex-Rays decompiler plugin. :param offset: Offset of function. :raises: RuntimeError on failure. :returns: tinfo_t object on success. """ tif = ida_typeinf.tinfo_t() decompiled = _get_decompiled_function(offset) if not decompiled: # not sure what for shenanigans happened to get None back.... raise RuntimeError("Expected cfuncptr_t object, received None") decompiled.get_func_type(tif) return tif
def is_set_at(ea): """ This function allow to test if a type is defined at a particular address. This function will return False if a type is not set but ida may be able to guess it. This means that this function may return False while :func:`BipType.get_at` return a type, if this function return True :func:`BipType.get_at` should always return a type. :param ea: The address at which to make the test. :return: True if a type is defined at the address given in argument, False otherwise. """ tif = tinfo_t() return ida_nalt.get_tinfo(tif, ea)
def add_argument_imm(self, value, basic_type): """ Add an immediate value to the function argument list. """ op_tinfo = ida_typeinf.tinfo_t(basic_type) mop_imm = ida_hexrays.mop_t() mop_imm.make_number(value, op_tinfo.get_size()) call_arg = ida_hexrays.mcallarg_t() call_arg.make_number(value, op_tinfo.get_size()) call_arg.type = op_tinfo self.call_info.args.push_back(call_arg) self.call_info.solid_args += 1
def get_ownertype(self): tif = ida_typeinf.tinfo_t() success = ida_struct.get_member_tinfo(tif, self.mem) # if type information is available in Ida analysis if success: if ida_typeinf.is_type_ptr_or_array(tif.get_realtype()) or self.get_size()>=8: return "pointer" else: return "scalar" # return type scalar by default else: if self.get_size()>=8: return "pointer" else: return "scalar"
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 resolve_objc_self_to_class(self, ea): ''' Get the objective c class for the current function RDI value based on the class of the first argument to the current function ''' f_start = idc.get_func_attr(ea, idc.FUNCATTR_START) tif = ida_typeinf.tinfo_t() idaapi.get_tinfo2(f_start, tif) funcdata = idaapi.func_type_data_t() tif.get_func_details(funcdata) # not happy about casting to a string and then regex replacing... but that's the best I could come up with replace_reg = re.compile(' \*', re.IGNORECASE) objc_self_type = funcdata[0].type return objc_self_type
def type_info(self): """ Property which allow to get the information on the type of the operand if defined. This will return an object which inherit from :class:`BipType` if defined or ``None`` if not. .. note:: By default this does not seems to be defined by IDA. :return: A :class:`BipType` object defined for this operand or ``None`` if it was not defined. """ ti = tinfo_t() if not get_op_tinfo(ti, self.ea, self.opnum): # recuperation of the type failed return None return bip.base.biptype.BipType.from_tinfo(ti)
def get_at(ea=None): """ Function which will create an object which inherit from :class:`BipType` representing the type at the current address. This function will **not** set the type at the address given and it may not be set if it was guess by ida. Internally this function will first try to get the type at the address, if no type are defined it will try to guess it. If ida is not able to guess it it will return ``None``. .. todo:: make something better when no type are set ? .. note:: **Implementation** Ida allow to guess the type but this "guess" ignore the fact that this may have been set. It seems necessary to use ida_nalt.get_tinfo for recuperating the type set, it will fail if no type has been set. If no type were set the guess_tinfo is then used, it will typically fail if the data is undefined, in this case None will be return. This may change in the future as by default a tinfo_t ``empty`` is true (but not the tinfo_t.is_unknown). :param ea: The address at which to get the type. If ``None`` the screen address will be used. :return: An object which inherit from :class:`BipType` representing the type at the address given in argument. ``None`` will be return if no type is define and ida was not able to guess it . """ if ea is None: ea = ida_kernwin.get_screen_ea() tif = tinfo_t() # try to get the define type # this seems to be define in ida_nalt... if ida_nalt.get_tinfo(tif, ea): # no need to make a copy in this case return BipType.from_tinfo_no_copy(tif) # no type define, try to guess it # don't know when GUESS_FUNC_TRIVIAL is return so consider failure if guess_tinfo(tif, ea) == GUESS_FUNC_OK: return BipType.from_tinfo_no_copy(tif) # not able to guess, this should be a tinfo_t empty ? (tif.empty() ?) return None