在 IDA Pro 中识别由于 Switch/Case 引起的跳转语句

逆向工程 拆卸 蟒蛇
2021-06-28 04:29:58

我想在 IDA Pro 反汇编二进制文件中识别由于 switch/case 引起的跳转语句。我的最终目标是读取跳转表条目。我也对函数表/vtables 感兴趣。对于 switch/case,我将跳转语句视为:

  1. jmp ds:off_20B280CC[ebx*4]

  2. jmp dword ptr ds:loc_6B2A825C[ecx*4] 【问:是因为切换/跳跃吗?】

正如我从 中看到的,这些跳转的操作数类型GetOperandValue(inst.ea, 0)是“内存引用”(类型值 2)。类似的跳转语句jb short loc_6B2A8154具有操作数类型“立即近地址”(类型值 7)。但是,像jmp ds:__imp_memsetthunk函数中调用导入函数的跳转语句也有操作数类型“内存引用”。

有什么办法可以区分 switch/case 和 thunk 函数的跳转语句吗?

2个回答

在许多情况下,IDA 已经知道跳转是跳转表的一部分,并且可能是切换的结果。如果这是真的,您可以使用 IDAPython 访问它。

相关函数是get_switch_info_ex(ea)get_switch_info_ex(ea)查看IDAPython 的文档,我们发现:

calc_switch_cases(insn_ea,py_swi)

获取有关交换机案例的信息。

返回的信息可以如下使用:

for idx in xrange(len(results.cases)):
    cur_case = results.cases[idx]
    for cidx in xrange(len(cur_case)):
        print "case: %d" % cur_case[cidx]
    print "  goto 0x%x" % results.targets[idx]

@param insn_ea:'间接跳转'指令的地址@param si:开关信息

@return:一个有 2 个成员的结构:'cases' 和 'targets'。

返回:cases_and_targets_t

要从results示例中获取变量,我们使用以下代码:

si = idaapi.get_switch_info_ex(ea)
results = idaapi.calc_switch_cases(ea, si)
if not results:
    print "No switch related jump at 0x{:X}".format(ea)

因此,要检查指令是否为开关,您可以使用以下函数:

def is_switch(ea):
    si = idaapi.get_switch_info_ex(ea)
    results = idaapi.calc_switch_cases(ea, si)
    return bool(results)

如果您想使用它,我已经为 Sark 中的 IDA 开关编写了一个基本的包装类。见这里

在 x86 架构上,由 switch/case 语句引起的跳转语句通常遵循以下模式:

jmp <offset> [<index-register> * 4]   ;; pointers are 4 bytes

其中<index-register>包含正在打开的值,“4”是比例因子。根据您的编译器,搜索带有索引内存操作数的跳转指令可能会有很好的结果。

但是,如果编译器确定这样做会产生更高效的代码,则编译器可以自由地将 switch 语句编译为cmpandjnz语句的级联例如:

switch (foo) {
case 1: do_something(); break;
case 10000: do_something_else(); break;
case 1000000: do_final_thing(); break;
}

作为表查找实现将非常低效。因此,仅查找索引内存操作数不一定会在您的程序中找到所有 switch/case。

您还需要考虑到编译器可能会以不同的方式编写开关。它完全有可能确定发出序列更好

mov eax,table_offset[eax*4]
jmp eax

或者,就像从为 8086 或 80286 实模式编写的手写汇编程序改编的旧手卷二进制文件中一样:

shl eax,4
jmp table_offset[eax]

为了涵盖所有基础,您将被迫使用其他技术(如向后切片)来确定已编译的 switch/case 语句的目标。