识别switch语句的跳转表

逆向工程 反汇编者
2021-06-18 04:31:45

我正在使用线性反汇编器 (beaEngine),每当我到达跳转表部分时,我都会收到一个错误(或错误的解码,因为 beaEngine 错误地认为它是代码)。

我怎样才能把这个部分识别为一个跳转表?跳表的特点是什么?

通过第 3 方应用程序找到的跳转表示例:

.text:600F49CB dd 偏移 loc_600F496E  
.text:600F49CB dd 偏移 loc_600F4984  
.text:600F49CB dd 偏移 loc_600F4979  
.text:600F49CB dd 偏移 loc_600F4979  
.text:600F49CB dd 偏移 loc_600F4984  
.text:600F49CB dd 偏移 loc_600F499E   
.text:600F49CB dd 偏移 loc_600F499E   
.text:600F49CB dd 偏移 loc_600F498F   
.text:600F49CB dd 偏移 loc_600F498F   

为了澄清和回答评论中提出的问题:这个可执行文件确实有一个重定位表。但是,beaEngine 似乎没有使用它,因为我向它提供了一个字节流而不是整个二进制文件。

能否通过重定位表来理解是否是跳转表并计算其长度?对于我检查过的一个示例,它似乎确实有效。

4个回答

跳转表在重定位表中脱颖而出,因为它们是紧密打包的代码地址数组,而“正常”代码引用间隔更大,中间有操作码或其他数据。

然而,从重定位的角度来看,vtables(C++ 风格对象的虚拟函数表)和函数表(C 风格对象,在某些库中大量使用)与跳转表几乎完全相同。所不同的是后两种类型“代码的地址阵列”的包含功能的地址,而跳转表条目通常指向功能。

但是,如果不分析引用它们的代码,就很难分析跳转表。这意味着在这种情况下,从重定位表收集的信息更适合用于辅助任务,例如告诉您反汇编程序可能遗漏的潜在跳转表,而不是用于主要分析。

恕我直言,没有简单的方法可以做到。最好的办法是编写智能反汇编器,这样它就会找到并推断出表的大小。例如跳转表代码的特征之一:

and eax, NUMBER_OF_CASES
jmp dword ptr ds:[eax*4 + switch_table_start_addr]

另一种快速且容易出错的方法可能是查找具有最小和最大 RVA 范围的一行 DWORD,例如:

dd IMAGEBASE+0x1000
dd IMAGEBASE+0xXXXX
dd IMAGEBASE+0xYYYY
dd IMAGEBASE+0xFFFF
...

但这可能会发现更多与开关表无关的数据,尤其是在大型应用程序中。

好吧..我确实发现确实如您所说的 jmp 表示跳转表。

所以我现在要做的是

  1. 寻找那些跳跃
  2. 解析先前的操作码并查找“cmp”,它指示 switch 语句中的“默认”部分(因此,指示跳转表的大小,因为它是案例数 * 4(在 32 位上))。
  3. 计算跳转表的起始地址和结束地址。

现在我有一个不同的问题,但我所描述的通常适用于开关的连续值(比如 1-10)。但是当它不连续时,编译器会创建一个“switch 语句的间接表”,看起来有点不同,我不知道如何计算它的大小。

有任何想法吗?那些东西没有标准吗?我怎么知道我是否已经涵盖了跳转表的所有选项?

Cristina Cifuentes 和 Mike Van Emmerik 的论文“Recovery of Jump Table Case Statements from Binary Code”(Proceedings of the 7th International Workshop on Program Comprehension,1999)详细讨论了这个问题。这是一个有趣的阅读。

从逆向工程的角度来看,理解编译器通常如何将 switch 语句映射到机器代码(它们并不总是映射到单个跳转表)也很有用。罗伯特伯恩斯坦 ( Software: Practice and Experience , Oct. 1985)的论文“为案例陈述生成好的代码”对此进行了一些详细的讨论。