这道题的总体思路还是比较清晰的,没有搞一些乱七八糟的东西来混淆我们,它就是用三个函数对输入的 flag 进行加密之后,再和 byte_602080 对比

# 主函数

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
int v4; // eax
char v6[4]; // [rsp+4h] [rbp-93Ch] BYREF
int i; // [rsp+8h] [rbp-938h]
int v8; // [rsp+Ch] [rbp-934h]
_DWORD v9[260]; // [rsp+10h] [rbp-930h] BYREF
char v10[16]; // [rsp+420h] [rbp-520h] BYREF
char s[256]; // [rsp+430h] [rbp-510h] BYREF
char v12[1032]; // [rsp+530h] [rbp-410h] BYREF
unsigned __int64 v13; // [rsp+938h] [rbp-8h]

v13 = __readfsqword(0x28u);
v10[0] = 16;
v10[1] = 32;
v10[2] = 48;
v10[3] = 48;
v10[4] = 32;
v10[5] = 32;
v10[6] = 16;
v10[7] = 64;
memset(s, 0, sizeof(s));
v8 = strlen(s);
memset(v12, 0, 0x400uLL);
printf("please input your flag:");
scanf("%s", s);
memset(v9, 0, 0x408uLL);
sub_4006B6(v9, v10, 8);
v3 = strlen(s);
sub_4007DB(v9, s, v3);
v4 = strlen(s);
sub_4008FA(s, v4, v12, v6);
for ( i = 0; i <= 50; ++i )
{
if ( v12[i] != byte_602080[i] )
{
puts("Wrong");
return 0LL;
}
}
puts("Good");
return 0LL;
}

这个数组的值已经给出来了

1
2
3
4
5
6
[0x5A, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 0x7C, 0x66,
0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56, 0x4C, 0x7C, 0x79, 0x6E,
0x65, 0x55, 0x52, 0x79, 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56,
0x4A, 0x67, 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,
0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 0x76, 0x5A,
0x45, 0x3D]

接下来就看三个函数分别做了些什么吧

# sub_4006B6 函数

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
bool __fastcall sub_4006B6(_DWORD *a1, __int64 a2, int a3)
{
bool result; // al
int i; // [rsp+1Ch] [rbp-18h]
int j; // [rsp+1Ch] [rbp-18h]
int v6; // [rsp+20h] [rbp-14h]
int v7; // [rsp+24h] [rbp-10h]
int v8; // [rsp+28h] [rbp-Ch]
_DWORD *v9; // [rsp+2Ch] [rbp-8h]

*a1 = 0;
a1[1] = 0;
v9 = a1 + 2;
for ( i = 0; i <= 255; ++i )
v9[i] = i;
v7 = 0;
result = 0;
LOBYTE(v6) = 0;
for ( j = 0; j <= 255; ++j )
{
v8 = v9[j];
v6 = (v6 + v8 + *(v7 + a2));
v9[j] = v9[v6];
v9[v6] = v8;
result = ++v7 >= a3;
if ( v7 >= a3 )
v7 = 0;
}
return result;
}

这里很明显可以看出就是 RC4 加密,这里是在创建 S 盒,并且进行了一部分的置换

# sub_4007DB 函数

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
_DWORD *__fastcall sub_4007DB(_DWORD *a1, __int64 a2, int a3)
{
_DWORD *result; // rax
int i; // [rsp+18h] [rbp-1Ch]
int v5; // [rsp+1Ch] [rbp-18h]
int v6; // [rsp+20h] [rbp-14h]
int v7; // [rsp+24h] [rbp-10h]
int v8; // [rsp+28h] [rbp-Ch]
_DWORD *v9; // [rsp+2Ch] [rbp-8h]

v5 = *a1;
v6 = a1[1];
v9 = a1 + 2;
for ( i = 0; i < a3; ++i )
{
v5 = (v5 + 1);
v7 = v9[v5];
v6 = (v6 + v7);
v8 = v9[v6];
v9[v5] = v8;
v9[v6] = v7;
*(i + a2) ^= LOBYTE(v9[(v7 + v8)]);
}
*a1 = v5;
result = a1;
a1[1] = v6;
return result;
}

这里是 RC4 加密的后一部分,再进行了最后的 S 盒的置换后,用新的 S 盒和我们输入的 flag 进行异或运算,得到加密后的结果,这里 v9 就是最后的 S 盒,a2 是 flag

# sub_4008FA 函数

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
_DWORD *__fastcall sub_4008FA(__int64 a1, int a2, const char *a3, _DWORD *a4)
{
int v4; // eax
int v5; // eax
unsigned __int8 v6; // al
int v7; // eax
unsigned __int8 v8; // al
int v9; // eax
int v10; // edx
_DWORD *result; // rax
char v13; // [rsp+2Dh] [rbp-13h]
unsigned __int8 v14; // [rsp+2Eh] [rbp-12h]
unsigned __int8 v15; // [rsp+2Fh] [rbp-11h]
int v16; // [rsp+30h] [rbp-10h]
int v17; // [rsp+34h] [rbp-Ch]

v16 = 0;
v17 = 0;
while ( v17 < a2 )
{
v4 = v17++;
v13 = *(v4 + a1);
if ( v17 >= a2 )
{
v6 = 0;
}
else
{
v5 = v17++;
v6 = *(v5 + a1);
}
v14 = v6;
if ( v17 >= a2 )
{
v8 = 0;
}
else
{
v7 = v17++;
v8 = *(v7 + a1);
}
v15 = v8;
a3[v16] = ((v13 >> 2) & 0x3F) + 61;
a3[v16 + 1] = (((v14 >> 4) | (16 * v13)) & 0x3F) + 61;
a3[v16 + 2] = (((v8 >> 6) | (4 * v14)) & 0x3F) + 61;
v9 = v16 + 3;
v16 += 4;
a3[v9] = (v15 & 0x3F) + 61;
}
if ( a2 % 3 == 1 )
{
a3[--v16] = 61;
}
else if ( a2 % 3 != 2 )
{
goto LABEL_15;
}
a3[v16 - 1] = 61;
LABEL_15:
v10 = strlen(a3);
result = a4;
*a4 = v10;
return result;
}

