IDAPython:如何检查指令是条件分支还是跳转?

逆向工程 艾达 蟒蛇
2021-06-29 19:16:22

如果基本块末尾的指令是跳转/分支指令(如BJNZ),我想知道使用 IDAPython并确定它是否有条件。我需要它以一种与 CPU 无关的方式,而不依赖于助记符。

我无法通过在 $IDA_DIR/python 目录中进行 grepping 来找到如何执行此操作。

2个回答

你可以试试我写的这个函数,它在我做的一些快速测试中工作得很好。您可能需要实现更多代码来处理边缘情况,但通常它应该可以工作。

该函数是用以下通用假设编写的(并非总是如此):

  • 一个 Branch\Jump 指令至少有一个本地引用
  • 在真/假情况下,条件分支将使流程继续到下一条指令。

这是我认为有帮助的注释代码:

from idaapi import *
from idc import *

def check_conditional(func_ea, addr):
    # Get: function's base address, suspicious branch/jmp address
    # Returns: a strings which says whether the address is a Conditional Branch, Unconditional Branch or neither

    f_start = get_func(func_ea).startEA
    f_end = FindFuncEnd(f_start)

    # Get local function's references made from the instruction
    refs = CodeRefsFrom(addr, 0)
    refs = set(filter(lambda x: x>=f_start and x<=f_end, refs)) 
    if refs:
        # Add a reference to the next instruction if the flow continues to it
        next_head = NextHead(addr, f_end)
        if isFlow(GetFlags(next_head)):
            # refs holds the referenced address so you can use them later
            refs.add(next_head)
            return "Conditional Branch"
        else:
            return "Unconditional Branch"           
    else:
        return "Not JMP/Branch at all"

这个解决方案不是 100% 架构不可知的,但它更简单一点,添加对具有延迟槽的架构的支持应该不会太困难。如果您只对流程是否有条件感兴趣,那么您可以忽略该Rfirst0部分。

def conditional_branches_for(ea):
    func = idaapi.get_func(ea)

    for basicblock in idaapi.FlowChart(func):
        # Insert logic here for getting branch for archs with delay slots
        last_ea = PrevHead(basicblock.endEA)

        # Get any Xrefs that are not part of the regular flow
        if Rfirst0(last_ea) == idaapi.BADADDR:
            print('Skipping {:#x} because it is not a branch'.format(last_ea))
            continue

        successors = len(tuple(basicblock.succs()))
        if successors == 0:
            branch_type = 'does not branch'
        elif successors == 1:
            branch_type = 'has an unconditional branch'
        else:
            branch_type = 'has a conditional branch'

        print('BasicBlock at {:#x} {}'.format(last_ea, branch_type))