攻防世界 REVERSE 新手区/maze

攻防世界 REVERSE 新手区/maze

攻防世界 REVERSE 新手区/maze

老规矩先查壳,没加壳而且是64位的

攻防世界 REVERSE 新手区/maze

用IDA64位打开,找到main函数F5查看伪代码

攻防世界 REVERSE 新手区/maze

这是一个迷宫题,必然是有迷宫图在里面,然后走的路径就是输出的flag

接下来还是分析代码

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed __int64 v3; // rbx
  signed int v4; // eax
  bool v5; // bp
  bool v6; // al
  const char *v7; // rdi
  __int64 v9; // [rsp+0h] [rbp-28h]

  v9 = 0LL;
  puts("Input flag:");
  scanf("%s", &s1, 0LL);
  if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != 125 )
  {
LABEL_22:
    puts("Wrong flag!");
    exit(-1);
  }
  v3 = 5LL;
  if ( strlen(&s1) - 1 > 5 )
  {
    while ( 1 )
    {
      v4 = *(&s1 + v3);
      v5 = 0;
      if ( v4 > 78 )
      {
        v4 = (unsigned __int8)v4;
        if ( (unsigned __int8)v4 == 79 )
        {
          v6 = sub_400650((_DWORD *)&v9 + 1);
          goto LABEL_14;
        }
        if ( v4 == 111 )
        {
          v6 = sub_400660((int *)&v9 + 1);
          goto LABEL_14;
        }
      }
      else
      {
        v4 = (unsigned __int8)v4;
        if ( (unsigned __int8)v4 == 46 )
        {
          v6 = sub_400670(&v9);
          goto LABEL_14;
        }
        if ( v4 == 48 )
        {
          v6 = sub_400680((int *)&v9);
LABEL_14:
          v5 = v6;
          goto LABEL_15;
        }
      }
LABEL_15:
      if ( !(unsigned __int8)sub_400690(asc_601060, HIDWORD(v9), (unsigned int)v9) )
        goto LABEL_22;
      if ( ++v3 >= strlen(&s1) - 1 )
      {
        if ( v5 )
          break;
LABEL_20:
        v7 = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( asc_601060[8 * (signed int)v9 + SHIDWORD(v9)] != 35 )
    goto LABEL_20;
  v7 = "Congratulations!";
LABEL_21:
  puts(v7);
  return 0LL;
}

通过分析可知输入的flag长度为24,存储在s1中,且开头为nctf{,最后一个必然是 ‘}

哦对了,这题目挺奇怪的,跟进函数再按ESC返回函数的参数会变,这是变完后的,下面是一开始的代码

当你跟进while语句中的四个函数再返回时,这些函数的参数都会改变,最后变成上面的那段代码。不知道是不是我IDA的版本问题

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rsi
  signed __int64 v4; // rbx
  signed int v5; // eax
  char v6; // bp
  char v7; // al
  const char *v8; // rdi
  __int64 v10; // [rsp+0h] [rbp-28h]

  v10 = 0LL;
  puts("Input flag:");
  scanf("%s", &s1, 0LL);
  if ( strlen(&s1) != 24 || (v3 = "nctf{", strncmp(&s1, "nctf{", 5uLL)) || *(&byte_6010BF + 24) != 125 )
  {
LABEL_22:
    puts("Wrong flag!");
    exit(-1);
  }
  v4 = 5LL;
  if ( strlen(&s1) - 1 > 5 )
  {
    while ( 1 )
    {
      v5 = *(&s1 + v4);
      v6 = 0;
      if ( v5 > 78 )
      {
        v5 = (unsigned __int8)v5;
        if ( (unsigned __int8)v5 == 79 )
        {
          v7 = sub_400650((char *)&v10 + 4, v3);
          goto LABEL_14;
        }
        if ( v5 == 111 )
        {
          v7 = sub_400660((char *)&v10 + 4, v3);
          goto LABEL_14;
        }
      }
      else
      {
        v5 = (unsigned __int8)v5;
        if ( (unsigned __int8)v5 == 46 )
        {
          v7 = sub_400670(&v10, v3);
          goto LABEL_14;
        }
        if ( v5 == 48 )
        {
          v7 = sub_400680(&v10, v3);
LABEL_14:
          v6 = v7;
          goto LABEL_15;
        }
      }
LABEL_15:
      v3 = (const char *)HIDWORD(v10);
      if ( !(unsigned __int8)sub_400690(asc_601060, HIDWORD(v10), (unsigned int)v10) )
        goto LABEL_22;
      if ( ++v4 >= strlen(&s1) - 1 )
      {
        if ( v6 )
          break;
LABEL_20:
        v8 = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )
    goto LABEL_20;
  v8 = "Congratulations!";
LABEL_21:
  puts(v8);
  return 0LL;
}

看到最后有个输出 v8 = “Congratulations!”;的那么就从这里开始作为突破口

  if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )
    goto LABEL_20;
  v8 = "Congratulations!";
LABEL_21:
  puts(v8);
  return 0LL;

跟进asc_601060,看来这里存储的是迷宫图,数了下长度为64

攻防世界 REVERSE 新手区/maze

查一下35的ASCII码为# ,而迷宫里面刚好有一个#,最后是要走到这里才能输出Congratulations!

然后看while循环里面,有几个数字,这些就是走的方式,由这4个键控制上下左右。

攻防世界 REVERSE 新手区/maze

然后就是一个一个点进函数看了

攻防世界 REVERSE 新手区/maze
攻防世界 REVERSE 新手区/maze

攻防世界 REVERSE 新手区/maze
攻防世界 REVERSE 新手区/maze

攻防世界 REVERSE 新手区/maze
攻防世界 REVERSE 新手区/maze

攻防世界 REVERSE 新手区/maze攻防世界 REVERSE 新手区/maze

然后看到这个函数

攻防世界 REVERSE 新手区/maze
攻防世界 REVERSE 新手区/maze

查了下ASCII码35是#,32是‘ ’(空格) ,意思就是说必须要按着路径来走,不能走到障碍物上,然后看这句
result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);,迷宫存储是一个一维数组,但是一定是按二维数组来使用,从这句代码中推出每一行是8个字符,然后将那段迷宫按8个字符一行来划分。

我把空格的地方填成了x,最初始的位置替换成了I,这样更路线好看一些

      Ix******
      *xxx*xx*
      ***x*x**
      **xx*x**
      *xx*#xx*
      **x***x*
      **xxxxx*
      *********

数了一下从起始位置走到终点#刚好18步

接下来就分析上下左右是由哪些字符所控制的

从上面的四个函数的返回值两两相同,要么是返回 v1>0 要么是返回 v1<8

可以先随便带一个数进去,比如一开始一定是要往右走一步,假设右是79,带入发现撞到障碍物了,然后假设右是111,发现可以不会撞到障碍物。依次带入发现,46不行,48可以。

然后要往下走一步,带入发现,111和48可以,79和46不行。

说明111和48是分别控制右或下的,79和46是分别控制左或上的,所以可以分成两种情况

 	79		111		46		48
 1)上		下		左		右
 2)左		右		上		下
      Ix******
      *xxx*xx*
      ***x*x**
      **xx*x**
      *xx*#xx*
      **x***x*
      **xxxxx*
      *********

你问我为啥不用ASCII码对应的字母表示,,,因为这玩意太容易搞混了,79对应的是O(字母大O)111对应的是o(字母小o)48对应的是0(数字0)46对应的是.(小数点.)

然后按照路径来走分为两种情况,写出脚本

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
/*    Ix******
      *xxx*xx*
      ***x*x**
      **xx*x**
      *xx*#xx*
      **x***x*
      **xxxxx*
      *********/
      int a[] = {48,111,48,48,111,111,46,111,111,111,48,48,48,48,79
      ,79,46,46};
      int b[] = {111,48,111,111,48,48,79,48,48,48,111,111,111,111,
      46,46,79,79};
      printf("第一种情况:nctf{");
      for(int i = 0; i < 18; i++)
      {
          printf("%c",a[i]);
      }
      printf("}\n");
      printf("第二种情况:nctf{");

      for(int i = 0; i < 18; i++)
      {
          printf("%c",b[i]);
      }
      printf("}\n");
    return 0;

}

运行得到结果:(请在里面找一下0和O)

攻防世界 REVERSE 新手区/maze

正确flag是第二个

上一篇:数组翻转(JS)


下一篇:boost::reverse_iterator用法的测试程序