zoukankan      html  css  js  c++  java
  • BUUCTF--[GWCTF 2019]re3

    测试文件:https://lanzous.com/ic9ox7a

     SMC自修改代码

    代码分析

    首先,我们使用插件Findcript可以发现,这段程序中存在的加密方式

    去混淆

     1 void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
     2 {
     3   signed int i; // [rsp+8h] [rbp-48h]
     4   char s; // [rsp+20h] [rbp-30h]
     5   unsigned __int64 v5; // [rsp+48h] [rbp-8h]
     6 
     7   v5 = __readfsqword(0x28u);
     8   __isoc99_scanf("%39s", &s, a3);
     9   if ( (unsigned int)strlen(&s) != 32 )
    10   {
    11     puts("Wrong!");
    12     exit(0);
    13   }
    14   mprotect(&dword_400000, 0xF000uLL, 7);
    15   for ( i = 0; i <= 223; ++i )
    16     *((_BYTE *)sub_402219 + i) ^= 0x99u;
    17   sub_40207B(&unk_603170, 61440LL);
    18   sub_402219(&s);
    19 }

    主函数中,第15~16行代码对函数sub_402219进行了异或0x99的操作,打开sub_402219函数,可以看到(选中0x402219区域,按D转换为Data形式)

    因此,我们首先需要将数据还原,使用IDC脚本

    #include <idc.idc>
    
    static main()
    {
        auto addr = 0x402219;
        auto i;
        for(i = 0; i <= 223; ++i){
            PatchByte(addr+i,Byte(addr+i)^0x99);
        }
    }

    选中数据,右键分析选中区域,选择force强制执行,最后选中我们编译后的代码,按P键形成函数。得到

    __int64 __fastcall sub_402219(__int64 a1)
    {
      unsigned int v2; // [rsp+18h] [rbp-D8h]
      signed int i; // [rsp+1Ch] [rbp-D4h]
      char v4; // [rsp+20h] [rbp-D0h]
      unsigned __int64 v5; // [rsp+E8h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      sub_400A71(&v4, &unk_603170);
      sub_40196E(&v4, a1);
      sub_40196E(&v4, a1 + 16);
      v2 = 1;
      for ( i = 0; i <= 31; ++i )
      {
        if ( *(_BYTE *)(i + a1) != byte_6030A0[i] )
          v2 = 0;
      }
      return v2;
    }

    主函数

    void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
    {
      signed int i; // [rsp+8h] [rbp-48h]
      char s; // [rsp+20h] [rbp-30h]
      unsigned __int64 v5; // [rsp+48h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      __isoc99_scanf("%39s", &s, a3);
      if ( (unsigned int)strlen(&s) != 32 )
      {
        puts("Wrong!");
        exit(0);
      }
      mprotect(&dword_400000, 0xF000uLL, 7);
      for ( i = 0; i <= 223; ++i )
        *((_BYTE *)sub_402219 + i) ^= 0x99u;
      sub_40207B((__int64)&Val0);                   
      sub_402219((__int64)&s);
      JUMPOUT(loc_4021F5);
    }

    可以看到,主要的函数就是sub_40207B和sub_402219,我们倒着分析。(将)

    sub_402219函数

    __int64 __fastcall sub_402219(__int64 a1)
    {
      unsigned int v2; // [rsp+18h] [rbp-D8h]
      signed int i; // [rsp+1Ch] [rbp-D4h]
      char v4; // [rsp+20h] [rbp-D0h]
      unsigned __int64 v5; // [rsp+E8h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      sub_400A71((__int64)&v4, (__int64)&Val0);     // 使用Val0作为初始秘钥,轮秘钥生成
      sub_40196E((__int64)&v4, a1);                 // 前16位AES加密
      sub_40196E((__int64)&v4, a1 + 16);            // 后16位AES加密
      v2 = 1;
      for ( i = 0; i <= 31; ++i )
      {
        if ( *(_BYTE *)(i + a1) != byte_6030A0[i] )
          v2 = 0;
      }
      return v2;
    }

    很明显的看得出,就是将Val0作为初始秘钥来加密我们输入的flag,最后与byte_6030A0比较。byte_6030A0已知为

    BC0AADC0147C5ECCE0B140BC9C51D52B46B2B9434DE5324BAD7FB4B39CDB4B5B

    因此,我们只需要知道Val0就能反解出flag。

    sub_40207B函数

     1 unsigned __int64 __fastcall sub_40207B(__int64 a1)
     2 {
     3   char v2; // [rsp+10h] [rbp-50h]
     4   __int64 v3; // [rsp+20h] [rbp-40h]
     5   __int64 v4; // [rsp+30h] [rbp-30h]
     6   __int64 v5; // [rsp+40h] [rbp-20h]
     7   unsigned __int64 v6; // [rsp+58h] [rbp-8h]
     8 
     9   v6 = __readfsqword(0x28u);
    10   Encrypt_Func(&BASE64_table_603120, 0x40uLL, (__int64)&v2);// 将BASE64表传入Encrypt_Func加密后存储到v2
    11   Encrypt_Func(&CRC32_table_603100, 0x14uLL, (__int64)&v3);
    12   Encrypt_Func(&Prime_Constants_char_6030C0, 0x35uLL, (__int64)&v4);
    13   Encrypt_Func(MD5_Constants_4025C0, 0x100uLL, (__int64)&v5);
    14   Encrypt_Func(&v2, 0x40uLL, a1);               // 再将v2进行Encrypt_Func加密,存储到a1
    15   return __readfsqword(0x28u) ^ v6;
    16 }

    实际这段函数,与我们a1(即Val0)有关的第14行代码,将v2进行某种加密后赋值给a1,因此这里只需要第10行代码,其余的加密都无关。

    第10行代码将Base64表传入加密,得到v2。

    我们能够通过调试得到Val0的值为

    .bss:0000000000603170 Val0 db 0CBh                            ; DATA XREF: main+B9↑o
    .bss:0000000000603170                                         ; sub_402219+28↑o
    .bss:0000000000603171 db  8Dh
    .bss:0000000000603172 db  49h ; I
    .bss:0000000000603173 db  35h ; 5
    .bss:0000000000603174 db  21h ; !
    .bss:0000000000603175 db 0B4h
    .bss:0000000000603176 db  7Ah ; z
    .bss:0000000000603177 db  4Ch ; L
    .bss:0000000000603178 db 0C1h
    .bss:0000000000603179 db 0AEh
    .bss:000000000060317A db  7Eh ; ~
    .bss:000000000060317B db  62h ; b
    .bss:000000000060317C db  22h ; "
    .bss:000000000060317D db  92h
    .bss:000000000060317E db  66h ; f
    .bss:000000000060317F db 0CEh

    CB8D493521B47A4CC1AE7E62229266CE

    总结

    因此总的过程就是

    1. 使用Base64表经过Encrypt_Func函数两次加密,传入Val0
    2. 使用Val0作为Key,进行AES加密
    3. 结果与byte_6030A0比较

    脚本

    from Crypto.Cipher import AES
    import codecs
    
    aes = AES.new(decode_hex('CB8D493521B47A4CC1AE7E62229266CE')[0], AES.MODE_ECB)
    print(aes.decrypt(decode_hex('BC0AADC0147C5ECCE0B140BC9C51D52B46B2B9434DE5324BAD7FB4B39CDB4B5B')[0]))

    得到

    get flag!

    flag{924a9ab2163d390410d0a1f670}

  • 相关阅读:
    XXX is not in the sudoers file
    git错误“无法推送一些引用到xxx"的解决方法
    mysql开启远程访问
    ubuntu 在启动器中启动webstorm和phpstorm
    ubuntu nginx卸载和安装
    基于grunt构建的前端集成开发环境
    fullPage.js
    常见的HTTP状态码
    JS随机数
    CSS3简单的动画
  • 原文地址:https://www.cnblogs.com/Mayfly-nymph/p/12829168.html
Copyright © 2011-2022 走看看