c++中if语句的反汇编以及优化
只有一个if的情况
首先我们通过观察debug选项下的汇编代码,对if语句有一个大致的了解。程序的源代码非常简单,如下:
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc == 0)
printf("argc == 0");
return 0;
}
我们先在if语句处下断点,直接在vs2022中进行调试

从图中我们可以看到,反汇编使用的判断与0相比较,并进行了jne不等于则跳转指令,跳转的地址正是整个if逻辑的结束地址。如果跳转条件不成立的话,则执行的是满足源代码中if语句块的代码。
接下来我们修改一下源代码,如下:
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc > 0)
printf("argc > 0");
return 0;
}
同样是在debug编译选项中,进行调试,同样在if语句处下断点

这里使用了jle小于等于则跳转指令,跳转的目的地是整个if语句的结束地址。如果跳转条件不成立的话,则执行的是满足源代码中if语句块的代码。
我们源代码中使用的是大于比较符,而汇编代码中使用的是小于等于跳转指令。似乎汇编中的判断条件总是和源代码中相反的,我们先不着急下结论,在多看几种情况。
if和else
我们修改一下先前的源代码,如下:
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc == 0)
printf("argc == 0");
else
printf("argc != 0");
return 0;
}
使用Debug编译选项。同样在if处下断点,反汇编代码如下:

同样的,if语句的反汇编代码中进行的是和源代码中相反的判断,跳转的地址是else语句块的起始地址。如果跳转条件不成立,则会执行if语句块中的代码,并且我们可以看到,为了跳过紧挨着if语句块反汇编代码末尾的else反汇编代码块,在if反汇编代码块的末尾处,使用了无条件跳转指令跳转到整个if_else逻辑的结束地址处。
我们知道,if与else语句和条件表达式语句(三目运算符)的功能是相同的,我之前写过一篇博客,里面讨论了对条件表达式的反汇编和优化,既然两者的功能是相同的,那么它们在反汇编的优化上是否有相似之处呢?答案是有的。读者可以参考那篇文章,自己构造测试代码并反汇编,看看其中的优化方案。笔者实验后发现,if...else语句和条件表达式使用的是同一套优化策略。
if多分枝语句
先给出我们的测试代码:
#include <stdio.h>
int main(int argc, char* argv[])
{
if (argc > 0) {
printf("argc > 0");
}
else if (argc == 0) {
printf("argc == 0");
}
else {
printf("argc <= 0");
}
return 0;
}
在Release下编译并反汇编,得到的汇编代码如下所示:

在反汇编视图中我们可以看到,先是使用了test指令对argc进行按位与运算,判断argc是否为0,并设置了SF(为1时表示运算结果为负数),而jle指令执行的是小于等于则跳转,要执行jle指令会先检查SF标志位(其实还需要检查OF溢出标志位,但test指令并不影响该标志位,始终将其置0),当SF标志位为1时,执行jle指令。所以test和jle指令的结合,用于判断一个数是否小于等于0,这与源代码中的>0相对应。如果跳转不执行,说明符合>0的条件,接下来就执行第一个if语句块中的代码,并直接ret,而不是跳转到整个多分枝if的结束处。
有趣的是对于后两个语句块的优化,它们使用了条件运算符的优化方案(详情见该篇博客),把两个printf的参数都加载,然后使用条件传输指令选择正确的参数地址,避免了使用分支跳转代码,加快了流水线的执行。
总结
在逆向还原代码的时候,因为if语句和条件判断语句的相似之处,我们可以自行选择将其还原成对应的c代码。

浙公网安备 33010602011771号