一次使用PhpParser的解密之旅

大兄弟 2018年06月26日4   2114

最近有网友找我解密微擎2C的文件,接到文件浏览了一遍,原来是goto混淆,最后我是拒绝了解密。

这种加密方式已经不是第一次见了,以前也有很多人找我解这种,当时也尝试过去还原,捣鼓了3天左右,不得不说这种加密看起来很简单,其实还原起来非常吃力,第一次解密以失败告终。后来遇到这种加密的单子也是直接拒绝了,因为太难了,如果按照当时的见识和技术去研究的话,直到解密完成起码要花1-2个月,而且解密这种文件顶多也就收四、五块钱一个,所以没有必要浪费这么多时间去研究。

但是拒绝这个单子后回想,以前看过一个大神分析解密魔方的文章,使用的就是PhpParser,并且效果还很不错。所以过后考虑一天,决定研究PhpParser去解密。

庆幸的是,研究PhpParser的同时,我发现了goto混淆器的源代码,是一位外国大佬用PhpParser写的,写得很棒。这里就不提供相关混淆器的信息了,我刚写出来逆向器,想多赚几块钱,就不想泛滥混淆器了。

哈哈,有了源代码后就可以研究他的混淆算法了。

讲解goto加密之前先上几张样本图:

blob.png

blob.png


哈哈,看起来是不是觉得没什么的?其实刚开始我跟大家的想法是一样的,看起来也没太多地方混淆,因为函数名都还是可见的,只不过是打乱了代码顺序而已。

打乱代码顺序,这是这种加密的核心思想,也是解密的关键。可能有人会说,这种混淆完全可以手动还原,是的,的确可以,只要顺着它的执行顺序就可以还原了,方案是可行的但意义并不大,因为如果你的文件非常庞大有些甚至高达80KB,将近8W的混淆代码,所以高额的时间消耗会让你吃不消,甚至想放弃。所以还是乖乖用机器还原吧。

经过研究,这种混淆器的混淆点和解决办法主要分以下几点:

  1. 单纯的if语句:这种单一存在的语句在混淆器中处理后,其中的condition会被反向 也就是条件反向的处理,所以处理if语句前需要遍历一次语法树进行condition的反向还原,即de_cond

  2. 复杂的if语句:比如if..else..和if..elseif..else等,这种if语句的处理只需正常处理即可

  3. 嵌套if语句:编程过程中,语句的嵌套是不可少的,而在混淆器处理过的代码中是没有嵌套可言的,一切都是在外层存在的,在处理if过程中,加入一个节点嗅探,如果嗅探到所嵌套的节点有if语法,则让本次混淆节点再走一遍综合if解析 ([funcion parse_if_stmt]) 

  4. 正常stmt混淆:比如echo "hello world";会被混淆成label xxxx: echo "hello world"; goto next_label;,也就是三个语法块为一条语句,然后使用这三个语法点构成一个混淆节点,类似$tunk[] = array($label,$stmt,$goto),其他语句相同

这里还原难度最大的还是if语句的还原,以为还涉及嵌套的if。下面是if语句的语句处理点:

  1. 解析elseif语法:如果当前节点的下一个节点为if语法块,则进行elseif的还原

  2. 解析else语法:如果遇到的混淆中间节点为goto的,则认定为else语句结束点并进行结束处理。

  3. 检测是否为跳点:由于混淆器内部会有多余的label进行干扰,需要构建一个跳点检测器进行跳点处理,处理foreach等loop语句的干扰节点,遇到这些节点后将跳过一个混淆节点。

  4. 建立节点监听器:最后需要有一个语法树的traverse,你可以建立一个自己的节点监听器,监听过来的语法,然后处理如function、ClassMethod、TryCatch等节点的混淆语法树。

最后说说我写解密脚本过程遇到的问题吧。

对于上面提到的正常的stmt混淆的还原还是比较简单的,写脚本的第一天就完成了这一步。

最让人头疼的还是处理if语句,外层的还原还是很简单的,但是遇到嵌套的处理时就非常头大了,我一共用了3天写这个脚本,其中两天都是在处理if语句。


PhpParser真的很强大,无论你把语法树改成什么样它都可以将你的语法树还原成正常代码。

最后放两张上图的解密代码

blob.png


blob.png