def vtable_overrides(class_vtable, super_vtable, class_vlength=None, super_vlength=None, new=False, methods=False): """Get the overrides of a virtual method table. A generator that returns the index of each override in the virtual method table. The initial empty entries are skipped, so the first virtual method is at index 0. Arguments: class_vtable: The vtable of the class. super_vtable: The vtable of the ancestor to compare against for overrides. Options: class_vlength: The length of class_vtable. If None, it will be calculated. super_vlength: The length of super_vtable. If None, it will be calculated. new: If True, include new virtual methods not present in the superclass. Default is False. methods: If True, then the generator will produce a tuple containing the index, the overridden method in the subclass, and the original method in the superclas, rather than just the index. Default is False. """ assert class_vtable # Get the vtable lengths. if class_vlength is None: class_vlength = vtable_length(class_vtable) if super_vlength is None: super_vlength = vtable_length(super_vtable) assert class_vlength >= super_vlength >= 0 # Skip the first VTABLE_OFFSET entries. class_vtable += VTABLE_OFFSET * idau.WORD_SIZE super_vtable += VTABLE_OFFSET * idau.WORD_SIZE class_vlength -= VTABLE_OFFSET super_vlength -= VTABLE_OFFSET # How many methods are we iterating over? if new: nmethods = class_vlength else: nmethods = super_vlength # Iterate through the methods. for i in xrange(nmethods): # Read the old method. super_method = None if i < super_vlength: super_method = idau.read_word(super_vtable + i * idau.WORD_SIZE) # Read the new method. (It's always in range.) class_method = idau.read_word(class_vtable + i * idau.WORD_SIZE) # If they're different, yield. if class_method != super_method: if methods: yield i, class_method, super_method else: yield i
def vtable_methods(vtable, start=VTABLE_OFFSET, length=None, nmethods=None): """Get the methods in a virtual method table. A generator that returns each method in the virtual method table. The initial empty entries are skipped. Arguments: vtable: The address of the virtual method table. (This includes the initial empty entries.) Options: start: The index at which to start returning values. All prior indexes are skipped. Default is VTABLE_OFFSET, meaning the initial empty entries will be skipped. length: The length of the vtable, including the initial empty entries. Specify this value to read the entire vtable if the length is already known. nmethods: The number of methods to read, excluding the initial empty entries. If None, the whole vtable will be read. Default is None. """ assert vtable # Get the length of the vtable. if nmethods is not None: length = nmethods + VTABLE_OFFSET elif length is None: length = vtable_length(vtable) # Read the methods. for i in xrange(start, length): yield idau.read_word(vtable + i * idau.WORD_SIZE)
def load(addr, dtyp): if not addr: return None if dtyp == idaapi.dt_qword: size = 8 elif dtyp == idaapi.dt_dword: size = 4 else: return None return idau.read_word(addr, size)
def _get_vtable_metaclass(vtable_addr, metaclass_info): """Simulate the getMetaClass method of the vtable and check if it returns an OSMetaClass.""" getMetaClass = idau.read_word(vtable_addr + _VTABLE_GETMETACLASS * idau.WORD_SIZE) def on_RET(reg): on_RET.ret = reg['X0'] on_RET.ret = None _emulate_arm64(getMetaClass, getMetaClass + idau.WORD_SIZE * _MAX_GETMETACLASS_INSNS, on_RET=on_RET) if on_RET.ret in metaclass_info: return on_RET.ret
def untag_pointers_in_range(start, end): assert kernel.kernelcache_format == kernel.KC_12_MERGED, 'Wrong kernelcache format' ea, tp = start, None while True: ea = tagged_pointer_next(ea, tp, end) if ea is None or ea >= end: break tp = idau.read_word(ea) if not is_tagged_pointer(tp): _log(1, 'Tagged pointer traversal failed: ea={:x}, tp={:x}'.format(ea, tp)) break untag_pointer(ea, tp)
def class_vtable_method(classinfo, index): """Get the virtual method for a class by index. Arguments: classinfo: The class information of the class. index: The index of the virtual method, skipping the empty entries (that is, the first virtual method is at index 0). """ # Get the vtable for the class. methods = classinfo.vtable_methods count = classinfo.vtable_nmethods if index >= count: return None return idau.read_word(methods + index * idau.WORD_SIZE)
def _process_stub_template_1(stub): """A template to match the following stub pattern: ADRP X<reg>, #<offset>@PAGE LDR X<reg>, [X<reg>, #<offset>@PAGEOFF] BR X<reg> """ adrp, ldr, br = idau.Instructions(stub, count=3) if (adrp.itype == idaapi.ARM_adrp and adrp.Op1.type == idaapi.o_reg and adrp.Op2.type == idaapi.o_imm and ldr.itype == idaapi.ARM_ldr and ldr.Op1.type == idaapi.o_reg and ldr.Op2.type == idaapi.o_displ and ldr.auxpref == 0 and br.itype == idaapi.ARM_br and br.Op1.type == idaapi.o_reg and adrp.Op1.reg == ldr.Op1.reg == ldr.Op2.reg == br.Op1.reg): offset = adrp.Op2.value + ldr.Op2.addr target = idau.read_word(offset) if target and idau.is_mapped(target): return target