zoukankan      html  css  js  c++  java
  • CISCN 2021 西南赛区 Reverse Writeup

    babyRe

    主要加密代码如下

    _BYTE *__fastcall encode(const char *a1)
    {
      _BYTE *v2; // [rsp+18h] [rbp-28h]
      signed __int64 v3; // [rsp+20h] [rbp-20h]
      int v4; // [rsp+30h] [rbp-10h]
      int v5; // [rsp+34h] [rbp-Ch]
      __int64 v6; // [rsp+38h] [rbp-8h]
    
      v3 = strlen(a1);
      if ( v3 % 3 )
        v6 = 4 * (v3 / 3 + 1);
      else
        v6 = 4 * (v3 / 3);
      v2 = malloc(v6 + 1);
      v2[v6] = 0;
      v5 = 0;
      v4 = 0;
      while ( v5 < v6 - 2 )
      {
        v2[v5] = a3zanjvbmdZekol[(unsigned __int8)a1[v4] >> 2];
        v2[v5 + 1] = a3zanjvbmdZekol[(16 * a1[v4]) & 0x30 | ((unsigned __int8)a1[v4 + 1] >> 4)];
        v2[v5 + 2] = a3zanjvbmdZekol[(4 * a1[v4 + 1]) & 0x3C | ((unsigned __int8)a1[v4 + 2] >> 6)];
        v2[v5 + 3] = a3zanjvbmdZekol[a1[v4 + 2] & 0x3F];
        v4 += 3;
        v5 += 4;
      }
      if ( v3 % 3 == 1 )
      {
        v2[v5 - 2] = 61;
        v2[v5 - 1] = 61;
      }
      else if ( v3 % 3 == 2 )
      {
        v2[v5 - 1] = 61;
      }
      return v2;
    }
    

    容易看出是 base64 变种,编写脚本根据常量表进行替换即可正常解密

    import base64
    
    a="3ZAnJVbMd/zEkolRBDW4KUYT0ga1PF9j86qwuXHciCOfr2tLmexGhpSI+NQ5y7sv"
    b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    s="gHe6gIrSlYUqkGPeg4KNo4Vql4g6g4UqgHgHl4JNonBhlbk+och="
    c=""
    for i in s:
        c=c+b[a.find(i)]
    print(base64.b64decode(c))
    

    一次脱单的机会

    程序主要代码如下

          if ( *(float *)&dword_7FF705EB5638 <= 0.0 )
          {
            if ( IsDebuggerPresent() )
              goto LABEL_22;
            sub_7FF705EB1020("f");
            if ( IsDebuggerPresent() )
              goto LABEL_22;
            sub_7FF705EB1020("l");
            if ( IsDebuggerPresent() )
              goto LABEL_22;
            sub_7FF705EB1020("ag");
            sub_7FF705EB1020("{md5(");
            v8 = 0i64;
            v13[2] = _mm_load_si128((const __m128i *)&xmmword_7FF705EB35D0);
            v13[1] = _mm_load_si128((const __m128i *)&xmmword_7FF705EB35E0);
            v13[0] = _mm_load_si128((const __m128i *)&xmmword_7FF705EB35F0);
            v13[3] = v13[0];
            v13[4] = 0i64;
            if ( _mm_cvtsi128_si32(v13[0]) )
            {
              do
              {
                sub_7FF705EB1020("%c");
                ++v8;
              }
              while ( v13[0].m128i_i32[v8] );
            }
            if ( IsDebuggerPresent() )
              goto LABEL_22;
            sub_7FF705EB1020(")");
            if ( IsDebuggerPresent() )
              goto LABEL_22;
            sub_7FF705EB1020("}");
          }
    

    在主函数开头下断点,然后修改 ip 跳过 IsDebuggerPresent,直接运行到中间输出的地方即可得到 flag

    ag{md5(女神早已不是女神
    

    注意编码要选择 utf8,不要选 gbk

    import hashlib
    
    s=hashlib.md5('女神早已不是女神'.encode('utf8')).hexdigest()
    print(s)
    

    CryptoMachine

    分析异常处理

    程序启动时首先调用 TlsCallback 函数

    ```cpp
    char *__stdcall TlsCallback_0(int a1, int a2, int a3)
    {
      char *result; // eax
      DWORD flOldProtect; // [esp+8h] [ebp-Ch] BYREF
      LPVOID v5; // [esp+Ch] [ebp-8h]
      LPVOID lpAddress; // [esp+10h] [ebp-4h]
    
      if ( a2 == 1 )
      {
        lpAddress = &loc_4025DB;
        VirtualProtect(&loc_4025DB, 5u, 0x40u, &flOldProtect);
        *(_BYTE *)lpAddress = 0xE9;
        *(_DWORD *)((char *)lpAddress + 1) = (char *)sub_4021D0 - (char *)lpAddress - 5;// jmp sub_4021D0
        v5 = SEH_412700;
        VirtualProtect(SEH_412700, 1u, 0x40u, &flOldProtect);
        *(_DWORD *)v5 = 0x1B8;
        *((_WORD *)v5 + 2) = 0xC300;                // ret
        VirtualProtect(sub_4021D0, 0x30u, 0x40u, &flOldProtect);
        *((_BYTE *)sub_4021D0 + 38) = 0xE8;         // call unknown_libname_17
        *(_DWORD *)((char *)sub_4021D0 + 39) = (char *)unknown_libname_17 - (char *)((char *)sub_4021D0 + 38) - 5;
        *((_BYTE *)sub_4021D0 + 43) = 0xE9;
        result = (char *)sub_4021D0 + 43;
        *((_DWORD *)sub_4021D0 + 11) = (_BYTE *)lpAddress - ((char *)sub_4021D0 + 43);// jmp lpAddress
      }
      return result;
    }
    

    通过 SMC 操作修改 __scrt_common_main_seh 函数,使程序在进入主函数前调用 sub_4021D0 函数

    .text:004021D0 sub_4021D0 proc near                    ; DATA XREF: TlsCallback_0+39↓o
    .text:004021D0                                         ; TlsCallback_0+86↓o ...
    .text:004021D0 push    offset main
    .text:004021D5 push    large dword ptr fs:0
    .text:004021DC mov     large fs:0, esp
    .text:004021E3 push    offset sub_4021A0
    .text:004021E8 push    large dword ptr fs:0
    .text:004021EF mov     large fs:0, esp
    .text:004021F6 call    _register_thread_local_exe_atexit_callback ; Microsoft VisualC universal runtime
    .text:004021FB jmp     loc_4025E0
    .text:004021FB sub_4021D0 endp
    

    sub_4021D0 函数在 SEH 链中添加虚拟机入口

    进入主函数,首先将需要加密的 flag.docx 读入到 block,随后创建 output 文件用来存放加密后的密文

      Buffer = 0;
      nNumberOfBytesToRead = 0;
      CreateFileA(FileName, 0x80000000, 1u, 0, 3u, 0, 0);
      stack = malloc(0x100u);
      memset(stack, 0, 0x100u);
      ReadFile(0, &Buffer, 0, &nNumberOfBytesToRead, 0);
      Block = malloc(0x3000u);
      memset(Block, 0, 0x3000u);
      v3 = (int (__stdcall *)(const char *, unsigned int, int, _DWORD))sub_401820(aCsgbpNdlk);
      v4 = (void (__stdcall *)(int, void *, int, int *, _DWORD))sub_401820(aRdcgbljb);
      nNumberOfBytesToRead = sub_401820(aWskwacokm);
      v5 = v3("flag.docx", 0x80000000, 1, 0);
      outfile = ((int (__stdcall *)(const char *, int, int, _DWORD, int, _DWORD, _DWORD))v3)(
                  "output",
                  0x40000000,
                  1,
                  0,
                  2,
                  0,
                  0);
      v4(v5, Block, 12288, &blocklen, 0);
      MEMORY[0x44D000] = 0;
    

    MEMORY[0x44D000] = 0 会触发 SEH 调用虚拟机

      reg = envp[46];
      if ( *reg == 0xB4900D8B )
      {
        envp[0x2E] = (_DWORD *)((char *)envp[0x2E] - 3);
        v23 = (int (__cdecl *)(_DWORD))sub_401820(aSdvvjmgileooiu);
        v15 = v23(sub_4018C0);
        result = 0;
      }
      else if ( envp[46] < (_DWORD *)dword_41A8D8 || (unsigned int)envp[46] > 0x41AA5C )
      {
        result = 1;
      }
    

    第一次进入虚拟机时会调用 SetUnhandledExceptionFilter(sub_4018C0) 注册 UnhandledException 异常处理函数 sub_4018C0

    第二次进入虚拟机时会返回 1 抛出 UnhandledException 异常进行反调试,在调试状态下不会执行 sub_4018C0 导致程序退出

    int __cdecl sub_4018C0(int a1)
    {
      if ( **(_DWORD **)(*(_DWORD *)(a1 + 4) + 184) == 0x8B0000C6 )
        *(_DWORD *)(*(_DWORD *)(a1 + 4) + 184) = dword_41A8D8;
      return -1;
    }
    

    sub_4018C0 对虚拟机的状态进行初始化,将 ip 指向虚拟机的指令段,由于处理器无法识别虚拟机指令,所以返回时会触发异常再次进入虚拟机

    这里把内存中 envp[46] 的值手动修改成 0x0041A8D8 即可正常调试虚拟机

    分析虚拟机

    虚拟机主要代码如下

        switch ( *reg )
        {
          case 1:
            v26 = 0;
            envp[46] = reg + 1;                     // nop
            break;
          case 0xA0:
            mem[reg[1]] = reg[2];                   // mem(a1)=a2
            envp[46] = reg + 3;
            break;
          case 0xB0:
            v22 = mem[reg[1]];
            ++sp_;
            *((_DWORD *)stack + sp_) = v22;         // push(mem(a1))
            envp[46] = reg + 2;
            break;
          case 0xB5:
            mem[reg[1]] = *((_DWORD *)stack + sp_--);// mem(a1)=pop()
            envp[46] = reg + 2;
            break;
          case 0xC0:
            m1 = mem[reg[1]];                       // mem(0)=cmp(mem(a1),mem(a2))
            m2 = mem[reg[2]];
            mem[0] = 0;
            if ( m1 <= m2 )
            {
              if ( m1 == m2 )
              {
                mem[0] |= 2u;                       // 010 mem(a1)==mem(a2)
              }
              else if ( m1 < m2 )
              {
                mem[0] |= 1u;                       // 001 mem(a1)<mem(a2)
              }
            }
            else
            {
              mem[0] |= 4u;                         // 100 mem(a1)>mem(a2)
            }
            envp[46] = reg + 3;
            break;
          case 0xD0:
            mem[reg[1]] ^= mem[reg[2]];             // mem(a1)^=mem(a2)
            envp[46] = reg + 3;
            break;
          case 0xD1:
            mem[reg[1]] += mem[reg[2]];             // mem(a1)+=mem(a2)
            envp[46] = reg + 3;
            break;
          case 0xD2:
            mem[reg[1]] -= mem[reg[2]];             // mem(a1)-=mem(a2)
            envp[46] = reg + 3;
            break;
          case 0xD3:
            ++mem[reg[1]];                          // mem(a1)++
            envp[46] = reg + 2;
            break;
          case 0xD4:
            --mem[reg[1]];                          // mem(a1)--
            envp[46] = reg + 2;
            break;
          case 0xE0:
            envp[46] = (_DWORD *)(4 * reg[1] + 0x41A8D8);// jmp(a1)
            break;
          case 0xE1:
            if ( (mem[0] & 2) != 0 )
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);// jeq
            else
              envp[46] = reg + 2;
            break;
          case 0xE2:
            if ( (mem[0] & 2) != 0 )                // jne
              envp[46] = reg + 2;
            else
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);
            break;
          case 0xE3:
            if ( (mem[0] & 4) != 0 )
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);// ja
            else
              envp[46] = reg + 2;
            break;
          case 0xE4:
            if ( (mem[0] & 6) != 0 )
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);// jae
            else
              envp[46] = reg + 2;
            break;
          case 0xE5:
            if ( (mem[0] & 1) != 0 )
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);// jb
            else
              envp[46] = reg + 2;
            break;
          case 0xE6:
            if ( (mem[0] & 3) != 0 )
              envp[46] = (_DWORD *)(4 * reg[1] + 4303064);// jbe
            else
              envp[46] = reg + 2;
            break;
          case 0xF0:
            v4 = sub_401810(0);                     // random
            v5 = rand();
            srand(v5 + v4);
            v6 = (unsigned __int8)rand();
            v7 = ((unsigned __int8)rand() << 8) | v6;
            v8 = ((unsigned __int8)rand() << 16) | v7;
            mem[0] = ((unsigned __int8)rand() << 24) | v8;// mem(0)=rand()
            envp[46] = reg + 1;
            break;
          case 0xF1:
            mem[4] = (int)stack;                    // mem(4)=stack
            envp[46] = reg + 1;
            break;
          case 0xF2:
            mem[4] = (int)Block;                    // mem(4)=block
            envp[46] = reg + 1;
            break;
          case 0xF3:
            WriteFile = (void (__cdecl *)(int, int, int, char *, _DWORD))sub_401820(aWskwacokm);
            buf = mem[reg[1]];
            len = mem[reg[2]];
            WriteFile(outfile, buf, len, v14, 0);   // write(outfile,mem(a1),mem(a2))
            envp[46] = reg + 3;
            break;
          case 0xF4:
            v16 = mem[reg[1]];
            hCrypto = (PINFORMATIONCARD_CRYPTO_HANDLE)mem[reg[2]];
            v17 = mem[reg[3]];
            encrypt(hCrypto, v9, v10, v11, v12, v13);
            envp[46] = reg + 4;
            break;
          case 0xF6:
            mem[0] = blocklen;                      // mem(0)=len
            envp[46] = reg + 1;
            break;
          case 0:
            free(stack);                            // exit
            free(Block);
            _loaddll(0);
            break;
        }
    

    简单分析可以看出是使用栈和寄存器的虚拟机

    用 IDA 的 Export Data 功能导出虚拟机指令,再编写脚本进行翻译

    #include <cstdio>
    
    unsigned char str[] =
    {
      0xA0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 
      0x04, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 
      0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 
      0x02, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x02, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 
      0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 
      0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0xE4, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xF0, 0x00, 
      0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0xD3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xE0, 0x00, 
      0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 
      0xD1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 
      0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0xB5, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xB5, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
      0xB5, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xF2, 0x00, 
      0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x02, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x04, 0x00, 
      0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
      0xF3, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 
      0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x01, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x02, 0x00, 
      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 
      0xD1, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 
      0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0x02, 0x00, 
      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, 
      0x09, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0xD2, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
      0xD1, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 
      0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 
      0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x00, 
      0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
      0xF3, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    
    int main(){
        unsigned int *op_=(unsigned int *)str;
        unsigned int *op=op_;
        while(op-op_<sizeof(str)/4){
            printf("%d: ",op-op_);
            switch(*op){
                case 0:
                    printf("exit
    ");
                    op++;
                    break;
                case 1:
                    printf("nop
    ");
                    op++;
                    break;
                case 0xA0:
                    printf("mem(%d)=%d
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xB0:
                    printf("push(mem(%d))
    ",*(op+1));
                    op+=2;
                    break;
                case 0xB5:
                    printf("mem(%d)=pop()
    ",*(op+1));
                    op+=2;
                    break;
                case 0xC0:
                    printf("mem(0)=cmp(mem(%d),mem(%d))
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xD0:
                    printf("mem(%d)^=mem(%d)
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xD1:
                    printf("mem(%d)+=mem(%d)
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xD2:
                    printf("mem(%d)-=mem(%d)
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xD3:
                    printf("mem(%d)++
    ",*(op+1));
                    op+=2;
                    break;
                case 0xD4:
                    printf("mem(%d)--
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE0:
                    printf("jmp %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE1:
                    printf("jeq %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE2:
                    printf("jne %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE3:
                    printf("ja %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE4:
                    printf("jae %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE5:
                    printf("jb %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xE6:
                    printf("jbe %d
    ",*(op+1));
                    op+=2;
                    break;
                case 0xF0:
                    printf("mem(0)=rand()
    ");
                    op+=1;
                    break;
                case 0xF1:
                    printf("mem(4)=stack
    ");
                    op+=1;
                    break;
                case 0xF2:
                    printf("mem(4)=block()
    ");
                    op+=1;
                    break;
                case 0xF3:
                    printf("write(outfile,mem(%d),mem(%d))
    ",*(op+1),*(op+2));
                    op+=3;
                    break;
                case 0xF4:
                    printf("encrypt(mem(%d),mem(%d),mem(%d))
    ",*(op+1),*(op+2),*(op+3));
                    op+=4;
                    break;
                case 0xF6:
                    printf("mem(0)=blocklen
    ");
                    op+=1;
                    break;
            }
        }
    }
    

    翻译结果如下

    0: mem(2)=0
    3: mem(3)=4
    6: mem(1)=16
    9: push(mem(2))
    11: mem(2)=0
    14: mem(4)=4
    17: mem(0)=cmp(mem(2),mem(4))
    20: jae 29
    22: mem(0)=rand()
    23: push(mem(0))
    25: mem(2)++
    27: jmp 17
    29: mem(4)=stack
    30: mem(4)+=mem(3)
    33: push(mem(4))
    35: mem(5)=pop()
    37: mem(0)=pop()
    39: mem(0)=pop()
    41: mem(0)=pop()
    43: mem(0)=pop()
    45: mem(2)=pop()
    47: mem(4)=block()
    48: mem(4)+=mem(2)
    51: encrypt(mem(4),mem(5),mem(1))
    55: write(outfile,mem(5),mem(1))
    58: write(outfile,mem(4),mem(1))
    61: mem(2)+=mem(1)
    64: mem(0)=blocklen
    65: mem(2)+=mem(1)
    68: mem(0)=cmp(mem(2),mem(0))
    71: mem(2)-=mem(1)
    74: jb 9
    76: mem(0)=blocklen
    77: mem(0)-=mem(2)
    80: mem(4)+=mem(1)
    83: encrypt(mem(4),mem(5),mem(0))
    87: write(outfile,mem(5),mem(1))
    90: write(outfile,mem(4),mem(1))
    93: exit
    

    进一步化简得到伪代码

    offset=0
    do{
        stack[4:20]=random();
        msg=block()+offset
        key=&stack[4]
        encrypt(msg,key,16)
        write(outfile,key,16)
        write(outfile,msg,16)
        offset+=16
    }while(offset<blocklen)
    remain=blocklen-offset
    msg+=16
    encrypt(msg,key,remain)
    write(outfile,key,16)
    write(outfile,msg,16)
    exit
    

    可以看出程序的算法是每次随机生成 128 位的密钥,然后用 encrypt 函数加密明文中 128 位的数据,再将密钥和密文一起输出到文件

    分析加密算法

    encrypt 函数的主要代码如下

        do
        {
          v17 = 0;
          v18 = *(int *)((char *)&dword_418A24 + v16);
          v42 = *(int *)((char *)&dword_418A2C + v16);
          v40 = *(int *)((char *)&dword_418A28 + v16);
          v41 = *(int *)((char *)&dword_418A20 + v16);
          v39 = v18;
          v43 = (int)&v49 + v16;
          v19 = (char *)&v49 + v16;
          do
          {
            v20 = sub_401250(v18, v35.m128i_i32[v17]);
            v21 = sub_401250(v42, v37.m128i_i32[v17]) ^ v20;
            v22 = sub_401250(v41, *((_DWORD *)&v34 + v17)) ^ v21;
            v23 = sub_401250(v40, v36.m128i_i32[v17]);
            v18 = v39;
            v19 += 4;
            ++v17;
            *((_DWORD *)v19 - 1) = v23 ^ v22;
          }
          while ( v17 < 4 );
          v16 = v48 + 16;
          v48 = v16;
        }
        while ( v16 < 64 );
    

    分析算法特征

    1. dword_418A24 处的内容为 [2,3,1,1],对应 AES 中列混合的系数

    2. sub_401250 中多次出现异或 0x1B 的操作,0x1B 对应二进制 0b00011011,对应 AES 中 (GF(2^8)) 上乘法规定的的不可约多项式 (m(x)=x^8+x^4+x^3+x^1+1)

    3. encrypt 中第三个参数为 16,对应 AES 中块的大小 16 字节(128位)

    虽然程序中没有静态的 sbox 表可供 FindCrypt 插件查询算法,但是通过以上三点可以推断出这里的 encrypt 函数即为 AES 加密算法

    编写解密脚本

    解密脚本如下

    from Crypto.Cipher import AES
    
    with open('output','rb') as f1:
        with open('output.docx','wb') as f2:
            while(1):
                k=f1.read(16)
                c=f1.read(16)
                cipher=AES.new(k,AES.MODE_ECB)
                msg=cipher.decrypt(c)
                f2.write(msg)
    

    打开 output.docx 即可得到 flag

  • 相关阅读:
    BUUCTF-[强网杯 2019]随便注
    Oracle 存储过程
    java.lang.OutOfMemoryError: Java heap space
    Oracle 约束
    Docker 学习1 容器技术基础入门
    Kubernetes 学习1 Devops 核心要点和k8s架构概述
    mysql Sql语句
    Shell 编程详解
    git 学习
    Linux awk学习
  • 原文地址:https://www.cnblogs.com/algonote/p/14854278.html
Copyright © 2011-2022 走看看