x86 反汇编/代码优化
一个优化编译器可能是现存最复杂、最强大、最有趣的程序之一。本章将讨论优化,但不会包含常见优化的表格。
编译器在两个时间点可以执行优化:首先,在中间表示中,其次,在代码生成期间。
在中间表示中,编译器可以执行各种优化,通常基于数据流分析技术。例如,考虑以下代码片段
x = 5;
if(x != 5)
{
//loop body
}
优化编译器可能会注意到,在 "if (x != 5)" 的位置,x 的值始终是常量 "5"。这允许用 "5" 代替 x,得到 "5 != 5"。然后编译器注意到结果表达式完全在常量上操作,因此可以在现在计算其值,而不是在运行时计算,从而优化条件为 "if (false)"。最后,编译器看到这意味着 if 条件语句的语句体永远不会执行,因此可以完全省略 if 条件语句的整个语句体。
考虑相反的情况
x = 5;
if(x == 5)
{
//loop body
}
在这种情况下,优化编译器会注意到 IF 条件语句始终为真,甚至不会费心编写代码来测试 x。
另一组优化可以在中间表示级别或代码生成级别执行,它们是控制流优化。大多数这些优化处理无用分支的消除。考虑以下代码
if(A)
{
if(B)
{
C;
}
else
{
D;
}
end_B:
}
else
{
E;
}
end_A:
在这段代码中,一个简单的编译器会从 C 块生成一个跳转到 end_B,然后从 end_B 生成另一个跳转到 end_A(以绕过 E 语句)。显然,跳转到一个跳转是不高效的,因此优化编译器会从 C 块生成一个直接跳转到 end_A。
不幸的是,这会使代码更加混乱,并且会阻止很好地恢复原始代码。对于复杂的函数,可能需要考虑仅由 if()-goto; 序列组成的代码,而无法识别更高层次的语句,例如 if-else 或循环。
识别高层次语句层次结构的过程称为“代码结构化”。
一旦编译器筛选了代码中所有逻辑上的低效率,代码生成器就会接管。代码生成器通常会用更快的机器指令替换某些速度较慢的机器指令。
例如,指令
beginning:
...
loopnz beginning
运行速度比等效指令集
beginning:
...
dec ecx
jne beginning
慢得多。那么,为什么编译器会使用 loopxx 指令呢?答案是大多数优化编译器永远不会使用 loopxx 指令,因此,作为逆向工程师,您可能永远不会在实际代码中看到它被使用。
那么指令
mov eax, 0
怎么样?mov 指令相对较快,但处理器中较快的一部分是算术单元。因此,使用以下指令更有意义
xor eax, eax
因为 xor 在很少的处理器周期内运行(并且同时节省了三个字节),因此比 "mov eax, 0" 快。xor 指令的唯一缺点是它会更改处理器标志,因此它不能用在比较指令和相应的条件跳转之间。
当循环需要运行固定的少量迭代时,通常最好展开循环,以减少执行的跳转指令数量,并且在许多情况下防止处理器的分支预测失败。考虑以下 C 循环,它调用函数 MyFunction()
5 次
for(x = 0; x < 5; x++)
{
MyFunction();
}
转换为汇编代码,我们看到它大致变为
mov eax, 0
loop_top:
cmp eax, 5
jge loop_end
call _MyFunction
inc eax
jmp loop_top
loop_end:
每次循环迭代都需要执行以下操作
- 将 eax(变量 "x")中的值与 5 进行比较,如果大于或等于 5,则跳转到末尾
- 递增 eax
- 跳转回循环顶部。
请注意,如果我们手动重复对 MyFunction()
的调用,我们将删除所有这些指令
call _MyFunction
call _MyFunction
call _MyFunction
call _MyFunction
call _MyFunction
这个新版本不仅占用的磁盘空间更小,因为它使用的指令更少,而且运行速度更快,因为它执行的指令更少。这个过程称为循环展开。
C 和 C++ 语言允许定义 inline
类型的函数。内联函数是类似于宏的函数。在编译期间,对内联函数的调用将被该函数的语句体替换,而不是执行 call
指令。除了使用 inline
关键字声明内联函数之外,优化编译器还可以决定将其他函数也内联。
函数内联类似于循环展开,用于提高代码性能。非内联函数需要一个 call 指令,几个指令来创建堆栈帧,然后还需要几个指令来销毁堆栈帧并从函数返回。通过复制函数的语句体而不是进行调用,机器代码的大小会增加,但执行时间会减少。
不一定要确定相同的代码部分最初是作为宏、内联函数创建的,还是仅仅是复制粘贴的。但是,在反汇编时,可以将这些代码块分离出来,将其视为独立的内联函数,这有助于使代码保持一致。