《C++反汇编与逆向分析技术揭秘》笔记-第5章流程控制语句的识别

5.1if语句
1.注意,if语句转换的条件跳转指令与if语句的判断结果是相反的。
2.如遇到以下指令序列,可高度怀疑是if语句组成的单分支结构。由于循环结构也会出现类似代码,因此分析过程中还需结合上下文。


3.表达式短路和if语句这两种分支结构实现过程一样的,如下。


5.2if...else...语句
1.如遇到以下指令序列,先考察其中的两个跳转指令,当第一个条件跳转指令跳转到地址else begin处之前有JMP指令,则将其视为if...else...组成的双分支结构。


2.使用02优化选项(即Release版),与条件表达式使用了同样的优化手法(即无分支优化)。很多情况下,条件表达式的反汇编代码和if...else..一样的。


5.3用if构成的多分支流程(和5.2差不多)
1.如遇到以下指令序列,需要考虑各跳转指令之间的关系。当每个条件跳转指令的跳转地址之前都紧跟JMP指令,并且它们跳转的地址值一样时,可视为一个多分支结构。


2.使用02优化选项,既然是对效率的优化,就会尽量减少分支中指令的使用。如下debug版(上图)和Release版(下图)对比,就省去了else对应的JMP指令,从而提升效率。

5.4-5.6switch
1.如遇到以下指令序列,需要重点考察每个条件跳转指令后是否跟有语句块,以辨别switch分支结构(即不跟语句块)。根据每个条件跳转到的地址分辨case语句块首地址。如果case语句块内有break,会出现jmp作为结尾。如果没有break,可参考两个条件跳转到的目标地址,这两个地址之间的代码便是一个case语句块。
2.四种优化形式:if...else...优化、有序线性优化、非线性索引优化、判定树优化。
3.在switch分支数小于4的情况下,采用模拟if...else if的方法进行优化。这样做并没有发挥出switch优势,在效率上也没有if...else if强。
4.当分支数大于3,且case的判定值存在明显线性关系组合时,采用有序线性优化:将每个case语句块的地址预先保存在数组中,依据switch语句的参数查询数组,从而得到对应case语句块的首地址。例子如下。
分析:(1)编写代码时无须有序排列case值,编译器会在编译过程对case线性地址表进行排序。如这里case顺序为3、7、1、2、5、4,在case线性地址表的第0项存放case1语句块的首地址,即表是有序的。
(2)为了达到线性有序,对于没有case对应数值的情况,编译器以switch的结束地址或者default语句块的首地址(如果存在的话)填充对应的表格项。如这里没有case 6,对应填充数组下标5。
(3)ja指令是无符号比较,大于则跳转,当输入的数值为0或一个负数时,同样会大于6。
(4)Debug版和Release版反汇编代码基本一致。
(5)当switch为一个有序线性组合时,会对其case语句块制作地址表,以减少比较跳转次数。
5.对于非线性的switch结构,采用非线性索引优化,组成结构如下。
分析:(1)一张case语句块地址表,有几个case语句块就有几项,default语句块也在其中,如果没有则保存一个switch的结束地址。不会像有序线性地址表那样,重复保存switch的结束地址,造成极大的空间浪费。
(2)一张case语句块索引表,保存地址表的编号(即下标值),索引表中最多可以存储256项,每一项1字节,这决定了case值不可以超过1字节的最大表示范围(0-255)。
(3)虽然节省空间,但由于执行时需要通过索引表查询地址表,会多出依次查询地址表的过程,因此效率也会有所下降。
例子如下。


6.当最大case值与最小case值之差大于255,超出索引1字节的表达范围时,非线性索引优化同样会造成空间浪费,采用判定树优化:将每个case值作为一个节点,找到这些节点的中间值作为根节点,以此形成一棵二叉平衡树,以每个节点为判定值,大于和小于关系分别对应左子树和右子树,这样可以提高效率。例子如下。


在Release版下,使用IDA查看编译器如何进行优化,如下。
分析:(1)可以看出有一个根节点,左边的多分支流程结构很像一个switch语句,而右边则是一个多次比较判断,和if...else...类似。
(2)在优化过程中,检测树的左子树或右子树能否满足if...else...优化、有序线性优化、非线性索引优化,利用这3种优化来降低树的高度。选择优化也是有条件的,即选择效率最高又满足匹配条件的。如果以上3种优化都无法匹配,就会选择使用判定树进行优化。


5.7do、while、for的比较
1.do循环:先执行循环体,后比较判断。goto语句也可以用来模拟do循环。

如遇到以下指令序列,即可判定它为do循环结构。注意它与if不同,是一个向上跳转的过程,所以条件跳转的逻辑与源码中的逻辑相同。


2.while循环:先比较判断,后执行循环体。while循环结构使用了2个跳转指令完成循环,因为多使用了一个跳转指令,所以while循环比do循环效率低一些。

如遇到以下指令序列,即可判定为while循环结构。在条件跳转到的地址之前会有JMP指令,向上跳转,回到条件比较指令处。

注意while循环结构很可能被优化成do循环结构,通常会被嵌套在if单分支结构中,如下。


3.for循环:先初始化,再比较判断,最后执行循环体。for循环结构需要3个跳转指令完成循环,因此执行速度最慢。

如遇到以下指令序列,即可判定为for循环结构。


5.8编译器对循环结构的优化
1.在02选项下,while循环和for循环都可以使用do循环进行优化。
2.代码外提,在不影响循环结果的前提下。
3.强度削弱,即用等价的低强度运算替换代码中的高强度运算,如用加法代替乘法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值