LitCTF 2025 WriteUp (Reverse方向)

3k 词

easy_rc4:

由于是rc4对称加密,所以先随便输入一些东西,再断点patch输入的内容为密文,即可自动解密得到flag:LitCTF{71bb2a06417a5306ba297ddcfce7b1b0}.

easy_tea:

程序在4148D0、414F90有花,去花得到TEA加密函数和主逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl sub_4148D0(unsigned int *a1, _DWORD *a2)
{
int n4; // eax
unsigned int v3; // [esp+D0h] [ebp-2Ch]
unsigned int v4; // [esp+DCh] [ebp-20h]
int n32; // [esp+E8h] [ebp-14h]
int v6; // [esp+F4h] [ebp-8h]

v6 = 0;
v4 = *a1;
v3 = a1[1];
for ( n32 = 0; n32 < 32; ++n32 )
{
v6 += 0x114514;
v4 += (a2[1] + (v3 >> 5)) ^ (v6 + v3) ^ (*a2 + 16 * v3);
v3 += (a2[3] + (v4 >> 5)) ^ (v6 + v4) ^ (a2[2] + 16 * v4);
}
*a1 = v4;
n4 = 4;
a1[1] = v3;
return n4;
}
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
__int64 sub_414F90()
{
int v0; // edx
__int64 v2; // [esp-8h] [ebp-1ACh]
int n5; // [esp+D0h] [ebp-D4h]
char v4[108]; // [esp+DCh] [ebp-C8h] BYREF
char *v5; // [esp+148h] [ebp-5Ch]
int v6[14]; // [esp+154h] [ebp-50h] BYREF
int v7[5]; // [esp+18Ch] [ebp-18h] BYREF
int savedregs; // [esp+1A4h] [ebp+0h] BYREF

v7[0] = 0x11223344;
v7[1] = 0x55667788;
v7[2] = 0x99AABBCC;
v7[3] = 0xDDEEFF11;
v6[0] = 0x977457FE;
v6[1] = 0xDA3E1880;
v6[2] = 0xB8169108;
v6[3] = 0x1E95285C;
v6[4] = 0x1FE7E6F2;
v6[5] = 0x2BC5FC57;
v6[6] = 0xB28F0FA8;
v6[7] = 0x8E0E0644;
v6[8] = 0x68454425;
v6[9] = 0xC57740D9;
v6[10] = 0;
v6[11] = 0;
v5 = 0;
j_memset(v4, 0, 0x64u);
pritnf(std::cout, "输入你的flag:");
scanf(std::cin, v4);
v5 = v4;
for ( n5 = 0; n5 < 5; ++n5 )
TEA((unsigned int *)&v5[8 * n5], v7);
if ( compare((int)v6, (int)v5) )
pritnf(std::cout, "恭喜你回答正确捏");
else
pritnf(std::cout, "错误~");
sub_4112D5(&savedregs, &dword_41513C, 0, v0);
return v2;
}

编写脚本解密:

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
def b2dle(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='little', signed=False) for i in range(0, len(byte_array), 4)]

