攻防世界 REVERSE 新手区/maze
老规矩先查壳,没加壳而且是64位的
用IDA64位打开,找到main函数F5查看伪代码
这是一个迷宫题,必然是有迷宫图在里面,然后走的路径就是输出的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
查一下35的ASCII码为# ,而迷宫里面刚好有一个#,最后是要走到这里才能输出Congratulations!
然后看while循环里面,有几个数字,这些就是走的方式,由这4个键控制上下左右。
然后就是一个一个点进函数看了
然后看到这个函数
查了下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)
正确flag是第二个