前言
作者:
浪子花梦
,一个有趣的程序员 ~
此系列文章都是一些基础的文章,每篇文章都通过几个小例子快速的了解 Win32反汇编与OD的使用,在此作个笔记
如若对您有帮助,记得三连哟 ~
Win32反汇编(一) 初步探索Win32反汇编 与 Ollydbg的简单使用
Win32反汇编(二)几种常见的指令反汇编详解:EAX、MOVSX与MOVZX、LEA、SUB、CMP与转移指令
Win32反汇编(三)深层次的了解各种转移指令:IF语句有符号与无符号跳转
Win32反汇编(四)栈的工作原理与堆栈平衡,函数方法参数的调用约定
Win32反汇编(五)C/C++中的 if-else 与 switch-case 的正向分析与反向分析
这篇文章我们除了对 C/C++中的循环、置加减反汇编之外,还对VS的优化进行分析,就是当设置某一个优化属性时对应的反汇编分析,能让我们对程序有一个更好的理解 . . .
for 循环是高级语言中比较灵活的一种循环语句了,比较常用,所以我们先来分析一下 for 循环语句的反汇编形式,如下所示,我们准备一个 for循环的代码:
#include
int main() {
printf("start:\n");
int sum = 0;
for (int i = 1; i <= 10; ++i) {
sum += i;
}
printf("sum = %d\n", sum);
printf("end!");
return 0;
}
我们用 for循环计算 1 - 10 之间的累和,下面我们来看看反汇编是什么样子的,使用 OD调试如下所示,这是没有用VS进行优化的情况:
看样子是非常多的指令,如果没有优化的话,效率将会比较低,所以我们可以将它理解为第一代版本,下面我们会分析其它的一些优化过的版本,下面我们来分析一下这个程序吧 . . .
首先,程序在开始时会分配一个栈空间,然后执行 printf函数,如下所示:
然后对这两个局部变量分别初始化为 0、1 . . .
下面用红色框框起来的指令就是将 i 与 10作比较,如下所示:
如果条件成立,则执行下面的几条指令,如下所示:
实现了一个加法运算,然后通过 jmp 跳转到上面的指令,将一个内存单元的值(i的值)+ 1,然后做同样的事,继续判断,运行流程动图如下所示:
整个程序非常的简单,但是我们通过VS将这个程序进行一个高级优化,那么反汇编之后是什么样子的呢?
如下所示,我们将 VS 开启为最大优化:
然后我们重新编译 cpp代码,通过 OD打开如下所示:
我们发现上面的那些指令已经被压缩成这么短短几行了,而且上面也没跳转指令、比较指令了,那么上面的几行指令是什么意思呢?
我们观察上面的指令,它并没有 for循环的影子,它直接把 1 ~ 10 的结果先算出来了,然后直接作为 printf的参数传入,这样就不需要一次一次的计算判断了,这样效率就大大的提高了,有兴趣的朋友可以看看《编译原理》这本书 . . .
.
.
我们只要把上面的 for语句的反汇编了解后,do-while、while理解起来非常的容易 . . .
首先,我们准备一个 do-while的cpp代码,这是没有优化的版本,如下所示:
#include
int main() {
printf("start:\n");
int sum = 0;
int i = 1;
do {
sum += i;
i += 1;
} while (i <= 10);
printf("sum = %d\n", sum);
printf("end!");
return 0;
}
我们做的事情是和上面 for 是一样的,都是计算 1 ~ 10 之和,通过OD反汇编如下所示:
do-while的执行体是下面的这些指令:
刚开始是没有判断的,先执行这些指令,然后通过 jle (小于等于)跳转到上面的指令, 和0A(10)作比较,这是比较好理解的,我们主要来分析一下开启优化的反汇编是什么样子的,如下所示:
主要代码只是几行,可见这IDE是多么强大,这样效率就大大的提高了,我们来分析这几行指令,首先,是通过 ecx、eax来存储两个变量的值(sum、i),然后使用 inc来使值 + 1,速度加快了 n倍,强的一批 . . .
.
.
有了 for、do-while 的基础, while还不是手到擒来吗?不急,我们一定要熟悉每一种语句的反汇编方式,打好基础,这样才能学好逆向,cpp代码如下所示:
#include
int main() {
printf("start:\n");
int sum = 0;
int i = 1;
while (i <= 10) {
sum += i;
++i;
}
printf("sum = %d\n", sum);
printf("end!");
return 0;
}
首先是没有优化的反汇编结果,使用OD调试如下所示:
首先就是直接使用CMP进行比较,如果不成立则执行 jg指令跳转到后面的指令,如果成立则顺序执行,重复同样的行为10 次,动图演示如下所示:
下面我们来试试优化的 while语句的反汇编是什么样子的,如下所示:
这个和 for循环是差不多的,直接计算出循环内的数值是多少,然后直接调用 printf函数输出就行了 . . .
.
.
下面我们来看看++、- -运算符的使用,cpp代码如下所示(无优化的情况):
#include
int main() {
printf("start:\n");
int i = 1;
int j = i++; // i = 2 j = 1
i = j++; // i = 1 j = 2
j = ++i; // i = 2 j = 2
i = ++j; // i = 3 j = 3
printf("%d %d\n", i, j);
printf("end!");
return 0;
}
就是简单的运算而已,下面我们通过 OD来调试这个程序,看看发生了什么事,如下所示:
我们发现这个程序非常的长,其实运用了大量的mov、add指令,这是没有优化的情况,我们来看看这些指令代表什么意思,我们先来看最开始的这几条指令,如下所示:
他对应着后面注释的两条C语句,int j = i++ 做的事情就是先将 i的值给 j,然后i的值 + 1,我们可以从对应的汇编指令中看出,然后下面做的大量类似的操作都是一样的原理,下面我们来看一下- -运算符是什么样子的,如下所示:
操作的方式都是一样的,只不过 add 换成了 sub,下面我们来看一下优化的样子是什么样子的,如下所示:
我们发现直接将 1,1作为printf的参数进行打印,并没有之前的一系列的操作,因为编译器在编译时已经将 i,j的值算出来了,所以觉得这些语句没有必要执行了,这就是IDE的强大之处 . . .
inc 表示 ++ 运算, dec 表示 - -运算,这两种指令比add、sub快多了 . . . 以后有机会再演示吧,^ _ ^
.
.