zoukankan      html  css  js  c++  java
  • 一道VM的逆向所引发的符号执行思路

    逆向虚拟机保护

    虚拟机保护类的题目需要找到虚拟机的vm_code(字节码),各个handler,然后进一步分析虚拟机保护代码的流程。
    用IDA打开程序,经分析后0x403040全局变量地址处存储的就是所有的字节码,共114个。

    所有的字节码:

      0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
      0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
      0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
      0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
      0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 
      0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00
      0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
      0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
      0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
      0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 
      0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
      0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
      0x36, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
      0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
      0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
      0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
      0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
      0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 
      0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
      0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
      0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF, 
      0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF,
      0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 
      0x07, 0x00, 0x00, 0x00, 0x84, 0xFF, 0xFF, 0xFF, 
      0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF,
      0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
      0x07, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00
    

    我们进入vm函数后发现,虚拟机的流程大致是将我们的输入flag与a1字节码数组运算后存到V4数组中,然后比较V4数组与a1字节码中的某些数据是否相等,相等则输入正确。所以我们可以先得出数组V4。

    我们将v4[v8] != a1[v10 + 1] 改为 v4[v8] = a1[v10 + 1]的出正确的数组,同时记录虚拟机的执行顺序即执行字节码的顺序。最后的出V4数组与执行字节码的顺序分别为:

    unsigned char v4[] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
    unsigned char Index[100] = { 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114 };
    

    将虚拟机的算法的逆过程结合V4和Index得出代码求出flag。

    int __cdecl vm_operad(int *a1, int a2)
    {
    
      int result; // eax
      unsigned char Str[100] = {0}; // [esp+13h] [ebp-E5h]
      unsigned char v4[100] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xa7, 0x31, 0xf1, 0x28, 0x84, 0xc1, 0x1e, 0x7a }; // [esp+77h] [ebp-81h]
      char v5; // [esp+DBh] [ebp-1Dh]
      int v6; // [esp+DCh] [ebp-1Ch]
      int v7; // [esp+E0h] [ebp-18h]
      int v8; // [esp+E4h] [ebp-14h]
      int v9; // [esp+E8h] [ebp-10h]
      int v10; // [esp+ECh] [ebp-Ch]
     
      char Index[] = {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114};
      
      v10 = 0x72;
      v9 = 0xf;
      v7 = 0xf;
      v6 = 0xf;
      int i = 0;
      while ( 1 )
      {
        result = v10;
        if ( sizeof(Index) - i + 1 == 0 )
          return result;
    
        i++;
        switch ( a1[Index[sizeof(Index) - i]] )
        {
          case 1:
    	--v9;
    	--v7;
    	--v10;
    	v5 = v4[v7];
            break;
          case 2:
    	v10 -= 2;
    	Str[v9] = v5 - a1[v10 + 1];
            break;
          case 3:
    	v10 -= 2;
    	Str[v9] = v5 + LOBYTE(a1[v10 + 1]);
            break;
          case 4:
    	v10 -= 2;
    	Str[v9] = v5 ^ a1[v10 + 1] ;
            break;
          case 5:
    	v10 -= 2;
    	Str[v9] = v5 / a1[v10 + 1];
            break;
          case 6:
    	--v10;
            break;
          case 7:		
            v10 -= 2;		
    	break;
          case 8:
    	--v6;
    	--v10;
    	v5 = Str[v6]; 
            break;
          case 10:
            --v10;
            break;
          case 11:
    	--v10;
    	Str[v9] = v5 + 1;     
            break;
          case 12:
    	--v10;
    	Str[v9] = v5 - 1;
            break;
          default:
            continue;
        }
      }
    }
    

    利用符号执行

    符号执行

    符号执行指的是用符号值来代替某些变量的真实值,用符号值来遍历程序所有的执行分支,而用真实值只能遍历一条分支。然后通过约束求解引擎来在筛选所有的分支选出符合条件的那一个,最后得出正确的输入值。

    angr

    angr是二进制程序分析框架,可以用来进行完成符号执行。
    在此题目中我们希望程序执行到vm函数自然返回处,而不会执行到what a shame...处,这样就说明其输入正确。
    vm函数自然返回处的地址为0x40175E

    what a shame...处的地址为0x4016e6

    所以我们可以利用angr写python符号执行脚本:

    import angr                                                #导入angr库
    p = angr.Project('signal.exe',auto_load_libs=False)        #创建一个Project,程序不引用任何其他导入库
    st = p.factory.entry_state()                               #将程序加载并执行到入口处,返回其状态
    sm = p.factory.simulation_manager(st)                      #创建一个模拟执行器
    sm.explore(find=0x40175E, avoid=0x4016E6)                  #设置约束条件,find是程序需要执行到的地址,avoid是程序不能执行到的地址
    print(sm.found[0].posix.dumps(0))                          #输出结果
    
    

    参考:https://www.52pojie.cn/thread-1176826-1-1.html

  • 相关阅读:
    Hadoop与hbase单机环境安装
    Hive集成Hbase
    正确搭建hbase完全分布式集群(二)
    正确搭建hbase完全分布式集群(一)
    zookeeper 及 独立hbase 的安装与配置
    sqoop安装及导入sqlserver数据
    hadoop+hive+sqoop安装笔记
    如何正确安装mysql 8
    安装nginx为windows服务
    python web编程之django post请求
  • 原文地址:https://www.cnblogs.com/revercc/p/14311233.html
Copyright © 2011-2022 走看看