AVALON 巅峰极客2018 巅峰极客 逆向题 Simple Base-N write up

巅峰极客 逆向题 Simple Base-N write up

环境配置

系统 : win10 64bit
程序 : Simple Base-N.exe
要求 : 输入口令
使用工具 :ida pro

开始分析

静态分析

在ida中查看main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // ecx
  char *v4; // eax
  bool v5; // cf
  unsigned __int8 v6; // dl
  int v7; // eax
  int v9; // eax
  const char *v10; // edx

  sub_401590(std::cout, "please input your flag:");
  sub_4017D0(std::cin);
  if ( (signed int)strlen(my_str_405018) >= 10 )
  {
    sub_401100(my_str_405018);
    v3 = "guvf_vf_n_snxr_synt";
    v4 = my_str_405018;
    while ( 1 )
    {
      v5 = (unsigned __int8)*v4 < *v3;
      if ( *v4 != *v3 )
        break;
      if ( !*v4 )
        goto LABEL_7;
      v6 = v4[1];
      v5 = v6 < v3[1];
      if ( v6 != v3[1] )
        break;
      v4 += 2;
      v3 += 2;
      if ( !v6 )
      {
LABEL_7:
        v7 = 0;
        goto LABEL_9;
      }
    }
    v7 = -v5 | 1;
LABEL_9:
    if ( !v7 )
    {
      sub_401590(std::cout, "try a little bit harder!\n");
      return 0;
    }
    sub_4012C0(v3);
    sub_401310(&my_str_405018[1]);
    v9 = strcmp(a0, "weNTDk5LZsNRHk6cVogqTZmFy2NRP7X4ZHLTBZwg");
    if ( v9 )
      v9 = -(v9 < 0) | 1;
    v10 = "Congratulations!!!\n";
    if ( v9 )
      v10 = "soooooooooorry\n";
    sub_401590(std::cout, v10);
    system("pause");
  }
  return 0;
}

查看对输入的字符串进行操作的关键函数:

signed int __thiscall sub_401100(const char *this)
{
  const char *v1; // edi
  unsigned int v2; // esi
  char v3; // cl

  v1 = this;
  v2 = 0;
  if ( strlen(this) )
  {
    do
    {
      v3 = v1[v2];
      if ( (unsigned __int8)(v3 - 97) <= 0x19u )
        v1[v2] = (v3 - 84) % 26 + 97;
      if ( (unsigned __int8)(v3 - 65) <= 0x19u )
        v1[v2] = (v3 - 52) % 26 + 65;
      ++v2;
    }
    while ( v2 < strlen(v1) );
  }
  return 1;
}


还有另一个函数:

char *__fastcall sub_401310(int a1, int a2)
{
  int v2; // esi
  char *v3; // edi
  int v4; // ebx
  int v6; // [esp+10h] [ebp-4h]

  v2 = 0;
  v6 = a2;
  if ( a2 > 0 )
  {
    v3 = a0;
    v4 = a2;
    do
    {
      sub_401170(v3);
      v2 += 5;
      v4 -= 5;
      v3 += 8;
    }
    while ( v2 < v6 );
  }
  return a0;
}

