AST还原技术专题:浅谈去控制流平坦化的思路及方法

举报
悦来客栈的老板 发表于 2021/03/29 22:45:02 2021/03/29
4.3k+ 0 0
【摘要】 一. while-switch结构的控制流 这类平坦化代码很简单,常见于经过obfuscator在线工具混淆后的控制流平坦化。一般代码段不会很长,常见的 switch-case 基本都在10个分支以内,因此是否还原,并不影响阅读。 代码举例: var _0x42b38e = "5|4|3|1|2|0"["split"]('|'), _0x435210 = 0; ...

一. while-switch结构的控制流

这类平坦化代码很简单,常见于经过obfuscator在线工具混淆后的控制流平坦化。一般代码段不会很长,常见的 switch-case 基本都在10个分支以内,因此是否还原,并不影响阅读。

代码举例:


       var _0x42b38e = "5|4|3|1|2|0"["split"]('|'), _0x435210 = 0;
       while (true) {
        switch (_0x42b38e[_0x435210++]) {
        case '0':
        _0x352bac[_0x4447b2] = _0x38b230;
       continue;
        case '1':
        _0x38b230["__proto__"] = _0x529196["bind"](_0x529196);
       continue;
        case '2':
        _0x38b230["toString"] = _0x1bd819["toString"]["bind"](_0x1bd819);
       continue;
        case '3':
       var _0x1bd819 = _0x352bac[_0x4447b2] || _0x38b230;
       continue;
        case '4':
       var _0x4447b2 = _0x124cae[_0x31cdb9];
       continue;
        case '5':
       var _0x38b230 = _0x529196["constructor"]['prototype']["bind"](_0x529196);
       continue;
        }
        break;
       }
   
  

变量_0x42b38e 充当着分发器的角色,_0x435210则是索引,每次索引自增匹配case,从case语句中取相关的代码,直到全部取完。这类代码结构固定,case语句中无更改索引值的代码,因此,取出来的代码顺序是固定的。使用AST编写插件很容易,并能达到一劳永逸的结果。

二.  while-if结构的控制流

这种控制流与第一种比较类似,也是在while循环外面直接定义一个分发器(数组元素全部是常量),只不过switch-case语句变成了if-else 结构,还有一点就是索引值(变量_0x435210)可能会在 代码中改变,不再是自增。其实如果仔细观察,这类代码可以直接改写成 switch-case结构。

代码举例:


       switch(dispatch++)
       {
         ......
        if (dispatch == 5)
         {
         if (somecode)
          {
        dispatch += 2;//在这里更改了dispatch的值,干扰了下一条执行的语句,会走向不同的分支
          }
          ......
         }
        else if (dispatch == 6)
         {
        ......
         }
         ......
       }
   
  

这类代码其实也很好处理,可以先还原成switch-case结构的代码,再进行处理。

唯一需要注意的是 索引值在代码中可能变化,这时需要考虑代码还原后的样子,可能是 while循环,也可能是 if-else 结构。

三.  while-switch-switch...结构的控制流

这类代码其实与上面的代码区别不大,只不过不再有分发器,而是给定 一个初始值,通过这个初始值分别计算出另外多个值来定位 switch-case-switch-case...节点,复杂度增加了。

switch语句嵌套了switch语句,甚至有多重嵌套,看起来确实无从下手。

其实也有技巧,既然代码变复杂了,那就想办法简化它。

对于 while-switch-switch 这种结构,如:


       while(dispatch)
       {
         .....//通过dispath计算出switch中的a和b
        switch(a)
         {
          ......
         case 11:
       switch(b)
        {
        ......
       case 11:
        {
        ......
        dispatch = 5;
       break;
        }
        ......
        }
        ......
         }
       }
   
  

经过分析,所有有用的代码基本都在第二个switch中,当然有些核心代码也可能在第一个switch-case结构中,但是第一个switch大部分都充当了一个继续分发的角色。

如何简化代码,很简单,在此不表。总之,还原前可以做些预处理,不再受垃圾代码的干扰即可。

而对于 while-switch-switch-switch 结构,也可以先简化,再还原,和while-switch-switch 结构没啥区别。

总结:

  • 复杂的代码简单化,也就是我常说的看看能不能转变成熟悉的代码

  • 如何判断什么时候是while循环,什么时候是if-else语句。这个没有什么好的技巧,有时候需要手动还原去找规律找条件。

  • 使用递归语句容易死循环,因此建议使用非递归代码。

备注:所有内容首发于公众号,之后会更新AST反混淆实战、Javascript基础知识、APP逆向、C++、数据结构与算法等等一系列教程,也会更新一些自己的学习心得等,欢迎大家关注。

文章来源: blog.csdn.net,作者:悦来客栈的老板,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq523176585/article/details/115302509

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。