逆向题解二

MoeCTF新生赛逆向A_game

逆向题解二_第1张图片

step1: 先探探这个exe的底

 拉到die里头。发现是个64位程序。

逆向题解二_第2张图片 

 step2:拉到IDA64里面反编译瞅瞅源码

 按F5,反编译的源码如下。

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  size_t v4; // rbx
  char Str[96]; // [rsp+20h] [rbp-80h]
  int v7; // [rsp+80h] [rbp-20h]
  int k; // [rsp+90h] [rbp-10h]
  int j; // [rsp+94h] [rbp-Ch]
  int i; // [rsp+98h] [rbp-8h]
  int v11; // [rsp+9Ch] [rbp-4h]

  _main(*(_QWORD *)&argc, argv, envp);
  puts("<---  moectf2021  --->");
  puts(" [A_game] Welcome to moectf2021.");
  puts("Let's play a game!");
  puts("Now input your answer, and if you are right, I will give you flag");
  printf("input : ");
  memset(Str, 0, sizeof(Str));
  v7 = 0;
  scanf("%s", Str);
  if ( strlen(Str) != 49 )
  {
    puts("It's not enough.");
    system("pause");
    exit(0);
  }
  v11 = 0;
  for ( i = 0; i <= 8; ++i )
  {
    for ( j = 0; j <= 8; ++j )
    {
      if ( !box[9 * i + j] )
      {
        v3 = v11++;
        box[9 * i + j] = Str[v3] - 48;
      }
    }
  }
  check1();
  check2();
  check3();
  puts("Congratulations!!!!");
  puts("Enjoy the beauty of reverse and sudoku!");
  printf("And here is your flag : moectf{");
  for ( k = 0; ; ++k )
  {
    v4 = k;
    if ( v4 >= strlen(Str) )
      break;
    putchar((char)(Str[k] ^ magic[k]));
  }
  putchar(125);
  return 0;
}

阅读完整个程序之后大概明白了str就是和flag直接相关的那个东西。而且他的长度是49.从如下源码可以看出:

if ( strlen(Str) != 49 )
  {
    puts("It's not enough.");
    system("pause");
    exit(0);
  }

然后需要重点关注下面几个程序:直觉告诉我这三个程序绝对很有用。双击查看他们的具体代码。但是看完之后感觉有点懵。这三个程序到底想干什么?先跳过这步接着往下面看,看看下面有什么有用的信息。

  //check1();
__int64 check1()
{
  __int64 result; // rax
  signed int j; // [rsp+24h] [rbp-Ch]
  signed int i; // [rsp+28h] [rbp-8h]
  int k; // [rsp+2Ch] [rbp-4h]

  for ( i = 0; i <= 8; ++i )
  {
    for ( j = 1; j <= 9; ++j )
    {
      for ( k = 0; ; ++k )
      {
        result = (unsigned int)(char)box[9 * i + k];
        if ( j == (_DWORD)result )
          break;
        if ( k == 8 )
        {
          printf("Wrong!!!Try again!!!");
          system("pause");
          exit(0);
        }
      }
    }
  }
  return result;
}
 // check2();
__int64 check2()
{
  __int64 result; // rax
  signed int j; // [rsp+24h] [rbp-Ch]
  signed int i; // [rsp+28h] [rbp-8h]
  int k; // [rsp+2Ch] [rbp-4h]

  for ( i = 0; i <= 8; ++i )
  {
    for ( j = 1; j <= 9; ++j )
    {
      for ( k = 0; ; ++k )
      {
        result = (unsigned int)(char)box[9 * k + i];
        if ( j == (_DWORD)result )
          break;
        if ( k == 8 )
        {
          printf("Wrong!!!Try again!!!");
          system("pause");
          exit(0);
        }
      }
    }
  }
  return result;
}
 // check3();
__int64 check3()
{
  __int64 result; // rax
  signed int j; // [rsp+2Ch] [rbp-14h]
  signed int i; // [rsp+30h] [rbp-10h]
  signed int k; // [rsp+34h] [rbp-Ch]
  int v4; // [rsp+38h] [rbp-8h]
  int v5; // [rsp+3Ch] [rbp-4h]

  for ( i = 0; i <= 8; i += 3 )
  {
    for ( j = 0; j <= 8; j += 3 )
    {
      for ( k = 1; k <= 9; ++k )
      {
        v5 = 0;
        v4 = 0;
        while ( 1 )
        {
          result = (unsigned int)(char)box[9 * (i + v5) + j + v4];
          if ( k == (_DWORD)result )
            break;
          if ( v5 == 2 && v4 == 2 )
          {
            printf("Wrong!!!Try again!!!");
            system("pause");
            exit(0);
          }
          if ( ++v4 == 3 )
          {
            ++v5;
            v4 = 0;
          }
        }
      }
    }
  }
  return result;
}

关注完重要的函数,接下来看得看看重要的变量,经过筛选发现下面这几个变量有点意思:

box[96],magic[64],str[49].  