int __fastcall sub_401170(unsigned __int8 *a1, signed int a2)
{
  signed int v2; // ebx
  int v3; // eax
  __int64 v4; // rt0
  __int64 v5; // rax
  __int64 v6; // rdi
  int v7; // kr08_4
  unsigned int v8; // ecx
  unsigned __int64 v9; // rdi
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  __int64 v13; // rax
  __int64 v14; // rax
  signed __int64 v15; // rax
  __int64 v16; // rax
  int result; // eax

  v2 = a2;
  v3 = a1[1];
  HIDWORD(v4) = __CFADD__(v3, *a1 << 8);
  LODWORD(v4) = v3 + (*a1 << 8);
  v5 = a1[4];
  v6 = (a1[3] + ((a1[2] + (v4 << 8)) << 8)) << 8;
  v7 = (a1[3] + ((a1[2] + ((_DWORD)v4 << 8)) << 8)) << 8;
  v8 = v5 + ((a1[3] + ((a1[2] + ((_DWORD)v4 << 8)) << 8)) << 8);
  v9 = v5 + __PAIR__(HIDWORD(v6), v7);
  MEMORY[0] = *((_BYTE *)&dword_40507C + (HIDWORD(v9) >> 3));
  v10 = *((unsigned __int8 *)&dword_40507C + ((__PAIR__(HIDWORD(v9), v8) >> 30) & 0x1F));
  *(_BYTE *)(HIDWORD(v10) + 1) = v10;
  v11 = *((unsigned __int8 *)&dword_40507C + ((v9 >> 25) & 0x1F));
  *(_BYTE *)(HIDWORD(v11) + 2) = v11;
  v12 = *((unsigned __int8 *)&dword_40507C + ((v9 >> 20) & 0x1F));
  *(_BYTE *)(HIDWORD(v12) + 3) = v12;
  v13 = *((unsigned __int8 *)&dword_40507C + ((v9 >> 15) & 0x1F));
  *(_BYTE *)(HIDWORD(v13) + 4) = v13;
  v14 = *((unsigned __int8 *)&dword_40507C + ((v9 >> 10) & 0x1F));
  *(_BYTE *)(HIDWORD(v14) + 5) = v14;
  v15 = (v9 >> 5) & 0x1F;
  LODWORD(v9) = v9 & 0x1F;
  *(_BYTE *)(HIDWORD(v15) + 6) = *((_BYTE *)&dword_40507C + v15);
  v16 = *((unsigned __int8 *)&dword_40507C + v9);
  MEMORY[7] = *((_BYTE *)&dword_40507C + v9);
  if ( v2 >= 5 )
    return HIDWORD(v16);
  switch ( v2 )
  {
    case 1:
      *(_WORD *)(HIDWORD(v16) + 2) = 15677;
      goto LABEL_6;
    case 2:
LABEL_6:
      *(_DWORD *)(HIDWORD(v16) + 4) = 1027423549;
      return HIDWORD(v16);
    case 3:
      *(_WORD *)(HIDWORD(v16) + 6) = 15677;
      LODWORD(v16) = HIDWORD(v16);
      *(_BYTE *)(HIDWORD(v16) + 5) = 61;
      break;
    case 4:
      *(_BYTE *)(HIDWORD(v16) + 7) = 61;
      result = HIDWORD(v16);
      break;
    default:
      return HIDWORD(v16);
  }
  return result;
}

一个是rot13加密。在静态分析中另一个函数的密码表为:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

动态则发现:

aAbcdefghijklmn db 'NoPqRsTuVwXyZaBcDeFgHiJkLm567234'

从密码表的位数来判断,这一个函数是base32编码。

演绎推理

从程序的逻辑来看,是先做了rot13加密,然后再对密文做base32编码,最终结果与硬编码对比即可。那么要提取flag很简单,利用rot13是一个简单对称加密算法的特点,算出硬编码base32解码后的原文,然后直接以此作为输入给程序,提取rot13加密后的密文就是flag。

编写程序

填写52pojie给出的模板如下:

s = "weNTDk5LZsNRHk6cVogqTZmFy2NRP7X4ZHLTBZwg"
table = "NoPqRsTuVwXyZaBcDeFgHiJkLm567234"

def find(x):
    if(x=='='):
        return 0
    return table.index(x)

for i in range(len(s)//8):
    p = s[i*8:i*8+8]
    t = 0
    for j in p:
        t = t<<5
        t += find(j)
    for j in range(5):
        print(chr((t&0xff00000000)>>32), end='')
        t = t<<8

夺旗成功

运行脚本输出如下:

C:\Users\dreamcracker\Desktop\Simple_Base-N_e0b657bcd4a9b04fbfa1fb0c4f16847e>test.py
L@h_Xa@J_o@f332_@Aq_e0g13

依此结果作为输入,提取加密结果:

 my_str_405018 db 'Y@u_Kn@W_b@s332_@Nd_r0t13',0

就得到了正确的flag:

C:\Users\dreamcracker\Desktop\Simple_Base-N_e0b657bcd4a9b04fbfa1fb0c4f16847e>"Simple Base-N.exe"
please input your flag:Y@u_Kn@W_b@s332_@Nd_r0t13
Congratulations!!!

参考资料

  1. Rot13密码 http://ctf.ssleye.com/rot13.html
  2. Rot13 https://www.jianshu.com/p/87cc386fa538
  3. Base-N算法及逆向方法初探 https://www.52pojie.cn/thread-793420-1-1.html
  4. Base16,Base32,Base64编码的介绍 https://blog.csdn.net/lili13897741554/article/details/82177472