AVALON 零散的一些题目 安恒杯月赛 2019-01 Old-drive write up

安恒杯月赛 2019-01 Old-drive write up

环境配置

系统 : win10 64bit
程序 : Old-drive.zip or Old-drive.zip
要求 : 输入口令
使用工具 :ida pro

开始分析

静态分析

使用ida载入程序,发现主流程如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // ecx
  signed int v4; // eax
  char v6; // [esp+0h] [ebp-40h]
  char Dst; // [esp+1h] [ebp-3Fh]
  char v8; // [esp+27h] [ebp-19h]
  int v9; // [esp+34h] [ebp-Ch]
  __int16 v10; // [esp+38h] [ebp-8h]
  char v11; // [esp+3Ah] [ebp-6h]

  v6 = 0;
  memset(&Dst, 0, 0x31u);
  printf("input flag:\n");
  scanf("%50s", &v6);
  if ( strlen(&v6) == 40 )
  {
    for ( i = 0; i < (char *)nullsub_1 - (char *)sub_401000; ++i )
      *((_BYTE *)sub_401000 + i) ^= 0xBBu;
    v10 = 32123;
    v9 = 'galf';
    v11 = 0;
    v4 = 0;
    do
    {
      if ( *(&v6 + v4) != *((_BYTE *)&v9 + v4) )
        goto LABEL_8;
      ++v4;
    }
    while ( v4 < 5 );
    LOBYTE(i) = v8;
    if ( v8 != *((_BYTE *)&v9 + v4) )
LABEL_8:
      exit(0);
    sub_4010B0(i, &v6);
  }
  return 0;
}

动态调试

这样看着不是很直观,直接ida调试程序,发现主要流程在检查格式:

    v9 = 'galf';
    v11 = 0;
    v4 = 0;
    do
    {
      if ( *(&v6 + v4) != *((_BYTE *)&v9 + v4) )
        goto LABEL_8;
      ++v4;
    }
    while ( v4 < 5 );
    LOBYTE(i) = v8;
    if ( v8 != *((_BYTE *)&v9 + v4) )

所以flag格式为flag{.+}

进入第一个关键函数

.text:004010B0 loc_4010B0:                             ; CODE XREF: _main+B4↓p
.text:004010B0 push    ebp
.text:004010B1 mov     ebp, esp

可以看到有很多乱码,这里鼠标选择.text:004010B0.text:0040125E,按下c然后一直ok就可以。之后对函数头右击Create Function就可以了:

int __fastcall sub_4010B0(int a1, _BYTE *a2)
{
  signed int v2; // eax
  unsigned __int8 *v3; // ecx
  int v4; // edi
  char v5; // cl
  char v6; // al
  char v7; // cl
  char v8; // al
  char v9; // dl
  unsigned int v10; // kr00_4
  signed int v11; // esi
  char *v12; // ebx
  int v13; // edi
  unsigned int v14; // eax
  char *v15; // ecx
  unsigned int v16; // edx
  signed int v17; // ecx
  int v18; // esi
  unsigned int v19; // eax
  signed int v20; // ecx
  signed int v21; // eax
  _BYTE *v23; // [esp+8h] [ebp-2Ch]
  char *v24; // [esp+Ch] [ebp-28h]
  unsigned int v25; // [esp+10h] [ebp-24h]
  char Dest[4]; // [esp+14h] [ebp-20h]
  char *Source; // [esp+18h] [ebp-1Ch]
  int v28; // [esp+1Ch] [ebp-18h]
  char v29; // [esp+28h] [ebp-Ch]
  char v30; // [esp+29h] [ebp-Bh]
  char v31; // [esp+2Ah] [ebp-Ah]
  char v32; // [esp+2Bh] [ebp-9h]
  char v33; // [esp+2Ch] [ebp-8h]
  char v34; // [esp+2Dh] [ebp-7h]

  v23 = a2;
  v2 = 5;
  v3 = (unsigned __int8 *)&unk_4021B8;
  do
  {
    v4 = *v3++;
    if ( ((char)a2[v2] ^ 0x86) != v4 )
LABEL_12:
      exit(0);
    ++v2;
  }
  while ( v2 < 11 );
  v5 = a2[11];
  strcpy(&v28, "c19zbWNf");
  v29 = v5;
  v6 = a2[12];
  v31 = a2[13];
  v7 = a2[15];
  v30 = v6;
  v8 = a2[14];
  v9 = a2[16];
  v33 = v7;
  v34 = v9;
  v32 = v8;
  v10 = strlen(&v29);
  v11 = v10 / 3 + (v10 % 3 != 0);
  v12 = (char *)malloc(4 * v11 + 1);
  v24 = v12;
  memset(v12, 0, 4 * v11 + 1);
  if ( v11 > 0 )
  {
    Source = &v29;
    v25 = v10 / 3 + (v10 % 3 != 0);
    do
    {
      v13 = 0;
      *(_DWORD *)Dest = 0;
      strncpy(Dest, Source, 3u);
      v14 = 0;
      v15 = &Dest[strlen(Dest) + 1];
      v16 = v15 - &Dest[1];
      if ( v15 != &Dest[1] )
      {
        v17 = 16;
        do
        {
          v18 = Dest[v14++] << v17;
          v17 -= 8;
          v13 |= v18;
        }
        while ( v14 < v16 );
      }
      v19 = 0;
      v20 = 18;
      do
      {
        if ( v16 + 1 <= v19 )
          v12[v19] = 61;
        else
          v12[v19] = aAbcdefghijklmn[(v13 >> v20) & 0x3F];
        v20 -= 6;
        ++v19;
      }
      while ( v20 > -6 );
      Source += 3;
      v12 += 4;
      --v25;
    }
    while ( v25 );
    v12 = v24;
  }
  v21 = 0;
  do
  {
    if ( *((_BYTE *)&v28 + v21) != *((_BYTE *)&v28 + v21 + v12 - (char *)&v28) )
      goto LABEL_12;
    ++v21;
  }
  while ( v21 < 8 );
  return sub_401000(v23);
}