看下面这段代码我们知道:str[]数组的49个数字每一个都要先减去48然后再嵌入box[]。而且嵌入box的位置好像还有要求。

  for ( i = 0; i <= 8; ++i )
  {
    for ( j = 0; j <= 8; ++j )
    {
      if ( !box[9 * i + j] )
      {
        v3 = v11++;
        box[9 * i + j] = Str[v3] - 48;
      }
    }
  }

接着看magic。这句话说明magic要和str一一进行异或。

putchar((char)(Str[k] ^ magic[k]));

 双击magic和box,看看这两个数组到底存着什么数据。看着有点乱,我们复制下来拉到python里面用正则表达式把数字提取出来。

逆向题解二_第3张图片

 

 最后就是下面这段程序:看printf输出应该是在输出flag,前面有半段,后面还有个putchar(125),这个在c语言里面就是输出 } 的。这就说明这段程序绝对是通过异或运算得到最终的flag。

而magic这个数组我们有。

printf("And here is your flag : moectf{");
  for ( k = 0; ; ++k )
  {
    v4 = k;
    if ( v4 >= strlen(Str) )
      break;
    putchar((char)(Str[k] ^ magic[k]));
  }
  putchar(125);

 step3:柳暗花明又一村。

到这里整理整理思路。

按照程序来看:他是将我们输入的str数组嵌入到box里面。然后再用三个check函数去检查box是否符合题意,最后用str和magic异或得到flag的ascll码。

现在magic已知,box也部分已知,看来找到str是关键。那下面我们就去找str,而找str好像只能通过三个check函数来找(这个思路给我埋下了一个小坑,让我在试图反向推测str时耗费了好大的时间,真的花了好长时间,其实就怪自己眼瞎没看到那个最重要的关键词)。

时间过去了好多,那三个check我也没完全搞懂,总感觉缺少条件,正准备跳过这道题做下一道。然后突然看到了这句话:Enjoy the beauty of reverse and sudoku!

逆向题解二_第4张图片 

 sudoku!sudoku!suduku!这不是数独嘛!然后突然想到那个box[96]里面有零,有非0数字,这个box该不会是棋盘吧!然后三个check函数就对应9*9数独的三个条件。再一看box,果然是个棋盘状。

逆向题解二_第5张图片 

然后瞬间就懂了,str[]中的数组就是这个数独对应0位置的数字+48,因此只需要解出这个数独就可以,压根不需要去看那三个check。拿出纸笔,多年数独功底发挥了作用。

逆向题解二_第6张图片 

step4: 异或得到flag。

然后写个程序把新填上的数字提取出来+48拼凑成str[]就可以.代码如下。

box = [0, 0, 5, 0, 0, 4, 3, 6, 0,
       0, 0, 0, 0, 5, 0, 0, 2, 4,
       0, 4, 9, 6, 7, 0, 0, 0, 0,
       1, 0, 6, 0, 2, 0, 0, 3, 0,
       9, 0, 0, 7, 0, 0, 1, 0, 8,
       0, 3, 0, 0, 0, 5, 0, 9, 0,
       2, 0, 0, 5, 0, 7, 0, 0, 9,
       7, 0, 4, 0, 0, 0, 8, 0, 0,
       0, 9, 0, 0, 4, 0, 0, 0, 6,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

aim = [8, 2, 5, 9, 1, 4, 3, 6, 7,
       6, 7, 1, 3, 5, 8, 9, 2, 4,
       3, 4, 9, 6, 7, 2, 5, 8, 1,
       1, 8, 6, 4, 2, 9, 7, 3, 5,
       9, 5, 2, 7, 6, 3, 1, 4, 8,
       4, 3, 7, 1, 8, 5, 6, 9, 2,
       2, 6, 8, 5, 3, 7, 4, 1, 9,
       7, 1, 4, 2, 9, 6, 8, 5, 3,
       5, 9, 3, 8, 4, 1, 2, 7, 6
       ]
#提取数字拼凑str
str = []
for i in range(0, 9):
    for j in range(0, 9):
        if (box[9 * i + j] == 0):
            str.append(aim[9 * i + j] + 48)

print('moectf{', end='')
magic = [0x6B, 0x2, 0x66, 0x70, 0x44, 0x69, 0x7E, 0x6E, 0x43, 0x4A, 0x78,
         0x4A, 0x6D, 0x60, 0x56, 0, 0x51, 0x59, 0x50, 0x43, 0x50, 0x51,
         0x6D, 0x74, 0x2, 0x55, 0x50, 0x52, 0x6E, 0x6F, 0x79, 0x40, 0x5D,
         0x4B, 0x1E, 0x19, 0x1C, 0x74, 3, 0x54, 7, 0x4C, 0x52, 0x6A, 0x60,
         0x50, 0x58, 0x40, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
'''异或求flag'''
for k in range(0, 1000):
    v4 = k
    if v4 >= len(str):
        break
    print(chr(str[k] ^ magic[k]), end='')
print(chr(125))

大功告成。 

 sum:不要陷入死思维,尽可能的挖掘有用信息,很可能一个很小的单词就是打开僵局的突破点。

还有就是坚持,不要轻易放弃。

你可能感兴趣的:(reverse)