逆向迷宮題總結(持續更新) 2019華南師大CTF新生賽maze,攻防世界新手區:NJUPT CTF 2017,BUUCTF:不一樣的flag

CTF逆向入門:迷宮題學習記錄(持續更新)

**




(前言)

本文將不斷更新以具體題目爲載體,分析CTF逆向迷宮題的解題思路與方法。由於本人是新手,學識有限,若有錯漏或不當之處,歡迎指出。


提示:以下是本篇文章正文內容,下面案例可供參考

一、 逆向迷宮題概述

迷宮題當然是走迷宮最後得到flag啦!
主要步驟:
1.分析程序的主函數,找到迷宮
2分析程序,確定走迷宮的方法(上下左右…怎麼走)
3寫程序或手走迷宮,得到flag



二、 具體題目分析

1. 2019華南師大CTF新生賽maze

題目地址:https://github.com/scnu-sloth/hsctf-2019-freshmen
在這裏插入圖片描述在hex-view裏看到了迷宮,下一步是找走迷宮的規則和方法
點開check函數看到

bool __cdecl check(char *flag)
{
   
     
  char *v1; // eax
  int v2; // eax
  char *cur; // [esp+Ch] [ebp-4h]170個字符,其中有一個是空字符'0'

  cur = &maze[14];//這是一個含14*12的迷宮
  while ( *flag && *cur != '*' )//由此可見'*'是迷宮的牆
  {
   
     
    v1 = flag++;
    v2 = *v1;
    if ( v2 == 'd' )
    {
   
     
      ++cur;//d爲向右走一步
    }
    else if ( v2 > 'd' )
    {
   
     
      if ( v2 == 's' )
      {
   
     
        cur += 13;//s爲向右13步,在本二維數組中爲向左下方一步或向右13步
      }
      else
      {
   
     
        if ( v2 != 'w' )
          return 0;
        cur -= 13;//w爲向右上方一步或向左13步
      }
    }
    else
    {
   
     
      if ( v2 != 'a' )
        return 0;
      --cur;//a爲向左一步
    }
  }
  return *cur == '#';//'#'是迷宮終點
}

由題意,這是個14*12的迷宮,@是起點,#是終點,flag走迷宮的路徑
d:向前一步,a:向後一步,s:向前13步,w:向後13步
在這裏插入圖片描述由main函數可見,flag長度即走迷宮步數一共爲24步
手畫迷宮如下,X表示不可走,O表示可走
寫代碼生成迷宮



int main()
{
   
     
	char s[]="**************@************-************-***-**-*****--*****-*****-***#**-*****--**----******-*****-******-****--******---**-*******-*-----******-------*****************";
	int i,j;
	for(i=0;i<12;i++)
	{
   
     
		for(j=0;j<14;j++)
			{
   
     
				if(s[i*14+j]=='-')
				cout<<"0"<<' ';
				else if(s[i*14+j]=='*')
				cout<<"X"<<' ';
				else 
				cout<<s[i*14+j]<<' ';
			}
		cout<<endl;
	}
	return 0;
} 

在這裏插入圖片描述
太菜了不會寫程序走迷宮,只能手走 qvq.
在這裏插入圖片描述flag{sssssdsssddsdddwwdwwaaaw}

2.2020華南師大CTF新生賽maze

題目地址https://github.com/scnu-sloth/hsctf-2020-freshmen

3.攻防世界新手區:NJUPT CTF 2017 maze

題目地址:https://adworld.xctf.org.cn/task/answer?type=reverse&number=4&grade=0&id=5084&page=1
拖進IDA發現迷宮所在
在這裏插入圖片描述
在這裏插入圖片描述


