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}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM