[How2DEOBF] #2 控制流混淆
序
在前一篇专栏中,我们已经简单了解了关于反混淆的一些内容。在本篇专栏中,我们将针对控制流混淆进行简单的分析。控制流是指按一定的顺序排列程序元素来决定程序执行的顺序,在大部分混淆器中,为了使目标程序的执行逻辑更加难懂,会进行控制流混淆。
控制流混淆的方法有很多,可有Exception Jump,FLA,Bogus Jump乃至GEN3(Skidfuscator)之类,后又会有实现细节上的偏差,可能有些甚至叫法都不固定(比如存在JunkJump这种称呼),比较杂乱。
但人的脑袋总归是有限的,很多控制流混淆都有共通的解法。这里,我们将对一个简单的控制流混淆进行反混淆,从而初步展现一种解决思路。
分析
还是以这个目标程序为例,这次我们使用Caesium混淆器为程序添加了一个控制流混淆。


对比之后我们发现,原来的GOTO竟然变成了这一段奇怪的东西。
由于没有使用Renamer,这个Getstatic指令所获取的field显然是混淆器生成的(1362525270),那么这到底是什么呢?
为了搞清楚,让我们查看一下它。

看起来,它似乎被指定了初始值。对整个程序查验之后,也没有发现对它的修改指令。这样,我们可以确定它是一个不变的量,那么上面那一段的执行结果也就确定了:1 0,所以会跳转到Label 8的位置,也就与混淆前的GOTO执行效果一致了。
好,这样我们也就进行反混淆了——那当然没那么简单。
让我们看看类似的混淆还在哪些地方被运用了——

先前的情况是IFNE,而这里则是IFEQ,如果我们像刚才那样想当然认定只有一种情况,那反混淆的结果就是不完全的,因此,在分析时,应当考虑可能的多种情况。虽说我们可以等反混淆一次之后再分析查验,不过这实在是不够优雅(强迫症)。
保持着谨慎的态度,我们继续分析,果然又有新的发现。

仿照对于最初的那种情况的思路,我们也发现,这个IFGE执行结果也是依赖于一个固定的int类型的field。
这下不好,是否还存在其他可能的跳转呢?
于是,偷懒的我们悄悄看了一下混淆器的源码。

看来我们分析的确实比较到位,四种情况里考察到了三种(事实上示例程序确实不存在第四种情况)。
现在我们可以掌握思路了,这个控制流混淆实质上就是把GOTO拆成4个效果等价的instructions,其中前两个负责跳转,后两个则是异常处理。我们只要对所有这种形式的跳转分析并修改为初始的跳转语句,就可以完成反混淆。
实现
说了这么多,总算进入正题了,这次我们仍然使用与上一篇专栏同款的反混淆器编写Transformer。
遍历所有class的method,并匹配这种跳转。

我们知道这些跳转实质上都是GOTO,所以全部替换成GOTO就完事了。

等等,我们似乎少了什么。
没错,还有那些混淆器生成的field需要去除,虽然这些东西对最后反混淆的结果几乎没有影响了,但是谁叫我们是强迫症呢。
让我们记录下这些跳转时使用的field,然后最后再清除它们。
最后完整的代码差不多是这样:

运行之后,成功去除了控制流混淆。
后记
以上便是一个简单的控制流混淆的处理流程。在处理这种混淆时,尤其要留心观察,考虑多种情况,而且不能暴躁(那就更难想到好的处理策略了)。要想提升分析能力,只能够多多实践,可以自行寻找一些样本或是混淆器进行尝试。
下载示例程序:https://wwcx.lanzoum.com/b032jht7i(5080)