Example #1
0
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
Example #2
0
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!')
Example #3
0
    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)
Example #4
0
 def OnDeleteLine(self, n):
     ea = self.items[n][2]
     idc.del_func(ea)
     return (Choose.ALL_CHANGED, n)
Example #5
0
 def OnDeleteLine(self, n):
     ea = self.items[n][2]
     idc.del_func(ea)
     return (Choose.ALL_CHANGED, n)
Example #6
0
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)
Example #7
0
 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