强制 IDA 递归地重新分析函数?

逆向工程 艾达 idapro-sdk
2021-07-08 11:41:24

我如何强制 IDA 递归地重新分析一个函数(意味着包括它内部的每个函数等等)并识别所有函数内部的所有函数?因为现在当映射 PE 时,IDA 无法识别其中的任何函数,这与我分析 PE 文件时不同,我必须单击每个函数 (unk_xxx) 并在其开头按 P 以便它可以识别功能..

单击选项中的重新分析也不起作用。

此处建议的 Alt + P 也不起作用:

强制重新分析函数的 API (Alt-P)

在反编译器视图中,alt+P 根本不做任何事情,在反编译中它只是编辑函数。

使用 IDA 7.5。

2个回答

在调试期间,IDA 会进行有限数量的自动分析以加快调试体验而不干扰进程。您可以做的是将要分析的模块复制到数据库中,然后停止调试器,让IDA完成对保存数据的自动分析。这可以通过模块列表中的“分析模块”命令来完成。

请注意,如果模块在下次运行时加载到另一个地址(例如由于ASLR),您将在数据库中发生冲突,因此如果您需要经常重新运行该进程,则不建议这样做,但更多适用于这种情况的“调试大部分完成,现在我需要静态分析它”。

如果有人觉得需要在非调试设置中以编程方式执行此操作,这就是我将使用的代码。

注意#1:有一些我自己的快捷功能GetFuncStart没有包括在内,我相信你可以解决它们。Commenter只需要删除的引用

注意#2:所采取的看似极端的措施ZeroFunction是我发现确保函数真正重建的唯一方法。ida_auto 函数似乎没有什么作用。如果适合您,您可以选择不那么极端的东西。

注意#3:被调用函数的顺序被颠倒以尝试从底向上重建,希望允许 IDA 对参数等进行更有根据的猜测。

注意 #4:许多多余的变量、列表等来自更大的原始函数,它(除其他外)产生 .dot 可视化图形。我已经把它们留在了,以免有人找到它们的用途。

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)