不寻常的 x86 switch 语句?

逆向工程 x86
2021-06-16 10:40:58

我遇到了以下 x86(使用某些版本的 Visual Studio AFAIK 构建)switch 语句:

0x1009E476  cmp edx, 0x3B
0x1009E479  jnz switch_statement

switch_statement:
0x1009E591  movzx ecx, byte [indirect_table+edx]
0x1009E598  jmp dword [table1+ecx*4]

indirect_table:
0x1009E7AB  db 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07
            db 0x07, 0x07, 0x06, 0x8B, 0xFF

table1:
0x1009E7B8  dd ptr code1
            dd ptr code2
            dd ptr code3
            dd ptr code4
            dd ptr code5
            dd ptr code6
            dd 0x00000000 
0x1009E7D4  dd 0x01060600, 0x06020606, 0x06060306, 0x06060606 ; Note: nothing directly references this data.
            dd 0x06040606, 0x06060606, 0x06060606, 0x06060606
            dd 0x06060606, 0x06060606, 0x06060606, 0x06060606
            dd 0x06060606, 0x06060606, 0x06060606, 0x06060606
            dd 0x06060606

中的任何索引都indirect_table不会引用 中的 6 个指针中的任何一个table1索引 6 将取消引用空指针,索引 7 将取消引用0x01060600和索引0x8B0xFF最终取消引用垃圾。因此,一切最终都会违反访问权限。

所以这也许是编译器的优化,table1的数据跟在6个代码指针和1个空指针后面,看起来像一个间接表,巧合的是所有的索引都适合这个switch语句(0-6)。虽然二进制文件没有对此数据的引用,但如果EBX已知是0x29或向上,它将引用此数据。编译器可能已经决定EBX不会0-0x29因此将间接表位置向后移动以正确排列。那么cmp edx, 0x3B在这当中扮演什么角色呢?

这是编译器代码生成问题,编译器优化,还是我严重误解了代码?

如果优化,任何支持阅读材料将不胜感激。

1个回答

要么之前某处有检查,要么编译器知道edx不小于 41 (0x29)。0x3B 可能由单个开关标签处理,因此编译器添加了此检查以避免双重内存查找(或者可能在源代码中有实际if之前switch)。

0x1009E7D4 处的表用于检索跳转表条目索引 - Visual C++ 编译器总是在跳转之后放置间接表。0x1009E7AB 可能是前一个开关的间接表的一部分。并且8B FFmov edi, edi,这里用于对齐。

这种特定的优化(零索引没有减法)似乎非常罕见;我想我只在 Windows DLL 中看到过它,它经常使用 PGO 和其他技巧来实现最后几个百分点的性能。