maze writeup
攻防世界的一道迷宫题,第一次接触这样的题,个人感觉很有意思,收获也挺多,做一篇笔记记录一下。
程序分析
__int64 sub_4006B0()
{
signed __int64 v0; // rbx
signed int v1; // eax
bool v2; // bp
bool v3; // al
const char *v4; // rdi
__int64 v6; // [rsp+0h] [rbp-28h]
v6 = 0LL;
puts("Input flag:");
scanf("%s", &s1, 0LL);
if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != 125 )// flag长度为24字节,前五个字节为"nctf{",最后一个字节要是"}"
{
LABEL_22:
puts("Wrong flag!");
exit(-1);
}
v0 = 5LL;
if ( strlen(&s1) - 1 > 5 )
{
while ( 1 )
{
v1 = *(&s1 + v0); // 对每一位进行判断
v2 = 0;
if ( v1 > 78 )
{
v1 = (unsigned __int8)v1;
if ( (unsigned __int8)v1 == 'O' ) // "O"表示向左移动
{
v3 = sub_400650((__int64)&v6 + 4);
goto LABEL_14;
}
if ( v1 == 'o' ) // "o"表示向右移动
{
v3 = sub_400660((__int64)&v6 + 4);
goto LABEL_14;
}
}
else
{
v1 = (unsigned __int8)v1;
if ( (unsigned __int8)v1 == '.' ) // "."表示向上移动
{
v3 = sub_400670((__int64)&v6);
goto LABEL_14;
}
if ( v1 == '0' ) // "0"表示向下移动
{
v3 = sub_400680((__int64)&v6);
LABEL_14:
v2 = v3;
goto LABEL_15;
}
}
LABEL_15:
if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v6), v6) )// asc_601060是字符串首地址
goto LABEL_22;
if ( ++v0 >= strlen(&s1) - 1 )
{
if ( v2 )
break;
LABEL_20:
v4 = "Wrong flag!";
goto LABEL_21;
}
}
}
if ( asc_601060[8 * (signed int)v6 + SHIDWORD(v6)] != '#' )
goto LABEL_20;
v4 = "Congratulations!";
LABEL_21:
puts(v4);
return 0LL;
}
主函数如上所示。我开始看的时候没有看明白,后来发现给出了四种操作符,对应上下左右四种移动,给出的字符串要想象成一个二维图像,相当于要用一个二维数组表示出来。
flag的格式已经给我们规定好了:nctf{......}。
__int64 __fastcall sub_400690(__int64 a1, int a2, int a3)
{
__int64 result; // rax
result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);
LOBYTE(result) = (_DWORD)result == 32 || (_DWORD)result == 35;
return result;
}
if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v6), v6) )// asc_601060是字符串首地址
goto LABEL_22;
sub_400690函数这里拉出来看一下,SHIDWORD这里是一个宏定义,定义如下:
#define SHIDWORD(x) (*((int32*)&(x)+1))
对字符串如何取值或者对二级指针理解还不清楚的,可以尝试敲一下下面的测试代码:
#include<stdio.h>
#include<string.h>
int main()
{
__int64 v6;
v6=1LL;char* a="abcdefghjk";
printf("%p
",a);
printf("%p
",*(&a));
printf("%p
",&a);
printf("%c
",*a);
printf("%c",*(a+1));
return 0;
}
宏定义和四种操作符理解不了,尝试下面的测试代码:
#include<stdio.h>
#include<string.h>
#define SHIDWORD(x) (*((__int32*)&(x)+1))
int main()
{
__int64 v6;
v6=1LL;
char* a="abcdefghjk";
printf("%p
",&v6);
printf("%p
",(__int64)&v6);
printf("%p
",&v6+4);
printf("%p
",(__int64)&v6+4);
printf("%p",&(SHIDWORD(v6)));
return 0;
}
sub_400690中,v6是行数,即y坐标,SHIDWORD(v6)是列数,即x坐标,(__int64)asc_601060是字符串首地址。后面的判断表示走的时候,只能走“#”和空格。
我们先把字符串转换成一个二维的迷宫
maze=" ******* * **** * **** * *** *# *** *** *** *********"
x=""
New_maze=""
for a in maze:
if a==" ":
New_maze+="0"
elif a=="*":
New_maze+="1"
else:
New_maze+=a
x=""
for i in range(len(New_maze)):
x+=New_maze[i]
if (i+1)%8==0:
print(x)
x=""
然后从头开始,“O”表示向左移动,“o”表示向右移动,“.”表示向上移动,“0”表示向下移动(上下这里要注意,容易搞错)。四种操作符看不懂,就好好看一下上面给出的测试代码。
最后得到flag:nctf{o0oo00O000oooo..OO}。