solved part1

直接用python把flag的第一部分找出来:

➜  playground python ./add_prefix.py
F2 EE EF F5 D9 EF
[ 0xF2,  0xEE,  0xEF,  0xF5,  0xD9,  0xEF ]
➜  playground python
Python 2.7.14+ (default, Mar 13 2018, 15:23:44) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> data = [ 0xF2,  0xEE,  0xEF,  0xF5,  0xD9,  0xEF ]
>>> for ch in data:
...     print chr(ch^0x86),
... 
t h i s _ i

solved part2

根据算法结构以及算法常数aAbcdefghijklmn数组可知,这是一个base64算法,密文在代码段中已经给出c19zbWNf,解码出来是:s_smc_

进入关键函数2

调试技巧

这里多次从头调试可以发现,给定部分正确的flag可以进入关键函数sub_401000。而第二次调试的时候却会引发异常,代码似乎也变了。这里可以采取以下两种方法:
1. 在虚拟机中调试,走入一个关键函数保存一个镜像,发现有奇怪的错误时直接回滚就可以。
2. 这个样本有两个关键函数,所以理论上只需要调试两次,那么结束调试时删除idb文件,直接从零开始就可以。

发现迷宫

这里,程序主流程是这样的:

char __cdecl sub_401000(int a1)
{
  int v1; // edx
  int v2; // ecx
  int v3; // edx
  int v4; // ecx
  signed int v5; // edx
  char *v6; // ecx
  char v7; // al
  char result; // al
  char v9; // [esp+8h] [ebp-64h]
  char v10; // [esp+10h] [ebp-5Ch]
  int v11; // [esp+4Ch] [ebp-20h]
  int v12; // [esp+50h] [ebp-1Ch]
  int v13; // [esp+54h] [ebp-18h]
  int v14; // [esp+58h] [ebp-14h]
  int v15; // [esp+5Ch] [ebp-10h]
  __int16 v16; // [esp+60h] [ebp-Ch]

  v1 = *(_DWORD *)(a1 + 21);
  strcpy(&v9, "--------g +    ++ + ++ ++ + #+ ++ ++++ ++ ++++ ++      +--------");
  v11 = *(_DWORD *)(a1 + 17);
  v2 = *(_DWORD *)(a1 + 25);
  v12 = v1;
  v3 = *(_DWORD *)(a1 + 29);
  v13 = v2;
  v4 = *(_DWORD *)(a1 + 33);
  v14 = v3;
  LOWORD(v3) = *(_WORD *)(a1 + 37);
  v15 = v4;
  v16 = v3;
  v5 = 0;
  v6 = &v10;
  do
  {
    v7 = *((_BYTE *)&v11 + v5);
    switch ( v7 )
    {
      case 97:
        v6 += 8;
        break;
      case 113:
        --v6;
        break;
      case 119:
        ++v6;
        break;
      case 50:
        v6 -= 8;
        break;
    }
    result = *v6;
    if ( *v6 == 35 )
    {
      printf("Congratulation!!!!!!\n");
LABEL_15:
      exit(0);
    }
    if ( result != 32 )
      goto LABEL_15;
    ++v5;
  }
  while ( v5 < 22 );
  return result;
}

在内存中查看v9,以每行八字节的方式显示:

0019FE58  2D 2D 2D 2D  2D 2D 2D 2D  --------
0019FE60  67 20 2B 20  20 20 20 2B  g +    +
0019FE68  2B 20 2B 20  2B 2B 20 2B  + + ++ +
0019FE70  2B 20 2B 20  23 2B 20 2B  + + #+ +
0019FE78  2B 20 2B 2B  2B 2B 20 2B  + ++++ +
0019FE80  2B 20 2B 2B  2B 2B 20 2B  + ++++ +
0019FE88  2B 20 20 20  20 20 20 2B  +      +
0019FE90  2D 2D 2D 2D  2D 2D 2D 2D  --------

调试发现,我们的开始位置是0x67,最终到达0x23就算成功。
移动按键杯设定为:

↑ 2
↓ a
← q
→ w

solved part3

最终我们可以确定走出迷宫的路线为:waaaaawwwww22222qqqaaw

夺旗成功

输入拼接而成的flag,提示成功:

C:\Users\Vincent_GU\Downloads\mission\anheng\old-drive\Old-drive>Old-drive3.exe
input flag:
flag{this_is_smc_waaaaawwwww22222qqqaaw}
Congratulation!!!!!!