去控制流平坦化学习

控制流平坦化是OLLVM中使用到的一种代码保护方式,它还有2个兄弟-虚假控制流和指令替换,这3种保护方式可以累加,对于静态分析来说混淆后代码非常复杂。

控制流平坦化的主要思想就是以基本块为单位,通过一个主分发器来控制程序的执行流程。

例如一个常见的if-else分支结构的程序可以是这样:
在这里插入图片描述经过控制流平坦化之后,得到了一个相当规整的流程图:
在这里插入图片描述
控制流平坦化在代码上体现出来可以简要地理解为是while+switch的结构,其中的switch可以理解为主分发器。这一点在IDA的反汇编里面可以很明显地体现出来。

混淆前:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax

  if ( argc == 2 )
  {
    if ( check_password(argv[1]) )
      puts("Congratulation!");
    else
      puts("error");
    result = 0;
  }
  else
  {
    puts("error");
    result = 1;
  }




  return result;
}

混淆后:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // eax
  int v4; // eax
  signed int v5; // ecx
  signed int v7; // [rsp+44h] [rbp-1Ch]
  int v8; // [rsp+58h] [rbp-8h]

  v8 = 0;
  v7 = 1633153878;
  do
  {
    while ( v7 > -1424773165 )
    {
      if ( v7 > -608328430 )
      {
        if ( v7 > 1948349962 )
        {
          if ( v7 == 1948349963 )
          {
            v8 = 1;
            v7 = -1883523171;
            puts("error");
          }
        }
        else if ( v7 > 1633153877 )
        {
          if ( v7 == 1633153878 )
          {
            v3 = 1228678806;
            if ( argc != 2 )
              v3 = 1948349963;
            v7 = v3;
          }
        }
        else
        {
          switch ( v7 )
          {
            case -608328429:
              v8 = 0;
              v7 = -1883523171;
              break;
            case -549365528:
              v7 = -608328429;
              puts("Congratulation!");
              break;
            case 1228678806:
              v4 = check_password(argv[1]);
              v5 = -1424773164;
              if ( v4 )
                v5 = -549365528;
              v7 = v5;
              break;
          }
        }
      }
      else if ( v7 == -1424773164 )
      {
        v7 = -608328429;
        puts("error");
      }
    }
  }
  while ( v7 != -1883523171 );
  return v8;
}

从IDA的流程图中也可以很容易地识别出经过控制流平坦化的程序。
在这里插入图片描述
可以看到,经过平坦化之后的代码块大部分整齐地堆积在程序流图的下方,而这部分代码就是真正有意义的代码块,称之为相关块。
而去平坦化要做的就是在茫茫人海中识别出相关块,然后理清楚这些相关块之间的关系。
目前去控制流平坦化最有效的办法是利用符号执行。具体的脚本和使用我参考了
反混淆:恢复被OLLVM保护的程序
而控制流平坦化的理论和去平坦化的算法参考了
反混淆:恢复被OLLVM保护的程序

然鹅在用腾讯安全应急响应中心提供的工具时因为angr和barf版本不同报了很多错,我在https://github.com/SnowGirls/deflat下载了这个工具的更新版,遇到部分报错如下:

  File "default.py", line 32, in statement_inspect
    if len(expressions) != 0 and isinstance(expressions[0], pyvex.expr.ITE):
TypeError: object of type 'generator' has no len()

谷歌了一下,发现了和我遇到同样问题的师傅

修复去除控制流平坦化工具deflat.py

这位师傅罗列了很多的报错和修改方法,在参考并修改后执行成功。
在这里插入图片描述在这里插入图片描述可以发现经过去平坦化之后的代码已经恢复了大部分的可读性。

上面的反混淆脚本在处理控制流平坦化时只能解决简单的while+switch套娃。如果输入的数据能够影响循环的走向,我们就不得不处理已经经过的分支,否则因为分支过多而爆内存…个人猜想可以通过手动patch的方法对程序执行的路径树进行“剪枝”,或者把一个控制流平坦化流程图分成2个。目前还没有实际遇到过。以后遇到的时候再具体分析。

参考:

反混淆:恢复被OLLVM保护的程序
https://www.freebuf.com/articles/network/144896.html

利用符号执行去除控制流平坦化
https://security.tencent.com/index.php/blog/msg/112

修复去除控制流平坦化工具deflat.py
https://www.jianshu.com/p/cf72c6128b7a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值