def extract_info_from_IDA(self): prots = ida_bytes.get_qword(self.ea + 0x10) if prots: count = ida_bytes.get_qword(prots) entrysize = 0x8 p_ea = prots + 8 for i in range(count): proto_ea = idc.get_qword(p_ea) self.prots.append(proto_ea) p_ea += entrysize type_info = ida_bytes.get_qword(self.ea + 0x48) for idx in range(0, 4): # 0: inst_meths # 1: class_meths # 2: opt_inst_meths # 3: opt_class_meths meth_list = ida_bytes.get_qword(self.ea + 0x18 + idx * 8) if meth_list: entrysize = ida_bytes.get_dword(meth_list) count = ida_bytes.get_dword(meth_list + 4) ea = meth_list + 8 for i in range(0, count): sel = idc.get_bytes(idc.Qword(ea), idc.get_item_size(idc.Qword(ea)) - 1) meth_type = idc.get_bytes(idc.Qword(type_info), idc.get_item_size(idc.Qword(type_info)) - 1) self.meths[sel] = meth_type ea += entrysize type_info += 8
def __init__(self, ea): self.ea = ea # ea: objc_data address self.info = ida_bytes.get_qword(ea + 0x20) self.superclass = ida_bytes.get_qword( ida_bytes.get_qword(ea) + 0x08) # idc.Name(self.superclass): _OBJC_METACLASS_$_UIView self.name = idc.get_bytes(idc.Qword(self.info + 0x18), idc.get_item_size(idc.Qword(self.info + 0x18)) - 1) self.classref = None self.superref = None self.prots = [] self.ivars = dict() self.props = dict()
def handle_protocol(self, ea): protocol_ea = ida_bytes.get_qword(ea) protocol_name = cstr(ida_bytes.get_qword(protocol_ea + 8)) method_list_ea = ida_bytes.get_qword(protocol_ea + 3 * 8) p = Protocol(protocol_name, ea=protocol_ea) self.print('@protocol', protocol_name) # todo: support class methods for method in method_list(method_list_ea): key = '- ' + cstr(method.name) p.methods.append(key) self.print(key) self.print('@end') self.print() self.protocols.append(p) self.protocol_lookup[p.name] = p self.lookup[ea] = p
def find_pointers(start, end): for va in range(start, end - 0x8): ptr = ida_bytes.get_qword(va) if idc.get_segm_start(ptr) == idc.BADADDR: continue yield va, ptr
def handle_class(self, ea): clazz_ea = ida_bytes.get_qword(ea) clazz = Objc2Class(ida_bytes.get_bytes(clazz_ea, Objc2Class.length)) # if clazz.info & 7 != 0: # swift meta_class = Objc2Class( ida_bytes.get_bytes(clazz.isa, Objc2Class.length)) meta_class.info = (meta_class.info >> 3) << 3 meta_info = Objc2ClassRo( ida_bytes.get_bytes(meta_class.info, Objc2ClassRo.length)) clazz.info = (clazz.info >> 3) << 3 clazz_info = Objc2ClassRo( ida_bytes.get_bytes(clazz.info, Objc2ClassRo.length)) c = Clazz(cstr(clazz_info.name), ea=clazz_ea) self.print('@interface', cstr(clazz_info.name)) for method in method_list(meta_info.base_meths): key = '+ ' + cstr(method.name) c.methods[key] = method.imp self.print(key) for method in method_list(clazz_info.base_meths): key = '- ' + cstr(method.name) c.methods[key] = method.imp self.print(key) self.print('@end') self.print() self.classes.append(c) self.class_lookup[c.name] = c self.lookup[ea] = c
def value(self): """ Property which return the value corresponding to the data of a numberable elements. This property works only if the :meth:`~BipData.is_numerable` and :meth:`~BipData.has_data` properties returned True. For getting value of an element which is not numerable use the :meth:`~BipElt.bytes` property. This property is link to the type defined or guessed by IDA and it is a good idea to assure you have the proper type before using it. :return: An integer representing the value of the data or ``None`` if the data element is not numerable or do not have data. """ if not self.has_data: return None elif self.is_unknown or self.is_byte: return ida_bytes.get_wide_byte(self.ea) elif self.is_word: return ida_bytes.get_wide_word(self.ea) elif self.is_dword: return ida_bytes.get_wide_dword(self.ea) elif self.is_qword: return ida_bytes.get_qword(self.ea) else: return None
def build_from_IDA(): seg = ida_segment.get_segm_by_name('__objc_protolist') for ea in range(seg.startEA, seg.endEA, 8): p = Protocol(ida_bytes.get_qword(ea)) p.extract_info_from_IDA() Protocol.pool_indexed_by_ea[p.ea] = p Protocol.pool_indexed_by_name[p.name] = Protocol.pool_indexed_by_name[p.name] + [ p, ] if p.name in Protocol.pool_indexed_by_name else [p, ]
def build_from_IDA(): seg = ida_segment.get_segm_by_name('__objc_classlist') for class_ea in range(seg.startEA, seg.endEA, 8): objc_data = ida_bytes.get_qword(class_ea) cls = OCClass(objc_data) cls.extract_info_from_IDA() OCClass.pool_indexed_by_name[cls.name] = cls if cls.name not in OCClass.pool_indexed_by_name else \ OCClass.pool_indexed_by_name[cls.name] OCClass.pool_indexed_by_ea[cls.ea] = cls
def read_xword(self): if bitness == 32: res = ida_bytes.get_dword(self.addr) elif bitness == 64: res = ida_bytes.get_qword(self.addr) else: # bitness == 16 res = ida_bytes.get_word(self.addr) self.inc(self.bitness >> 3) return res
def extract_info_from_IDA(self): for xref in idautils.XrefsTo(self.ea): frm = xref.frm if idc.SegName(frm) == '__objc_classrefs': self.classref = frm if idc.SegName(frm) == '__objc_superrefs': self.superref = frm base_ivars = ida_bytes.get_qword(self.info + 0x30) if base_ivars and idc.SegName(base_ivars) == '__objc_const': entrysize = ida_bytes.get_dword(base_ivars) count = ida_bytes.get_dword(base_ivars + 4) ea = base_ivars + 8 for i in range(count): offset = ida_bytes.get_dword(idc.get_qword(ea)) _type = idc.get_bytes(idc.Qword(ea + 0X10), idc.get_item_size(idc.Qword(ea + 0X10)) - 1) _name = idc.get_bytes(idc.Qword(ea + 0X08), idc.get_item_size(idc.Qword(ea + 0X08)) - 1) # self.ivars[offset] = _type self.ivars[_name] = _type ea += entrysize base_props = ida_bytes.get_qword(self.info + 0x40) if base_props and idc.SegName(base_props) == '__objc_const': entrysize = ida_bytes.get_dword(base_props) count = ida_bytes.get_dword(base_props + 4) ea = base_props + 8 for i in range(count): _type = idc.get_bytes(idc.Qword(ea + 0X08), idc.get_item_size(idc.Qword(ea + 0X08)) - 1) _name = idc.get_bytes(idc.Qword(ea), idc.get_item_size(idc.Qword(ea)) - 1) self.props[_name] = _type ea += entrysize base_prots = ida_bytes.get_qword(self.info + 0x28) if base_prots and idc.SegName(base_prots) == '__objc_const': count = ida_bytes.get_qword(base_prots) entrysize = 0x8 p_ea = base_prots + 8 for i in range(count): proto_ea = idc.get_qword(p_ea) self.prots.append(proto_ea) Protocol.add_implementing_class(proto_ea, self.ea) p_ea += entrysize
def _init_func_thunk_ctrl_flow(self): """Initializes the control flow redirections and targets using function thunks""" # We only support the ELF format for now inf = ida_idaapi.get_inf_structure() if inf.filetype != ida_ida.f_ELF: return # List the function thunks first input_file_path = ida_nalt.get_input_file_path() image_parser = create_elf_image_parser(input_file_path) function_thunk_list = image_parser.get_function_thunk_list() # Go through each function thunk, and look at its cross references; there # should always be only one user, which is the wrapper around the imported # function is_32_bit = image_parser.get_image_bitness() == 32 for function_thunk in function_thunk_list: thunk_va = function_thunk.start redirection_dest = (ida_bytes.get_wide_dword(thunk_va) if is_32_bit else ida_bytes.get_qword(thunk_va)) caller_address = ida_xref.get_first_cref_to(redirection_dest) if caller_address == ida_idaapi.BADADDR: continue redirection_source = idc.get_func_attr(caller_address, idc.FUNCATTR_START) caller_function_name = ida_funcs.get_func_name(redirection_source) if function_thunk.name in caller_function_name: print( "anvill: Redirecting the user {:x} of thunk {} at rva {:x} to {:x}" .format( redirection_source, function_thunk.name, function_thunk.start, redirection_dest, )) self.add_control_flow_redirection(redirection_source, redirection_dest) print( "anvill: Adding target list {:x} -> [{:x}, complete=True] for {}" .format(caller_address, redirection_dest, function_thunk.name)) self.set_control_flow_targets(caller_address, [redirection_dest], True)
def is_method_a_getter(self, sel, f=None): base_props = ida_bytes.get_qword(self.info + 0x40) if not base_props: return False entrysize = ida_bytes.get_dword(base_props) count = ida_bytes.get_dword(base_props + 4) p_ea = base_props + 8 for i in range(count): p_name = idc.get_bytes(idc.Qword(p_ea), idc.get_item_size(idc.Qword(p_ea)) - 1) if p_name == sel: return True p_ea += entrysize return False
def _init_ctrl_flow_redirections(self): """Initializes the control flow redirections using function thunks""" # We only support the ELF format for now inf = ida_idaapi.get_inf_structure() if inf.filetype != ida_ida.f_ELF: return # List the function thunks first input_file_path = ida_nalt.get_input_file_path() image_parser = create_elf_image_parser(input_file_path) function_thunk_list = image_parser.get_function_thunk_list() # Go through each function thunk, and look at its cross references; there # should always be only one user, which is the wrapper around the imported # function # # Note that the __libc_start_main # thunk does not need redirection since # it's called directly without any wrapper function from the module entry # point is_32_bit = image_parser.get_image_bitness() == 32 for function_thunk in function_thunk_list: if function_thunk.name == "__libc_start_main": continue thunk_va = ida_nalt.get_imagebase() + function_thunk.start redirection_dest = ( ida_bytes.get_wide_dword(thunk_va) if is_32_bit else ida_bytes.get_qword(thunk_va) ) caller_address = ida_xref.get_first_cref_to(redirection_dest) if caller_address == ida_idaapi.BADADDR: continue redirection_source = idc.get_func_attr(caller_address, idc.FUNCATTR_START) print( "anvill: Redirecting the user {:x} of thunk {} at rva {:x} to {:x}".format( redirection_source, function_thunk.name, function_thunk.start, redirection_dest, ) ) self.add_control_flow_redirection(redirection_source, redirection_dest)
def get_qword(ea=None, original=False): """ Static method allowing to get the value of one qword at an address. :param ea: The address at which recuperating the value. If ``None`` the screen address is used. :param original: If True the value recuperated will be the original one (before a patch). Default: False. :return: An integer corresponding to the value at the address. """ if ea is None: ea = ida_kernwin.get_screen_ea() if original: return ida_bytes.get_original_qword(ea) else: return ida_bytes.get_qword(ea)
def main(): for segstart, segend, segname in enum_segments(): if segname not in ('.rdata', 'UPX1' ): continue for src, dst, psize in find_pointers(segstart, segend): if idc.get_segm_name(dst) not in (".rdata", "UPX0"): continue if psize == 8: size = ida_bytes.get_qword(src + 0x8) else: size = ida_bytes.get_dword(src + 0x4) if size > 0x100: continue if size <= 2: continue buf = ida_bytes.get_bytes(dst, size) if not buf: continue if b"\x00" in buf: continue try: s = buf.decode("ascii") except UnicodeDecodeError: continue print("string pointer: 0x%x -> 0x%x: %s" % (src, dst, s)) ida_bytes.del_items(src, 1) ida_bytes.set_cmt(dst, s, True) # pointer ida_bytes.del_items(src, psize) ida_bytes.create_data(src, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR) # this doesn't seem to always work :-( idc.op_plain_offset(src, -1, 0) ida_name.set_name(src, "s_%x" % (src)) ida_bytes.set_cmt(src, s, True) # size ida_bytes.del_items(src + psize, psize) ida_bytes.create_data(src + psize, idc.FF_QWORD if size == 8 else idc.FF_DWORD, psize, idc.BADADDR)
def get_ptr(ea=None): """ Recuperate the value of a pointer at an address. This will handle automatically the correct size of the pointer. :param int ea: the address at which get the pointer value. If ``None`` the screen address is used. :return: the pointer value """ if ea is None: ea = ida_kernwin.get_screen_ea() info = ida_idaapi.get_inf_structure() if info.is_64bit(): return ida_bytes.get_qword(ea) elif info.is_32bit(): return ida_bytes.get_wide_dword(ea) else: return ida_bytes.get_wide_word(ea)
def modify_value(self): ea = self.get_current_expr_ea() if not ea or not idaapi.is_loaded(ea): return stack_val = 0 if idaapi.inf_is_64bit(): stack_val = ida_bytes.get_qword(ea) else: stack_val = ida_bytes.get_dword(ea) b = idaapi.ask_str("0x%X" % stack_val, 0, "Modify value") if b is not None: try: value = int(idaapi.str2ea(b)) if idaapi.inf_is_64bit(): idc.patch_qword(ea, value) else: idc.patch_dword(ea, value) self.reload_info() except: idaapi.warning("Invalid expression")
def get_OSMetaClass_ctor_args(ea): # args = idaapi.get_arg_addrs(ea) # Lazy solution - just let unicorn emulate it up to the BL # First we gotta figure out the start of this function fxnstart = ida_funcs.get_func(ea).start_ea # Then we emulate it up to the function call params = emulate(fxnstart, ea) # for i in range(len(params)): # print("x{}: {}".format(i+1, hex(params[i]))) classname = read_cstring(params[0]) superclass = params[1] superclass_name = None if superclass != 0: superclass_name = idc.demangle_name(idaapi.get_name(params[1]), get_inf_attr(idc.INF_LONG_DN)) # In case the superclass pointer references a pointer that is # meant to be resolved later if superclass_name == None: superclass_name = idaapi.get_name(ida_bytes.get_qword(params[1])) superclass_name = idc.demangle_name(superclass_name, get_inf_attr(idc.INF_LONG_DN)) superclass_name = superclass_name[0:superclass_name.find("::")] args = [superclass_name, classname.decode(), params[2]] # if superclass_name == "AUAUnitDictionary": # for i in range(len(params)): # print("x{}: {}".format(i+1, hex(params[i]))) # print(args) # return return args
def get_qword(self, offset): """Read qword from IDB""" self.ret = ida_bytes.get_qword(offset) return self.ret
def get_double(ea): return struct.unpack("d", struct.pack("Q", ida_bytes.get_qword(ea)))[0]
def get_int64(ea): return struct.unpack("q", struct.pack("Q", ida_bytes.get_qword(ea)))[0]
def main(): global til til = ida_typeinf.get_idati() capstone_init() unicorn_init() all_names = list(idautils.Names()) # First, get inheritance tree for all classes # We do this in two passes: # First pass: for each class, gather class name, parent class name, # and superclass name, and connect them # Second pass: for each inheritance hierarchy object, we check # if the parent pointer is None (signaling a top-level # class), and add that to the `ihs` list # We can do this whole thing by processing xrefs to OSMetaClass::OSMetaClass OSMetaClass_ctor = get_OSMetaClass_ctor() if OSMetaClass_ctor == 0: print("Could not find OSMetaClass::OSMetaClass") return print("Got OSMetaClass::OSMetaClass at {}".format(hex(OSMetaClass_ctor))) # key,value pairs of all inheritance hierarchy objects ih_dict = {} # only those inheritance hierarchy objects which represent # a top-level parent class (aka inheriting from OSObject) ihs = [] xrefs = list(idautils.XrefsTo(OSMetaClass_ctor)) num = 0 for xref in xrefs: frm = xref.frm # test # frm = 0x63920 # print("xref from {}".format(hex(frm))) args = get_OSMetaClass_ctor_args(frm) pname = args[0] cname = args[1] if cname == "OSMetaClassBase": print("xref from {}".format(hex(frm))) print(args) if pname == cname: continue csz = args[2] # if pname == "AUAUnitDictionary" and cname == "AUAMixerUnitDictionary": # print(args) # return new_parent = pname is not None and pname not in ih_dict new_child = cname not in ih_dict if new_parent: ih_dict[pname] = InheritanceHierarchy(None, pname, csz, csz) if new_child: ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: # Update class size for only child classes ih_dict[cname].sz = csz if pname == None: # If this class has no superclass, it must be parent class, # so make its InheritanceHierarchy object ih_dict[cname] = InheritanceHierarchy(None, cname, csz) else: child_ih = ih_dict[cname] parent_ih = ih_dict[pname] parent_ih.add_child(child_ih) child_ih.parent = parent_ih child_ih.totsz = child_ih.sz + parent_ih.totsz # if cname == "AUAUnitDictionary": # print("AUAUnitDictionary sz: {}".format(ih_dict[pname].sz)) # print(args) # return # if cname == "AUAMixerUnitDictionary": # print("AUAMixerUnitDictionary sz: {}".format(ih_dict[cname].sz)) # print(args) # return num += 1 # if num == 10: # break print("First pass: {} classes processed".format(num)) num = 0 # Second pass for ih in ih_dict.values(): if ih.parent == None: # print("Adding {} to the ihs list".format(ih.name)) num += 1 ihs.append(ih) print("Second pass: {} classes added to ihs list".format(num)) num = 0 wants_class_hierarchy = ida_kernwin.ask_yn(0, "Dump class hierarchy?") if wants_class_hierarchy: hierch_file = open( "{}/iOS/Scripts/iokit_hier.txt".format(str(Path.home())), "w") for ih in ihs: dump_hierarchy_to_file(hierch_file, ih, 0) print("File written to {}".format(hierch_file.name)) hierch_file.close() return vtables = [] for cur_name in all_names: ea = cur_name[0] name = cur_name[1] if "ZTV" in name: vtables.append(cur_name) struct_file = open( "{}/iOS/Scripts/structs_from_vtabs.h".format(str(Path.home())), "w") is_standalone_kext = ida_kernwin.ask_yn(0, "Standalone kext?") if is_standalone_kext: # If this is from a standalone kext, I need to write some # definitions for common objects that follow my struct format, # otherwise, things get really screwed struct_file.write( "struct __cppobj ExpansionData {};\n\n" "struct __cppobj OSMetaClassBase_vtbl;\n\n" "struct __cppobj OSMetaClassBase_mbrs {};\n\n" "struct __cppobj OSMetaClassBase {\n" "\tOSMetaClassBase_vtbl *__vftable /*VFT*/;\n" "\tOSMetaClassBase_mbrs m;\n" "};\n\n" "struct __cppobj OSObject_mbrs : OSMetaClassBase_mbrs {\n" "\tint retainCount;\n" "};\n\n" "struct __cppobj OSObject_vtbl : OSMetaClassBase_vtbl {};\n\n" "struct __cppobj OSObject {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSObject_mbrs m;\n" "};\n\n" "struct __cppobj OSMetaClass_mbrs : OSMetaClassBase_mbrs {\n" "\tExpansionData *reserved;\n" "\tconst OSMetaClass *superClassLink;\n" "\tconst OSSymbol *className;\n" "\tunsigned int classSize;\n" "\tunsigned int instanceCount;\n" "};\n\n" "struct __cppobj OSMetaClass {\n" "\tOSMetaClassBase_vtbl *__vftable;\n" "\tOSMetaClass_mbrs m;\n" "};\n\n" "struct __cppobj OSCollection_vtbl;\n" "struct __cppobj OSCollection_mbrs : OSObject_mbrs {\n" "\tunsigned int updateStamp;\n" "\tunsigned int fOptions;\n" "};\n\n" "struct __cppobj OSCollection {\n" "\tOSCollection_vtbl *__vftable;\n" "\tOSCollection_mbrs m;\n" "};\n\n" "struct __cppobj OSArray_vtbl;\n" "struct __cppobj OSArray_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tvoid *array;\n" "};\n\n" "struct __cppobj OSArray {\n" "\tOSArray_vtbl *__vftable;\n" "\tOSArray_mbrs m;\n" "};\n\n" "struct __cppobj OSDictionary::dictEntry {\n" "\tconst OSSymbol *key;\n" "\tconst OSMetaClassBase *value;\n" "};\n\n" "struct __cppobj OSDictionary_vtbl;\n" "struct __cppobj OSDictionary_mbrs : OSCollection_mbrs {\n" "\tunsigned int count;\n" "\tunsigned int capacity;\n" "\tunsigned int capacityIncrement;\n" "\tOSDictionary::dictEntry *dict;\n" "};\n\n" "struct __cppobj OSDictionary {\n" "\tOSDictionary_vtbl *__vftable;\n" "\tOSDictionary_mbrs m;\n" "};\n\n" "struct __cppobj OSSet_vtbl;\n" "struct __cppobj OSSet_mbrs : OSCollection_mbrs {\n" "\tOSArray *members;\n" "};\n\n" "struct __cppobj OSSet {\n" "\tOSSet_vtbl *__vftable;\n" "\tOSSet_mbrs m;\n" "};\n\n" "struct __cppobj OSString_mbrs : OSObject_mbrs {\n" "\tunsigned __int32 flags : 14;\n" "\tunsigned __int32 length : 18;\n" "\tchar *string;\n" "};\n\n" "struct __cppobj OSString {\n" "\tOSObject_vtbl *__vftable;\n" "\tOSString_mbrs m;\n" "};\n\n" "struct __cppobj OSSymbol : OSString {};\n\n") num_failed_get_type = 0 cnt = 0 for vtable in vtables: demangled_name = idc.demangle_name(vtable[1], get_inf_attr(idc.INF_LONG_DN)) # unless this is a vtable for OSMetaClassBase, OSMetaClassMeta, # or OSMetaClass, skip anything metaclass related if "::MetaClass" in demangled_name: continue class_name = ida_name.extract_name(demangled_name, len("`vtable for'")) if class_name in BLACKLIST: continue ea = vtable[0] #+ 8; while ida_bytes.get_qword(ea) == 0: ea += 8 # print("EA: {}".format(hex(ea))) if is_unknown(ida_bytes.get_flags(ea)): continue # if class_name != "IOSkywalkPacket": # continue # if class_name != "AHTHSBufferStatic": # continue # if class_name != "HSMSPITest": # continue # if class_name != "AppleMesa": # continue # if class_name != "AppleUSBHostController": # continue # if class_name != "AppleEmbeddedPCIE": # continue # if class_name != "SimpleEval": # continue # if class_name != "AppleUSBCDCControl": # continue # if class_name != "IOHDCP2TransmitterAuthSession": # continue # if class_name != "IOAVService::DisplayIDParser::readDisplayID": # continue # if class_name != "IOMFB::TypedProp": # continue # if class_name != "IOMFB::UPBlock_GenPipe_v2": # continue # if class_name != "AppleMesaSEPDriver": # continue # if class_name != "IOAVController": # continue # if class_name != "AppleConvergedIPCICEBBBTIInterface": # continue # if class_name != "ApplePPM": # continue cnt += 1 # print("{}".format(class_name)) # skip NULL pointers until we hit a function while ida_bytes.get_qword(ea) == 0: ea += 8 num_virts = 0 num_dtors = 0 num_untyped = 0 num_noname = 0 fxn_name_list = [] fxn_name_dict = {} struct_fields = [] fwd_decls = set() fwd_decls.add(class_name) svt = SymbolicatedVtable(class_name) ioc = IOKitClass(svt) ioc.svt = svt # vtables seem to be NULL terminated while True: fxn_ea = ida_bytes.get_qword(ea) # end of vtable for this class if fxn_ea == 0: break # print("Type for {}/{} @ {}: {}".format(hex(fxn_ea), fxn_name, hex(ea), fxn_type)) # if fxn_type == None: # num_failed_get_type += 1 # ea += 8 # continue # default to this for ___cxa_pure_virtual fxn_args = "void" fxn_call_conv = "" fxn_mangled_name = ida_name.get_ea_name(fxn_ea) fxn_name = ida_name.demangle_name(fxn_mangled_name, get_inf_attr(idc.INF_LONG_DN)) if fxn_name == None: # ___cxa_pure_virtual fxn_name = ida_name.get_ea_name(fxn_ea) # some other thing? if len(fxn_name) == 0: fxn_name = "noname{}".format(num_noname) else: fxn_type = idc.get_type(fxn_ea) if fxn_type == None: # sometimes this happens, don't know why # the only thing fxn_type would have provided was # the calling convention, so we need to manually # reconstruct parameter list, and assume calling # convention is __fastcall # if mangled_fxn_name == "__ZN25IOGeneralMemoryDescriptor7doUnmapEP7_vm_mapyy": # exit(0) fxn_return_type = "__int64" fxn_call_conv = "__fastcall" fxn_args_string = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # if fxn_args_string == "IOService *, unsigned int, void *, void (*)(OSObject *, AppleUSBCDCControl*, void *, USBCDCNotification *)": # # print("Hello") # fxn_args_string = "IOService *, unsigned int, void *, void (*)(OSObject *, void (*)(TestType *, AppleUSBCDCControl*, void *, USBCDCNotification *), AppleUSBCDCControl*, void *, USBCDCNotification *)" # print("fxn args: {}".format(fxn_args_string)) # if fxn_args_string == "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *": # fxn_args_string = "OSObject *, void (*)(OSObject *, IOHDCPAuthSession *), IOHDCPMessageTransport *, IOHDCPInterface *, unsigned long long" fxn_args_string = fxn_args_string.replace( "{block_pointer}", "*") if fxn_args_string.find(",") != -1: # print("More than one arg: {}".format(fxn_args_list)) # extra comma makes the parser happy fxn_args_types_list = get_arg_type_list( fxn_args_string + ",") # print("More than one arg for {}: {}".format(fxn_name, fxn_args_types_list)) # print() fxn_args = "" argnum = 0 # print(type(fxn_args_types_list)) to_fwd_decl = get_fwd_decls(fxn_args_types_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) for arg_type in fxn_args_types_list: if argnum == 0: fxn_args += "{} *__hidden this, ".format( class_name) else: fxn_args += "{}, ".format(fix_type(arg_type)) argnum += 1 fxn_args = fxn_args[:-2] else: fxn_args = "{} *__hidden this".format(class_name) arg_type = fxn_name[fxn_name.find("(") + 1:fxn_name.rfind(")")] # print("Only one arg for {}: {}".format(fxn_name, arg_type)) arg_type_list = [arg_type] # print("Only one arg: {}".format(arg_type_list)) to_fwd_decl = get_fwd_decls(arg_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) if arg_type != "void" and len(arg_type) > 0: fxn_args += ", {}".format(fix_type(arg_type)) else: all_except_args = fxn_type[:fxn_type.find("(")] # first, if there's no spaces, there's no calling # convention specifed if all_except_args.find(" ") == -1: fxn_return_type = all_except_args # Also, this having no spaces could mean IDA messed # up, so we should use the demangled name instead # and parse that fxn_type = "(" + fxn_name[fxn_name.find("(") + 1:] # print("No spaces in args, using {} as fxn_type".format(fxn_type)) else: double_underscore = all_except_args.rfind("__") if double_underscore != -1: fxn_return_type = all_except_args[: double_underscore] fxn_call_conv = all_except_args[double_underscore:] else: fxn_return_type = all_except_args # get args # print("fxn_type: {}".format(fxn_type)) fxn_args = fxn_type[fxn_type.find("(") + 1:fxn_type.rfind(")")] fxn_args_type_list = get_arg_type_list(fxn_args + ",") fixed_fxn_args_type_list = [] # Fix up args for arg_type in fxn_args_type_list: # Remove __hidden arg_type = arg_type.replace("__hidden", "") # Check for a pointer. This is an easy case, we # just delete everything from the first * star = arg_type.find("*") if star != -1: arg_type = arg_type[0:star] else: # Otherwise, find the last space, and delete # from there # But in case there was no name for this # parameter, check if the token after the last # space is not an IDA type or primitive type lspace = arg_type.rfind(" ") if lspace != -1: token = arg_type[lspace:].replace(" ", "") if not is_primitive_type( token) and not is_ida_type(token): arg_type = arg_type[0:lspace] # print("arg_type: {}".format(arg_type)) fixed_fxn_args_type_list.append(arg_type) # to_fwd_decl = get_fwd_decls(fxn_args_type_list) to_fwd_decl = get_fwd_decls(fixed_fxn_args_type_list) if len(to_fwd_decl) > 0: fwd_decls.update(to_fwd_decl) # print("fxn_type is not None for {}: fxn args: {}".format(fxn_name, fxn_args_type_list)) # print("fxn_type is not None: will fwd declare: {}".format(to_fwd_decl)) # get function name # remove 'classname::' and params fxn_name = fxn_name[fxn_name.find("::") + 2:fxn_name.find("(") + 1] fxn_name = fxn_name[:fxn_name.find("(")] # replace any '~' fxn_name = fxn_name.replace("~", "DTOR{}_".format(num_dtors)) # remove any < and > fxn_name = fxn_name.replace("<", "") fxn_name = fxn_name.replace(">", "") if fxn_name in list(fxn_name_dict.keys()): fxn_name_dict[fxn_name] += 1 fxn_name += "_{}".format(fxn_name_dict[fxn_name]) else: fxn_name_dict[fxn_name] = -1 if "DTOR" in fxn_name: num_dtors += 1 curfield = "" if fxn_name == "___cxa_pure_virtual": # struct_fields.append("\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format(num_virts, # fxn_args)) curfield = "\tvoid __noreturn (__cdecl *___cxa_pure_virtual{})({});".format( num_virts, fxn_args) num_virts += 1 else: # struct_fields.append("\t{} ({} *{})({});".format(fxn_return_type, # fxn_call_conv, fxn_name, fxn_args)) curfield = "\t{} ({} *{})({});".format(fxn_return_type, fxn_call_conv, fxn_name, fxn_args) svt.add(curfield) ea += 8 # return # Some classes won't have xrefs to OSMetaClass::OSMetaClass, # like OSMetaClassBase if class_name in ih_dict: ih_dict[class_name].ioc = ioc # Just write forward decls for now for decl in fwd_decls: struct_file.write("struct __cppobj {};\n".format(decl)) struct_file.write("\n") # cnt += 1 # if cnt == 5: # break print("{} IOKit vtables".format(len(vtables))) # Now create the header file to import into IDA # for ih in ihs: # dump_ih(ih, 0) generate_header_file(struct_file, ihs) # fixup_header_file(struct_file) struct_file.close()
def read_qword(self): res = ida_bytes.get_qword(self.addr) self.inc(8) return res