逆向工程 | C 语言之循环分支
一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:460500587
微信公众号:计算机与网络安全
ID:Computer-network
循环是在代码中应用频率最高的一种编程语法,例如用于复制字符串、遍历图片像素点等。
C语言的循环主要分为for、while与do-while 3种,但是当我们以逆向的角度来看待这些循环结构的时候,会发现其本质上只有一种,而且即便再次细分,也仅仅能分离出两种情况。
一、do-while循环
先看下面代码清单1。
代码清单1 do-while循环
以上代码应该比较容易理解,0x41是字母A的ASCII码,变量nNum的初始值是26,因此0x41+(26-nNum)配合着每次的nNum——,实质上就是一个从字母A到Z的打印过程。
现在让我们从Debug版反汇编代码的角度看看小Baby是怎么唱ABC歌的。
以上代码还是较容易理解的,让我们再看看Release版的反汇编代码。
通过以上代码我们不难看出,编译器直接将我们的代码优化为以下模式了:
聪明的编译器,直接看透了我们代码的本质,把我们稍显复杂的代码变得非常简单。但是这只是个巧合,编译器的真实意图是想将我们的减法运算尽可能地转换为加法运算,原因很简单,CPU本质上只会做加法运算,因此加法对于CPU来说是执行速度最快的计算。
我们现在可以看到的最大的特点就是一个有条件判断的向上跳转。因此可以这样理解:如果我们看到了一个判断分支的跳转是向上的,那么这必然就是一个循环。以下是总结的特点:
二、while循环
为了便于理解,我们将精力放在软件逆向上,因此下面这个示例代码与上面的几乎一样,就是换了种语法而已,具体如代码清单2所示。
代码清单2 while循环
代码清单3所示是上述代码的Debug版反汇编代码。
代码清单3 while循环的Debug版反汇编代码
除了在循环头部多了两条用于判断是否跳出循环的指令外,其他的与do-while循环如出一辙。那Release版的代码又会是怎样的呢?反汇编代码如下:
请不要怀疑代码错了,事实就是这样,do-while与while生成的Release版代码在这里看就是100%相同的。编译器很明显已经探测出了我们的循环判断用的是一个常量,因此不存在首次执行条件不匹配的情况。
当然,如果我们将判断条件改为一个变量,那么就是另外一番景象了。修改后的源码如代码清单4所示。
代码清单4 修改后的while循环
下面是其Release版的反汇编代码:
我们可以看出,用变量做判断条件很明显与常量不一样,而关于优化,很显然编译器只是单纯地将0x41+(26-argc)优化成了0x5B-argc。
到此,已经可以对while循环的反汇编特征做一个有效的总结了。
三、for循环
for循环与while循环本质上是一样的,唯一的不同在于for循环在循环体内多了一个步长部分。接下来我们一起看看for循环的反汇编代码。先看源码,如代码清单5所示。
代码清单5 for循环
接下来我们再看看Debug版的反汇编代码,如代码清单6所示。
代码清单6 for循环的Debug版反汇编代码
显然从循环语句这个集合来讲是do-while先诞生的,而后是while,最后才是for,这从一个侧面说明for应该是最“高级”的了。
从执行效率上看,代码最短且判断最少的就是do-while循环了。
但是事实又是如何呢,让我们一起看看Release版反汇编。
由于本例中又使用了常量,并且基本逻辑也没有改变,因此这段代码与do-while、while一模一样,有疑问的朋友可以返回上面仔细观察一下,连地址都是一样的。
很明显,while与for都是以do-while为基础框架的,只不过是在里面加了一些小判断。为了让您更清晰地看到它们之间的异同,下面是一个变量版for循环的反汇编例子,如代码清单7所示。
代码清单7 判断值为变量的for循环
下面是它的Release版反汇编代码:
以变量为判断条件的for循环与while循环所生成的代码是完全相同的,包括地址都是一样的。下面是对Debug版for循环的一个反汇编特点的总结:
到此,我们应该可以做一个总结了,Debug版下3种循环各不相同,Release版下可总结如下:
当循环采用常量为判断条件时,相同逻辑的3种循环生成的代码完全相同。
当循环采用变量为判断条件时,相同逻辑的while与for生成的代码完全相同,而do-while则自成一格。
四、循环体的语句外提优化
只有在循环体内被重复操作,并且不影响其最终结果的语句,才会被编译器做外提优化处理。我们看看代码清单8。
代码清单8 循环体的语句外提演示代码
通过这段代码我们可以发现一处可以优化的地方,就是argc=(int)argv这条语句,很明显这条语句即便是被执行再多次其结果也不会发生任何变化。代码清单9可以很好地证实我们的想法。
代码清单9 循环体的语句外提的Release版反汇编代码
由代码清单9可知,循环体内很明显没有"argc=(int)argv;"的代码,这段代码被提到了外面,这就是编译进行的代码外提优化。
微信公众号:计算机与网络安全
ID:Computer-network