这道题刚打开看它的主函数的时候,看到它的控制流程图

而且主函数的代码也是一个非常长的循环,我们需要对它进行控制流平坦化,这里我是在 kali 中用 deflat.py 脚本操作的

平坦化之后就可以看正常的代码了

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
__int64 __fastcall main(int a1, char **a2, char **a3)
{
signed __int64 v4; // [rsp+1E0h] [rbp-110h]
int i; // [rsp+1E8h] [rbp-108h]
int v6; // [rsp+1ECh] [rbp-104h]
int v7; // [rsp+1ECh] [rbp-104h]
char s1[48]; // [rsp+1F0h] [rbp-100h] BYREF
char s[60]; // [rsp+220h] [rbp-D0h] BYREF
unsigned int v10; // [rsp+25Ch] [rbp-94h]
char *v11; // [rsp+260h] [rbp-90h]
int v12; // [rsp+26Ch] [rbp-84h]
bool v13; // [rsp+272h] [rbp-7Eh]
unsigned __int8 v14; // [rsp+273h] [rbp-7Dh]
int v15; // [rsp+274h] [rbp-7Ch]
char *v16; // [rsp+278h] [rbp-78h]
int v17; // [rsp+284h] [rbp-6Ch]
int v18; // [rsp+288h] [rbp-68h]
bool v19; // [rsp+28Fh] [rbp-61h]
char *v20; // [rsp+290h] [rbp-60h]
int v21; // [rsp+298h] [rbp-58h]
bool v22; // [rsp+29Fh] [rbp-51h]
__int64 v23; // [rsp+2A0h] [rbp-50h]
bool v24; // [rsp+2AFh] [rbp-41h]
__int64 v25; // [rsp+2B0h] [rbp-40h]
__int64 v26; // [rsp+2B8h] [rbp-38h]
__int64 v27; // [rsp+2C0h] [rbp-30h]
__int64 v28; // [rsp+2C8h] [rbp-28h]
int v29; // [rsp+2D0h] [rbp-20h]
int v30; // [rsp+2D4h] [rbp-1Ch]
char *v31; // [rsp+2D8h] [rbp-18h]
int v32; // [rsp+2E0h] [rbp-10h]
int v33; // [rsp+2E4h] [rbp-Ch]
bool v34; // [rsp+2EBh] [rbp-5h]

v10 = 0;
memset(s, 0, 0x30uLL);
memset(s1, 0, sizeof(s1));
printf("Input:");
v11 = s;
if ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 )
goto LABEL_43;
while ( 1 )
{
__isoc99_scanf("%s", v11);
v6 = 0;
if ( dword_603058 < 10 || (((dword_603054 - 1) * dword_603054) & 1) == 0 )
break;
LABEL_43:
__isoc99_scanf("%s", v11);
}
while ( 1 )
{
do
v12 = v6;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
v13 = v12 < 64;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 )
;
if ( !v13 )
break;
v14 = s[v6];
do
v15 = v14;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
if ( v15 == 10 )
{
v16 = &s[v6];
*v16 = 0;
break;
}
v17 = v6 + 1;
do
v6 = v17;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
}
for ( i = 0; ; ++i )
{
do
v18 = i;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
do
v19 = v18 < 6;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
if ( !v19 )
break;
do
v20 = s;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
v4 = *&v20[8 * i];
v7 = 0;
while ( 1 )
{
v21 = v7;
do
v22 = v21 < 64;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
if ( !v22 )
break;
v23 = v4;
v24 = v4 < 0;
if ( v4 >= 0 )
{
v27 = v4;
do
v28 = 2 * v27;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
v4 = v28;
}
else
{
v25 = 2 * v4;
do
v26 = v25;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
v4 = v26 ^ 0xB0004B7679FA26B3LL;
}
v29 = v7;
do
v7 = v29 + 1;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
}
v30 = 8 * i;
v31 = &s1[8 * i];
if ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 )
LABEL_55:
*v31 = v4;
*v31 = v4;
if ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 )
goto LABEL_55;
v32 = i + 1;
}
do
v33 = memcmp(s1, &unk_402170, 0x30uLL);
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 );
v34 = v33 != 0;
while ( dword_603058 >= 10 && (((dword_603054 - 1) * dword_603054) & 1) != 0 )
;
if ( v34 )
puts("Wrong!");
else
puts("Correct!");
return v10;
}

