一次使用 PhpParser 的解密之旅

2019-04-15 17:50:11 阅读:9 编辑

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

https://gochannel.org/links/link/snapshot/7443