这个函数很像 base64,因为它在 43~48 行有一个三字节变四字节的操作,不过又不是完整的 base64,感觉也不像是换表的操作,根本没有找到表,所以最后还是直接逆运算搞出来了

# 整体思路

前两个函数是 RC4 加密,具体的 v9 值可以动调提取数据

通过汇编代码可以看出来这个 v9 的值应该就是 edx 里面的值了,esi 是输入的值,动调的时候直接在 xor 这里下个断点就可以看了,下面是 v9 的值也就是最后的 S 盒

1
2
3
4
5
6
7
8
9
[0x10, 0x59, 0x9C, 0x92, 0x06, 0x22, 0xCF, 0xA5, 0x72, 0x1E,
0x45, 0x6A, 0x06, 0xCB, 0x08, 0xC3, 0xE4, 0x49, 0x5A, 0x63,
0x0C, 0xDF, 0xF6, 0x5F, 0x08, 0x28, 0xBD, 0xE2, 0x10, 0x15,
0x1F, 0x6E, 0xAA, 0x5A, 0xCA, 0xEC, 0x80, 0xAF, 0x9B, 0x16,
0xBB, 0x3D, 0x13, 0x2F, 0x6A, 0xA4, 0xC7, 0x2E, 0xBC, 0x4B,
0x60, 0x9A, 0xAF, 0xE9, 0xCE, 0xDA, 0x67, 0x39, 0xBA, 0x3B,
0x85, 0xEB, 0xD2, 0x6B, 0xAB, 0x06, 0x6B, 0x10, 0x57, 0x2C,
0x88, 0x70, 0xF7, 0x4F, 0xAA, 0x7F, 0x12, 0x47, 0xD6, 0xDE,
0x74, 0xB2, 0x1D, 0xA4, 0xD7, 0x76, 0x9A, 0xE0]

在输入的 flag 和这个 v9 异或之后,又在第三个函数中将其 3 变 4,并有一点点的运算在里面,这个我们直接拿最后的结果反向算一遍就可以了

其中 v13,v14,v8 也就是 v15,分别是原来的三个字节,而在这里它被改变成了 a3 [v16] 到 a3 [v16+3] 相当于四个字节,所以原来的三个字节就是前两个函数加密后的结果,我们只要把它四个四个一组恢复成三字节就行了,还要注意一下这个位移和与运算的关系,v14 和 v15 放到一起的时候要把 0x3F 拆开,不然 flag 会不完整

# 脚本

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
a = [0x5A, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 0x7C, 0x66,
0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56, 0x4C, 0x7C, 0x79, 0x6E,
0x65, 0x55, 0x52, 0x79, 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56,
0x4A, 0x67, 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,
0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 0x76, 0x5A,
0x45, 0x3D]
flag = ''
for i in range(0, len(a), 4):
flag += chr((((a[i] - 61) & 0x3F) << 2) | (((a[i+1] - 61) & 0x30) >> 4))
flag += chr((((a[i+1] - 61) & 0x0F) << 4) | (((a[i+2] - 61) & 0x3C) >> 2)) # 这里0x30和0x3C可以去掉
flag += chr(((a[i+3] - 61) & 0x3F) | (((a[i+2] - 61) & 0x03) << 6))

S = [0x10, 0x59, 0x9C, 0x92, 0x06, 0x22, 0xCF, 0xA5, 0x72, 0x1E,
0x45, 0x6A, 0x06, 0xCB, 0x08, 0xC3, 0xE4, 0x49, 0x5A, 0x63,
0x0C, 0xDF, 0xF6, 0x5F, 0x08, 0x28, 0xBD, 0xE2, 0x10, 0x15,
0x1F, 0x6E, 0xAA, 0x5A, 0xCA, 0xEC, 0x80, 0xAF, 0x9B, 0x16,
0xBB, 0x3D, 0x13, 0x2F, 0x6A, 0xA4, 0xC7, 0x2E, 0xBC, 0x4B,
0x60, 0x9A, 0xAF, 0xE9, 0xCE, 0xDA, 0x67, 0x39, 0xBA, 0x3B,
0x85, 0xEB, 0xD2, 0x6B, 0xAB, 0x06, 0x6B, 0x10, 0x57, 0x2C,
0x88, 0x70, 0xF7, 0x4F, 0xAA, 0x7F, 0x12, 0x47, 0xD6, 0xDE,
0x74, 0xB2, 0x1D, 0xA4, 0xD7, 0x76, 0x9A, 0xE0]

for i in range(len(flag)):
flag += chr(ord(flag[i]) ^ S[i])

print(flag)

我这个脚本有点 bug,输出的结果前面会有一串乱码,但是结果是对的

得到 flag {e10adc3949ba59abbe56e057f20f883e}