但实际上这还是挺长的,不过这里面有很多重复的无用语句可以删掉,官方的 wp 也有一个脚本可以简化代码,我就直接看简化后的代码了

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed __int64 v4; // [rsp+1E0h] [rbp-110h]
signed int j; // [rsp+1E8h] [rbp-108h]
signed int i; // [rsp+1ECh] [rbp-104h]
signed int k; // [rsp+1ECh] [rbp-104h]
char s1[48]; // [rsp+1F0h] [rbp-100h]
char s[60]; // [rsp+220h] [rbp-D0h]
unsigned int v10; // [rsp+25Ch] [rbp-94h]
char *v11; // [rsp+260h] [rbp-90h]
int v12; // [rsp+26Ch] [rbp-84h]
bool v13; // [rsp+272h] [rbp-7Eh]
unsigned __int8 v14; // [rsp+273h] [rbp-7Dh]
int v15; // [rsp+274h] [rbp-7Ch]
char *v16; // [rsp+278h] [rbp-78h]
int v17; // [rsp+284h] [rbp-6Ch]
int v18; // [rsp+288h] [rbp-68h]
bool v19; // [rsp+28Fh] [rbp-61h]
char *v20; // [rsp+290h] [rbp-60h]
int v21; // [rsp+298h] [rbp-58h]
bool v22; // [rsp+29Fh] [rbp-51h]
__int64 v23; // [rsp+2A0h] [rbp-50h]
bool v24; // [rsp+2AFh] [rbp-41h]
__int64 v25; // [rsp+2B0h] [rbp-40h]
__int64 v26; // [rsp+2B8h] [rbp-38h]
__int64 v27; // [rsp+2C0h] [rbp-30h]
__int64 v28; // [rsp+2C8h] [rbp-28h]
int v29; // [rsp+2D0h] [rbp-20h]
int v30; // [rsp+2D4h] [rbp-1Ch]
char *v31; // [rsp+2D8h] [rbp-18h]
int v32; // [rsp+2E0h] [rbp-10h]
int v33; // [rsp+2E4h] [rbp-Ch]
bool v34; // [rsp+2EBh] [rbp-5h]

v10 = 0;
memset(s, 0, 0x30uLL);
memset(s1, 0, 0x30uLL);
printf("Input:", 0LL);
v11 = s;
__isoc99_scanf("%s", s, (dword_603054 - 1), 3788079310LL);
for ( i = 0; ; ++i )
{
v12 = i;
v13 = i < 64;
if ( i >= 64 )
break;
v14 = s[i];
v15 = v14;
if ( v14 == 10 )
{
v16 = &s[i];
*v16 = 0;
break;
}
v17 = i + 1;
}
for ( j = 0; ; ++j )
{
v18 = j;
v19 = j < 6;
if ( j >= 6 )
break;
v20 = s;
v4 = *&s[8 * j];
for ( k = 0; ; ++k )
{
v21 = k;
v22 = k < 64;
if ( k >= 64 )
break;
v23 = v4;
v24 = v4 < 0;
if ( v4 >= 0 )
{
v27 = v4;
v28 = 2 * v4;
v4 *= 2LL;
}
else
{
v25 = 2 * v4;
v26 = 2 * v4;
v4 = 2 * v4 ^ 0xB0004B7679FA26B3LL;
}
v29 = k;
}
v30 = 8 * j;
v31 = &s1[8 * j];
*v31 = v4;
v32 = j + 1;
}
v33 = memcmp(s1, &unk_402170, 0x30uLL);
v34 = v33 != 0;
if ( v33 != 0 )
puts("Wrong!");
else
puts("Correct!");
return v10;
}

简化后再看它的逻辑就比较清晰了,先是输入一串长为 0x30 也就是 48 的字符串,将每八个字节分为一组一共六组,然后取每一组的首个数据进行正负判断,如果是正就乘 2 也就是左移一位,为负也是左移一位但还要进行一次异或运算,一共循环 64 次,这个是用 CRC32 算法来取得一个查表法所用的表,而在后面还有一次 CRC64 的加密,这里我还不是很懂,只能搞出这个表,看网上的 wp 最后一步都要再右移八位,这个应该就是 CRC64 的操作了,最后在和 & unk_402170 进行对比,这个就是最后的数据了,脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
a = [0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC,
0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4]
flag = ''

for s in a:
for i in range(64):
b = s & 1
if b == 1:
s ^= 0xB0004B7679FA26B3
s //= 2
if b == 1:
s |= 0x8000000000000000
print(s)

j = 0
while j < 8:
flag += chr(s & 0xFF)
s >>= 8
j += 1

print(flag)

得到 flag {6ff29390-6c20-4c56-ba70-a95758e3d1f8}