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}

posted @ 2020-05-05 01:01  Hk_Mayfly  阅读(1299)  评论(0编辑  收藏  举报