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 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 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 OnDeleteLine(self, n): ea = self.items[n][2] idc.del_func(ea) return (Choose.ALL_CHANGED, n)
def resolveFunctionChunks(analyzer, scs): """Resolve all of the (external) function chunks that we can manage. Args: analyzer (instance): analyzer instance to be used scs (list): list of (sark) code segments """ seen_candidates = defaultdict(int) for sc in scs: for function in sc.functions: outer_blocks = [] for block in idaapi.FlowChart(function.func_t): if block.end_ea < function.start_ea or function.end_ea <= block.start_ea: try: block_function = sark.Function(block.start_ea) except sark.exceptions.SarkNoFunction: block_function = None # Only interested in chunks which are not already functions if block_function is None or block_function.start_ea != block.start_ea: outer_blocks.append(block) # Function chunks which are switch cases, should be fixed elif block_function is not None and analyzer.switch_identifier.isSwitchCase( block.start_ea): analyzer.logger.debug( "Deleted switch case function: 0x%x", block.start_ea) idc.del_func(block.start_ea) outer_blocks.append(block) # check if there is something to scan if len(outer_blocks) == 0: continue # start by resetting the function idc.del_func(function.start_ea) ida_funcs.add_func(function.start_ea) # Now try to check for chunks for sc in scs: for function in sc.functions: outer_blocks = [] for block in idaapi.FlowChart(function.func_t): if block.end_ea < function.start_ea or function.end_ea <= block.start_ea: try: block_function = sark.Function(block.start_ea) except sark.exceptions.SarkNoFunction: block_function = None # Only interested in chunks which are not already functions if block_function is None or block_function.start_ea != block.start_ea: outer_blocks.append(block) # Function chunks which are switch cases, should be fixed elif block_function is not None and analyzer.switch_identifier.isSwitchCase( block.start_ea): analyzer.logger.debug( "Deleted switch case function: 0x%x", block.start_ea) idc.del_func(block.start_ea) outer_blocks.append(block) # check if there is something to scan if len(outer_blocks) == 0: continue # scan the block for connectivity groups connectivity_mapping = {} connectivity_id = 0 id_mappings = {} for block in outer_blocks: if block.start_ea not in connectivity_mapping: connectivity_mapping[block.start_ea] = connectivity_id id_mappings[connectivity_id] = connectivity_id connectivity_id += 1 cur_id = connectivity_mapping[block.start_ea] for succs in block.succs(): # if unmarked, add him to our group if succs.start_ea not in connectivity_mapping: connectivity_mapping[succs.start_ea] = cur_id # if marked, set our group ID to match his group ID (effectively using the minimal ID) else: id_mappings[cur_id] = id_mappings[connectivity_mapping[ succs.start_ea]] # Now pick the minimal candidate of each connectivity group group_candidate_mapping = {} for block in outer_blocks: cur_id = id_mappings[connectivity_mapping[block.start_ea]] if cur_id not in group_candidate_mapping: group_candidate_mapping[cur_id] = block.start_ea else: group_candidate_mapping[cur_id] = min( block.start_ea, group_candidate_mapping[cur_id]) # Now fix mis-analysed switch cases original_start = function.start_ea original_end = function.end_ea tentative_func_end = original_end for cur_id, candidate in group_candidate_mapping.items(): seen_candidates[candidate] += 1 # Handle the switch cases if analyzer.switch_identifier.isSwitchCase(candidate): tentative_func_end = max(tentative_func_end, candidate) # check if we had a switch case outside of our function if tentative_func_end > original_end: # scan the range and delete each function in it for offset in range(tentative_func_end - original_end): try: func = sark.Function(original_end + offset) if func.end_ea != original_end: idc.del_func(func.start_ea) analyzer.logger.debug("Deleted function at: 0x%x", func.end_ea) except sark.exceptions.SarkNoFunction: pass # now re-define the original function analyzer.logger.debug( "Re-defined the (switch) function at: 0x%x", original_start) idc.del_func(original_start) ida_funcs.add_func(original_start) # can move on to the next function continue # Each candidate should be a function on it's own (unless it is already contained in another function) for cur_id, candidate in group_candidate_mapping.items(): idc.del_func(original_start) external_func = None contained_chunk = False # Check what happens when the candidate is adjacent to the end of the function if candidate == original_end: idc.del_func(candidate) contained_chunk = True else: # candidate might be inside a different function try: func = sark.Function(candidate) # If our chunk is the legit ending of a given function, don't ruin it contained_chunk = func.start_ea <= candidate and candidate < func.end_ea if func.start_ea != original_start and not contained_chunk: external_func = func.start_ea idc.del_func(func.start_ea) except sark.exceptions.SarkNoFunction: pass # Should the chunk be a standalone function? if not contained_chunk: ida_funcs.add_func(candidate) # Restore the original function ida_funcs.add_func(original_start) # If needed, restore the external (container) function if external_func is not None: ida_funcs.add_func(external_func) analyzer.logger.debug( "Re-defined the function at: 0x%x, candidate at: 0x%x", original_start, candidate)
def __call__(self): idc.del_func(self.start_ea)
def DoDescentParser(self, Prologue): ''' @brief Walk the function leveraging a recursive descent parser. @detail Starting with a prologue walk each instruction until the associated epilogue is reached. For functions with multiple epilogues, iterate over each one. As each instruction is traversed, do the following three things: - Undefine the instruction - Mark the instruction as code - Check to see if the instruction is already a member of another function If an instruction is a member of another function, undefine that function and place it in a queue. At the end of traversing each function, a new function is going to be created with the new prologue and the new epilogue. In addition, the undefined function queue is going to be iterated over and each function will be redefined. This should clean up messy function much thanks to the author of "Practical Malware Analysis" for the break down of the algorithm in Chapter 8. ''' # # jmps = [eval("idaapi."+name) for name in dir(idaapi) if "NN_j" in name] # jcc_terminators = [ 'jnz', 'jz', 'jo', 'jno', 'js', 'jns', 'je', 'jne', 'jb', 'jnae', 'jc', 'jnb', 'jae', 'jnc', 'jbe', 'jna', 'ja', 'jnbe', 'jl', 'jnge', 'jge', 'jnl', 'jle', 'jng', 'jg', 'jnle', 'jp', 'jpe', 'jnp', 'jpo', 'jcxz', 'jecxz' ] #print EndAddresses while len(self.deferred_targets) > 0: curr_insn_ea = self.deferred_targets.pop() if curr_insn_ea in self.instructions_walked: # # skip instructions that were already walked # continue #for target in self.deferred_targets: # print "deferred target: %08x" % target while curr_insn_ea not in Prologue.possible_epilogues: # # walk only to a known epilogue # #print "Current EA: %08x" % (curr_insn_ea) self.instructions_walked.append(curr_insn_ea) # # Verify current instruction information # curr_insn = ida_ua.insn_t() decode_result = ida_ua.decode_insn(curr_insn, curr_insn_ea) if decode_result < 1: # # break if instruction invalid # break represented_insn_dism = idc.generate_disasm_line( curr_insn_ea, 0) curr_insn_dism = idc.generate_disasm_line(curr_insn_ea, 1) if curr_insn_dism != represented_insn_dism: # # If the item shown at this address in IDA does not match # what should be shown (due to obfuscation), fix it # #print "Instructions don't match: %08x" % (curr_insn_ea) idc.del_items(curr_insn_ea, 1) #idc.plan_and_wait(curr_insn_ea, curr_insn_ea+curr_insn.size) idc.create_insn(curr_insn_ea) #idc.plan_and_wait(curr_insn_ea, curr_insn_ea+curr_insn.size) curr_func_name = idc.get_func_name(curr_insn_ea) if curr_func_name: # # check if in function, undefine function, add to list to redefine later # #print "Part of another function: %08x" % (curr_insn_ea) curr_func_ea = idc.get_name_ea_simple(curr_func_name) func_end_ea = idc.find_func_end(curr_func_ea) idc.del_func(curr_func_ea) for curr_function in self.wrong_functions: if curr_function not in curr_function: self.wrong_functions.append( [curr_func_ea, func_end_ea]) if curr_insn_dism.startswith(tuple(jcc_terminators)): # # JCC conditionals, recursion control case # #print "Adding jcc target: %08x" % (curr_insn_ea) jmp_target_ea = self.GetInstuctionTargetAddress(curr_insn) if jmp_target_ea not in self.deferred_targets: self.deferred_targets.append(jmp_target_ea) curr_insn_ea = curr_insn_ea + curr_insn.size elif curr_insn_dism.startswith("jmp"): jmp_target_ea = self.GetInstuctionTargetAddress(curr_insn) #print "Adding jump target: %08x" % (curr_insn_ea) if jmp_target_ea not in self.deferred_targets: self.deferred_targets.append(jmp_target_ea) break elif curr_insn_dism.startswith("retn"): break else: curr_insn_ea = curr_insn_ea + curr_insn.size if curr_insn_ea in Prologue.possible_epilogues: Prologue.connected_epilogues.append(curr_insn_ea) continue