美文网首页
上海大学生网络安全 Reverse Cyvm

上海大学生网络安全 Reverse Cyvm

作者: 喝豆腐脑加糖 | 来源:发表于2018-11-08 16:56 被阅读0次

    最后用angr跑一下结果就出来了。比赛半天没想到,一直在哪僵着看算法。

    # coding=utf8
    
    import angr
    import claripy
    a = angr.Project('cyvm')
    b= [claripy.BVS('argv %d' % i, 8) for i in range(32)]
    c = claripy.Concat(*b)
    st = a.factory.blank_state(addr=0x400CB1)
    sm = a.factory.simulation_manager(st)
    sm.explore(find=0x400CD2)
    found = sm.found[0]
    print sm.found[0].posix.dumps(0) 
    
    图片.png

    正好一直在弄vm ,比赛为了快速出结果angr最好。结束之后还是按照流程分析一下程序吧。

    图片.png

    进入主函数,看到40068有两个形参。看下byte_602100数组的内容是多少。


    图片.png

    之后进入函数,找到内核部分。


    图片.png

    可以清楚的看出是vm的类型。

    • 先mark一下vm的概念逻辑

    虚拟机通常是围绕一个解码 和执行VM指令的主循环构建的,通过循环带有一个转发器,它调用一个函数要在函数列表里进行挑选;对应程序里面这块跳转列表。

    图片.png

    跟进每个偏移,观察每部分的开头和结尾的跳转部分。


    图片.png 图片.png

    除40089d,400c6e其余的结尾都跳转到400c72位置。


    图片.png

    暂时只知道比较,具体是什么数值后面再判断。

    将这些按照下面找到的对照参考。引用的是看雪论坛:(https://bbs.pediy.com/thread-247037.htm

    1. 建立虚拟机Context
      2 进入虚拟机循环
      3 读取VM.EIP地址处的字节码,检查指令的类型,以下是支持的指令类型:
      4 二进制指令
      5 一元指令
      6 流程控制指令
      7 特殊指令
      8 调试指令
      9 NOP和HLT("退出VM")指令。-后者结束虚拟机循环。
      10 跳转到VM循环的起始处。
    VM的初始化块/初始化函数
    执行VM程序中的指令的循环块/循环函数
    通用块/函数,用于解码VM指令的参数、寄存器、寻址模式以及VM创造者任何想要解码的东西
    一个用来执行每个VM指令任务的列表块。这些指令大致相当于现代CPU中用于分解和执行常见ASM指令的微代码。
    一组宏指令,VM相关,不容易映射到ASM操作码,这些指令可能更难理解。
    

    分析流程

    程序首先有许多宏定义,这些宏定义可以是程序中的寄存器,也可以是指令。之后进入循环,通过类似switch-case:操作(handler),对符合条件的数据依次从定义好的数组中读出,组合成我们熟悉的汇编。

    while ( 2 )
      {
        if ( v4 < a2 )
        {
          switch ( *(v4 + a1) ) //byte_602100 处的数据
          {
            case 15:
              __isoc99_scanf(&unk_400D78, &s); 
              v5 += strlen(&s); 
              ++v4;
              continue;
            case 3:
              *(&v6 + *(v4 + 1LL + a1) - 20) = *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 16: 
              *(&v6 + *(v4 + 1LL + a1) - 20) = *(v4 + 2LL + a1);
              v4 += 3;
              continue;
            case 1:
              *(&s + *(&v6 + *(v4 + 2LL + a1) - 20)) = *(&v6 + *(v4 + 1LL + a1) - 20);
              v4 += 3;
              continue;
            case 2:
              *(&v6 + *(v4 + 1LL + a1) - 20) = *(&s + *(&v6 + *(v4 + 2LL + a1) - 20));
              v4 += 3;
              continue;
            case 4:
              *(&v6 + *(v4 + 1LL + a1) - 20) += *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 17:
              *(&v6 + *(v4 + 1LL + a1) - 20) += *(v4 + 2LL + a1);
              v4 += 2;
              continue;
            case 6:
              *(&v6 + *(v4 + 1LL + a1) - 20) ^= *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 5:
              *(&v6 + *(v4 + 1LL + a1) - 20) -= *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 18:
              ++*(&v6 + *(v4 + 1LL + a1) - 20);
              v4 += 2;
              continue;
            case 19:
              --*(&v6 + *(v4 + 1LL + a1) - 20);
              v4 += 2;
              continue;
            case 11:
              *(&v6 + *(v4 + 1LL + a1) - 20) |= *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 7:
              *(&v6 + *(v4 + 1LL + a1) - 20) &= *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 10:
              v8 = *(&v6 + *(v4 + 1LL + a1) - 20) != *(&v6 + *(v4 + 2LL + a1) - 20);
              v4 += 3;
              continue;
            case 12:
              if ( v8 )
                v4 = *(v4 + 1LL + a1);
              else
                v4 += 2;
              continue;
            case 13:
              printf("%d,", *(&v6 + *(v4 + 1LL + a1) - 20));
              v4 += 2;
              continue;
            case 9:
              v4 = *(v4 + 1LL + a1);
              continue;
            default:
              ++v4;
              continue;
            case 14:
              goto LABEL_24;
          }
        }
        break;
      }
    

    reg[0] = 0x20
    reg[2] = 0
    jmp 36
    reg[1] = s[reg[2]]
    reg[2]++
    reg[3] = s[reg[2]]
    reg[2]--
    reg[1] ^= reg[3]
    reg[1] ^= reg[2]
    s[reg[2]] = reg[1]
    reg[2]++

    (会看着很乱,按照流程就能写出来)
    作用就是判断字符串的长度,将数组中的字符和他后一位进行异或,之后再和它当前位置异或,在和储存的结果进行比较。

    图片.png
    按照此处结果写逆运算:
    poc
    #include<stdio.h>
    int main()
    {
     int a[]={0x0A,0x0C,0x04,0x1F,0x48,0x5A,0x5F,0x03,0x62,0x67,0x0E,0x61,0x1E,0x19,0x08,0x36,0x47,0x52,0x13,0x57,0x7C,0x39,0x54,0x4B,0x05,0x05,0x45,0x77,0x15,0x26,0x0E,0x62};
     for (int i=0;i<32;i++){
        a[i]=a[i]^i;
     }
     for(int i=31;i>=0;i--)
     {
        a[i-1]=a[i-1]^a[i];
     }
     for (int i=0;i<32;i++)
     {
        printf("%c",a[i]);
     }
     return 0;
    }

    相关文章

      网友评论

          本文标题:上海大学生网络安全 Reverse Cyvm

          本文链接:https://www.haomeiwen.com/subject/naqwxqtx.html