• wp | re | 2021祥云杯 逆向部分


    今年是第二年打祥云杯,只能说逆向的题型多钟多样,上来一个内核题就给我整蒙了。

    出了这两个小题目,简单记录一下。

    勒索解密

    吐槽:你家勒索病毒连个图形化都没有,谁知道往哪儿给你打钱啊

    这个题是win32的逆向,主要的逻辑比较清晰的,重点就是加密,然后疯狂的查文档去看就行了,主要分析如下:

    main函数还是比较清晰的:

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // ecx
      void *v4; // ecx
      void *v5; // eax
      _DWORD *i; // esi
      unsigned int v7; // edi
      int v8; // ebx
      void **v9; // ebx
      void **v10; // edi
      size_t v11; // ecx
      size_t v12; // ecx
      void *v13; // ecx
      void *v14; // eax
      int v16; // [esp+10h] [ebp-C0h] BYREF
      char v17[4]; // [esp+14h] [ebp-BCh]
      void *Block[5]; // [esp+18h] [ebp-B8h] BYREF
      unsigned int v19; // [esp+2Ch] [ebp-A4h]
      void *v20[6]; // [esp+30h] [ebp-A0h] BYREF
      HCRYPTPROV phProv[21]; // [esp+48h] [ebp-88h] BYREF
      void *Src[5]; // [esp+9Ch] [ebp-34h] BYREF
      unsigned int v23; // [esp+B0h] [ebp-20h]
      __int64 v24; // [esp+B4h] [ebp-1Ch] BYREF
      int v25; // [esp+BCh] [ebp-14h]
      int v26; // [esp+CCh] [ebp-4h]
    
      v19 = 15;
      Block[4] = 0;
      LOBYTE(Block[0]) = 0;
      sub_11458A0(Block, ".bmp", 4u);
      v26 = 0;
      sub_1146D00((int)&v16, v3, Block, v17[0]);
      v26 = -1;
      if ( v19 >= 0x10 )
      {
        v4 = Block[0];
        if ( v19 + 1 >= 0x1000 )
        {
          if ( ((int)Block[0] & 0x1F) != 0 )
    LABEL_4:
            _invalid_parameter_noinfo_noreturn();
          v5 = (void *)*((_DWORD *)Block[0] - 1);
          if ( v5 >= Block[0] )
            _invalid_parameter_noinfo_noreturn();
          if ( (unsigned int)(Block[0] - v5) < 4 )
            _invalid_parameter_noinfo_noreturn();
          if ( (unsigned int)(Block[0] - v5) > 0x23 )
            _invalid_parameter_noinfo_noreturn();
          v4 = (void *)*((_DWORD *)Block[0] - 1);
        }
        j_j___free_base(v4);
      }
      v24 = 0i64;
      v25 = 0;
      v26 = 1;
      sub_1145AA0(1, (char)"C:\XX_CTF_XX\", (int)&v24);
      for ( i = (_DWORD *)v24; i != (_DWORD *)HIDWORD(v24); i += 8 )// 遍历文件进行加密
      {
        v7 = i[6];
        v8 = i[7];                                  // i应该是个结构体数组
                                                    // i[0] 指向文件名
                                                    // i[6]是文件长度
        v23 = 15;
        Src[4] = 0;
        LOBYTE(Src[0]) = 0;
        sub_1145580(Src, i, 0, 0xFFFFFFFF);         // 读取文件或者校验文件
                                                    // 
        LOBYTE(v26) = 2;
        if ( v8 | v7 && v8 <= 0 )
        {
          if ( v7 <= 0x100000 )
          {
            sub_1146B70(v20, (int)Src);             // 创建一个新文件?
            memset(phProv, 0, sizeof(phProv));
            phProv[8] = 15;
            phProv[7] = 0;
            LOBYTE(phProv[3]) = 0;
            phProv[16] = 15;
            phProv[15] = 0;
            LOBYTE(phProv[11]) = 0;
            phProv[0] = 0;
            phProv[1] = 0;
            phProv[2] = 0;
            phProv[10] = 0;
            phProv[18] = 0;
            phProv[19] = 0;
            phProv[9] = 0;
            phProv[17] = 0;
            phProv[20] = 0;
            LOBYTE(v26) = 4;
            v9 = v20;
            if ( v20[5] >= (void *)0x10 )
              v9 = (void **)v20[0];
            v10 = Src;
            if ( v23 >= 0x10 )
              v10 = (void **)Src[0];
            if ( CryptAcquireContextA(phProv, 0, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18u, 0xF0000000) )
            {
              if ( *(_BYTE *)v10 )
                v11 = strlen((const char *)v10);
              else
                v11 = 0;
              sub_11458A0(&phProv[3], v10, v11);
              if ( *(_BYTE *)v9 )
                v12 = strlen((const char *)v9);
              else
                v12 = 0;
              sub_11458A0(&phProv[11], v9, v12);
              sub_11418F0((int)phProv);             // 这里创建了key
                                                    // 并进行了加密操作
              if ( phProv[10] )
                j_j_j___free_base((void *)phProv[10]);
              if ( phProv[18] )
                j_j_j___free_base((void *)phProv[18]);
              if ( phProv[19] )
                j_j_j___free_base((void *)phProv[19]);
              if ( phProv[1] )
                CryptDestroyKey(phProv[1]);
              if ( phProv[2] )
                CryptDestroyKey(phProv[2]);
              if ( phProv[0] )
                CryptReleaseContext(phProv[0], 0);
            }
            else
            {
              if ( phProv[10] )
                j_j_j___free_base((void *)phProv[10]);
              if ( phProv[18] )
                j_j_j___free_base((void *)phProv[18]);
              if ( phProv[19] )
                j_j_j___free_base((void *)phProv[19]);
              if ( phProv[1] )
                CryptDestroyKey(phProv[1]);
              if ( phProv[2] )
                CryptDestroyKey(phProv[2]);
              if ( phProv[0] )
                CryptReleaseContext(phProv[0], 0);
            }
            sub_11459A0(&phProv[11]);               // 释放一些资源
            sub_11459A0(&phProv[3]);
            sub_11459A0(v20);
            LOBYTE(v26) = 1;
            sub_11459A0(Src);
          }
          else
          {
            LOBYTE(v26) = 1;
            sub_11459A0(Src);
          }
        }
        else
        {
          LOBYTE(v26) = 1;
          if ( v23 >= 0x10 )
          {
            v13 = Src[0];
            if ( v23 + 1 >= 0x1000 )
            {
              if ( ((int)Src[0] & 0x1F) != 0 )
                goto LABEL_4;
              v14 = (void *)*((_DWORD *)Src[0] - 1);
              if ( v14 >= Src[0] || (unsigned int)(Src[0] - v14) < 4 || (unsigned int)(Src[0] - v14) > 0x23 )
                goto LABEL_4;
              v13 = (void *)*((_DWORD *)Src[0] - 1);
            }
            j_j___free_base(v13);
          }
        }
      }
      sub_1146390(&v24);
      return 0;
    }
    

    重点看创建了key和加密部分的这个函数,跟进去:

    char __thiscall sub_11418F0(int this)
    {
      char v3; // bl
      int v4; // ecx
      BOOL v5; // eax
      int v6; // ecx
      unsigned int v7; // edi
      void *v8; // eax
      __int128 v9; // [esp+Ch] [ebp-84h] BYREF
      HCRYPTHASH phHash; // [esp+1Ch] [ebp-74h] BYREF
      int v11[23]; // [esp+20h] [ebp-70h] BYREF
      __int128 pbData; // [esp+7Ch] [ebp-14h] BYREF
    
      if ( !(unsigned __int8)sub_11410F0() )
        return 0;
      pbData = 0ui64;
      v9 = 0ui64;
      v11[0] = 0x67452301;                          // 这里像是key
                                                    // 
      v11[1] = 0xEFCDAB89;
      v11[2] = 0x98BADCFE;
      v11[3] = 0x10325476;
      v11[4] = 0;
      v11[5] = 0;
      sub_1147290(0x41u);
      sub_11473F0(&v9, (int)v11);
      *(_QWORD *)&pbData = __PAIR64__(DWORD1(v9), HIDWORD(v9));
      HIDWORD(pbData) = v9;                         // 这里的具体操作是:
                                                    // "0123456789abcdeffedcba9876543210" 转码:
                                                    // v9="21e7b18e4fd4544b28c8aea3b22fc60e"
      DWORD2(pbData) = _time64(0);
      phHash = 0;
      v3 = 0;
      if ( CryptCreateHash(*(_DWORD *)this, 0x800Cu, 0, 0, &phHash) )// sha256
      {
        if ( CryptHashData(phHash, (const BYTE *)&pbData, 16u, 0) )// 应该是把key弄过去了
                                                    // 对上面的16个字符进行sha256
        {
          v5 = CryptDeriveKey(*(_DWORD *)this, 0x660Eu, phHash, 0, (HCRYPTKEY *)(this + 4));// AES 660e
                                                    // 
          v3 = 0;
          v4 = 1;
          if ( v5 )
            v3 = 1;
        }
      }
      if ( phHash )
        CryptDestroyHash(phHash);
      if ( !v3 )
        return 0;
      if ( !sub_11415D0((HCRYPTPROV *)this, v4) )   // 解密了一个公钥?
                                                    // 使用base64存的
                                                    // 
        return 0;
      if ( !sub_1141700((void *)this, &pbData, v6) )// 加密
        return 0;
      v7 = 16 * ((*(_DWORD *)(this + 36) + 15) / 16 + 1);
      v8 = operator new[](v7, (const struct std::nothrow_t *)&unk_1175EB0);
      *(_DWORD *)(this + 72) = v8;
      if ( !v8 )
        return 0;
      memset(v8, 0, v7);
      memmove_0(*(void **)(this + 72), *(const void **)(this + 40), *(_DWORD *)(this + 36));
      if ( !sub_11417D0((_DWORD *)this) )           // 最后的对flag文件进行的加密
                                                    // 
        return 0;
      sub_1141400((_DWORD *)this);                  // 写文件
      return 1;
    }
    

    重点就是这个key是怎么来的,然后下面的aes 660e是查文档知道的。

    这个key有一部分是time(0)的时间戳,动调一下和输入无关,这个地方我的第一想法是爆破,但是转念一想好像没必要,直接查了flag文件的创建时间就知道了时间戳,夹在里面就得到了key:

    然后关注文件输出的部分:

    重点就是这三部分的内容,我自己写flag试了一试,第一部分是文件加密的结果,第二部分是key加密的结果,key我们有了,不关心,最后还有4bytes的内容,也无所谓,最后直接对flag.bmp.ctf_encrypt解密然后去掉最后的东西就行了

    脚本如下(写的比较乱,很多测试数据在里面,将就一下):

    from Crypto.Cipher import AES
    import base64
    import hashlib
    import time 
    
    # 字符类型的时间
    tss1 = '2021-8-16 15:17:25'
    tss1 = '2021-8-21 16:21:37'
    timeArray = time.strptime(tss1, "%Y-%m-%d %H:%M:%S")
    timeStamp = int(time.mktime(timeArray))
    print(hex(timeStamp))
    
    
    def fill(b, x):
    	while len(b) % 16 != 0:
    		b += x
    	return b
    
    #0x6120b08d
    #0x611a1105 0x6120b791
    # data= b'flag{12345678901234567890}'
    data = b"xa2Kx19.Rxac1xcd0xb1x06x86	x1bx9dxc1xc854Sx88x1ax9axd9xf0xa1Sxc12xa5.$xadHx03x1fJxb5L'bxdb'x8cO4xd4x94"
    data = open('flag.bmp.ctf_crypter','rb').read()
    # key = bytes.fromhex('B22FC60E4FD4544B6bAE206121E7B18E')
    #key = bytes.fromhex('B22FC60E4FD4544B8db0206121E7B18E')
    key = bytes.fromhex('B22FC60E4FD4544B05111a6121E7B18E')  #flag
    #key = bytes.fromhex('B22FC60E4FD4544B91b7206121E7B18E')
    key = hashlib.new('sha256', key).digest()[:16]
    print(key)
    print(len(key))
    
    aes = AES.new(key, AES.MODE_CBC, iv=b'x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x01')
    _data = aes.decrypt(fill(data, b'x00'))
    #print(_data)
    open('flag.bmp','wb').write(_data)
    

    最后就是简单的AES-CBC,但是我根据文件头去掉多余信息以后图片查看器还是打不开,但是ps能打开,就不管那么多了,直接拿flag:

    Rev_Dizzy

    简单题,就是IDA会卡住,出来5000行代码,对输入进行+-^运算,把所有+换成-,-换成+,然后异或不变反着跑一下直接就能出来了,脚本太长不贴了,结果如下:

    结语

    后面还有一个macos的题,一个hardware的题,一个内核APC题,没做出来,没办法,慢慢补了。

    就这样吧。

    本文来自博客园,作者:Mz1,转载请注明原文链接:https://www.cnblogs.com/Mz1-rc/p/15172017.html

  • 相关阅读:
    论文研读
    论文研读
    2019春 软件工程实践 助教总结
    第十三次作业成绩汇总
    第九次作业成绩汇总
    第十七周助教工作总结
    Docker 学习笔记(四):Bug 日志与其他零散知识
    bash 和 powershell 常用命令集锦
    Kubernetes 学习笔记(二):本地部署一个 kubernetes 集群
    Kubernetes 学习笔记(一):基础概念
  • 原文地址:https://www.cnblogs.com/Mz1-rc/p/15172017.html
Copyright © 2020-2023  润新知