【攻防世界】Reverse系列之maze

【攻防世界】Reverse系列之maze

该题目是个64位的ELF文件,直接使用idea反汇编查看main函数伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rbx
int v4; // eax
char v5; // bp
char v6; // al
const char *v7; // rdi
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) != 125 )
{
LABEL_22:
puts("Wrong flag!");
exit(-1);
}
#这几句代码可以得到flag格式位nctf{.............}
v3 = 5LL;
if ( strlen(&s1) - 1 > 5 )
{
while ( 1 )
{
v4 = *(&s1 + v3);
v5 = 0;
if ( v4 > 78 )
{
if ( (unsigned __int8)v4 == 'O' )
{
v6 = sub_400650(v10);
goto LABEL_14;
}
if ( (unsigned __int8)v4 == 'o' )
{
v6 = sub_400660(v10);
goto LABEL_14;
}
}
else
{
if ( (unsigned __int8)v4 == '.' )
{
v6 = sub_400670(&v9);
goto LABEL_14;
}
if ( (unsigned __int8)v4 == '0' )
{
v6 = sub_400680(&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;
}

接下来代码会进入一个循环体,主要代码如下。将s1中的字符串与’o’、’0‘、’O‘、’.‘这四个字符进行比较,然后调用sub_400650()和sub_400660()对v10和v9进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
v4 = *(&s1 + v3);
v5 = 0;
if ( v4 > 78 )
{
if ( (unsigned __int8)v4 == 'O' )
{
v6 = sub_400650(v10);
goto LABEL_14;
}
if ( (unsigned __int8)v4 == 'o' )
{
v6 = sub_400660(v10);
goto LABEL_14;
}
}
else
{
if ( (unsigned __int8)v4 == '.' )
{
v6 = sub_400670(&v9);
goto LABEL_14;
}
if ( (unsigned __int8)v4 == '0' )
{
v6 = sub_400680(&v9);

下面我们来看看sub_400650()和sub_400660()这两个函数,这两个函数很简单,就是做了个位置+1和-1的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool __fastcall sub_400650(_DWORD *a1)
{
int v1; // eax

v1 = (*a1)--;
return v1 > 0;
}

bool __fastcall sub_400660(int *a1)
{
int v1; // eax

v1 = *a1 + 1;
*a1 = v1;
return v1 < 8;
}

结合上面的代码可以得到如下信息

1
2
3
4
O  v10位置-1
o v10位置+1
. v9位置+1
0 v9位置-1

接着往下阅读代码,这里sub_400690()对v10[0]和v9进行操作,还需要满足括号中为true

1
2
3
4
5
6
7
8
9
10
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;
}

sub_400690代码如下,这里result为a1中的遍历元素,并且只能是’ ‘和’#’

1
2
3
4
5
6
7
8
__int64 __fastcall sub_400690(__int64 a1, int a2, int a3)
{
__int64 result; // rax

result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);
LOBYTE(result) = (_DWORD)result == ' ' || (_DWORD)result == '#';
return result;
}

a1形参代表的实参为asc_601060,其数据为

1
*******   *  **** * ****  * ***  *#  *** *** ***     *********

根据代码调整下格式,空格使用0来代替

1
2
3
4
5
6
7
8
00******
*000*00*
***0*0**
**00*0**
*00*#00*
**0***0*
**00000*
********

也可以判断出上方的v10和v9代表的是行和列,相当于个二维数组。所以这里重新对上面四个字符赋值

1
2
3
4
O  左
o 右
. 上
0 下

接下来继续看代码,这里if判断要使得上方二维数组的元素为’#’才能输出”Congratulations!”

1
2
3
4
5
6
if ( asc_601060[8 * v9 + v10[0]] != '#' )
goto LABEL_20;
v7 = "Congratulations!";
LABEL_21:
puts(v7);
return 0LL;

根据题目中提到的迷宫,推测是从二维数组从开头找到一条到”#”的路径,通过这条路径来逆向得出输入的字符

如下找到一条,从0出发则是”右下右右下下左下下下下右右右右上上左左”

转换为字符为’o0oo00O000oooo..OO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 00******
# *000*00*
# ***0*0**
# **00*0**
# *00*#00*
# **0***0*
# **00000*
# ********

str = "右下右右下下左下下下右右右右上上左左"
str = str.replace('上', '.')
str = str.replace('下', '0')
str = str.replace('左', 'O')
str = str.replace('右', 'o')

str = 'nctf{' + str + '}'

print(str)

得到flag为nctf{o0oo00O000oooo..OO}

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信