分析main函數

  int v9; // [rsp+0h] [rbp-28h] BYREF
  int v10[9]; // [rsp+4h] [rbp-24h] BYREF

  v10[0] = 0;
  v9 = 0;//迷宮從最左上角開始走
  puts("Input flag:");
  scanf("%s", &s1);
  if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != '}' )//如果是nctf{開頭且長度爲24就進入下一步
  {
   
     
LABEL_22://要盯緊它,判斷什麼情況下會失敗
    puts("Wrong flag!");
    exit(-1);
  }
  v3 = 5LL;
  if ( strlen(&s1) - 1 > 5 )
  {
   
     
    while ( 1 )
    {
   
     
      v4 = *(&s1 + v3);
      v5 = 0;
      if ( v4 > 'N' )
      {
   
     
        if ( (unsigned __int8)v4 == 'O' )
        {
   
     
          v6 = sub_400650(v10);//這裏對v10進行了操作
          goto LABEL_14;
        }
        if ( (unsigned __int8)v4 == 'o' )
        {
   
     
          v6 = sub_400660(v10);//這裏對v10進行了操作
          goto LABEL_14;
        }
      }
      else
      {
   
     
        if ( (unsigned __int8)v4 == '.' )
        {
   
     
          v6 = sub_400670(&v9);//這裏對&v9進行了操作
          goto LABEL_14;
        }
        if ( (unsigned __int8)v4 == '0' )
        {
   
     
          v6 = sub_400680(&v9);//這裏對&v9進行了操作
LABEL_14:
          v5 = v6;
          goto LABEL_15;
        }
      }
LABEL_15:
      if ( !(unsigned __int8)sub_400690((__int64)asc_601060, v10[0], v9) )//這裏要關注障礙檢測,還有非常重要的東西
        goto LABEL_22;
      if ( ++v3 >= strlen(&s1) - 1 )
      {
   
     
        if ( v5 )
          break;
LABEL_20:
        v7 = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( asc_601060[8 * v9 + v10[0]] != '#' )//終點爲#
    goto LABEL_20;
  v7 = "Congratulations!";
LABEL_21:
  puts(v7);
  return 0LL;
}

由僞代碼可見,這是個8*8的迷宮,有四個判斷條件,分別進入了四個函數。
‘O’,‘o’,‘0’,’.'這四個字符分別控制不同方向
O左,o右,0下,.上(稍後會說怎麼判斷)
四個方向函數如下


bool __fastcall sub_400650(_DWORD *a1)//O
{
   
     
  int v1; // eax

  v1 = (*a1)--;//向左一步
  return v1 > 0;//防止越界
}
bool __fastcall sub_400660(int *a1)//o
{
   
     
  int v1; // eax

  v1 = *a1 + 1;//向右一步
  *a1 = v1;
  return v1 < 8;
}
bool __fastcall sub_400670(_DWORD *a1)//.
{
   
     
  int v1; // eax

  v1 = (*a1)--;//向上一步
  return v1 > 0;
}
bool __fastcall sub_400680(int *a1)//0
{
   
     
  int v1; // eax

  v1 = *a1 + 1;
  *a1 = v1;//向下一步
  return v1 < 8;
}

可是怎麼分辨v9和v10各自控制的是上下還是左右呢?
點開sub_400690函數,看到

__int64 __fastcall sub_400690(__int64 a1, int a2, int a3)
{
   
     //a1是數組的首地址,a2是v10,a3是v9
  __int64 result; // rax

  result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);//a3乘以8表明v10在二維數組中表示上下
  LOBYTE(result) = (_DWORD)result == ' ' || (_DWORD)result == '#';
  return result;
}

從這裏我們得知v10表示行,就是控制上下。v9表示列,就是控制左右。
而且只有‘ ’ 和#可以走,否則返回return 0。’*'是迷宮的牆。
寫代碼生成迷宮,0可走,X爲牆。

#include<iostream>
using namespace std;
int main()
{
   
     
	char s[]="  *******   *  **** * ****  * ***  *#  *** *** ***     *********";
	int i,j;
	for(i=0;i<8;i++)
	{
   
     
		for(j=0;j<8;j++)
			{
   
     
				if(s[i*8+j]==' ')
				cout<<"0"<<' ';
				else if(s[i*8+j]=='*')
				cout<<"X"<<' ';
				else 
				cout<<"#"<<' ';
			}
		cout<<endl;
	}
	return 0;
} 

在這裏插入圖片描述走迷宮得到flag爲 nctf{o0oo00O000oooo…OO}

4.BUUCTF:不一樣的flag

題目地址:https://buuoj.cn/challenges
拖進IDA查看main函數

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] == '1' )
      exit(1);//是個5*5的迷宮且1爲牆
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == '#' )
    {
   
     //‘#’是迷宮終點
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }
  }
}

一目瞭然,顯然是一道迷宮題。
1,2,3,4分別控制上下左右

using namespace std; 
int main()
{
   
     
	char s[]="*11110100001010000101111#";
	int i,j;
	for(i=0;i<5;i++)
	{
   
     
		for(j=0;j<5;j++)
			{
   
     
				if(s[i*5+j]=='0')
				cout<<"0"<<' ';
				else if(s[i*5+j]=='1')
				cout<<"X"<<' ';
				else 
				cout<<s[i*5+j]<<' ';
			}
		cout<<endl;
	}
	return 0;
} 

走迷宮
在這裏插入圖片描述Get the flag!: flag{222441144222}

三,總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章