def __PltResolver(jmprel, strtab, symtab): idx = 0 while True: r_off = idc.get_qword(jmprel + 0x18 * idx) r_info1 = idc.get_wide_dword(jmprel + 0x18 * idx + 0x8) r_info2 = idc.get_wide_dword(jmprel + 0x18 * idx + 0xc) r_addend = idc.get_qword(jmprel + 0x18 * idx + 0x10) if r_off > 0x7fffffff: return if r_info1 == 7: st_name = idc.get_wide_dword(symtab + r_info2 * 0x18) name = idc.get_strlit_contents(strtab + st_name) # rename got idc.set_name(r_off, name.decode("ascii") + '_ptr') plt_func = idc.get_qword(r_off) if debug_mode: print(hex(plt_func.start_ea), name) # rename plt idc.set_name(plt_func, 'j_' + name.decode("ascii")) SetFuncFlags(plt_func) # rename plt.sec for addr in idautils.DataRefsTo(r_off): plt_sec_func = idaapi.get_func(addr) if plt_sec_func: plt_sec_func_addr = plt_sec_func.start_ea idc.set_name(plt_sec_func_addr, '_' + name.decode("ascii")) SetFuncFlags(plt_sec_func_addr) else: print("[!] idaapi.get_func({}) failed".format( hex(addr))) idx += 1
def __init__(self, objc_class_va, segment_map, arch=ARCH_X86_64): """Create a new ObjcClass instance Arguments: objc_class_va {number} -- Virtual address of the Objective-C class to parse segment_map {dictionary} -- A dictionary mapping segment names to a start/end virtual address tuple Keyword Arguments: arch {number} -- CPU architecture. Either ARCH_X86_64 or ARM64 (default: {ARCH_X86_64}) """ self.arch = arch self.segment_map = segment_map class_ro_va = get_qword(objc_class_va + self.OBJC2_CLASS_RO_OFFSET) self.name_pointer = get_qword(class_ro_va + self.OBJC2_CLASS_RO_NAME_OFFSET) self.method_list = [] if class_ro_va == BADADDR or class_ro_va == 0: self.class_ro_va = None return self.class_ro_va = class_ro_va class_methods_va = get_qword(class_ro_va + self.OBJC2_CLASS_RO_BASE_METHODS_OFFSET) if class_methods_va == BADADDR or class_methods_va == 0: self.class_methods_va = None return self.class_methods_va = class_methods_va msg("Class found at virtual address: 0x%x\n" % objc_class_va) msg("Class name: %s\n" % get_strlit_contents(self.name_pointer)) #Parse the method_list_t struct and build a list of methods self.method_list = ObjcMethodList(class_methods_va, segment_map, arch=arch)
def resolve(self, alphabet, nids, symbols, libraries): if self.INFO > Relocation.R_X86_64_ORBIS_GOTPCREL_LOAD: self.INDEX = self.INFO >> 32 self.INFO &= 0xFF symbol = next(value for key, value in enumerate(symbols) if key + 2 == self.INDEX)[1] else: self.INDEX = 0 # Library try: lid1 = alphabet[symbol[12:13]] # [base64]# if symbol[13:14] == '#': library = libraries[lid1] # [base64][base64]# elif symbol[14:15] == '#': lid2 = alphabet[symbol[13:14]] library = libraries[lid1 + lid2] else: raise # Not a NID except: library = '' # Function Name (Offset) == Symbol Value + AddEnd (S + A) # Library Name (Offset) == Symbol Value (S) # Resolve the NID... idc.set_cmt(idc.get_qword(self.OFFSET) - 0x6, 'NID: ' + symbol, False) function = nids.get(symbol[:11], symbol) # Rename the Jump Function... idc.set_name(self.OFFSET, '__imp_' + function, SN_NOCHECK | SN_NOWARN | SN_FORCE) # Rename the Real Function... idc.add_func(idc.get_qword(self.OFFSET) - 0x6) idc.set_name( idc.get_qword(self.OFFSET) - 0x6, function, SN_NOCHECK | SN_NOWARN) try: import_node = idaapi.netnode(library, 0, True) import_node.supset(ea2node(self.OFFSET), function) # Requires customized loader.i / ida_loader.py(d) idaapi.import_module(library, None, import_node.index(), None, 'linux') except: pass return self.type()
def GetDyn(): phoff = idc.get_qword(ida_ida.inf_get_min_ea() + 0x20) + ida_ida.inf_get_min_ea() phnum = idc.get_wide_word(ida_ida.inf_get_min_ea() + 0x38) phentsize = idc.get_wide_word(ida_ida.inf_get_min_ea() + 0x36) for i in range(phnum): p_type = idc.get_wide_dword(phoff + phentsize * i) if p_type == 2: # PY_DYNAMIC dyn_addr = idc.get_qword(phoff + phentsize * i + 0x10) return dyn_addr
def ParseDyn(dyn, tag): idx = 0 while True: v1, v2 = idc.get_qword(dyn + idx * 0x10), idc.get_qword(dyn + idx * 0x10 + 8) if v1 == 0 and v2 == 0: return if v1 == tag: return v2 idx += 1
def __init__(self, method_va, segment_map): """Do not instantiate directly Arguments: method_va segment_map """ msg("Found method at virtual address: 0x%x\n" % method_va) self.method_va = method_va self.segment_map = segment_map self.name_pointer = get_qword(method_va) self.method_type = get_qword(method_va + self.OBJC_METHOD_TYPE_OFFSET) self.method_pointer_va = method_va + self.OBJC_METHOD_IMP_OFFSET self.method_pointer = get_qword(self.method_pointer_va) self.patched_xrefs = [] objc_selrefs = segment_map["__objc_selrefs"] objc_msgrefs = segment_map["__objc_msgrefs"] objc_const = segment_map["__objc_const"] msg("Method name: %s\n" % get_func_name(self.method_pointer)) is_msg_ref, selector_ref, const_ref_count = self.get_xref( objc_selrefs, objc_msgrefs, objc_const) self.is_msg_ref = is_msg_ref self.const_ref_count = const_ref_count if not selector_ref: msg("No selref found.\n") self.selector_ref = None return if const_ref_count == 1: #We can only work with unambiguous situations where there is exactly one const reference #to the selector. self.selector_ref = selector_ref else: msg("Selector ref count not exactly 1. Potentially ambiguous: %d" % const_ref_count) # Otherwise this same selector is used by more than one class. (Or none at all) self.selector_ref = None return self.sel_ref_va = self.selector_ref.frm if is_msg_ref: # adjust pointer to beginning of message ref struct to get xrefs self.sel_ref_va -= POINTER_SIZE msg("selref VA: 0x%X - function VA: 0x%X\n" % (self.sel_ref_va, self.method_pointer)) #Find all the references to this *selref* (note: not the string itself but the selref) #These should precede calls to the method #Patch the references to the selref with a reference to the method implementation self.walk_selector_refs()
def assign_kmdf_structure_types(address): # Get the jmp to de import jmp_import_ea = next(idautils.XrefsTo(address)).frm # There is only one XREF to WdfVersionBind call_wdfVersionBind = next(idautils.XrefsTo(jmp_import_ea)).frm print(hex(call_wdfVersionBind)) argument_WdfBindInfo = find_function_arg(call_wdfVersionBind, "lea", "r8", 0) if argument_WdfBindInfo is None: print("Error: Argument WdfBindInfo wasn't found!") return wdfBindInfo = idc.get_operand_value(argument_WdfBindInfo, 1) idc.set_name(wdfBindInfo, '_WdfBindInfo') print("WdfBindInfo Struct: ", hex(wdfBindInfo)) if not assign_struct_to_address(wdfBindInfo, "_WDF_BIND_INFO"): print("The _WDF_BIND_INFO struct wasn't found in the database") return g_vars["_WDF_BIND_INFO"] = wdfBindInfo # Assign ComponentGlobals Name argument_WdfComponentGlobals = find_function_arg(call_wdfVersionBind, "lea", "r9", 0) wdfComponentGlobals = idc.get_operand_value(argument_WdfComponentGlobals, 1) g_vars["_WDF_COMPONENT_GLOBALS"] = wdfComponentGlobals idc.set_name(wdfComponentGlobals, '_WdfComponentGlobals') # Now assign the WDFFUNCTIONS to FuncTable wdfFunctions = idc.get_qword(wdfBindInfo + 0x20) g_vars["_WDFFUNCTIONS"] = wdfFunctions assign_struct_to_address(wdfFunctions, "_WDFFUNCTIONS") idc.set_name(wdfFunctions, 'g_WdfF_Functions')
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 req_patch(self, hash): addr, value, length = hash['addr'], hash['value'], hash['len'] if length == 4: prev_value = idc.get_wide_dword(addr) if not ida_bytes.create_data(ea, FF_DWORD, 4, ida_idaapi.BADADDR): rs_log('[x] ida_bytes.create_data FF_DWORD failed') if not ida_bytes.patch_dword(addr, value): rs_log('[x] patch_dword failed') if not idc.op_plain_offset(addr, 0, 0): rs_log('[x] op_plain_offset failed') elif length == 8: prev_value = idc.get_qword(addr) if not ida_bytes.create_data(addr, FF_QWORD, 8, ida_idaapi.BADADDR): rs_log('[x] ida_bytes.create_data FF_QWORD failed') if not ida_bytes.patch_qword(addr, value): rs_log('[x] patch_qword failed') if not idc.op_plain_offset(addr, 0, 0): rs_log('[x] op_plain_offset failed') else: rs_log("[x] unsupported length: %d" % length) return rs_log("patched 0x%x = 0x%x (previous was 0x%x)" % (addr, value, prev_value))
def getDbgMem(ea, size): b = b'' for i in range(0, (size & (~7)), 8): b += struct.pack("<Q", idc.get_qword(ea + i)) for i in range(size & 7): b += struct.pack("<B", idc.Byte(ea + (size & (~7)) + i)) return b
def _getOriginData(self, address, size): res = [] for offset in range(0, size, 64): tmp = get_bytes(address + offset, 64) if tmp == None: res.extend([pack("<Q", get_qword(address + offset + i)) for i in range(0, 64, 8)]) else: res.append(tmp) res = b"".join(res) return res[:size]
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 convert_pointer_to_offset(ea): # If this is code, skip it. flags = idc.get_full_flags(ea) if idc.is_code(flags): return # If the value at this address does not point into the kernelcache, skip it. value = idc.get_qword(ea) if not is_mapped(value, 8): return # Convert this value to a qword (in case it's unaligned) and then convert it into an # offset. idc.create_qword(ea) idc.op_plain_offset(ea, 0, 0)
def load(self, addr, dtyp): if addr is Unknown: return Unknown if not is_mapped_data(addr): return Unknown if dtyp == idaapi.dt_qword: return idc.get_qword(addr) elif dtyp == idaapi.dt_dword: return idc.get_wide_dword(addr) elif dtyp == idaapi.dt_word: return idc.get_wide_word(addr) elif dtyp == idaapi.dt_byte: return idc.get_wide_byte(addr) return Unknown
def read_word(ea, wordsize=WORD_SIZE): """Get the word at the given address. Words are read using Byte(), Word(), Dword(), or Qword(), as appropriate. Addresses are checked using is_mapped(). If the address isn't mapped, then None is returned. """ if not is_mapped(ea, wordsize): return None if wordsize == 1: return idc.get_wide_byte(ea) if wordsize == 2: return idc.get_wide_word(ea) if wordsize == 4: return idc.get_wide_dword(ea) if wordsize == 8: return idc.get_qword(ea) raise ValueError('Invalid argument: wordsize={}'.format(wordsize))
def _defineImportThunk(self, start, thunk_val): """If the binary has the Import Thunk filled, define it as a data chunk of appropriate size. """ info = idaapi.get_inf_structure() if info.is_64bit(): curr_val = idc.get_qword(start) if (curr_val == thunk_val): return ida_bytes.create_data(start, idaapi.FF_QWORD, 8, idaapi.BADADDR) elif info.is_32bit(): curr_val = ida_bytes.get_dword(start) if (curr_val == thunk_val): return ida_bytes.create_data(start, idaapi.FF_DWORD, 4, idaapi.BADADDR) return False
def get_metaclass_for_vtable(vtable, length): # Get the address of the ::getMetaClass() method. OSObject_method_count = 12 getMetaClass_index = 7 if length <= OSObject_method_count: return None getMetaClass = idc.get_qword(vtable + 8 * getMetaClass_index) # Emulate the method to get the return value. class GetReturnEmulator(Arm64Emulator): def __init__(self): super(GetReturnEmulator, self).__init__() self.return_value = None def RET(self): self.return_value = self.regs['X0'] emulator = GetReturnEmulator() getMetaClass_end = getMetaClass + 4 * 4 emulator.run(getMetaClass, getMetaClass_end) return emulator.return_value
def check_vtable(start, end=None): # We recognize a vtable by looking for an array of at least 2 pointers to code followed by a # NULL. # If no end was specified, go until the end of the segment. if end is None: end = idc.get_segm_end(start) # Check each address in the table. Stop once we've found something other than a pointer to # code. ended_with_zero = False ea = start while ea < end: method = idc.get_qword(ea) if method == 0: ended_with_zero = True break if not idc.is_code(idc.get_full_flags(method)): break ea += 8 # Compute the length. length = (ea - start) / 8 possible_vtable = ended_with_zero and length >= 2 return possible_vtable, length
def reload_info(self): if not dbg.is_process_suspended(): return False base_addr = None if self.base_expr is None: base_addr = idc.get_reg_value(dbg.registers.stack) else: base_addr = idaapi.str2ea(self.base_expr) if base_addr == idc.BADADDR: idaapi.warning("Invalid base expr: %s" % self.base_expr) return False if not idaapi.is_loaded(base_addr): idaapi.warning("Memory address is not loaded: $#x" % base_addr) return False self.ClearLines() dbg.set_thread_info() try: segm_end = idc.get_segm_end(base_addr) n_entries = config.n_stack_entries or ((segm_end-base_addr) // dbg.ptr_size) for i in range(n_entries): offset = i * dbg.ptr_size ptr = base_addr + offset if not idaapi.is_loaded(ptr): break value = idc.get_qword(ptr) self.add_line("%02d:%04X %s" % (i, offset, self.parse_value(ptr))) except Exception as e: idaapi.warning(str(e)) return False return True
def extract_vtable_pac_codes(vtable_ea): pac_codes = [] # Open the file. path = idc.get_input_file_path() with open(path, "rb") as kernelcache_file: # Seek to the offset of the vtable. offset = idaapi.get_fileregion_offset(vtable_ea) kernelcache_file.seek(offset) # Loop over each entry in the vtable. ea = vtable_ea while True: # Break if we've reached the end of the vtable. vmethod = idc.get_qword(ea) if vmethod == 0: break # Get the original value from the original file. original = kernelcache_file.read(8) value, = struct.unpack("<Q", original) # Extract the type code and add it to the list. pac_code = (value & 0x0000ffff00000000) >> 32 pac_codes.append(pac_code) # Advance. ea += 8 return pac_codes
def is_code_ptr(ea): return is_mapped_data(ea, 4) and idc.is_code( idc.get_full_flags(idc.get_qword(ea)))
def process_mod_init_segment_for_metaclasses(start, end, found_metaclass): for ea in range(start, end, 8): func = idc.get_qword(ea) process_mod_init_func_for_metaclasses(func, found_metaclass)
def getQWordValue(self, addr): return idc.get_qword(addr)
def get_qword(self, ea): return idc.get_qword(ea)
def cmd_get_qword(self, addr): return str(idc.get_qword(int(addr, 0)))
def calculate_caller_function_frame_info_x64(ea, sp, bp): PTR_SIZE = 8 debug('ea: 0x{:X}'.format(ea)) function_info = idaapi.get_func(ea) if function_info: function_start_ea = function_info.start_ea function_end_ea = function_info.end_ea else: debug('function_info is None') _, _, function_start_ea, function_end_ea = get_func_name_and_call_offset( ea) debug('function_start_ea: 0x{:X}, function_end_ea: 0x{:X}'.format( function_start_ea, function_end_ea)) prev_ea = idaapi.decode_prev_insn(insn, function_end_ea) #often padding is present at the end of a function. Iterate by padding to the last function's instruction if prev_ea == idaapi.BADADDR: prev_ea = FunctionAssemblyCalculator.iterateByPaddingToTheLastInstruction( function_end_ea) #often ret is not at the end of the function but earlier. Find it prev_ea = FunctionAssemblyCalculator.findRetInstruction(prev_ea) #case 2: find a 'pop rbp' or 'add rsp, X'. Skip or pops #prev_ea point to 'ret' instruction. Get previous instrucion prev_ea = idaapi.decode_prev_insn(insn, prev_ea) #if at the end some values are popped from stack then we need remember that pop_instruction_number = 0 while idc.print_insn_mnem(prev_ea) == "pop": debug(idc.get_operand_value(prev_ea, 0)) if idc.get_operand_value(prev_ea, 0) == OperandValueRegister.RBP: #pop rbp found. Unfortunately I don't konw a value of current rbp so I need calculate. debug('CASE 2.1: calculate bp from function prolog') # there is a need to use rbp. Unfortunately we don't know a corelation between rsp and rbp, so we need use simple heuristic to deterimne it. # Script will scan function from beginning looking for operation like: mov rbp, rsp or lea rbp, [rsp+0x00] difference = FunctionAssemblyCalculator.findRspRbpDifference( function_start_ea) #values for current funciton sp = bp + difference ret = idc.get_qword(sp) bp = sp - PTR_SIZE #Calculate registers for caller. ret takes a value from stack so I need change sp one more time sp += PTR_SIZE bp = idc.get_qword(bp) #get bp value from stack debug('sp: 0x{:X}'.format(sp)) debug('bp: 0x{:X}'.format(bp)) return (ret, sp, bp) else: prev_ea = idaapi.decode_prev_insn(insn, prev_ea) pop_instruction_number += 1 #CASE 2.2: get func base pointer from rbp debug('CASE 2.2: calclulate bp from function epilog') debug(idc.print_insn_mnem(prev_ea), idc.get_operand_value(prev_ea, 0)) old_sp = sp function_stack_size = 0 mnem = idc.print_insn_mnem(prev_ea) arg_0_type = idc.get_operand_type(prev_ea, 0) arg_0_value = idc.get_operand_value(prev_ea, 0) arg_1_type = idc.get_operand_type(prev_ea, 1) if mnem == "add" and arg_0_type == OperandType.GENERAL_REG and arg_0_value == OperandValueRegister.RSP and arg_1_type == OperandType.IMMEDIATE_VALUE: #this function has own place for stack. Count it function_stack_size = idc.get_operand_value(prev_ea, 1) sp += function_stack_size sp = sp + pop_instruction_number * PTR_SIZE ret = idc.get_qword(sp) #ret takes a value from stack so I need change sp one more time sp += PTR_SIZE #one more thing to do. Often previous instruction restoring rbp prev_ea = idaapi.decode_prev_insn(insn, prev_ea) mnem = idc.print_insn_mnem(prev_ea) arg_0_type = idc.get_operand_type(prev_ea, 0) arg_0_value = idc.get_operand_value(prev_ea, 0) arg_1_type = idc.get_operand_type(prev_ea, 1) if mnem == 'mov' and arg_0_type == OperandType.GENERAL_REG and arg_0_value == OperandValueRegister.RBP and arg_1_type == OperandType.MEMORY_REG: bp = idc.get_qword(old_sp + idc.get_operand_value(prev_ea, 1)) debug(mnem, 'type0: ', arg_0_type, arg_0_value, 'type0: ', arg_1_type, idc.get_operand_value(prev_ea, 1)) return (ret, sp, bp)