def create_func_by_prefix(func_name, prefix, force=False): addrs = [] start_addr = 0 func_addr = 0 while func_addr != idc.BADADDR: func_addr = ida_search.find_binary(start_addr, idc.BADADDR, prefix, 16, idc.SEARCH_DOWN) if func_addr == idc.BADADDR: break # already existing function but it is not the right prefix addr = idc.get_func_attr(func_addr, idc.FUNCATTR_START) if addr != idc.BADADDR and func_addr != addr: if not force: start_addr = func_addr + 4 continue idc.del_func(addr) idc.del_items(func_addr) # add_func is not applied to the existing function idc.add_func(func_addr) func_name = set_entry_name(func_addr, func_name) print("%s: 0x%x" % (func_name, func_addr)) addrs.append(func_addr) start_addr = func_addr + 4 return addrs
def add_stub_func(va, sc, nm): idaapi.patch_bytes(va, binascii.unhexlify(sc)) idc.create_insn(va) idc.add_func(va) mykutils.makename_safe(va, self._stubname(nm)) cmt = ('%s implementation generated by FLARE Code Grafter' % (nm)) idc.set_cmt(va, cmt, 1)
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 define_func(addr, name): if addr & 1: addr -= 1 idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 1, idaapi.SR_user) else: idaapi.split_sreg_range(addr, idaapi.str2reg("T"), 0, idaapi.SR_user) if idaapi.create_insn(addr): idc.add_func(addr) idaapi.set_name(addr, name, idaapi.SN_FORCE)
def format_vfunc_name(self, ea, current_func_name, proposed_func_name, class_name, parent_class_names): if current_func_name.startswith("j_"): # jump current_func_name = current_func_name.lstrip("j_") if current_func_name.startswith("qword_"): idc.auto_mark_range(ea, ea + 1, idc.AU_CODE) idc.create_insn(ea) idc.add_func(ea) current_func_name = api.get_addr_name(ea) print( "Info: qword in vtbl of {1} at 0x{0:X}, it may be an offset to undefined code" .format(ea, class_name)) # Previously renamed as a vfunc if current_func_name.startswith(class_name): # return the proposed func name in case it was updated since last run current_class_name = current_func_name.rsplit(".", 1)[0] return "{0}.{1}".format(current_class_name, proposed_func_name) # This should have been handled in the parent class if any( current_func_name.startswith(name) for name in parent_class_names): return "" if current_func_name.startswith("sub_"): return "{0}.{1}".format(class_name, proposed_func_name) if current_func_name.startswith("nullsub_"): return "{0}.{1}_nullsub".format(class_name, proposed_func_name) if current_func_name.startswith("loc_"): return "{0}.{1}_loc".format(class_name, proposed_func_name) if current_func_name.startswith("locret_"): return "{0}.{1}_locret".format(class_name, proposed_func_name) # Name it later in a child class when it gets overridden if current_func_name == "_purecall": return "" # Mangled func names, thanks IDA if current_func_name.startswith( "?") or current_func_name.startswith("_"): return "{0}.{1}".format(class_name, proposed_func_name) return None
def resolve(self, address, nids, symbol): # Resolve the NID... idc.set_cmt(self.VALUE, 'NID: ' + symbol, False) function = nids.get(symbol[:11], symbol) #print('Function: %s | number: %s' % (function, idaapi.get_func_num(self.VALUE))) if idaapi.get_func_num(self.VALUE) > 0: idc.del_func(self.VALUE) if self.VALUE > 0: idc.add_func(self.VALUE) idc.add_entry(self.VALUE, self.VALUE, function, True) idc.set_name(self.VALUE, function, SN_NOCHECK | SN_NOWARN) idc.set_cmt(address, '%s | %s' % (function, self.info()), False)
def remake_main(): # 先新建好所有函数, 再执行 # -- 脚本将start~end所有函数 undefined, 在start处make function import ida_bytes start_addr = 0x0402126 end_addr = 0x0402220 for i in range(start_addr, end_addr): ida_bytes.del_items(i) idc.jumpto(start_addr) idc.add_func(start_addr) import ida_hexrays # open pseudocode view ida_hexrays.open_pseudocode(0x0402126, ida_hexrays.OPF_NO_WAIT)
def makeUnkPushFuncs(start_ea, end_ea): """ Finds and fixes all dead functions not declared as functions following the pattern PUSH {..., LR} POP {..., PC} This also only makes functions until the first occurrance of a POP {..., PC}. However, this results in a function range error, and that can be fixed as well. :param start_ea: start of the range to fix :param end_ea: end of the range to fix :return: """ ea, pop_ea = next.deadfunc(start_ea, end_ea, ui=False, hexOut=False) while ea < end_ea: d = Data.Data(ea) if d.isCode(): print('Adding unknown push func @ %07X' % ea) idc.add_func(ea, pop_ea + 2) ea, pop_ea = next.deadfunc(pop_ea, end_ea, ui=False, hexOut=False)
def check_address(address): # Checks if given address contains virtual table. Returns True if more than 2 function pointers found # Also if table's addresses point to code in executable section, than tries to make functions at that addresses if helper.is_code_ea(address): return False if not idaapi.get_name(address): return False functions_count = 0 while True: func_address = helper.get_ptr(address) # print "[INFO] Address 0x{0:08X}".format(func_address) if helper.is_code_ea(func_address) or helper.is_imported_ea( func_address): functions_count += 1 address += const.EA_SIZE else: segment = idaapi.getseg(func_address) if segment and segment.perm & idaapi.SEGPERM_EXEC: idc.del_items(func_address, 1, idaapi.DELIT_SIMPLE) if idc.add_func(func_address): functions_count += 1 address += const.EA_SIZE continue break idaapi.auto_wait() return functions_count
def load_file(f, neflags, format): print('# PS3 Syscon Loader') # PS3 Syscon Processor and Library processor('arm', 'gnulnx_arm') print('# Creating ROM Segment...') address = 0x0 end = address + f.size() f.file2base(address, address, end, FILEREG_PATCHABLE) idaapi.add_segm(0x0, address, end, 'ROM', 'CODE', 0x0) # Processor Specific Segment Details idc.set_segm_addressing(address, 0x1) idc.set_segm_alignment(address, saAbs) idc.set_segm_combination(address, scPriv) idc.set_segm_attr(address, SEGATTR_PERM, SEGPERM_MAXVAL) idc.set_default_sreg_value(address, 'T', 0x0) idc.set_default_sreg_value(address, 'DS', 0x1) print('# Waiting for the AutoAnalyzer to Complete...') idaapi.auto_wait() # Create some missing functions... while address < end: address = idaapi.find_binary(address, end, '?? B5', 0x10, SEARCH_DOWN) idaapi.create_insn(address) # Pablo escobar if idc.print_insn_mnem(address + 2) in ['LDR', 'MOVS', 'SUB']: idc.add_func(address) else: idaapi.do_unknown(address) address += 4 print('# Done!') return 1 # PROGRAM END
def try_make_function(function_start, function_end=idc.BADADDR, target_location=None, require_term=True, end_mnem_bytes=None): """ Description: Given a function location, attempt to create a function. If function creation fails, delete any partially created functions. If function creation succeeds, ensure all of the function's bytes are analyzed as code. Input: function_start - The start_ea of the function to create function_end - The end_ea of the function to create. IDA will calculate if not provided. target_location - If provided, fail function creation if it does not include this EA require_term - If provided, fail function creation if the last instruction is not a ret or jmp end_mnem_bytes - If provided, fail function creation if the last instruction is not the provided bytes Instructions are entered as space separated bytes (i.e. '55' for 'push ebp') Output: Returns a tuple (function_start, function_end) for the created function if successful, None otherwise """ if function_start <= function_end: if idc.add_func(function_start, function_end): logger.debug('Created a function 0x%X - 0x%X.' % (function_start, function_end)) if require_term: last_mnem_ea = idc.get_item_head( idaapi.get_func(function_start).end_ea - 1) last_mnem = idc.print_insn_mnem(last_mnem_ea) if (end_mnem_bytes is None and 'ret' not in last_mnem and 'jmp' not in last_mnem) or \ (end_mnem_bytes and idc.get_bytes(last_mnem_ea, idc.get_item_size(last_mnem_ea)).encode('hex').upper() != end_mnem_bytes.upper()): idc.del_func(function_start) logger.debug( 'Deleted function at 0x%X - the function didn\'t end with the correct mnem/bytes.' % function_start) return if target_location is not None: if function_start <= target_location < idaapi.get_func( function_start).end_ea: idc.plan_and_wait(function_start, idaapi.get_func(function_start).end_ea) return function_start, function_end else: idc.del_func(function_start) logger.debug( 'Deleted function at 0x%X - the function didn\'t contain the target location.' % function_start) return else: logger.debug( 'Tried to create a function 0x%X - 0x%X, but IDA wouldn\'t do it.' % (function_start, function_end)) else: logger.debug('The end address was not greater than the start address!')
def fixFunctionRanges(start_ea, end_ea): """ Fixes all functions with improper returns, by finding their returns and changing their ranges For each function, it will ensure that it ends properly until the start of another function, or a data element with data xrefs to it. If it ends improperly, or there exists a matching return that is not part of the function, it's made part of the function This may not behave correctly around dead functions or null_subs. Run tools to Detect and fix those first. :param start_ea: start of the range to fix functions in :param end_ea: end of the range to fix functions in :return: """ # only look 50 instructions ahead, for range change searchLimit = 50 for func_ea in idautils.Functions(start_ea, end_ea): f = Function.Function(func_ea) # absolute end address of the function (if another function is detected or a data item with data xrefs is) stop_ea = f.func_ea + f.getSize() for i in range(stop_ea, stop_ea + searchLimit): if Function.isFunction(i) or Data.Data(i).getXRefsTo()[1]: stop_ea = i break # figure out the first return, and return type of this function. That should be consistent ret_ea = next.ret(f.func_ea, ui=False, hexOut=False) try: retType = InstDecoder.Inst(ret_ea).fields['magic'] # modify the function range to include all returns if Function.isFunction(ret_ea): ret_ea = next.unkret(f.func_ea, ui=False, hexOut=False) # this ret_ea is not within the function, if the return type is different if InstDecoder.Inst(ret_ea).fields['magic'] != retType: continue while f.func_ea < ret_ea < stop_ea: # detected that the function range is invalid, fix range print('ret %07X' % ret_ea) ret_ea = next.unkret(ret_ea, ui=False, hexOut=False) # this ret_ea is not within the function, if the return type is different if InstDecoder.Inst(ret_ea).fields['magic'] != retType: break idc.add_func(func_ea, ret_ea + 2) except ValueError: continue
def OnRefresh(self): self.Clear() addr_id = {} for (tid, chain) in self.result.items(): # Each node data will contain a tuple of the form: (Boolean->Is_thread, Int->Value, String->Label) # For threads the is_thread will be true and the value will hold the thread id # For exception handlers, is_thread=False and Value=Handler address # Add the thread node id_parent = self.AddNode( (True, tid, "Thread %X" % tid) ) # Add each handler for handler in chain: # Check if a function is created at the handler's address f = idaapi.get_func(handler) if not f: # create function idc.add_func(handler, idaapi.BADADDR) # Node label is function name or address s = get_func_name(handler) if not s: s = "%x" % handler # cache name self.names[handler] = s # Get the node id given the handler address # We use an addr -> id dictionary so that similar addresses get similar node id if not addr_id.has_key(handler): id = self.AddNode( (False, handler, s) ) addr_id[handler] = id # add this ID else: id = addr_id[handler] # Link handlers to each other self.AddEdge(id_parent, id) id_parent = id return True
def xex_load_exports(li): global export_table_va export_table = HvImageExportTable() slen = ctypes.sizeof(export_table) bytes = ida_bytes.get_bytes(export_table_va, slen) fit = min(len(bytes), slen) ctypes.memmove(ctypes.addressof(export_table), bytes, fit) if export_table.Magic[0] != XEX_EXPORT_MAGIC_0 or export_table.Magic[ 1] != XEX_EXPORT_MAGIC_1 or export_table.Magic[ 2] != XEX_EXPORT_MAGIC_2: print("[+] Export table magic is invalid! (0x%X 0x%X 0x%X)" % (export_table.Magic[0], export_table.Magic[1], export_table.Magic[2])) return 0 print("[+] Loading module exports...") print(export_table) ordinal_addrs_va = export_table_va + slen for i in range(0, export_table.Count): func_ord = export_table.Base + i func_va = ida_bytes.get_dword(ordinal_addrs_va + (i * 4)) if func_va == 0: continue func_va = func_va + (export_table.ImageBaseAddress << 16) func_name = x360_imports.DoNameGen(idc.get_root_filename(), 0, func_ord) # Add to exports list & mark as func if inside a code section func_segmclass = ida_segment.get_segm_class( ida_segment.getseg(func_va)) idc.add_entry(func_ord, func_va, func_name, 1 if func_segmclass == "CODE" else 0) if func_segmclass == "CODE": idc.add_func(func_va) return 1
def get_func_end(self, start): if (idc.add_func(start)): return idc.find_func_end(start) ea = start while (idc.get_wide_byte(ea) != 0xCC): idc.create_insn(ea) ea += idc.get_item_size(ea) if (ea - start > self.jit_max_size): return 0 return ea
def create(cls, start, end=None): """ Class method allowing to create a new function. :param int start: Start address for the function to create. :param int end: Facultative argument which indicate the end address of the function. If is is not provided (None, default value) it will try to create a function using the auto-analysis of IDA. :return: A new :class:`BipFunction` object corresponding to the function create. If this function was not able to create the new function a ``BipError`` will be raised. """ if end is None: end = 0xffffffffffffffff # default IDA value meaning auto analysis if not idc.add_func(start, end): raise BipError("Unable to create function at 0x{:X}".format(start)) return cls(start)
def fix_func_prolog(ea, end_ea=idc.BADADDR): global FUNC_BY_LS func_cnt = 0 func = ida_funcs.get_fchunk(ea) if func is None: func = ida_funcs.get_next_func(ea) ea = func.start_ea while func is not None and ea < end_ea: # if current function is small enough and there exists a function right # next to current function if (func.size() <= 8 and idc.get_func_attr( func.end_ea, idc.FUNCATTR_START) != idc.BADADDR): # If the next function can be connected, there must be a basic block reference. # xref.type == 21 means 'fl_F', which is an ordinary flow. if all( (func.start_ea <= xref.frm < func.end_ea) and xref.type == 21 for xref in XrefsTo(func.end_ea)): if func_cnt > 0 and func_cnt % 1000 == 0: print("%x <- %x: prolog merging (%d)." % (func.start_ea, func.end_ea, func_cnt)) ida_bytes.del_items(func.end_ea, ida_bytes.DELIT_EXPAND) ida_bytes.del_items(func.start_ea, ida_bytes.DELIT_EXPAND) ida_auto.auto_wait() status = idc.add_func(func.start_ea) if not status: print("Error merging 0x%x <- 0x%x" % (func.start_ea, func.end_ea)) else: func_cnt += 1 FUNC_BY_LS.discard(func.end_ea) ida_auto.auto_wait() func = ida_funcs.get_next_func(ea) if func: ea = func.start_ea print("Fixed %d functions" % func_cnt)
def analyze_linear_sweep(start_ea, end_ea=idc.BADADDR): global FUNC_BY_LS, FUNC_BY_LS_TIME if "FUNC_BY_LS" not in globals() or len(FUNC_BY_LS) == 0: FUNC_BY_LS = set() cand_cnt = 0 func_cnt = 0 ea = start_ea start_time = time.time() while ea < end_ea and ea != idc.BADADDR: ea, mode = find_next_func_cand(ea, end_ea) if ea == idc.BADADDR: break cand_cnt += 1 if cand_cnt % 10000 == 0: print("%x: %d/%d function has been found (%d secs)" % (ea, func_cnt, cand_cnt, time.time() - start_time)) # set IDA segment register to specify ARM mode old_flag = idc.get_sreg(ea, "T") if mode == THUMB: idc.split_sreg_range(ea, "T", 1, idc.SR_user) elif mode == ARM: idc.split_sreg_range(ea, "T", 0, idc.SR_user) else: print("Unknown mode") raise NotImplemented # add_func ignores the existing function, but existing function is # already filtered when finding the candidate status = idc.add_func(ea) if status: func_cnt += 1 FUNC_BY_LS.add(ea) # Wait IDA's auto analysis ida_auto.auto_wait() # even though add_func succeed, it may not be correct. # TODO: how to check the correctness? we may check the function end? func_end = idc.get_func_attr(ea, idc.FUNCATTR_END) if func_end > ea: ea = func_end else: # sometimes, ida returns wrong addr ea = next_addr_aligned(ea) else: if idc.get_func_attr(ea, idc.FUNCATTR_START) == idc.BADADDR: # IDA automatically make code, and this remains even though # add_func fails. ida_bytes.del_items(ea, ida_bytes.DELIT_EXPAND) # reset IDA segment register to previous ARM mode idc.split_sreg_range(ea, "T", old_flag, idc.SR_user) # Wait IDA's auto analysis ida_auto.auto_wait() ea = next_addr_aligned(ea) # linear sweep may choose wrong prologs. We merge the prologs of two # adjacent functions. if func_cnt > 0: fix_func_prolog(start_ea, end_ea) FUNC_BY_LS_TIME = time.time() - start_time print("Found %d/%d functions. (%d sec)" % (len(FUNC_BY_LS), cand_cnt, FUNC_BY_LS_TIME))
def load_file(f, neflags, format): print('# PS4 Module Loader') ps = Binary(f) # PS4 Processor, Compiler, Library bitness = ps.procomp('metapc', CM_N64 | CM_M_NN | CM_CC_FASTCALL, 'gnulnx_x64') # Load Aerolib... nids = load_nids(idc.idadir() + '/loaders/aerolib.csv') # Segment Loading... for segm in ps.E_SEGMENTS: # Process Loadable Segments... if segm.name() in [ 'CODE', 'DATA', 'SCE_RELRO', 'DYNAMIC', 'GNU_EH_FRAME', 'SCE_DYNLIBDATA' ]: address = segm.MEM_ADDR if segm.name() not in [ 'DYNAMIC', 'SCE_DYNLIBDATA' ] else segm.OFFSET + 0x1000000 size = segm.MEM_SIZE if segm.name() not in [ 'DYNAMIC', 'SCE_DYNLIBDATA' ] else segm.FILE_SIZE print('# Processing %s Segment...' % segm.name()) f.file2base(segm.OFFSET, address, address + segm.FILE_SIZE, FILEREG_PATCHABLE) if segm.name() not in ['DYNAMIC', 'GNU_EH_FRAME']: idaapi.add_segm(0, address, address + size, segm.name(), segm.type(), ADDSEG_NOTRUNC | ADDSEG_FILLGAP) # Processor Specific Segment Details idc.set_segm_addressing(address, bitness) idc.set_segm_alignment(address, segm.alignment()) idc.set_segm_attr(address, SEGATTR_PERM, segm.flags()) # Process Dynamic Segment.... elif segm.name() == 'DYNAMIC': stubs = {} modules = {} libraries = {} f.seek(segm.OFFSET) offset = segm.OFFSET dynamic = address dynamicsize = size for entry in xrange(size / 0x10): idc.set_cmt(address + (entry * 0x10), Dynamic(f).process(stubs, modules, libraries), False) ''' # Process Exception Handling Segment... elif segm.name() == 'GNU_EH_FRAME': # Exception Handling Frame Header Structure members = [('version', 'Version', 0x1), ('eh_frame_ptr_enc', 'Encoding of Exception Handling Frame Pointer', 0x1), ('fde_count_enc', 'Encoding of Frame Description Entry Count', 0x1), ('table_enc', 'Encoding of Table Entries', 0x1)] struct = segm.struct('EHFrame', members) idaapi.create_struct(address, 0x4, struct) # Exception Handling Structure members = [('exception', 'value', 0x8)] struct = segm.struct('Exception', members) for entry in xrange(size / 0x8): idaapi.create_struct(address + (entry * 0x8), 0x8, struct) ''' # Process SCE 'Special' Shared Object Segment... if segm.name() == 'SCE_DYNLIBDATA': # SCE Fingerprint idc.make_array(address, 0x14) idc.set_name(address, 'SCE_FINGERPRINT', SN_NOCHECK | SN_NOWARN | SN_FORCE) idc.set_cmt( address, ' '.join( x.encode('hex') for x in idc.get_bytes(address, 0x14)).upper(), False) # Dynamic Symbol Table try: # -------------------------------------------------------------------------------------------------------- # Dynamic Symbol Entry Structure members = [('name', 'Name (String Index)', 0x4), ('info', 'Info (Binding : Type)', 0x1), ('other', 'Other', 0x1), ('shtndx', 'Section Index', 0x2), ('value', 'Value', 0x8), ('size', 'Size', 0x8)] struct = segm.struct('Symbol', members) # Dynamic Symbol Table location = address + Dynamic.SYMTAB f.seek(segm.OFFSET + Dynamic.SYMTAB) symbols = {} for entry in xrange(Dynamic.SYMTABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Symbol(f).process(symbols), False) except: pass # Dynamic String Table try: # -------------------------------------------------------------------------------------------------------- # Dynamic String Table location = address + Dynamic.STRTAB f.seek(segm.OFFSET + Dynamic.STRTAB) # Stubs for key in stubs: idc.create_strlit(location + key, BADADDR) stubs[key] = idc.get_strlit_contents( location + key, BADADDR) idc.set_cmt(location + key, 'Stub', False) #print('Stubs: %s' % stubs) # Modules for key in modules: idc.create_strlit(location + key, BADADDR) modules[key] = idc.get_strlit_contents( location + key, BADADDR) idc.set_cmt(location + key, 'Module', False) #print('Modules: %s' % modules) # Libraries and LIDs lids = {} for key, value in libraries.iteritems(): idc.create_strlit(location + key, BADADDR) lids[value] = idc.get_strlit_contents( location + key, BADADDR) libraries[key] = idc.get_strlit_contents( location + key, BADADDR) idc.set_cmt(location + key, 'Library', False) #print('LIDs: %s' % lids) # Symbols for key in symbols: idc.create_strlit(location + key, BADADDR) symbols[key] = idc.get_strlit_contents( location + key, BADADDR) idc.set_cmt(location + key, 'Symbol', False) #print('Symbols: %s' % symbols) except: pass # Resolve Export Symbols try: symbols = sorted(symbols.iteritems()) location = address + Dynamic.SYMTAB + 0x30 f.seek(segm.OFFSET + Dynamic.SYMTAB + 0x30) for entry in xrange((Dynamic.SYMTABSZ - 0x30) / 0x18): Symbol(f).resolve(location + (entry * 0x18), nids, symbols[entry][1]) except: pass # Jump Table try: # -------------------------------------------------------------------------------------------------------- # Jump Entry Structure members = [('offset', 'Offset (String Index)', 0x8), ('info', 'Info (Symbol Index : Relocation Code)', 0x8), ('addend', 'AddEnd', 0x8)] struct = segm.struct('Jump', members) # PS4 Base64 Alphabet base64 = list( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-' ) alphabet = { character: index for index, character in enumerate(base64) } #print('Base64 Table: %s' % alphabet) # Jump Table location = address + Dynamic.JMPTAB f.seek(segm.OFFSET + Dynamic.JMPTAB) for entry in xrange(Dynamic.JMPTABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt( location + (entry * 0x18), Relocation(f).resolve(alphabet, nids, symbols, lids), False) except: pass # Relocation Table try: # -------------------------------------------------------------------------------------------------------- # Relocation Entry Structure (with specific addends) members = [('offset', 'Offset (String Index)', 0x8), ('info', 'Info (Symbol Index : Relocation Code)', 0x8), ('addend', 'AddEnd', 0x8)] struct = segm.struct('Relocation', members) # Relocation Table (with specific addends) location = address + Dynamic.RELATAB f.seek(segm.OFFSET + Dynamic.RELATAB) for entry in xrange(Dynamic.RELATABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Relocation(f).process(nids, symbols), False) except: pass # Hash Table try: # -------------------------------------------------------------------------------------------------------- # Hash Entry Structure members = [('bucket', 'Bucket', 0x2), ('chain', 'Chain', 0x2), ('buckets', 'Buckets', 0x2), ('chains', 'Chains', 0x2)] struct = segm.struct('Hash', members) # Hash Table location = address + Dynamic.HASHTAB f.seek(segm.OFFSET + Dynamic.HASHTAB) for entry in xrange(Dynamic.HASHTABSZ / 0x8): idaapi.create_struct(location + (entry * 0x8), 0x8, struct) except: pass # Dynamic Tag Table try: # -------------------------------------------------------------------------------------------------------- # Dynamic Tag Entry Structure members = [('tag', 'Tag', 0x8), ('value', 'Value', 0x8)] struct = segm.struct('Tag', members) f.seek(offset) for entry in xrange(dynamicsize / 0x10): idaapi.create_struct(dynamic + (entry * 0x10), 0x10, struct) idc.set_cmt( dynamic + (entry * 0x10), Dynamic(f).comment(address, stubs, modules, libraries), False) except: pass # Start Function idc.add_entry(ps.E_START_ADDR, ps.E_START_ADDR, 'start', True) print('# Waiting for the AutoAnalyzer to Complete...') idaapi.auto_wait() # Set No Return for __stack_chk_fail... try: function = idc.get_name_ea_simple('__stack_chk_fail') function = idaapi.get_func(function) function.flags |= FUNC_NORET idaapi.update_func(function) except: pass # Missed Function Creation... try: code = idaapi.get_segm_by_name('CODE') address = code.start_ea end = code.end_ea # Final Pass print('# Performing Final Pass...') while address < end: address = idaapi.find_not_func(address, SEARCH_DOWN) if idaapi.is_unknown(idaapi.get_flags(address)): idaapi.create_insn(address) else: idc.add_func(address) address += 4 except: pass print('# Done!') return 1
def xex_load_imports(li): global directory_entry_headers li.seek(directory_entry_headers[XEX_HEADER_IMPORTS]) import_desc = read_struct(li, XEXImportDescriptor) # seperate the library names in the name table import_libnames = [] cur_lib = "" for i in range(0, import_desc.NameTableSize): name_char = li.read(1) if name_char == '\0' or name_char == '\xCD': if cur_lib != "": import_libnames.append(cur_lib) cur_lib = "" else: cur_lib += name_char # read each import library table for i in range(0, import_desc.ModuleCount): table_addr = li.tell() table_header = read_struct(li, XEXImportTable) libname = import_libnames[table_header.ModuleIndex] variables = {} for i in range(0, table_header.ImportCount): record_addr = read_dwordBE(li) record_value = ida_bytes.get_dword(record_addr) record_type = (record_value & 0xFF000000) >> 24 ordinal = record_value & 0xFFFF import_name = x360_imports.DoNameGen(libname, 0, ordinal) if record_type == 0: # variable idc.create_data(record_addr, idc.FF_WORD, 2, idc.BADADDR) idc.create_data(record_addr + 2, idc.FF_WORD, 2, idc.BADADDR) idc.make_array(record_addr, 2) idc.set_name(record_addr, "__imp__" + import_name) variables[ordinal] = record_addr elif record_type == 1: # thunk # have to rewrite code to set r3 & r4 like xorlosers loader does # r3 = module index afaik # r4 = ordinal # important to note that basefiles extracted via xextool have this rewrite done already, but raw basefile from XEX doesn't! # todo: find out how to add to imports window like xorloser loader... ida_bytes.put_dword(record_addr + 0, 0x38600000 | table_header.ModuleIndex) ida_bytes.put_dword(record_addr + 4, 0x38800000 | ordinal) idc.add_func(record_addr, record_addr + 0x10) idc.set_name(record_addr, import_name) # add comment to thunk like xorloser's loader idc.set_cmt( record_addr + 4, "%s :: %s" % (libname.rsplit('.', 1)[0], import_name), 1) # this should mark the func as a library function, but it doesn't do anything for some reason # tried a bunch of things like idaapi.autoWait() before running it, just crashes IDA with internal errors... idc.set_func_flags( record_addr, idc.get_func_flags(record_addr) | idc.FUNC_LIB) # thunk means it's not a variable, so remove from variables dict if ordinal in variables: variables.pop(ordinal) else: print( "[+] %s import %d (%s) (@ 0x%X) unknown type %d!" % (libname, ordinal, import_name, record_addr, record_type)) # remove "__imp__" part from variable import names for ordinal in variables: import_name = x360_imports.DoNameGen(libname, 0, ordinal) idc.set_name(variables[ordinal], import_name) # Seek to end of this import table li.seek(table_addr + table_header.TableSize) return
def analyze_func_ptr(start_ea, end_ea): global FUNC_BY_PTR, FUNC_BY_PTR_TIME if "FUNC_BY_PTR" not in globals() or len(FUNC_BY_PTR) == 0: FUNC_BY_PTR = set() ea = start_ea func_cnt = 0 name_cnt = 0 start_time = time.time() while ea != idc.BADADDR and ea <= end_ea: status, ea = is_assigned(ea) if status: continue # now check function pointer func_ptr = ida_bytes.get_dword(ea) # TODO: skip other segments that are not code. # for those already assigned functions, we need to check the segment range. if not (start_ea <= func_ptr < end_ea): ea = next_addr_aligned(ea, 4) continue # we only target thumb function to reduce false positives if func_ptr & 1 == 0: ea = next_addr_aligned(ea, 4) continue func_ptr = func_ptr - 1 func_start = idc.get_func_attr(func_ptr, idc.FUNCATTR_START) if func_start != idc.BADADDR and func_start != func_ptr: # this is not a proper function pointer ea = next_addr_aligned(ea, 4) continue # new thumb function has been found! if func_start == idc.BADADDR: old_flag = idc.get_sreg(func_ptr, "T") idc.split_sreg_range(func_ptr, "T", 1, idc.SR_user) status = idc.add_func(func_ptr) if not status: # IDA automatically make code, and this remains even # though add_func fails. ida_bytes.del_items(func_ptr, ida_bytes.DELIT_EXPAND) idc.split_sreg_range(func_ptr, "T", old_flag, idc.SR_user) ea = next_addr_aligned(ea, 4) continue func_cnt += 1 FUNC_BY_PTR.add(ea) if func_cnt % 10000 == 0: print("%x: %d functions has been found. (%0.3f secs)" % (ea, func_cnt, time.time() - start_time)) # If we find a function, we try to assign a name. The name may be # derived by C++ structure. if analyze_funcname(ea, func_ptr): name_cnt += 1 func_name = idc.get_func_name(func_ptr) if name_cnt % 10000 == 0: print("%x: %d names has been found. (%0.3f secs)" % (ea, name_cnt, time.time() - start_time)) # print("%x: %x => %s" % (ea, func_ptr, func_name)) ea = next_addr_aligned(ea, 4) FUNC_BY_PTR_TIME = time.time() - start_time print("Found %d functions, renamed %d functions (%0.3f secs)" % (len(FUNC_BY_PTR), name_cnt, FUNC_BY_PTR_TIME))
def apply(self): if self.data_at_address: ida_bytes.del_items(self.address, ida_bytes.DELIT_SIMPLE) idc.add_func(self.address) return True
def __call__(self): idc.add_func(self.start_ea, self.end_ea)
def load_file(f, neflags, format): print('# PS4 Kernel Loader') ps = Binary(f) # PS4 Processor, Compiler, Library bitness = ps.procomp('metapc', CM_N64 | CM_M_NN | CM_CC_FASTCALL, 'gnulnx_x64') # Segment Loading... for segm in ps.E_SEGMENTS: if segm.name() == 'PHDR': kASLR = False if segm.FILE_SIZE == 0x118 else True # Process Loadable Segments... if segm.name() in ['CODE', 'DATA', 'SCE_RELRO']: address = segm.MEM_ADDR size = segm.MEM_SIZE # Dumped Kernel Fix-ups if segm.name() in ['DATA', 'SCE_RELRO'] and (idaapi.get_segm_by_name('CODE').start_ea != 0xFFFFFFFF82200000 or not kASLR): offset = address - idaapi.get_segm_by_name('CODE').start_ea dumped = segm.MEM_SIZE else: offset = segm.OFFSET dumped = segm.FILE_SIZE print('# Creating %s Segment...' % segm.name()) f.file2base(offset, address, address + dumped, FILEREG_PATCHABLE) idaapi.add_segm(0, address, address + size, segm.name(), segm.type(), ADDSEG_NOTRUNC | ADDSEG_FILLGAP) # Processor Specific Segment Details idc.set_segm_addressing(address, bitness) idc.set_segm_alignment(address, segm.alignment()) idc.set_segm_attr(address, SEGATTR_PERM, segm.flags()) # Process Dynamic Segment... elif segm.name() == 'DYNAMIC': code = idaapi.get_segm_by_name('CODE') data = idaapi.get_segm_by_name('DATA') relro = idaapi.get_segm_by_name('SCE_RELRO') # ------------------------------------------------------------------------------------------------------------ # Dynamic Tag Entry Structure members = [('tag', 'Tag', 0x8), ('value', 'Value', 0x8)] struct = segm.struct('Tag', members) # Dynamic Tag Table stubs = {} modules = {} location = segm.MEM_ADDR # Dumps are offset by a small amount if code.start_ea != 0xFFFFFFFF82200000: dumped = code.start_ea - 0xFFFFFFFF82200000 else: dumped = 0 f.seek(location - code.start_ea) for entry in xrange(segm.MEM_SIZE / 0x10): idaapi.create_struct(location + (entry * 0x10), 0x10, struct) idc.set_cmt(location + (entry * 0x10), Dynamic(f).process(dumped, stubs, modules), False) # ------------------------------------------------------------------------------------------------------------ # Hash Entry Structure members = [('bucket', 'Bucket', 0x2), ('chain', 'Chain', 0x2), ('buckets', 'Buckets', 0x2), ('chains', 'Chains', 0x2)] struct = segm.struct('Hash', members) # Hash Table try: location = Dynamic.HASHTAB size = Dynamic.HASHTABSZ except: location = Dynamic.HASH size = Dynamic.SYMTAB - location f.seek(location - code.start_ea) for entry in xrange(size / 0x8): idaapi.create_struct(location + (entry * 0x8), 0x8, struct) if kASLR: # -------------------------------------------------------------------------------------------------------- # Relocation Entry Structure (with specific addends) members = [('offset', 'Offset (String Index)', 0x8), ('info', 'Info (Symbol Index : Relocation Code)', 0x8), ('addend', 'AddEnd', 0x8)] struct = segm.struct('Relocation', members) # Relocation Table (with specific addends) location = Dynamic.RELATAB f.seek(location - code.start_ea) for entry in xrange(Dynamic.RELATABSZ / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Relocation(f).process(dumped, code.end_ea), False) # Initialization Function idc.add_entry(Dynamic.INIT, Dynamic.INIT, '.init', True) else: # -------------------------------------------------------------------------------------------------------- # Symbol Entry Structure members = [('name', 'Name (String Index)', 0x4), ('info', 'Info (Binding : Type)', 0x1), ('other', 'Other', 0x1), ('shtndx', 'Section Index', 0x2), ('offset', 'Value', 0x8), ('size', 'Size', 0x8)] struct = segm.struct('Symbol', members) # Symbol Table location = Dynamic.SYMTAB f.seek(location - code.start_ea) functions = {} # .symtab idc.add_entry(location, location, '.symtab', False) for entry in xrange((Dynamic.STRTAB - location) / 0x18): idaapi.create_struct(location + (entry * 0x18), 0x18, struct) idc.set_cmt(location + (entry * 0x18), Symbol(f).process(functions), False) # -------------------------------------------------------------------------------------------------------- # Dynamic String Table location = Dynamic.STRTAB # .strtab idc.add_entry(location, location, '.strtab', False) # Functions for key in functions: idc.create_strlit(location + key, BADADDR) functions[key] = idc.get_strlit_contents(location + key, BADADDR) idc.set_cmt(location + key, 'Function', False) functions = sorted(functions.iteritems(), key = operator.itemgetter(0)) #print('Functions: %s' % functions) # Resolve Functions location = Dynamic.SYMTAB f.seek(location - code.start_ea + 0x18) for entry in xrange((Dynamic.STRTAB - location - 0x18) / 0x18): Symbol(f).resolve(functions[entry][1]) # Fix-up if kASLR: address = relro.start_ea del_items(address, DELIT_SIMPLE, relro.end_ea - address) while address < relro.end_ea: create_data(address, FF_QWORD, 0x8, BADNODE) address += 0x8 address = code.start_ea # ELF Header Structure members = [('File format', 0x4), ('File class', 0x1), ('Data encoding', 0x1), ('File version', 0x1), ('OS/ABI', 0x1), ('ABI version', 0x1), ('Padding', 0x7), ('File type', 0x2), ('Machine', 0x2), ('File version', 0x4), ('Entry point', 0x8), ('PHT file offset', 0x8), ('SHT file offset', 0x8), ('Processor-specific flags', 0x4), ('ELF header size', 0x2), ('PHT entry size', 0x2), ('Number of entries in PHT', 0x2), ('SHT entry size', 0x2), ('Number of entries in SHT', 0x2), ('SHT entry index for string table\n', 0x2)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size for index, entry in enumerate(ps.E_SEGMENTS): # ELF Program Header Structure members = [('Type: %s' % entry.name(), 0x4), ('Flags', 0x4), ('File offset', 0x8), ('Virtual address', 0x8), ('Physical address', 0x8), ('Size in file image', 0x8), ('Size in memory image', 0x8), ('Alignment\n', 0x8)] for (comment, size) in members: flags = idaapi.get_flags_by_size(size) idc.create_data(address, flags if flags != 0 else FF_STRLIT, size, BADNODE) idc.set_cmt(address, comment, False) address += size # Wait for the AutoAnalyzer to Complete... print('# Waiting for the AutoAnalyzer to Complete...') idaapi.auto_wait() if kASLR: # Start Function idc.add_entry(ps.E_START_ADDR, ps.E_START_ADDR, 'start', True) # Xfast_syscall address = idaapi.find_binary(code.start_ea, code.end_ea, '0F 01 F8 65 48 89 24 25 A8 02 00 00 65 48 8B 24', 0x10, SEARCH_DOWN) idaapi.do_unknown(address, 0) idaapi.create_insn(address) idaapi.add_func(address, BADADDR) idaapi.set_name(address, 'Xfast_syscall', SN_NOCHECK | SN_NOWARN) # -------------------------------------------------------------------------------------------------------- # Znullptr's syscalls print('# Processing Znullptr\'s Syscalls...') # Syscall Entry Structure members = [('narg', 'Number of Arguments', 0x4), ('_pad', 'Padding', 0x4), ('function', 'Function', 0x8), ('auevent', 'Augmented Event?', 0x2), ('_pad1', 'Padding', 0x2), ('_pad2', 'Padding', 0x4), ('trace_args_func', 'Trace Arguments Function', 0x8), ('entry', 'Entry', 0x4), ('return', 'Return', 0x4), ('flags', 'Flags', 0x4), ('thrcnt', 'Thread Count?', 0x4)] struct = segm.struct('Syscall', members) znullptr(code.start_ea, code.end_ea, '4F 52 42 49 53 20 6B 65 72 6E 65 6C 20 53 45 4C 46', struct) # -------------------------------------------------------------------------------------------------------- # Chendo's cdevsw con-struct-or print('# Processing Chendo\'s cdevsw structs...') # cdevsw Entry Structure members = [('d_version', 'Version', 0x4), ('d_flags', 'Flags', 0x4), ('d_name', 'Name', 0x8), ('d_open', 'Open', 0x8), ('d_fdopen', 'File Descriptor Open', 0x8), ('d_close', 'Close', 0x8), ('d_read', 'Read', 0x8), ('d_write', 'Write', 0x8), ('d_ioctl', 'Input/Ouput Control', 0x8), ('d_poll', 'Poll', 0x8), ('d_mmap', 'Memory Mapping', 0x8), ('d_strategy', 'Strategy', 0x8), ('d_dump', 'Dump', 0x8), ('d_kqfilter', 'KQFilter', 0x8), ('d_purge', 'Purge', 0x8), ('d_mmap_single', 'Single Memory Mapping', 0x8), ('d_spare0', 'Spare0', 0x8), ('d_spare1', 'Spare1', 0x8), ('d_spare2', 'Spare2', 0x8), ('d_spare3', 'Spare3', 0x8), ('d_spare4', 'Spare4', 0x8), ('d_spare5', 'Spare5', 0x8), ('d_spare6', 'Spare6', 0x4), ('d_spare7', 'Spare7', 0x4)] struct = segm.struct('cdevsw', members) chendo(data.start_ea, data.end_ea, '09 20 12 17', struct) # -------------------------------------------------------------------------------------------------------- # Pablo's IDC try: print('# Processing Pablo\'s Push IDC...') # Script 1) Push it real good... pablo(code.start_ea, code.end_ea, 'C5 FA 5A C0 C5 F2 5A C9 C5 EA 5A D2 C5 FB 59 C1') pablo(code.start_ea, code.end_ea, 'C5 F9 7E C0 31 C9') pablo(code.start_ea, code.end_ea, '48 89 E0 55 53') pablo(code.start_ea, code.end_ea, 'B8 2D 00 00 00 C3') pablo(code.start_ea, code.end_ea, '31 C0 C3') pablo(code.start_ea, code.end_ea, '55 48 89') pablo(code.start_ea, code.end_ea, '48 81 EC A0 00 00 00 C7') pablo(code.start_ea, code.end_ea, '48 81 EC A8 00 00 00') # Script 2) Fix-up Dumped Data Pointers... if dumped or not kASLR: print('# Processing Pablo\'s Dumped Data Pointers IDC...') pablo(data.start_ea, data.end_ea, '?? FF FF FF FF') except: pass # -------------------------------------------------------------------------------------------------------- # Kiwidog's __stack_chk_fail if kASLR: print('# Processing Kiwidog\'s Stack Functions...') kiwidog(code.start_ea, code.end_ea, '73 74 61 63 6B 20 6F 76 65 72 66 6C 6F 77 20 64 65 74 65 63 74 65 64 3B') # -------------------------------------------------------------------------------------------------------- # Final Pass print('# Performing Final Pass...') address = code.start_ea while address < code.end_ea: address = idaapi.find_not_func(address, SEARCH_DOWN) if idaapi.isUnknown(idaapi.getFlags(address)): idaapi.create_insn(address) else: idc.add_func(address) address += 4 print('# Done!') return 1
def CallStackWalk(nn): class Result: """ Class holding the result of one call stack item Each call stack item instance has the following attributes: caller = ea of caller displ = display string sp = stack pointer """ def __init__(self, caller, sp): self.caller = caller self.sp = sp f = idaapi.get_func(caller) self.displ = "" if f: self.displ += idc.get_func_name(caller) t = caller - f.start_ea if t > 0: self.displ += "+" + hex(t) else: self.displ += hex(caller) self.displ += " [" + hex(sp) + "]" # get stack pointer sp = cpu.Esp seg = idaapi.getseg(sp) if not seg: return (False, "Could not locate stack segment!") stack_seg = Seg(seg) word_size = 2**(seg.bitness + 1) callers = [] sp = cpu.Esp - word_size while sp < stack_seg.end_ea: sp += word_size ptr = idautils.GetDataList(sp, 1, word_size).next() seg = idaapi.getseg(ptr) # only accept executable segments if (not seg) or ((seg.perm & idaapi.SEGPERM_EXEC) == 0): continue # try to find caller caller = IsPrevInsnCall(ptr) # we have no recognized caller, skip! if caller is None: continue # do we have a debug name that is near? if nn: ret = nn.find(caller) if ret: ea = ret[0] # function exists? f = idaapi.get_func(ea) if not f: # create function idc.add_func(ea, idaapi.BADADDR) # get the flags f = idc.get_flags(caller) # no code there? if not is_code(f): create_insn(caller) callers.append(Result(caller, sp)) # return (True, callers)