3

How can i force IDA to re analyze a function recursively (meaning including every function inside of it and so on) and recognize all the functions inside of all the functions? because right now when a PE is mapped, IDA does not recognize any function inside of it, unlike when i analyze the PE file, and i have to click on every function (unk_xxx) and press P in start of it so it can recognize the function..

clicking on reanalyze in options doesnt work either.

Alt + P as suggested here doesn't work either :

API to force reanalyze of function (Alt-P)

In decompiler view alt+P doesnt do anything at all, and in disassmbly it just edits the function.

Using IDA 7.5.

2
1

During debugging IDA does a limited amount of autoanalysis to speed up the debugging experience and not interfere with the process. What you can do is to copy the module to be analyzed to the database, then stop the debugger and let IDA finish the autoanalysis on the saved data. This can be done via the "Analyze module" command in the Modules list.

Please note that if the module is loaded at another address at next run (e.g. due to ASLR), you will have conflicts in the database, so this is not recommended if you need to re-run the process frequently, but more for the case of "debugging is mostly done, now I need to analyze it statically".

1

If someone feels the need to do this programmatically in a non-debug setting, this is the code I would use.

Note #1: There are some shortcut functions of my own GetFuncStart and such that are not included, I'm sure you can work them out. The reference to the Commenter class will just have to be removed.

Note #2: The seemingly extreme measures taken in ZeroFunction are the only way I have found to ensure a function is actually rebuilt. None of the ida_auto functions seem to do much. You may opt for something less extreme if it suits you.

Note #3: The order of called functions is reversed to attempt to do the rebuild from the bottom up, hopefully allowing IDA to make more educated guesses about arguments and such.

Note #4: The many superfluous variables, lists and such are from the larger original function which (amongst other things) produces .dot vizgraphs. I have left them in lest someone find a use for them.

def RebuildFuncAndSubs(ea):
    subs = RecurseCalled(ea)['calledLocs']
    subs.reverse()
    for addr in subs:
        ZeroFunction(addr)
        idc.auto_wait()
    ZeroFunction(ea)
    idc.auto_wait()


def RecurseCalled(ea=None, width=512):
    def all_xrefs_from(funcea, iteratee=None):
        if iteratee is None:
            iteratee = lambda x: x

        xrefs = []
        for (startea, endea) in Chunks(funcea):
            for head in Heads(startea, endea):
                xrefs.extend([GetFuncStart(x.to) for x in idautils.XrefsFrom(head) \
                        if x.type in (ida_xref.fl_CF, ida_xref.fl_CN, ida_xref.fl_JF, ida_xref.fl_JN) \
                        and not ida_funcs.is_same_func(funcea, x.to) \
                        and IsFunc_(x.to)])

        xrefs = list(set(xrefs))

        return xrefs

    if ea is None:
        ea = idc.get_screen_ea()

    calledNames = list()
    calledLocs = list()
    visited = set([])
    if isinstance(ea, list):
        pending = set(ea)
        initial = set([GetFuncStart(x) for x in ea])
    else:
        pending = set([ea])
        initial = set([GetFuncStart(ea)])
    depth = 0
    count = 0
    added = [1]
    functionCalls = collections.defaultdict(set)
    namedFunctionCalls = collections.defaultdict(set)
    fwd = dict()
    rev = dict()

    while pending and len(pending) < width:
        target = pending.pop()
        count += 1
        added[0] -= 1
        if added[0] < 1:
            depth += 1
            added.pop()

        visited.add(target)

        fnName = idc.get_func_name(target) or idc.get_name(target) or "0x%x" % ref
        fnStart = GetFuncStart(target)

        if fnStart < idc.BADADDR:
            target = fnStart
            visited.add(target)
            if not fnStart in initial:
                calledNames.append(fnName)
                calledLocs.append(fnStart)

        refs = all_xrefs_from(fnStart)
        refs = set(refs)
        refs -= visited
        size1 = len(pending)
        pending |= refs
        size2 = len(pending) - size1
        added.append(size2)

    return {'calledName': calledNames,
            'calledLocs': calledLocs,
           }


def ZeroFunction(ea, total=False):
    print("0x%x: ZeroFunction" % ea)
    start = 0
    end = 0
    # Don't hold the func_t object open
    func = clone_items(ida_funcs.get_func(ea))
    if not func:
        return BADADDR

    # Keep existing comments
    with Commenter(ea, 'func') as commenter:
        fnLoc = func.start_ea
        fnName = ida_funcs.get_func_name(fnLoc)
        flags = func.flags  # idc.get_func_attr(ea, FUNCATTR_FLAGS)
        # remove library flag
        idc.set_func_attr(fnLoc, FUNCATTR_FLAGS, flags & ~4)
        ida_name.del_local_name(fnLoc)
        ida_name.del_global_name(fnLoc)
        # RemoveAllChunks(ea)
        for start, end in idautils.Chunks(ea):
            idc.remove_fchunk(start, end)
        ida_funcs.del_func(func.start_ea)
        idc.set_color(fnLoc, CIC_FUNC, 0xffffffff)
        if not total:
            func = ida_funcs.func_t(fnLoc)
            res = ida_funcs.find_func_bounds(func, ida_funcs.FIND_FUNC_DEFINE | ida_funcs.FIND_FUNC_IGNOREFN)
            if res == ida_funcs.FIND_FUNC_UNDEF:
                print("0x%x ZeroFunction: func passed flow to unexplored bytes" % fnLoc)
            elif res == ida_funcs.FIND_FUNC_OK:
                ida_funcs.add_func_ex(func)

            idc.auto_wait()
            # remove library flag (again)
            idc.set_func_flags(fnLoc, idc.get_func_flags(fnLoc) & ~4)
            # return original function name
            
            idc.set_name(fnLoc, fnName, idc.SN_NOWARN)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.