查壳

32位,拖进IDA,老方法,找到flag,进入伪代码

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char v3[29]; // [esp+17h] [ebp-35h] BYREF
  int v4; // [esp+34h] [ebp-18h]
  int v5; // [esp+38h] [ebp-14h] BYREF
  int i; // [esp+3Ch] [ebp-10h]
  _BYTE v7[12]; // [esp+40h] [ebp-Ch] BYREF

  __main();
  v4 = 0;
  strcpy(v3, "*11110100001010000101111#");
  while ( 1 )
  {
    puts("you can choose one action to execute");
    puts("1 up");
    puts("2 down");
    puts("3 left");
    printf("4 right\n:");
    scanf("%d", &v5);
    if ( v5 == 2 )
    {
      ++*(_DWORD *)&v3[25];
    }
    else if ( v5 > 2 )
    {
      if ( v5 == 3 )
      {
        --v4;
      }
      else
      {
        if ( v5 != 4 )
LABEL_13:
          exit(1);
        ++v4;
      }
    }
    else
    {
      if ( v5 != 1 )
        goto LABEL_13;
      --*(_DWORD *)&v3[25];
    }
    for ( i = 0; i <= 1; ++i )
    {
      if ( *(int *)&v3[4 * i + 25] < 0 || *(int *)&v3[4 * i + 25] > 4 )
        exit(1);
    }
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 49 )
      exit(1);
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 35 )
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }
  }
}

这个伪代码有点意思,我也是研究了好久(个人比较笨),解说一下思路

依旧先找正确的输出,满足条件是v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 35 --> 等于'#'时就说明是正确的flag

'#'哪里来 --> 拉到开头我们能见到

是字符串的最后一个,这个字符串的长度为25,即 0~24。还有 " 1上 2下 3左 4右",让人迷茫,

再看看输入和判断,v5 和 v4 跟 v3 有什么关系为什么每个判断上或多或少的都有他两,甚至还有v7

对内存的解读,v3的内存为[esp+17h][esp+34h],v4为[esp+34h][esp+38h],v5为[esp+38h]~[esp+3Ch]

知道内存的分布后,我们先看异常退出的点(exit(1))

我们已经知道内存的分布,那么算一下(int类型的转换(int是四个字节即(双字)))即可知道,在这个循环中,检查的是v4和v5的值是否是小于 0 或者大于 4 的,满足则异常退出

在看看语句

++,我们知道自增嘛,那么*(_DWORD *)是什么意思呢DWORD的意思是双字(也就是四个字节),这句话的意思是指向v3[25]后边的四个字节(包括v3[25]),使它自增 1。这样这篇伪代码几乎就能看懂了(这里注意,这里的内存依旧属于v3的内存)

在看看最后的判断语句

-41?看看v7的内存分布,我们可以知道,v7对应的是v3前25个内存中各个的字符,结合上述,走迷宫? --> 在判断中还有 5 *

将v3前25给字符以5*5的排列来看

*1111
01000
01010
00010
1111#

答案就出来了,0处可以走,1处不能走,用1上 2下 3左 4右来代表方向,flag{222441144222}收工