def d2ble(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

encflag = bytearray.fromhex("FE57749780183EDA089116B85C28951EF2E6E71F57FCC52BA80F8FB244060E8E25444568D94077C5")
dwordenc = b2dle(encflag)
key = [0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF11]

delta = 0x114514
result = []
for j in range(0, len(dwordenc)//2):
m1 = dwordenc[2*j]
m2 = dwordenc[2*j+1]
ttl = (delta * 0x20) & 0xFFFFFFFF
for j in range(0x20):
m2 = (m2 - ((key[3] + (m1 >> 5)) ^ (ttl + m1) ^ (key[2] + (m1 << 4)))) & 0xFFFFFFFF
m1 = (m1 - ((key[1] + (m2 >> 5)) ^ (ttl + m2) ^ (key[0] + (m2 << 4)))) & 0xFFFFFFFF
ttl = (ttl - delta) & 0xFFFFFFFF
result.append(m1)
result.append(m2)
flag = d2ble(result)
flagbytes = bytearray(flag)
print(flagbytes.decode())

flag:LitCTF{590939df61690383a47ed1bc6ade9d51}.

pickle:

编写脚本解析pickle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import dill
import dis

with open('challenge.pickle', 'rb') as f:
check_func = dill.load(f)

print("Function name:", check_func.__name__)
print("Function type:", type(check_func))

code_obj = check_func.__code__

dis.dis(code_obj)

print("Constants in function:", code_obj.co_consts)

if check_func.__closure__:
for cell in check_func.__closure__:
print("Closure variable:", cell.cell_contents)

输出:

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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
Function name: check
Function type: <class 'function'>
5 0 RESUME 0

6 2 LOAD_GLOBAL 1 (NULL + input)
12 CACHE
14 LOAD_CONST 1 ('input your flag > ')
16 UNPACK_SEQUENCE 1
20 CALL 1
28 CACHE
30 STORE_SUBSCR
34 CACHE
36 CACHE
38 CACHE
40 CACHE
42 CACHE
44 CACHE
46 CACHE
48 CACHE
50 CACHE
52 UNPACK_SEQUENCE 0
56 CALL 0
64 CACHE
66 STORE_FAST 0 (user_input)

8 68 BUILD_LIST 0
70 STORE_FAST 1 (decrypted)

9 72 LOAD_GLOBAL 5 (NULL + range)
82 CACHE
84 LOAD_GLOBAL 7 (NULL + len)
94 CACHE
96 LOAD_FAST 0 (user_input)
98 UNPACK_SEQUENCE 1
102 CALL 1
110 CACHE
112 UNPACK_SEQUENCE 1
116 CALL 1
124 CACHE
126 GET_ITER
>> 128 FOR_ITER 34 (to 200)

10 132 LOAD_FAST 0 (user_input)
134 LOAD_FAST 2 (i)
136 BINARY_SUBSCR
140 CACHE
142 CACHE
144 CACHE
146 LOAD_CONST 2 (6)
148 BINARY_OP 10 (-)
152 STORE_FAST 3 (b)

11 154 LOAD_FAST 1 (decrypted)
156 STORE_SUBSCR
160 CACHE
162 CACHE
164 CACHE
166 CACHE
168 CACHE
170 CACHE
172 CACHE
174 CACHE
176 CACHE
178 LOAD_FAST 3 (b)
180 UNPACK_SEQUENCE 1
184 CALL 1
192 CACHE
194 POP_TOP
196 JUMP_BACKWARD 35 (to 128)

13 198 BUILD_LIST 0
>> 200 LOAD_CONST 3 ((85, 84, 174, 227, 132, 190, 207, 142, 77, 24, 235, 236, 231, 213, 138, 153, 60, 29, 241, 241, 237, 208, 144, 222, 115, 16, 242, 239, 231, 165, 157, 224, 56, 104, 242, 128, 250, 211, 150, 225, 63, 29, 242, 169))
202 LIST_EXTEND 1
204 STORE_FAST 4 (fflag)

14 206 BUILD_LIST 0
208 LOAD_CONST 4 ((19, 55, 192, 222, 202, 254, 186, 190))
210 LIST_EXTEND 1
212 STORE_FAST 5 (key_ints)

16 214 LOAD_CONST 5 (<code object encrypt at 0x000001895F61C330, file "d:\code\PYTHON\IPParser1.py", line 16>)
216 MAKE_FUNCTION 0
218 STORE_FAST 6 (encrypt)

23 220 PUSH_NULL
222 LOAD_FAST 6 (encrypt)
224 LOAD_FAST 4 (fflag)
226 LOAD_FAST 5 (key_ints)
228 UNPACK_SEQUENCE 2
232 CALL 2
240 CACHE
242 STORE_FAST 7 (encrypted_flag)

25 244 LOAD_FAST 1 (decrypted)
246 LOAD_FAST 7 (encrypted_flag)
248 COMPARE_OP 2 (<)
252 CACHE
254 POP_JUMP_IF_FALSE 17 (to 290)

26 256 LOAD_GLOBAL 11 (NULL + print)
266 CACHE
268 LOAD_CONST 6 ('Good job! You made it!')
270 UNPACK_SEQUENCE 1
274 CALL 1
282 CACHE
284 POP_TOP
286 LOAD_CONST 0 (None)
288 RETURN_VALUE

28 >> 290 LOAD_GLOBAL 11 (NULL + print)
300 CACHE
302 LOAD_CONST 7 ("Nah, don't give up!")
304 UNPACK_SEQUENCE 1
308 CALL 1
316 CACHE
318 POP_TOP
320 LOAD_CONST 0 (None)
322 RETURN_VALUE

Disassembly of <code object encrypt at 0x000001895F61C330, file "d:\code\PYTHON\IPParser1.py", line 16>:
16 0 RESUME 0

17 2 BUILD_LIST 0
4 STORE_FAST 2 (result)

18 6 LOAD_GLOBAL 1 (NULL + range)
16 CACHE
18 LOAD_GLOBAL 3 (NULL + len)
28 CACHE
30 LOAD_FAST 0 (flag_bytes)
32 UNPACK_SEQUENCE 1
36 CALL 1
44 CACHE
46 UNPACK_SEQUENCE 1
50 CALL 1
58 CACHE
60 GET_ITER
>> 62 FOR_ITER 56 (to 178)

19 66 LOAD_FAST 0 (flag_bytes)
68 LOAD_FAST 3 (i)
70 BINARY_SUBSCR
74 CACHE
76 CACHE
78 CACHE
80 LOAD_FAST 1 (key)
82 LOAD_FAST 3 (i)
84 LOAD_GLOBAL 3 (NULL + len)
94 CACHE
96 LOAD_FAST 1 (key)
98 UNPACK_SEQUENCE 1
102 CALL 1
110 CACHE
112 BINARY_OP 6 (%)
116 BINARY_SUBSCR
120 CACHE
122 CACHE
124 CACHE
126 BINARY_OP 12 (^)
130 STORE_FAST 4 (b)

20 132 LOAD_FAST 2 (result)
134 STORE_SUBSCR
138 CACHE
140 CACHE
142 CACHE
144 CACHE
146 CACHE
148 CACHE
150 CACHE
152 CACHE
154 CACHE
156 LOAD_FAST 4 (b)
158 UNPACK_SEQUENCE 1
162 CALL 1
170 CACHE
172 POP_TOP
174 JUMP_BACKWARD 57 (to 62)

21 176 LOAD_FAST 2 (result)
>> 178 RETURN_VALUE
Constants in function: (None, 'input your flag > ', 6, (85, 84, 174, 227, 132, 190, 207, 142, 77, 24, 235, 236, 231, 213, 138, 153, 60, 29, 241, 241, 237, 208, 144, 222, 115, 16, 242, 239, 231, 165, 157, 224, 56, 104, 242, 128, 250, 211, 150, 225, 63, 29, 242, 169), (19, 55, 192, 222, 202, 254, 186, 190), <code object encrypt at 0x000001895F61C330, file "d:\code\PYTHON\IPParser1.py", line 16>, 'Good job! You made it!', "Nah, don't give up!")

可以发现是先减去6再循环异或,cyberchef直接解密,先异或再加6得到flag:LitCTF{6d518316-5075-40ff-873a-d1e8d632e208}.

FeatureExtraction:

查看主函数:

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
// local variable allocation has failed, the output may be wrong!
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
int v4; // ebx
__int64 v5; // rax
int *buf; // [rsp+20h] [rbp-D0h] BYREF
__int64 n53i64; // [rsp+28h] [rbp-C8h]
__int64 v9; // [rsp+30h] [rbp-C0h] BYREF
__int64 v10[2]; // [rsp+40h] [rbp-B0h] BYREF
char output[32]; // [rsp+50h] [rbp-A0h] BYREF
__int64 mid[4]; // [rsp+70h] [rbp-80h] BYREF
char input[16]; // [rsp+90h] [rbp-60h] BYREF
_QWORD key[4]; // [rsp+A0h] [rbp-50h] BYREF
char cipher[34]; // [rsp+C0h] [rbp-30h] BYREF
_WORD v16[7]; // [rsp+E2h] [rbp-Eh] BYREF

sub_40ABA0(*(_QWORD *)&argc, argv, envp);
sub_43EE50(v16);
buf = ::cipher;
n53i64 = 53i64; //长度
sub_460310(cipher, &buf, v16);
sub_43EEC0(v16);
sub_43EE50((char *)v16 + 1);
buf = ::key;
n53i64 = 10i64; //长度
sub_460310(key, &buf, (char *)v16 + 1);
sub_43EEC0((char *)v16 + 1);
std::operator<<<std::char_traits<char>>(&q, "Welcome to ");
*(_QWORD *)&v16[3] = key;
v10[0] = sub_460270(key);
v9 = sub_460240(*(_QWORD *)&v16[3]);
while ( (unsigned __int8)sub_41FED0(v10, &v9) )
{
*(_DWORD *)&v16[1] = *(_DWORD *)sub_420D20(v10);
sub_468200(&q, (unsigned int)SLOBYTE(v16[1]));
sub_41F310(v10);
}
((void (__fastcall *)(__int64 *))sub_466300)(&q);
std::operator<<<std::char_traits<char>>(&q, "Accepting Input:");
sub_448030((__int64 *)input);
scanf(Format_, input);
if ( strlen_0(input) == 44 )
{
sub_40166A((__int64)mid, (__int64)input);
sub_4014F0(output, mid, key);
if ( !(unsigned __int8)strcmp_0(output, cipher) )
v5 = std::operator<<<std::char_traits<char>>(&q, "Verification failed.");
else
v5 = std::operator<<<std::char_traits<char>>(&q, "Congratulations!");
((void (__fastcall *)(__int64))sub_466300)(v5);
v4 = 0;
free_1(output);
free_1(mid);
}
else
{
v3 = std::operator<<<std::char_traits<char>>(&q, "Length wrong.");
((void (__fastcall *)(__int64))sub_466300)(v3);
v4 = 1;
}
free_0(input);
free_1(key);
free_1(cipher);
return v4;
}

发现先加载了密文和密钥LitCTF2025,然后打印了一些字符串,之后读取输入并检测长度为44,经过两个加密函数后与密文比较,动态调试发现第一个函数是把输入值的一个字节扩展为一个dword,查看第二个函数:

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
__int64 __fastcall sub_4014F0(__int64 output, _QWORD *input, _QWORD *key)
{
int v4; // ebx
int v5; // ebx
_DWORD *v6; // rax
_QWORD Block[4]; // [rsp+20h] [rbp-60h] BYREF
char Str1[9]; // [rsp+47h] [rbp-39h] BYREF
int len_key; // [rsp+50h] [rbp-30h]
int len_input; // [rsp+54h] [rbp-2Ch]
int len_input_2; // [rsp+58h] [rbp-28h]
int len_input_1; // [rsp+5Ch] [rbp-24h]

if ( (unsigned __int8)sub_425190(input) || (unsigned __int8)sub_425190(key) )
{
sub_460390(output);
}
else
{
len_input = len(input);
len_key = len(key);
*(_DWORD *)&Str1[5] = len_input + len_key - 1;
sub_43EE50((__int64)Str1);
*(_DWORD *)&Str1[1] = 0;
sub_4603B0((__int64)Block, *(int *)&Str1[5], (__int64)&Str1[1], (__int64)Str1);
sub_43EEC0((__int64)Str1);
for ( len_input_1 = 0; len_input_1 < len_input; ++len_input_1 )
{
for ( len_input_2 = 0; len_input_2 < len_key; ++len_input_2 )
{
v4 = *(_DWORD *)dword(input, len_input_1);
v5 = *(_DWORD *)dword(key, len_input_2) * v4;
v6 = (_DWORD *)sub_460470(Block, len_input_1 + len_input_2);
*v6 += v5;
}
}
sub_4602D0(output, (__int64)Block);
free_1(Block);
}
return output;
}

发现是一个卷积操作,编写脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scipy.signal import deconvolve
import numpy as np

key = np.array([76, 105, 116, 67, 84, 70, 50, 48, 50, 53], dtype=np.int64)
target = np.array([
5776,15960,28657,34544,40294,43824,51825,53033,58165,58514,
61949,56960,53448,49717,47541,45519,40607,40582,38580,42320,
41171,41269,39370,44224,48760,49558,48128,46531,47088,46181,
46707,46879,48098,52047,53933,56864,60564,64560,66744,63214,
60873,58245,55179,56857,51532,44308,32392,27577,19654,14342,
11721,9112,6625], dtype=np.int64)
m, remainder = deconvolve(target, key)
m = np.round(m).astype(int)
print(bytearray(m.tolist()).decode())

flag:LitCTF{1e5a6230-308c-47cf-907c-4bfafdec8296}.

Robbie Wanna Revenge:

查看GameAssembly.dll,发现加了UPX壳,UPX改成了LIT,脱壳后用IL2CppDumper进行dump,发现在反编译出来的Assembly-Csharp.dll中只有函数的地址而没有具体实现,打开GameAssembly.dll,用给出的ida脚本恢复符号表,找到加解密函数:

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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Hidden C++ exception states: #try_helpers=1
System_Byte_array *Assets_Scripts_Cipher__Transform(
System_Byte_array *input,
System_Byte_array *key,
const MethodInfo *method)
{
unsigned __int16 v4; // bx
__int64 m; // r13
System_Byte_array *hash; // rdi
int v7; // esi
int32_t v8; // r15d
__int64 v9; // rdx
__int64 v10; // r8
System_Security_Cryptography_HashAlgorithm_o *v11; // r12
unsigned int max_length_1; // r14d
System_Byte_array *input_1; // r9
int32_t value; // edi
System_Byte_array *src_1; // rdi
System_Array_o *dst; // rsi
unsigned int v17; // eax
_DWORD *v18; // rsi
__int64 v19; // r14
System_Security_Cryptography_HashAlgorithm_c *klass; // r10
uint16_t interface_offsets_count; // dx
Il2CppRuntimeInterfaceOffsetPair *interfaceOffsets; // r8
__int64 v23; // rax
System_ArgumentNullException_o *v25; // rbx
__int64 *v26; // rax
__int64 *v27; // rax
__int64 *v28; // rax
System_ArgumentNullException_o *v29; // rbx
_BYTE v30[16]; // [rsp+20h] [rbp-10h] BYREF
unsigned int max_length; // [rsp+30h] [rbp+0h]
_DWORD *v32; // [rsp+38h] [rbp+8h]
__int64 v33; // [rsp+40h] [rbp+10h]
__int64 m_1; // [rsp+48h] [rbp+18h]
System_Security_Cryptography_HashAlgorithm_o *v35; // [rsp+50h] [rbp+20h]
_BYTE *v36; // [rsp+58h] [rbp+28h]
System_Array_o *src; // [rsp+B8h] [rbp+88h]

src = (System_Array_o *)key;
if ( !byte_180CBA696 )
{
sub_1800E15E0(0xE3Fu);
byte_180CBA696 = 1;
key = (System_Byte_array *)src;
}
v4 = 0;
v33 = 0;
v32 = v30;
v36 = v30;
if ( !input )
{
v25 = (System_ArgumentNullException_o *)sub_1800775D0((__int64)System_ArgumentNullException_TypeInfo);
System_ArgumentNullException___ctor_6444969648(v25, StringLiteral_1524, 0);
sub_1800775E0((__int64)v25, 0, Method_Assets_Scripts_Cipher_Transform__);
}
if ( !key )
{
v29 = (System_ArgumentNullException_o *)sub_1800775D0((__int64)System_ArgumentNullException_TypeInfo);
System_ArgumentNullException___ctor_6444969648(v29, StringLiteral_228, 0);
sub_1800775E0((__int64)v29, 0, Method_Assets_Scripts_Cipher_Transform__);
}
max_length = input->max_length;
m = il2cpp_array_new_specific_0(byte___TypeInfo, max_length);
m_1 = m;
hash = (System_Byte_array *)il2cpp_array_new_specific_0(byte___TypeInfo, 0);
v7 = 0;
v8 = 0;
v11 = (System_Security_Cryptography_HashAlgorithm_o *)System_Security_Cryptography_SHA256__Create(0);
v35 = v11;
max_length_1 = 0;
input_1 = input;
while ( (int)max_length_1 < (int)max_length )
{
if ( !hash )
sub_18010BCE0(0);
if ( v7 < SLODWORD(hash->max_length) )
{
v17 = v7;
}
else
{
value = v8++;
if ( (System_BitConverter_TypeInfo->_2.bitflags2 & 2) != 0 && !System_BitConverter_TypeInfo->_2.cctor_finished )
il2cpp_runtime_class_init_0(System_BitConverter_TypeInfo, v9, v10, input_1);
src_1 = System_BitConverter__GetBytes_6446108288(value, 0);
if ( !byte_180CBA697 )
{
sub_1800E15E0(0xE3Eu);
byte_180CBA697 = 1;
}
if ( !src_1 )
sub_18010BCE0(0);
dst = (System_Array_o *)il2cpp_array_new_specific_0(
byte___TypeInfo,
(unsigned int)(LODWORD(src_1->max_length) + LODWORD(src[1].klass)));
System_Buffer__BlockCopy(src, 0, dst, 0, (int32_t)src[1].klass, 0);
System_Buffer__BlockCopy((System_Array_o *)src_1, 0, dst, (int32_t)src[1].klass, src_1->max_length, 0);
if ( !v11 )
sub_18010BCE0(0);
hash = System_Security_Cryptography_HashAlgorithm__ComputeHash(v11, (System_Byte_array *)dst, 0);
v7 = 0;
v17 = 0;
input_1 = input;
}
if ( max_length_1 >= LODWORD(input_1->max_length) )
{
v26 = sub_18010A830((int)max_length_1);
sub_18010BC70(v26, 0, 0);
}
v9 = v7++;
if ( !hash )
sub_18010BCE0(0);
if ( v17 >= LODWORD(hash->max_length) )
{
v27 = sub_18010A830((int)max_length_1);
sub_18010BC70(v27, 0, 0);
}
if ( !m )
sub_18010BCE0(0);
if ( max_length_1 >= *(_DWORD *)(m + 24) )
{
v28 = sub_18010A830((int)max_length_1);
sub_18010BC70(v28, 0, 0);
}
*(_BYTE *)((int)max_length_1 + m + 32) = input_1->m_Items[max_length_1] ^ hash->m_Items[v9];
++max_length_1;
}
v18 = v32;
*v32 = 139;
v19 = v33;
if ( v11 )
{
klass = v11->klass;
interface_offsets_count = v11->klass->_2.interface_offsets_count;
if ( interface_offsets_count )
{
interfaceOffsets = klass->_1.interfaceOffsets;
while ( (System_IDisposable_c *)interfaceOffsets[v4].interfaceType != System_IDisposable_TypeInfo )
{
if ( ++v4 >= interface_offsets_count )
goto LABEL_29;
}
v23 = (__int64)&klass->vtable + 16 * (unsigned int)interfaceOffsets[v4].offset;
}
else
{
LABEL_29:
v23 = sub_1800D6850(v11, System_IDisposable_TypeInfo, 0);
}
(*(void (__fastcall **)(System_Security_Cryptography_HashAlgorithm_o *, _QWORD))v23)(v11, *(_QWORD *)(v23 + 8));
}
if ( *v18 != 139 && v19 )
sub_1800775E0(v19, 0, 0);
return (System_Byte_array *)m;
}

发现是一个基于SHA256哈希的异或,查找这个函数的引用,发现有两个关键函数:

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
void UIManager__Awake(UIManager_o *this, const MethodInfo *method)
{
__int64 v2; // r8
__int64 v3; // r9
struct UIManager_StaticFields *static_fields; // rdx
UnityEngine_Object_o *instance; // rdi
__int64 v7; // rdx
__int64 v8; // r8
__int64 v9; // r9
__int64 v10; // rdx
__int64 v11; // r8
__int64 v12; // r9
System_Text_Encoding_o *UTF8; // rax
__int64 v14; // rdx
__int64 v15; // r8
__int64 v16; // r9
Robbie_c *Robbie_TypeInfo; // rcx
System_Text_Encoding_o *UTF8_1; // rsi
System_Byte_array *cipherText; // rbp
System_Text_Encoding_o *UTF8_2; // rdi
struct GameManager_o *instance_1; // rdx
System_Byte_array *key; // rax
System_Byte_array *v23; // rax
struct System_String_o *flag; // rax
struct UIManager_StaticFields *static_fields_1; // rdx
TMPro_TMP_Text_o *ContentText; // rcx
__int64 v27; // rdx
UnityEngine_Object_o *gameObject; // rbx
__int64 v29; // r8
__int64 v30; // r9

if ( !byte_180CBA6D4 )
{
sub_1800E15E0(0x3A1Bu);
byte_180CBA6D4 = 1;
}
static_fields = UIManager_TypeInfo->static_fields;
instance = (UnityEngine_Object_o *)static_fields->instance;
if ( (UnityEngine_Object_TypeInfo->_2.bitflags2 & 2) != 0 && !UnityEngine_Object_TypeInfo->_2.cctor_finished )
il2cpp_runtime_class_init_0(UnityEngine_Object_TypeInfo, static_fields, v2, v3);
if ( UnityEngine_Object__op_Inequality(instance, 0, 0) )
{
gameObject = (UnityEngine_Object_o *)UnityEngine_Component__get_gameObject((UnityEngine_Component_o *)this, 0);
if ( (UnityEngine_Object_TypeInfo->_2.bitflags2 & 2) != 0 && !UnityEngine_Object_TypeInfo->_2.cctor_finished )
il2cpp_runtime_class_init_0(UnityEngine_Object_TypeInfo, v27, v29, v30);
UnityEngine_Object__Destroy_6448812544(gameObject, 0);
}
else
{
UIManager_TypeInfo->static_fields->instance = this;
if ( (UnityEngine_Object_TypeInfo->_2.bitflags2 & 2) != 0 && !UnityEngine_Object_TypeInfo->_2.cctor_finished )
il2cpp_runtime_class_init_0(UnityEngine_Object_TypeInfo, v7, v8, v9);
UnityEngine_Object__DontDestroyOnLoad((UnityEngine_Object_o *)this, 0);
if ( (::Robbie_TypeInfo->_2.bitflags2 & 2) != 0 && !::Robbie_TypeInfo->_2.cctor_finished )
il2cpp_runtime_class_init_0(::Robbie_TypeInfo, v10, v11, v12);
if ( !byte_180CBA6C7 )
{
sub_1800E15E0(0x2C41u);
byte_180CBA6C7 = 1;
}
UTF8 = System_Text_Encoding__get_UTF8(0);
Robbie_TypeInfo = ::Robbie_TypeInfo;
UTF8_1 = UTF8;
if ( (::Robbie_TypeInfo->_2.bitflags2 & 2) != 0 && !::Robbie_TypeInfo->_2.cctor_finished )
{
il2cpp_runtime_class_init_0(::Robbie_TypeInfo, v14, v15, v16);
Robbie_TypeInfo = ::Robbie_TypeInfo;
}
cipherText = Robbie_TypeInfo->static_fields->cipherText;
UTF8_2 = System_Text_Encoding__get_UTF8(0);
if ( !byte_180CBA6B4 )
{
sub_1800E15E0(0x1D22u);
byte_180CBA6B4 = 1;
}
instance_1 = GameManager_TypeInfo->static_fields->instance;
if ( !instance_1
|| !UTF8_2
|| (key = (System_Byte_array *)((__int64 (__fastcall *)(System_Text_Encoding_o *, struct System_String_o *, const MethodInfo *))UTF8_2->klass->vtable._15_GetBytes.methodPtr)(
UTF8_2,
instance_1->fields.key,
UTF8_2->klass->vtable._15_GetBytes.method),
v23 = Assets_Scripts_Cipher__Transform(cipherText, key, 0),
!UTF8_1)
|| (flag = (struct System_String_o *)((__int64 (__fastcall *)(System_Text_Encoding_o *, System_Byte_array *, const MethodInfo *))UTF8_1->klass->vtable._31_GetString.methodPtr)(
UTF8_1,
v23,
UTF8_1->klass->vtable._31_GetString.method),
this->fields.flag = flag,
static_fields_1 = UIManager_TypeInfo->static_fields,
!static_fields_1->instance)
|| (ContentText = (TMPro_TMP_Text_o *)static_fields_1->instance->fields.ContentText) == 0 )
{
sub_18010BCE0(0);
}
TMPro_TMP_Text__set_text(ContentText, flag, 0);
}
}
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
// Hidden C++ exception states: #try_helpers=1
System_String_o *Robbie__GetFlag(const MethodInfo *method)
{
System_Text_Encoding_o *UTF8; // rax
__int64 v2; // rdx
__int64 v3; // r8
__int64 v4; // r9
Robbie_c *Robbie_TypeInfo; // rcx
System_Text_Encoding_o *UTF8_1; // rbx
System_Byte_array *cipherText; // rsi
System_Text_Encoding_o *UTF8_2; // rdi
struct GameManager_o *instance; // rdx
System_Byte_array *key; // rax
System_Byte_array *v11; // rax

if ( !byte_180CBA6C7 )
{
sub_1800E15E0(0x2C41u);
byte_180CBA6C7 = 1;
}
UTF8 = System_Text_Encoding__get_UTF8(0);
Robbie_TypeInfo = ::Robbie_TypeInfo;
UTF8_1 = UTF8;
if ( (::Robbie_TypeInfo->_2.bitflags2 & 2) != 0 && !::Robbie_TypeInfo->_2.cctor_finished )
{
il2cpp_runtime_class_init_0(::Robbie_TypeInfo, v2, v3, v4);
Robbie_TypeInfo = ::Robbie_TypeInfo;
}
cipherText = Robbie_TypeInfo->static_fields->cipherText;
UTF8_2 = System_Text_Encoding__get_UTF8(0);
if ( !byte_180CBA6B4 )
{
sub_1800E15E0(0x1D22u);
byte_180CBA6B4 = 1;
}
instance = GameManager_TypeInfo->static_fields->instance;
if ( !instance
|| !UTF8_2
|| (key = (System_Byte_array *)((__int64 (__fastcall *)(System_Text_Encoding_o *, struct System_String_o *, const MethodInfo *))UTF8_2->klass->vtable._15_GetBytes.methodPtr)(
UTF8_2,
instance->fields.key,
UTF8_2->klass->vtable._15_GetBytes.method),
v11 = Assets_Scripts_Cipher__Transform(cipherText, key, 0),
!UTF8_1) )
{
sub_18010BCE0(0);
}
return (System_String_o *)((__int64 (__fastcall *)(System_Text_Encoding_o *, System_Byte_array *, const MethodInfo *))UTF8_1->klass->vtable._31_GetString.methodPtr)(
UTF8_1,
v11,
UTF8_1->klass->vtable._31_GetString.method);
}

这两个函数负责解密flag,其中密文的位置是Robbie.cipherText,密钥的位置是GameManager.instance.key,回到Assembly-Csharp.dll查看这两个数据的地址:

1
2
3
4
5
6
7
8
9
// Token: 0x04000086 RID: 134
[Token(Token = "0x4000083")]
[FieldOffset(Offset = "0x232ED0")] //绝对偏移
private static byte[] cipherText;

// Token: 0x04000046 RID: 70
[Token(Token = "0x4000046")]
[FieldOffset(Offset = "0x40")] //相对偏移
public string key;

发现密文是一个绝对偏移量,但是密钥是相对偏移量,用cheatengine加载程序,找了很久也没找到,换个思路,程序中有一个playerwon函数,用cheatengine选择mono解析后尝试直接调用改函数,发现输出了flag:LitCTF{Rm4ldulG05le0xaN4_LITCTF2025_Wa4jhzlZ05cm0qhF4}.