Mini L-CTF 2025 WriteUp

8k 词

逆向压轴的brainfuck比较有意思.

Misc方向:

MiniForensicsⅠ:

打开虚拟机,桌面上有一个全是坐标的txt和一个流量包,
在此电脑 > 文档中找到一个隐藏文件夹nihao
其中有一个ai.rar和一个pwd.txt,提示密钥是7位纯数字,爆破得到密钥1846287
解压后发现给了一个没用的图片,并提示压缩包还有东西,
发现有一个ssl.log被标记为了type03(服务块)而非02(文件块),改为02,
重新计算CRC32得到40f42e68,小端序682ef440覆盖,发现已经可以识别出ssl.log,
用这个作为SSLKEYLOGFILE解密流量,导出对象,得到两个upload,
第一个是D盘的密钥521433-074470-317097-543499-149259-301488-189849-252032
第二个是一个zip压缩包,用密钥对D盘进行解密,
发现D盘有一个透明贴图空白文字的文件夹,里面有一个c.txt,也是一串坐标,转为图像后发现是fakeflag,
但是b是ac的平均值,求得a的坐标序列,转为图片得到flag:miniLCTF{forens1c5_s0ooooo_1nt4resting}.

MiniForensicsⅡ:

对upload2进行明文爆破(因为是zipcrypto store,且有一个png文件),
偏移量为0的位置已知89504E470D0A1A0A0000000D49484452,得到密钥组45797e52 f747cc4c 800bd117
解密得到一个base64:aHR0cHM6Ly9naXRodWIuY29tL3Jvb3QtYWRtaW4tdXNlci93aGF0X2RvX3lvdV93YW5uYV9maW5kLmdpdA==
解密得到https://github.com/root-admin-user/what_do_you_wanna_find.git
访问后发现有几个文件,有一个fakeflag,还有一个模拟哈希碰撞的python脚本,
在上传历史中找到上传脚本那次的记录,给了一个提示Here’s something that might be useful.
在脚本中注意到给出了一个sha1哈希值89045a3653af483b6bb390e27c10db16873a60d1
并且有这样一行:print(f”The hash value of historical commits is: {target_hash} calculated by the simulation of calculating hash machine”)
说明这个哈希是一个commit的对应哈希值,访问https://github.com/root-admin-user/what_do_you_wanna_find/commit/89045a3653af483b6bb390e27c10db16873a60d1
发现存在一个secret.py的隐藏文件,
下载运行这个py文件得到flag:miniLCTF{c0ngr4tul4ti0n5_70u’v3_g0t_th3_s3cr3ts}.

Reverse方向:

0.s1gn1n:

查找main没找到,依据字符串搜索,发现main有花,去花,查看main:

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[64]; // [esp+Ch] [ebp-44h] BYREF

printf("Enter flag: "); // "Enter flag: "
scanf("%s", v4); // "%s"
if ( check(v4) )
printf("Wrong!\n"); // "Wrong!\n"
else
printf("Correct!\n"); // "Correct!\n"
return 0;
}

查看check:

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
int __cdecl check(_BYTE *a1)
{
_DWORD *v2; // [esp+8h] [ebp-68h]
int v3; // [esp+Ch] [ebp-64h]
unsigned int p_1; // [esp+10h] [ebp-60h]
_BYTE *v5; // [esp+18h] [ebp-58h]
unsigned int i; // [esp+20h] [ebp-50h]
int v7; // [esp+24h] [ebp-4Ch] BYREF
unsigned int p; // [esp+28h] [ebp-48h] BYREF
char v9[64]; // [esp+2Ch] [ebp-44h] BYREF

memset(v9, 0, sizeof(v9));
v7 = 0;
v2 = gentree(a1);
iter((int)v2, (int)v9, &v7);
v5 = base64((int)v9, &v9[strlen(v9) + 1] - &v9[1], &p);
for ( i = p - 1; i; --i )
{
v5[i] ^= v5[i - 1];
v5[i] ^= key[i];
}
v3 = -28;
for ( p_1 = 0; p_1 < p; ++p_1 )
v3 = v3 + (char)v5[p_1] - 1;
return v3;
}

发现了三个函数,首先用gentree生成了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
29
30
31
32
33
34
35
36
37
_DWORD *__cdecl gen(_BYTE *a1)
{
int v2; // [esp+0h] [ebp-14h]
_DWORD *v3; // [esp+4h] [ebp-10h]
int v4; // [esp+8h] [ebp-Ch]
int v5; // [esp+Ch] [ebp-8h]
int v6; // [esp+10h] [ebp-4h]

if ( !a1 || !*a1 )
return 0;
v2 = 0;
v3 = malloc(0xCu);
*(_BYTE *)v3 = *a1;
v3[2] = 0;
v3[1] = 0;
tag[0] = (int)v3;
v4 = 1;
v5 = 1;
while ( a1[v5] )
{
v6 = tag[v2++];
*(_DWORD *)(v6 + 4) = malloc(0xCu);
**(_BYTE **)(v6 + 4) = a1[v5++];
*(_DWORD *)(*(_DWORD *)(v6 + 4) + 8) = 0;
*(_DWORD *)(*(_DWORD *)(v6 + 4) + 4) = 0;
tag[v4++] = *(_DWORD *)(v6 + 4);
if ( a1[v5] )
{
*(_DWORD *)(v6 + 8) = malloc(0xCu);
**(_BYTE **)(v6 + 8) = a1[v5++];
*(_DWORD *)(*(_DWORD *)(v6 + 8) + 8) = 0;
*(_DWORD *)(*(_DWORD *)(v6 + 8) + 4) = 0;
tag[v4++] = *(_DWORD *)(v6 + 8);
}
}
return v3;
}

这是一个生成完全二叉树的函数,会把传入的字符串转为二叉树结构,再看iter函数:

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl iter(int a1, int a2, _DWORD *a3)
{
int result; // eax

if ( a1 )
{
iter(*(_DWORD *)(a1 + 4), a2, a3);
*(_BYTE *)(a2 + (*a3)++) = *(_BYTE *)a1;
return iter(*(_DWORD *)(a1 + 8), a2, a3);
}
return result;
}

这是一个递归函数,最后会改变输入字符串的顺序,之后有一个base64加密,然后是一个异或以及密钥异或,最后将自身所有字符值加起来,减去28,减去自身长度,要求该值为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
53
54
55
56
from collections import deque
import base64

class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None

def gen(data: bytes):
if not data:
return None
root = Node(data[0])
tag = [root]
i = 1
while i < len(data):
current = tag.pop(0)
current.left = Node(data[i])
tag.append(current.left)
i += 1
if i < len(data):
current.right = Node(data[i])
tag.append(current.right)
i += 1
return root

def iter_tree(node, buffer, index):
if node is None:
return
iter_tree(node.left, buffer, index)
buffer.append(chr(node.val))
index[0] += 1
iter_tree(node.right, buffer, index)

def xor_chain(data):
result = data.copy()
for i in range(len(result)-1, 0, -1):
result[i] ^= result[i-1]
result[i] ^= key[i % len(key)]
return result

data = b"1234567890"
key = bytearray.fromhex("58697B061E382C20040F0107316B080E7A0A727226376F492116112F1A0D3C1F2B321A34377F0344160E01281E68642317093D646A696318180A1570")
tree = gen(data)
buffer = []
index = [0]
iter_tree(tree, buffer, index)
res = bytearray(''.join(buffer).encode())
enc = xor_chain(bytearray(base64.b64encode(res)))
sum_ = -28
for i in range(len(enc)):
sum_ += enc[i] - 1
print(res)
print(enc)
print(sum_)
assert sum_ == 0

由于key的长度为60位,所以对应的明文应该在43~45左右,假设异或后的值为全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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import base64
from collections import deque

class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None

def gen(length: int):
if length <= 0:
return None
root = Node(0)
queue = deque([root])
i = 1
while queue and i < length:
current = queue.popleft()
current.left = Node(i)
queue.append(current.left)
i += 1
if i < length:
current.right = Node(i)
queue.append(current.right)
i += 1
return root

def inorder_fill(node, data, index):
if node is None:
return
inorder_fill(node.left, data, index)
node.val = ord(data[index[0]])
index[0] += 1
inorder_fill(node.right, data, index)

def levelorder_extract(root, length):
result = []
queue = deque([root])
while queue:
current = queue.popleft()
result.append(chr(current.val))
if len(result) == length:
break
if current.left is not None:
queue.append(current.left)
if current.right is not None:
queue.append(current.right)
return ''.join(result)

key = bytearray.fromhex("58697B061E382C20040F0107316B080E7A0A727226376F492116112F1A0D3C1F2B321A34377F0344160E01281E68642317093D646A696318180A1570")
length = len(key)
encrypted = [0] * length

data = encrypted.copy()
for i in range(length):
if i > 0:
data[i] ^= data[i-1]
data[i] ^= key[i % len(key)]

res = bytearray(data)
swap = base64.b64decode(res).decode()
print(swap)
tree = gen(len(swap))
index = [0]
inorder_fill(tree, swap, index)

original_data = levelorder_extract(tree, len(swap))
print(original_data)

得到flag:miniLCTF{esrevER_gnir33nignE_Is_K1nd_0F_@rt}.

d1ffer3nce:

脱壳,发现是go语言写的,查看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
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
// main.main
void __fastcall main_main()
{
__int128 v0; // xmm15
int v1; // r8d
int v2; // r9d
int v3; // r10d
int v4; // r11d
__int64 v5; // rbx
char **Error_reading_input:; // rsi
int v7; // r8d
int v8; // r9d
int v9; // r10d
int v10; // r11d
__int64 v11; // rcx
int v12; // r8d
int v13; // r9d
int v14; // r10d
int v15; // r11d
unsigned __int64 ptr; // rbx
int v17; // eax
int v18; // ecx
int v19; // r9d
int v20; // r10d
int v21; // r11d
__int64 v22; // rax
int v23; // r8d
int v24; // r9d
int v25; // r10d
int v26; // r11d
__int64 n64_1; // rax
int v28; // r8d
int v29; // r9d
char *v30; // r10
int v31; // r11d
signed __int64 ptr_2; // rdx
unsigned __int64 v33; // rsi
__int64 v34; // rdi
signed __int64 ptr_3; // rcx
unsigned __int64 v36; // rbx
unsigned __int8 v37; // r9
__int64 n64; // rbx
__int64 v39; // rax
int v40; // r8d
int v41; // r9d
int v42; // r10d
int v43; // r11d
__int64 v44; // [rsp-30h] [rbp-F0h]
__int64 v45; // [rsp-28h] [rbp-E8h]
__int64 v46; // [rsp-20h] [rbp-E0h]
char v47; // [rsp+0h] [rbp-C0h] BYREF
char v48; // [rsp+20h] [rbp-A0h] BYREF
__int64 v49; // [rsp+40h] [rbp-80h]
unsigned __int64 ptr_1; // [rsp+48h] [rbp-78h]
__int64 v51; // [rsp+50h] [rbp-70h]
_QWORD v52[2]; // [rsp+58h] [rbp-68h] BYREF
_QWORD v53[2]; // [rsp+68h] [rbp-58h] BYREF
_QWORD v54[2]; // [rsp+78h] [rbp-48h] BYREF
_QWORD v55[2]; // [rsp+88h] [rbp-38h] BYREF
_QWORD v56[2]; // [rsp+98h] [rbp-28h] BYREF
__int128 v57; // [rsp+A8h] [rbp-18h]
string *p_string; // [rsp+B8h] [rbp-8h]

p_string = (string *)runtime_newobject(&RTYPE_string);
v55[0] = &RTYPE_string;
v55[1] = &off_5009D0; // "Enter input: 3814697265625"
fmt_Fprint((unsigned int)off_501128, qword_5994D8, (unsigned int)v55, 1, 1, v1, v2, v3, v4);
v54[0] = &RTYPE__ptr_string;
v54[1] = p_string;
v5 = qword_5994D0;
LODWORD(Error_reading_input:) = 1;
fmt_Fscanln((unsigned int)off_501148, qword_5994D0, (unsigned int)v54, 1, 1, v7, v8, v9, v10);
if ( v5 )
{
v57 = v0;
v56[0] = &RTYPE_string;
Error_reading_input: = &off_5009E0; // "Error reading input:"
v56[1] = &off_5009E0; // "Error reading input:"
*(_QWORD *)&v57 = *(_QWORD *)(v5 + 8);
*((_QWORD *)&v57 + 1) = v11;
log_Fatal((unsigned int)v56, 2, 2, 1, (unsigned int)&off_5009E0, v12, v13, v14, v15);// "Error reading input:"
}
ptr = (unsigned __int64)p_string->ptr;
v17 = runtime_stringtoslicebyte(
(unsigned int)&v48,
p_string->ptr,
p_string->len,
1,
(_DWORD)Error_reading_input:,
v12,
v13,
v14,
v15);
v22 = main_sub_1145141919(
v17,
ptr,
v18,
(__int64)"0123456789abcdef",
16,
16,
v19,
v20,
v21);
ptr_1 = ptr;
v51 = v22;
v49 = 2 * ptr;
n64_1 = runtime_makeslice(
(unsigned int)&RTYPE_uint8,
2 * (int)ptr,
2 * (int)ptr,
(unsigned int)"0123456789abcdef",
16,
v23,
v24,
v25,
v26);
ptr_2 = ptr_1;
v33 = v49;
v34 = v51;
ptr_3 = 0;
v36 = 0;
while ( ptr_2 > ptr_3 )
{
v37 = *(_BYTE *)(v34 + ptr_3);
v30 = "0123456789abcdef";
if ( v36 >= v33 )
goto LABEL_13;
*(_BYTE *)(n64_1 + v36) = aRuntimeErrorSc[(v37 >> 4) + 690];
v28 = v36 + 1;
v29 = (unsigned __int8)aRuntimeErrorSc[(v37 & 0xF) + 690];
if ( v33 <= v36 + 1 )
{
runtime_panicIndex(v36 + 1, v36, v33);
LABEL_13:
runtime_panicIndex(v36, v36, v33);
}
*(_BYTE *)(v36 + n64_1 + 1) = v29;
++ptr_3;
v36 += 2LL;
}
n64 = n64_1;
v39 = runtime_slicebytetostring((unsigned int)&v47, n64_1, v33, v34, v33, v28, v29, (_DWORD)v30, v31);
if ( n64 == 64
&& (unsigned __int8)runtime_memequal(
v39,
"729daebea2e3845b310f01f1b3e703c24c810a9ca0ed2c4d9252a214882d7721",
64) )
{
v53[0] = &RTYPE_string;
v53[1] = &off_5009F0; // "Your Flag Is Correct!"
fmt_Fprintln((unsigned int)off_501128, qword_5994D8, (unsigned int)v53, 1, 1, v40, v41, v42, v43, v44, v45, v46);
}
else
{
v52[0] = &RTYPE_string;
v52[1] = &off_500A00; // "Incorrect Flag! Try Again"
fmt_Fprintln((unsigned int)off_501128, qword_5994D8, (unsigned int)v52, 1, 1, v40, v41, v42, v43, v44, v45, v46);
}
}

发现有一个关键加密函数sub1145141919,此外密文的十六进制是729daebea2e3845b310f01f1b3e703c24c810a9ca0ed2c4d9252a214882d7721,查看加密函数:

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// main.sub_1145141919
__int64 __golang main_sub_1145141919(
int a1,
unsigned __int64 ptr,
int a3,
__int64 a4,
signed __int64 n16,
__int64 a6,
int a7,
int a8,
int a9)
{
__int128 v9; // xmm15
unsigned __int64 ptr_15; // rcx
int v11; // r8d
int v12; // r9d
int v13; // r10d
int v14; // r11d
__int64 v15; // rax
__int64 ptr_7; // r9
unsigned __int64 ptr_3; // r10
int v18; // r11d
unsigned __int64 ptr_4; // rdx
__int64 v20; // rsi
unsigned __int64 ptr_2; // rdi
unsigned __int64 ptr_8; // r8
unsigned __int64 ptr_5; // rcx
unsigned __int64 ptr_10; // rbx
unsigned __int64 ptr_18; // rcx
__int64 n4; // rcx
_DWORD *v27; // rcx
__int64 v28; // rdx
_DWORD *v29; // rsi
int ptr_13; // eax
__int64 result; // rax
unsigned int v32; // r8d
__int64 ptr_12; // rax
unsigned __int64 ptr_9; // rdx
signed __int64 ptr_14; // rcx
unsigned __int64 ptr_21; // r8
int v37; // r9d
unsigned __int64 ptr_11; // r10
unsigned __int64 v39; // r10
__int64 ptr_22; // r10
__int64 v41; // [rsp-8h] [rbp-A8h]
__int64 v42; // [rsp+0h] [rbp-A0h]
__int64 ptra; // [rsp+8h] [rbp-98h]
__int64 v44; // [rsp+10h] [rbp-90h]
__int64 v45; // [rsp+18h] [rbp-88h]
__int128 v46; // [rsp+3Ch] [rbp-64h]
__int128 v47; // [rsp+4Ch] [rbp-54h]
int v48; // [rsp+5Ch] [rbp-44h]
unsigned __int64 ptr_19; // [rsp+60h] [rbp-40h]
unsigned __int64 ptr_16; // [rsp+68h] [rbp-38h]
__int64 ptr_20; // [rsp+70h] [rbp-30h]
unsigned __int64 ptr_1; // [rsp+78h] [rbp-28h]
unsigned __int64 ptr_6; // [rsp+80h] [rbp-20h]
__int64 ptr_17; // [rsp+88h] [rbp-18h]
__int64 v55; // [rsp+90h] [rbp-10h]
_DWORD *v56; // [rsp+98h] [rbp-8h]

ptr_1 = ptr;
v55 = main_asdfihkjasfasjl(a1, ptr, a3, a4, n16, a6, a7, a8, a9);
ptr_6 = ptr_15;
ptr_16 = (ptr >> 2) + 1;
v15 = runtime_makeslice((unsigned int)&RTYPE_uint32, 0, (unsigned int)(ptr >> 2) + 1, a4, n16, v11, v12, v13, v14);
ptr_4 = ptr_6;
v20 = v55;
ptr_2 = ptr;
ptr_8 = ptr_16;
ptr_5 = 0;
ptr_10 = 0;
while ( (__int64)ptr_2 > (__int64)ptr_5 )
{
ptr_7 = ptr_5 + 4;
ptr_3 = ptr_2;
if ( (__int64)ptr_2 > (__int64)(ptr_5 + 4) )
ptr_2 = ptr_5 + 4;
if ( ptr_4 < ptr_2 )
goto LABEL_40;
if ( ptr_5 > ptr_2 )
goto LABEL_39;
ptr_2 -= ptr_5;
if ( ptr_2 <= 3 )
goto LABEL_38;
++ptr_10;
v18 = *(_DWORD *)(v20 + (((__int64)(ptr_5 - ptr_4) >> 63) & ptr_5));
if ( ptr_8 < ptr_10 )
{
ptr_17 = ptr_5 + 4;
v48 = v18;
v15 = runtime_growslice(
v15,
ptr_10,
ptr_8,
1,
(unsigned int)&RTYPE_uint32,
ptr_8,
ptr_7,
ptr_3,
v18,
v41,
v42,
ptra,
v44,
v45);
ptr_4 = ptr_6;
v20 = v55;
ptr_7 = ptr_17;
ptr_3 = ptr_1;
v18 = v48;
ptr_8 = ptr_18;
}
*(_DWORD *)(v15 + 4 * ptr_10 - 4) = v18;
ptr_2 = ptr_3;
ptr_5 = ptr_7;
}
v46 = v9;
ptr_2 = n16;
for ( n4 = 0; n4 < 4; ++n4 )
{
ptr_8 = 4 * n4;
ptr_7 = 4 * n4 + 4;
if ( n16 < ptr_7 )
break;
if ( ptr_8 > ptr_7 )
{
LABEL_37:
runtime_panicSliceB(ptr_8, ptr_10, ptr_7);
LABEL_38:
runtime_panicIndex(3, ptr_10, ptr_2);
LABEL_39:
v15 = runtime_panicSliceB(ptr_5, ptr_10, ptr_2);
LABEL_40:
runtime_panicSliceAcap(v15, ptr_10, ptr_2);
}
LODWORD(ptr_8) = *(_DWORD *)(a4 + (((__int64)(ptr_8 - a6) >> 63) & ptr_8));
LODWORD(ptr_7) = n4 + 1;
*((_DWORD *)&v46 + n4) = ptr_8;
}
v47 = v46;
if ( !ptr_10 )
return 0;
v27 = (_DWORD *)v15;
v28 = 2025 / (__int64)ptr_10 + 6;
LODWORD(v29) = *(_DWORD *)(v15 + 4 * ptr_10 - 4);
ptr_2 = ptr_10 - 1;
ptr_13 = 0;
LABEL_21:
if ( v28 > 0 )
{
LODWORD(ptr_7) = ptr_13 + 0x4D696E69;
v32 = ((unsigned int)(ptr_13 + 0x4D696E69) >> 2) & 3;
ptr_12 = 0;
goto LABEL_34;
}
ptr_19 = ptr_10;
v56 = v27;
ptr_20 = 4 * ptr_10;
result = runtime_makeslice(
(unsigned int)&RTYPE_uint8,
4 * (int)ptr_10,
4 * (int)ptr_10,
ptr_2,
(_DWORD)v29,
ptr_8,
ptr_7,
ptr_3,
v18);
ptr_9 = ptr_20;
ptr_14 = ptr_19;
v29 = v56;
for ( ptr_10 = 0; (__int64)ptr_10 < ptr_14; ptr_10 = ptr_21 )
{
ptr_21 = ptr_10 + 1;
ptr_2 = 4 * (ptr_10 + 1);
v37 = v29[ptr_10];
if ( ptr_2 > ptr_9 )
goto LABEL_32;
ptr_10 *= 4LL;
if ( ptr_2 < ptr_10 )
goto LABEL_31;
ptr_2 -= ptr_10;
ptr_11 = ptr_10;
ptr_10 = (__int64)(ptr_10 - ptr_9) >> 63;
v39 = ptr_10 & ptr_11;
if ( ptr_2 <= 3 )
{
runtime_panicIndex(3, ptr_10, ptr_2);
LABEL_31:
result = runtime_panicSliceB(ptr_10, ptr_10, ptr_2);
LABEL_32:
ptr_12 = runtime_panicSliceAcap(result, ptr_10, ptr_2);
while ( 1 )
{
LODWORD(v29) = v27[ptr_12]
+ ((((16 * (_DWORD)v29) ^ (v27[ptr_12 + 1] >> 3))
+ (((unsigned int)v29 >> 5) ^ (4 * v27[ptr_12 + 1])))
^ ((ptr_7 ^ v27[ptr_12 + 1]) + (*((_DWORD *)&v47 + (v32 ^ ptr_12 & 3)) ^ (unsigned int)v29)));
v27[ptr_12] = (_DWORD)v29;
ptr_12 = ptr_22;
LABEL_34:
if ( ptr_12 >= (__int64)ptr_2 )
break;
ptr_22 = ptr_12 + 1;
if ( ptr_10 <= ptr_12 + 1 )
{
runtime_panicIndex(ptr_12 + 1, ptr_10, ptr_10);
goto LABEL_37;
}
}
v18 = 16 * (_DWORD)v29;
ptr_3 = v32 ^ ptr_2 & 3;
LODWORD(ptr_8) = (ptr_7 ^ *v27) + (*((_DWORD *)&v47 + ptr_3) ^ (unsigned int)v29);
LODWORD(v29) = v27[ptr_10 - 1]
+ (ptr_8 ^ (((16 * (_DWORD)v29) ^ (*v27 >> 3)) + (((unsigned int)v29 >> 5) ^ (4 * *v27))));
v27[ptr_10 - 1] = (_DWORD)v29;
--v28;
ptr_13 = ptr_7;
goto LABEL_21;
}
*(_DWORD *)(result + v39) = v37;
}
return result;
}

发现是一个魔改XXTEA,轮数为6 + 2025 / n,delta为0x4D696E69,编写同构加解密脚本:

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
def bytes_to_dwords_little_endian(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 dwords_to_bytes_little_endian(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

def XXTEA(m, leng, k):
n = leng - 1
delta = 0x4D696E69
rounds = 6 + 2025 // (n + 1)
ttl = 0
for j in range(rounds):
ttl = (ttl + delta) & 0xFFFFFFFF
for i in range(n+1):
after = m[(i + 1) % len(m)]
before = m[(i - 1) % len(m)]
m[i] = (m[i] + ((((before >> 5) ^ (after << 2)) + ((after >> 3) ^ (before << 4))) ^ ((ttl ^ after) + (k[(i & 3) ^ ((ttl >> 2) & 3)] ^ before)))) & 0xFFFFFFFF
return m

def de_XXTEA(m, leng, k):
n = leng - 1
delta = 0x4D696E69
rounds = 6 + 2025 // (n + 1)
ttl = ((rounds + 1) * delta) & 0xFFFFFFFF
for j in range(rounds):
ttl = (ttl - delta) & 0xFFFFFFFF
for i in range(n, -1, -1):
after = m[(i + 1) % len(m)]
before = m[(i - 1) % len(m)]
m[i] = (m[i] - ((((before >> 5) ^ (after << 2)) + ((after >> 3) ^ (before << 4))) ^ ((ttl ^ after) + (k[(i & 3) ^ ((ttl >> 2) & 3)] ^ before)))) & 0xFFFFFFFF
return m

enc = bytearray.fromhex("729daebea2e3845b310f01f1b3e703c24c810a9ca0ed2c4d9252a214882d7721")

key = bytearray(b"0123456789abcdef")

test = bytearray(b"12345678901234567890123456789012\x04\x04\x04\x04")

dwordenc = bytes_to_dwords_little_endian(enc)
dwordkey = bytes_to_dwords_little_endian(key)
dwordtest = bytes_to_dwords_little_endian(test)
# 加解密测试
dwordresult = XXTEA(dwordtest, len(dwordtest), dwordkey)
result = dwords_to_bytes_little_endian(dwordresult)
for i in range(len(result)):
print(hex(result[i]),end=", ")
print()
dwordresult2 = de_XXTEA(dwordresult, len(dwordresult), dwordkey)
result2 = dwords_to_bytes_little_endian(dwordresult2)
for i in range(len(result2)):
print(hex(result2[i]), end=", ")
print()

# 解密flag
dwordflag = de_XXTEA(dwordenc, len(dwordenc), dwordkey)
flag = dwords_to_bytes_little_endian(dwordflag)
for i in range(len(flag)):
print(hex(flag[i]), end=", ")

得到flag:miniLCTF{W3lc0m3~MiN1Lc7F_2O25}.

x96re:

查看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
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int8 i; // [esp+1h] [ebp-D9h]
size_t len; // [esp+2h] [ebp-D8h]
int key[4]; // [esp+8h] [ebp-D2h] BYREF
_DWORD cipher[8]; // [esp+18h] [ebp-C2h]
int output[12]; // [esp+38h] [ebp-A2h] BYREF
__int16 v9; // [esp+68h] [ebp-72h]
int v10; // [esp+6Ah] [ebp-70h]
int v11; // [esp+6Eh] [ebp-6Ch]
int v12; // [esp+72h] [ebp-68h]
int v13; // [esp+76h] [ebp-64h]
int v14; // [esp+7Ah] [ebp-60h]
int v15; // [esp+7Eh] [ebp-5Ch]
int v16; // [esp+82h] [ebp-58h]
int v17; // [esp+86h] [ebp-54h]
int v18; // [esp+8Ah] [ebp-50h]
int v19; // [esp+8Eh] [ebp-4Ch]
int v20; // [esp+92h] [ebp-48h]
int v21; // [esp+96h] [ebp-44h]
__int16 v22; // [esp+9Ah] [ebp-40h]
char input[4]; // [esp+9Ch] [ebp-3Eh] BYREF
int v24; // [esp+A0h] [ebp-3Ah]
int v25; // [esp+A4h] [ebp-36h]
int v26; // [esp+A8h] [ebp-32h]
int v27; // [esp+ACh] [ebp-2Eh]
int v28; // [esp+B0h] [ebp-2Ah]
int v29; // [esp+B4h] [ebp-26h]
int v30; // [esp+B8h] [ebp-22h]
int v31; // [esp+BCh] [ebp-1Eh]
int v32; // [esp+C0h] [ebp-1Ah]
int v33; // [esp+C4h] [ebp-16h]
int v34; // [esp+C8h] [ebp-12h]
__int16 v35; // [esp+CCh] [ebp-Eh]
unsigned int v36; // [esp+CEh] [ebp-Ch]
int *p_argc; // [esp+D2h] [ebp-8h]

p_argc = &argc;
v36 = __readgsdword(0x14u);
memset(output, 0, sizeof(output));
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
v13 = 0;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 0;
v18 = 0;
v19 = 0;
v20 = 0;
v21 = 0;
v22 = 0;
key[0] = 0x35323032;
key[1] = 0x696E696D;
key[2] = 0x6674636C;
key[3] = 0x21212121;
*(_DWORD *)input = 0;
v24 = 0;
v25 = 0;
v26 = 0;
v27 = 0;
v28 = 0;
v29 = 0;
v30 = 0;
v31 = 0;
v32 = 0;
v33 = 0;
v34 = 0;
v35 = 0;
cipher[0] = 0xDCBEE7D4;
cipher[1] = 0x78FB2439;
cipher[2] = 0xC06E8000;
cipher[3] = 0xD3C34A2C;
cipher[4] = 0xF53837D5;
cipher[5] = 0xA9C8D88D;
cipher[6] = 0x20CBDAE5;
cipher[7] = 0x2551D478;
puts("please input flag:");
if ( !fgets(input, 50, stdin) )
goto LABEL_7;
len = strlen(input);
if ( len )
{
if ( input[len - 1] == 10 )
input[--len] = 0;
}
if ( len == 32 )
{
LABEL_7:
init();
whathappened();
encode_fun(0x20u, key, input, output);
for ( i = 0; i <= 0x1Fu; ++i )
{
if ( *((_BYTE *)output + i) != *((_BYTE *)cipher + i) )
{
printf("try again~~~");
return 1;
}
}
printf("congratulation!!!");
return 0;
}
else
{
printf("Invalid length!got %zu\n", len);
return 1;
}
}

发现了一个值为2025minilctf!!!!的密钥,以及32字节的密文,主要函数就是init、whathappened和encode_fun,查看init:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned int init()
{
_BYTE src_[65]; // [esp+Bh] [ebp-5Dh] BYREF
unsigned int v2; // [esp+4Ch] [ebp-1Ch]

v2 = __readgsdword(0x14u);
if ( mmap((void *)0xDEAD000, 0x100u, 7, 50, -1, 0) != (void *)0xDEAD000 )
{
puts("sorry");
exit(0);
}
*(_DWORD *)src_ = 0xFF0785C6;
*(_DWORD *)&src_[61] = *(_DWORD *)((char *)&dword_565581C8[15] + 1);
qmemcpy(&src_[1], (char *)dword_565581C8 - (src_ - &src_[1]), 4 * (((src_ - &src_[1] + 65) & 0xFFFFFFFC) >> 2));
qmemcpy((void *)0xDEAD000, src_, 0x40u);
MEMORY[0xDEAD040] = src_[64];
return v2 - __readgsdword(0x14u);
}

发现地址爆红,动态调试,发现是一个SMC,解出来的函数是:

1
2
3
4
5
6
7
8
int __usercall addr_@<eax>(int a1@<ebp>)
{
_BYTE retaddr[8]; // [esp+0h] [ebp+0h]

for ( *(_BYTE *)(a1 - 249) = 0; *(_BYTE *)(a1 - 249) <= 0x1Fu; ++*(_BYTE *)(a1 - 249) )
*(_BYTE *)(a1 + (__int16)(*(unsigned __int8 *)(a1 - 249) - 1) - 64) ^= 0x4Cu;
return MK_FP(*(_WORD *)retaddr, *(_DWORD *)retaddr)();
}

发现是把输入值的前30个异或了0x4C,查看whathappened:

1
2
3
4
5
6
7
8
void __cdecl whathappened()
{
_BYTE retaddr[8]; // [esp+4h] [ebp+4h]

*(_DWORD *)retaddr = 0xDEAD000;
*(_DWORD *)&retaddr[4] = 51;
JUMPOUT(0xDF7D000);
}

发现就是执行了上面的SMC函数,对输入值进行了简单异或,查看加密函数,发现是一个SM4加密,将密文提取解密得到3ac159d665b4ccfb25c0927c1a23edb3,flag:miniLCTF{3ac159d665b4ccfb25c0927c1a23edb3}.

rbf:

观察java层:

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
package com.doctor3.rbf;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

/* loaded from: classes2.dex */
public class MainActivity extends AppCompatActivity {
public native int Check(String str);

static {
System.loadLibrary("myrust");
}

/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(C0891R.layout.layout);
((Button) findViewById(C0891R.id.check)).setOnClickListener(new View.OnClickListener() { // from class: com.doctor3.rbf.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String obj = ((EditText) MainActivity.this.findViewById(C0891R.id.FlagText)).getText().toString();
if (obj.length() != 22 || !obj.startsWith("miniLCTF{") || !obj.endsWith("}")) {
Toast.makeText(MainActivity.this.getApplicationContext(), "Wrong format", 0).show();
return;
}
int Check = MainActivity.this.Check(obj.substring(9, 21));
if (Check == 1) {
Toast.makeText(MainActivity.this.getApplicationContext(), "GOOD", 0).show();
} else if (Check == 0) {
Toast.makeText(MainActivity.this.getApplicationContext(), "WRONG", 0).show();
} else {
Toast.makeText(MainActivity.this.getApplicationContext(), "ERROR" + Integer.toString(Check), 0).show();
}
}
});
}
}

发现flag中间的12字节会被带入so层检验,查看so层:

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
__int64 __fastcall Java_com_doctor3_rbf_MainActivity_Check(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // r9
char *s; // r14
size_t v5; // rax
_BYTE *v6; // rbx
__int64 v7; // rdx
__int64 v8; // r14
__int64 v9; // r9
size_t n; // r15
const void *src_1; // rbp
__int64 v12; // r13
char *dest; // r12
__int64 dest_2; // rax
__int64 v15; // rbp
size_t size; // rbx
const void *src_2; // r15
__int64 v18; // r12
__int64 dest_3; // rax
unsigned __int8 *dest_1; // r14
__int64 v21; // r15
unsigned int n48; // ebp
__int64 v24; // rcx
unsigned __int8 n8; // dl
__int128 v26; // kr00_16
__int64 v27; // rax
size_t size_3; // rcx
unsigned __int64 v29; // rsi
unsigned __int64 n0x400; // rdi
int v31; // r9d
size_t size_2; // r8
size_t v33; // r10
int n93; // r10d
int v35; // r8d
size_t v36; // r9
int n93_1; // r9d
size_t size_1; // rcx
__m256i src; // [rsp+0h] [rbp-C8h] BYREF
__int128 v40; // [rsp+20h] [rbp-A8h]
__int64 v41; // [rsp+30h] [rbp-98h]
__m256i s2; // [rsp+40h] [rbp-88h] BYREF
__int128 v43; // [rsp+60h] [rbp-68h]
__int64 v44; // [rsp+70h] [rbp-58h]
__int64 v45; // [rsp+80h] [rbp-48h]
__int64 v46; // [rsp+88h] [rbp-40h] BYREF
__int64 v47[7]; // [rsp+90h] [rbp-38h] BYREF

v46 = a1;
v47[0] = a3;
sub_245E0(&s2, &v46, v47);
if ( s2.m256i_i8[0] != 15 )
{
v41 = v44;
v40 = v43;
src = s2;
sub_615A0(aCalledResultUn, 43, &src, &off_6AED8, &off_6B140, v3);// "src\\lib.rs"
}
src.m256i_i64[2] = s2.m256i_i64[3];
*(_OWORD *)src.m256i_i8 = *(_OWORD *)&s2.m256i_u64[1];
s = (char *)sub_24F10(&src);
sub_24F30(&src);
v5 = strlen(s);
v6 = (_BYTE *)sub_5FAE0(s, v5 + 1);
v8 = v7;
sub_61180(&src, v6);
if ( src.m256i_i32[0] == 1 )
{
*(_OWORD *)s2.m256i_i8 = *(_OWORD *)&src.m256i_u64[1];
sub_615A0(aCalledResultUn, 43, &s2, &unk_6AEB8, &off_6B158, v9);// "src\\lib.rs"
}
n = src.m256i_u64[2];
if ( src.m256i_i64[2] < 0 )
{
v45 = 0;
goto LABEL_86;
}
src_1 = (const void *)src.m256i_i64[1];
v12 = 1;
dest = (char *)&dword_0 + 1;
if ( !src.m256i_i64[2] )
goto LABEL_7;
v45 = 1;
dest_2 = sub_243E0(src.m256i_u64[2], 1u);
if ( !dest_2 )
LABEL_86:
sub_5F836(v45, n, &off_6AEF8); // "C:\\Users\\dr3\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib/rustlib/src/rust\\library/alloc/src/slice.rs"
dest = (char *)dest_2;
LABEL_7:
memcpy(dest, src_1, n);
src.m256i_i64[0] = 0;
*(_OWORD *)&src.m256i_u64[1] = 1u;
if ( n )
{
sub_24090(&src, 0, n);
v15 = src.m256i_i64[2];
v12 = src.m256i_i64[1];
}
else
{
v15 = 0;
}
memcpy((void *)(v15 + v12), dest, n);
s2.m256i_i64[2] = n + v15;
*(_OWORD *)s2.m256i_i8 = *(_OWORD *)src.m256i_i8;
if ( n )
j_free_w(dest, n, 1);
*v6 = 0;
if ( v8 )
j_free_w(v6, v8, 1);
if ( p_n2 != 2 )
sub_611C0(p_Uninitialized, 13, &off_6B170); // "src\\lib.rs"
size = ::size;
if ( (::size & 0x8000000000000000LL) != 0LL )
{
v18 = 0;
goto LABEL_89;
}
src_2 = ::src;
if ( !::size )
{
dest_1 = (_BYTE *)(&dword_0 + 1);
goto LABEL_19;
}
v18 = 1;
dest_3 = sub_243E0(::size, 1u);
if ( !dest_3 )
LABEL_89:
sub_5F836(v18, size, &off_6B1A0); // "C:\\Users\\dr3\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\\lib/rustlib/src/rust\\library/alloc/src/slice.rs"
dest_1 = (unsigned __int8 *)dest_3;
LABEL_19:
memcpy(dest_1, src_2, size);
sub_5FB60(&src, s2.m256i_i64[1], s2.m256i_i64[2]);
if ( src.m256i_i64[2] == s2.m256i_i64[2]
&& (v21 = src.m256i_i64[1], !memcmp((const void *)src.m256i_i64[1], (const void *)s2.m256i_i64[1], src.m256i_u64[2])) )
{
if ( src.m256i_i64[0] )
j_free_w(v21, src.m256i_i64[0], 1);
sub_60B50(&src, &s2);
v24 = 0;
while ( src.m256i_i64[2] != v24 )
{
n8 = *(_BYTE *)(src.m256i_i64[1] + v24++) - 49;
if ( n8 < 8u )
{
n48 = 0;
if ( src.m256i_i64[0] )
j_free_w(src.m256i_i64[1], src.m256i_i64[0], 1);
goto LABEL_24;
}
}
if ( src.m256i_i64[0] )
j_free_w(src.m256i_i64[1], src.m256i_i64[0], 1);
v26 = *(_OWORD *)&s2.m256i_u64[1];
v27 = sub_24410(0x400u, 1u);
if ( !v27 )
sub_5F836(1, 1024, &off_6B050); // "src\\lib.rs"
if ( size )
{
size_3 = 0;
v29 = 0;
n0x400 = 0;
while ( 2 )
{
switch ( dest_1[size_3] )
{
case '+':
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B068);// "src\\lib.rs"
++*(_BYTE *)(v27 + n0x400);
goto LABEL_42;
case ',':
if ( v29 >= *((_QWORD *)&v26 + 1) )
sub_61365(v29, *((_QWORD *)&v26 + 1), &off_6B110);// "src\\lib.rs"
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B128);// "src\\lib.rs"
*(_BYTE *)(v27 + n0x400) = *(_BYTE *)(v26 + v29++);
goto LABEL_42;
case '-':
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B080);// "src\\lib.rs"
--*(_BYTE *)(v27 + n0x400);
goto LABEL_42;
case '.':
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B0F8);// "src\\lib.rs"
n48 = *(unsigned __int8 *)(v27 + n0x400);
j_free_w(v27, 1024, 1);
if ( n48 == 48 )
{
n48 = 0;
goto LABEL_25;
}
if ( n48 == 49 )
{
n48 = 1;
goto LABEL_25;
}
n48 = (unsigned __int8)n48;
break;
case '<':
--n0x400;
goto LABEL_42;
case '>':
++n0x400;
goto LABEL_42;
case '[':
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B098);// "src\\lib.rs"
if ( !*(_BYTE *)(v27 + n0x400) )
{
v31 = 1;
size_2 = size_3;
do
{
v33 = size_2++;
if ( size_2 >= size )
{
size_1 = size_3 + 1;
if ( size > size_1 )
size_1 = size;
sub_61365(size_1, size, &off_6B0B0);// "src\\lib.rs"
}
n93 = dest_1[v33 + 1];
if ( n93 == 91 )
{
++v31;
}
else if ( n93 == 93 )
{
--v31;
}
}
while ( v31 > 0 );
size_3 = size_2;
}
goto LABEL_42;
case ']':
if ( n0x400 >= 0x400 )
sub_61365(n0x400, 1024, &off_6B0C8);// "src\\lib.rs"
if ( *(_BYTE *)(v27 + n0x400) )
{
v35 = 1;
do
{
v36 = size_3--;
if ( size_3 >= size )
sub_61365(size_3, size, &off_6B0E0);// "src\\lib.rs"
n93_1 = dest_1[v36 - 1];
if ( n93_1 == 91 )
{
--v35;
}
else if ( n93_1 == 93 )
{
++v35;
}
}
while ( v35 > 0 );
}
goto LABEL_42;
default:
LABEL_42:
if ( ++size_3 < size )
continue;
goto LABEL_71;
}
break;
}
}
else
{
LABEL_71:
j_free_w(v27, 1024, 1);
n48 = 0;
}
}
else
{
if ( src.m256i_i64[0] )
j_free_w(src.m256i_i64[1], src.m256i_i64[0], 1);
n48 = 0;
}
LABEL_24:
if ( size )
LABEL_25:
j_free_w(dest_1, size, 1);
if ( s2.m256i_i64[0] )
j_free_w(s2.m256i_i64[1], s2.m256i_i64[0], 1);
return n48;
}

发现了一个执行brainfuck代码的虚拟机,其中输出部分将字节转为了数值,查找init_array段发现了一个解密函数:

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
__int64 sub_23820()
{
_BYTE *dest_1; // rax
_BYTE *dest_2; // rbx
__int64 n32030; // r12
__int64 n32030_2; // rax
__int64 p_n32030; // [rsp+8h] [rbp-160h] BYREF
__int128 v6; // [rsp+10h] [rbp-158h]
__int64 n32030_3; // [rsp+20h] [rbp-148h] BYREF
__int128 v8; // [rsp+28h] [rbp-140h]
char dest[258]; // [rsp+3Ch] [rbp-12Ch] BYREF
__int16 v10; // [rsp+13Eh] [rbp-2Ah]

dest_1 = (_BYTE *)sub_243E0(0x7D1EuLL, 1uLL);
if ( !dest_1 )
sub_5F836(1LL, 32030LL, &off_6AEF8);
dest_2 = dest_1;
memcpy(dest_1, src_, 0x7D1EuLL);
sub_24430(dest);
v10 = 0;
for ( n32030 = 0LL; n32030 != 32030; ++n32030 )
dest_2[n32030] ^= sub_245A0(dest, "myrust");
HIBYTE(v10) = 0;
p_n32030 = 32030LL;
*(_QWORD *)&v6 = dest_2;
*((_QWORD *)&v6 + 1) = 32030LL;
if ( p_n2 == 2 )
{
n32030_2 = p_n32030;
goto LABEL_8;
}
sub_24320(&p_n2, &p_n32030);
n32030_2 = p_n32030;
if ( p_n32030 != 0x8000000000000000LL )
{
LABEL_8:
v8 = v6;
n32030_3 = n32030_2;
sub_615A0(&unk_5CB4, 43LL, &n32030_3, &off_6AE98, &off_6B188);
}
return 0LL;
}

存在一个32030字节的密文,密钥为myrust,发现加密方式是RC4,解密后得到了三万个字符的brainfuck,运行发现要输入12个字节,并返回了0,恰好对应12字节空缺的flag,说明这就是这个程序的核心逻辑,编写代码读取brainfuck运行后的内存:

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
def brainfuck_interpreter_with_memory_dump(code, input_string):
memory = [0] * 0x400
pointer = 0
input_index = 0
output = []

i = 0
while i < len(code):
command = code[i]

if command == '>':
pointer += 1
elif command == '<':
pointer -= 1
elif command == '+':
memory[pointer] = (memory[pointer] + 1) % 256
elif command == '-':
memory[pointer] = (memory[pointer] - 1) % 256
elif command == '.':
output.append(memory[pointer])
elif command == ',':
if input_index < len(input_string):
memory[pointer] = ord(input_string[input_index])
input_index += 1
else:
memory[pointer] = 0
elif command == '[':
if memory[pointer] == 0:
loop_count = 1
while loop_count > 0:
i += 1
if code[i] == '[':
loop_count += 1
elif code[i] == ']':
loop_count -= 1
elif command == ']':
if memory[pointer] != 0:
loop_count = 1
while loop_count > 0:
i -= 1
if code[i] == ']':
loop_count += 1
elif code[i] == '[':
loop_count -= 1
i += 1
for idx, value in enumerate(memory):
if idx >= 13 and idx <= 24:
print(f"Encrypted index {hex(idx-13)}: {input_string[(idx-13) % len(input_string)]} {hex(value)}")
return ''.join(chr(c) for c in output)

bf_code = ""
output = brainfuck_interpreter_with_memory_dump(bf_code, "123456789012")
print(f"Output: {output}")

发现内存第13~24字节为密文,28字节为校验值,且加密貌似是一个简单的单字节加密,用以下脚本得到对照表:

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
def brainfuck_interpreter_with_memory_dump(code, input_string):
memory = [0] * 0x400
pointer = 0
input_index = 0
output = []

i = 0
while i < len(code):
command = code[i]

if command == '>':
pointer += 1
elif command == '<':
pointer -= 1
elif command == '+':
memory[pointer] = (memory[pointer] + 1) % 256
elif command == '-':
memory[pointer] = (memory[pointer] - 1) % 256
elif command == '.':
output.append(memory[pointer])
elif command == ',':
if input_index < len(input_string):
memory[pointer] = ord(input_string[input_index])
input_index += 1
else:
memory[pointer] = 0
elif command == '[':
if memory[pointer] == 0:
loop_count = 1
while loop_count > 0:
i += 1
if code[i] == '[':
loop_count += 1
elif code[i] == ']':
loop_count -= 1
elif command == ']':
if memory[pointer] != 0:
loop_count = 1
while loop_count > 0:
i -= 1
if code[i] == ']':
loop_count += 1
elif code[i] == '[':
loop_count -= 1
i += 1

return memory, ''.join(chr(c) for c in output)

def build_encryption_mapping(bf_code):
mapping = {}
for byte in range(0x00, 0x100):
input_string = chr(byte)
memory, _ = brainfuck_interpreter_with_memory_dump(bf_code, input_string)
encrypted_byte = memory[13]
mapping[byte] = encrypted_byte

print(f"Input: {hex(byte)} -> Encrypted: {hex(encrypted_byte)}")

return mapping

bf_code = ""

encryption_mapping = build_encryption_mapping(bf_code)

print("\nEncryption Mapping:")
for original, encrypted in encryption_mapping.items():
print(f"Original: {hex(original)} -> Encrypted: {hex(encrypted)}")

发现加密只有将每个字节的值加上0x9F,接下来要寻找密文,运行过程中监测输出的“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
53
54
55
56
57
def brainfuck_interpreter_with_memory_dump(code, input_string):
memory = [0] * 0x400
pointer = 0
input_index = 0
output = []
last_cell_28_value = memory[28]

i = 0
while i < len(code):
command = code[i]
if command == '>':
pointer += 1
elif command == '<':
pointer -= 1
elif command == '+':
memory[pointer] = (memory[pointer] + 1) % 256
elif command == '-':
memory[pointer] = (memory[pointer] - 1) % 256
elif command == '.':
output.append(memory[pointer])
elif command == ',':
if input_index < len(input_string):
memory[pointer] = ord(input_string[input_index])
input_index += 1
else:
memory[pointer] = 0
elif command == '[':
if memory[pointer] == 0:
loop_count = 1
while loop_count > 0:
i += 1
if code[i] == '[':
loop_count += 1
elif code[i] == ']':
loop_count -= 1
elif command == ']':
if memory[pointer] != 0:
loop_count = 1
while loop_count > 0:
i -= 1
if code[i] == ']':
loop_count += 1
elif code[i] == '[':
loop_count -= 1
if pointer == 28:
current_value = memory[28]
if current_value != last_cell_28_value:
print(f"{i}th command {command} {hex(last_cell_28_value)} -> {hex(current_value)}, {memory[28:29]}")
last_cell_28_value = current_value
i += 1

return memory, ''.join(chr(c) for c in output)

bf_code = ""

input_string = "aaaaaaaaaaaa"
memory, output = brainfuck_interpreter_with_memory_dump(bf_code, input_string)

发现是在最后一部分从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
<[-] 
>[-]
<<
[>+>+<<-]
>>[<<+>>-]
[-] ; 清空当前单元
++++++++++++ ; 设置为 12
><< ; 移动指针到某位
[->-<] ; 减去某值(可能用于判断)
+>
[<->[-]] ; 如果结果不为零,清空另一个单元

[-]+< ; 清空并设置标志位
[
>-> ; 某种条件判断
[-]>[-] ; 清空某些单元
<>+++++++ ; 加载一个值7
[<+++++++>-] ; 7 * 7 = 49(“1”)
<. ; 输出1
<<[-] ; 清空之前的单元
]
> ; 跳转到 else 分支
[
>[-]>[-] ; 清空某些单元
<>++++++ ; 加载一个值6
[<++++++++>-] ; 6 * 8 = 48(“0”)
<. ; 输出0
<- ; 减去1
]
<<<<<<<<<<<<<<<<<<<<<<<<<<< ; 指针左移回到起点

注意到这里正是最后输出0或1的关键逻辑,某个位置减去某值之后判断是否为0(即这个位置是否等于这个值),如果不等于就跳转到0(错误),否则跳转到1(正确),由于字符串的长度是12,合理猜测这里要和12进行比较的是经过某种校验后“正确的”字符个数,监测”[->-<]“这个结构体,在其运行时打印执行流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
位置3586进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 0, 37, 253, 0, 0]
位置5981进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 1, 0, 107, 199, 0, 0]
位置8672进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 2, 0, 31, 248, 0, 0]
位置10278进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 3, 0, 116, 147, 0, 0]
位置12678进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 4, 0, 157, 158, 0, 0]
位置14280进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 5, 0, 213, 102, 0, 0]
位置17231进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 6, 0, 184, 192, 0, 0]
位置19636进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 7, 0, 45, 169, 0, 0]
位置22317进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 8, 0, 134, 255, 0, 0]
位置25274进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 9, 0, 87, 243, 0, 0]
位置27972进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 10, 0, 205, 220, 0, 0]
位置30935进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 11, 0, 196, 229, 0, 0]
位置31904进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 12, 0, 0, 0, 0]
i = 41, res: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 12, 0, 0, 0, 0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
位置3586进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 0, 0, 40, 253, 0, 0]
位置5981进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 1, 0, 110, 199, 0, 0]
位置8672进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 2, 0, 33, 248, 0, 0]
位置10278进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 3, 0, 116, 147, 0, 0]
位置12678进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 4, 0, 158, 158, 0, 0]
位置14280进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 5, 0, 213, 102, 0, 0]
位置17231进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 6, 0, 185, 192, 0, 0]
位置19636进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 7, 0, 48, 169, 0, 0]
位置22317进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 8, 0, 137, 255, 0, 0]
位置25274进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 9, 0, 88, 243, 0, 0]
位置27972进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 10, 0, 205, 220, 0, 0]
位置30935进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 0, 11, 0, 199, 229, 0, 0]
位置31904进入 [->-<] 结构体,当前内存: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 1, 1, 12, 0, 0, 0, 0]
i = 42, res: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 201, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 1, 1, 12, 0, 0, 0, 0]

发现当右侧两者比较相等时会在左侧的对应内存索引上+1,而右边的数字是定值,这说明该数组即为密文:[253,199,248,147,158,102,192,169,255,243,220,229],每个字节在加上了0x9F后经过了某种加密得到了该数组,且该加密几乎是全局相关的,无法单字节爆破,经过仔细观察后发现貌似是某种矩阵,用python验证:

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
def trybf(code, input_string):
memory = [0] * 0x400
pointer = 0
input_index = 0
result = []
i = 0
while i < len(code):
command = code[i]
if command == '[' and i + 5 < len(code):
if code[i+1:i+6] == "->-<]":
# if i == 31904 and code[i+1:i+6] == "->-<]":
# print(f"位置{i}进入 [->-<] 结构体,当前内存: {memory[:32]}")
result.append(memory[28])
if command == '>':
pointer += 1
elif command == '<':
pointer -= 1
elif command == '+':
memory[pointer] = (memory[pointer] + 1) % 256
elif command == '-':
memory[pointer] = (memory[pointer] - 1) % 256
elif command == '.':
# output.append(chr(memory[pointer]))
pass
elif command == ',':
if input_index < len(input_string):
memory[pointer] = input_string[input_index]
input_index += 1
else:
memory[pointer] = 0
elif command == '[':
if memory[pointer] == 0:
loop_count = 1
while loop_count > 0 and i < len(code) - 1:
i += 1
if code[i] == '[':
loop_count += 1
elif code[i] == ']':
loop_count -= 1
elif command == ']':
if memory[pointer] != 0:
loop_count = 1
while loop_count > 0 and i > 0:
i -= 1
if code[i] == ']':
loop_count += 1
elif code[i] == '[':
loop_count -= 1

i += 1

return result

enc = bytearray([253,199,248,147,158,102,192,169,255,243,220,229])

matrix = []
for t in range(12):
reses = []
for i in range(2):
input_string = bytearray(b"\x00"*t) + bytearray([i]) + bytearray(b"\x00"*(10-t))
reses.append(trybf(bf_code, input_string))

print(reses[-1:])

for k in range(len(reses)-1):
for j in range(12):
print((reses[k+1][j] - reses[k][j]) & 0xFF,end = " ")
print()

matrix.append([reses[1][l] - reses[0][l] for l in range(12)])

print(matrix)

得到矩阵系数:

1
2
3
4
5
6
7
8
9
10
11
12
[[3, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 3], 
[3, 2, 0, 0, 2, 3, 1, 2, 3, 1, 3, 3],
[3, 2, 3, 2, 2, 0, 3, 2, 3, 0, 2, 0],
[3, 2, 0, 0, 1, 0, 0, 0, 3, 2, 0, 3],
[1, 2, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1],
[1, 0, 3, 0, 2, 3, 1, 1, 2, 2, 1, 2],
[0, 2, 3, 3, 0, 2, 2, 0, 2, 2, 1, 3],
[3, 1, 1, 2, 0, 0, 2, 0, 0, 2, 2, 2],
[0, 2, 1, 3, 1, 0, 2, 2, 0, 2, 3, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 3, 1],
[2, 0, 3, 2, 2, 2, 1, 1, 0, 3, 3, 2],
[3, 0, 1, 0, 1, 1, 2, 1, 2, 1, 1, 3]]

接下来恢复加密逻辑:

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
matr = [[3, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 3], 
[3, 2, 0, 0, 2, 3, 1, 2, 3, 1, 3, 3],
[3, 2, 3, 2, 2, 0, 3, 2, 3, 0, 2, 0],
[3, 2, 0, 0, 1, 0, 0, 0, 3, 2, 0, 3],
[1, 2, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1],
[1, 0, 3, 0, 2, 3, 1, 1, 2, 2, 1, 2],
[0, 2, 3, 3, 0, 2, 2, 0, 2, 2, 1, 3],
[3, 1, 1, 2, 0, 0, 2, 0, 0, 2, 2, 2],
[0, 2, 1, 3, 1, 0, 2, 2, 0, 2, 3, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 3, 1],
[2, 0, 3, 2, 2, 2, 1, 1, 0, 3, 3, 2],
[3, 0, 1, 0, 1, 1, 2, 1, 2, 1, 1, 3]]

m = bytearray(b"\x00"*12)
for i in range(12):
m[i] = (m[i] - 0x61) & 0xFF

res = [0]*12

for i in range(12):
for j in range(12):
res[j] += m[i] * matr[i][j]

for k in range(12):
res[k] &= 0xFF

print(res)

enc = [253,199,248,147,158,102,192,169,255,243,220,229]
assert res == enc

z3求解即可:

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
from z3 import *

matr = [
[3, 3, 2, 0, 1, 0, 1, 3, 3, 1, 0, 3],
[3, 2, 0, 0, 2, 3, 1, 2, 3, 1, 3, 3],
[3, 2, 3, 2, 2, 0, 3, 2, 3, 0, 2, 0],
[3, 2, 0, 0, 1, 0, 0, 0, 3, 2, 0, 3],
[1, 2, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1],
[1, 0, 3, 0, 2, 3, 1, 1, 2, 2, 1, 2],
[0, 2, 3, 3, 0, 2, 2, 0, 2, 2, 1, 3],
[3, 1, 1, 2, 0, 0, 2, 0, 0, 2, 2, 2],
[0, 2, 1, 3, 1, 0, 2, 2, 0, 2, 3, 0],
[0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 3, 1],
[2, 0, 3, 2, 2, 2, 1, 1, 0, 3, 3, 2],
[3, 0, 1, 0, 1, 1, 2, 1, 2, 1, 1, 3]
]

enc = [253, 199, 248, 147, 158, 102, 192, 169, 255, 243, 220, 229]

solver = Solver()

m = [BitVec(f'm{i}', 8) for i in range(12)]

res = [0] * 12
for j in range(12):
res[j] = Sum([m[i] * matr[i][j] for i in range(12)]) & 0xFF

for j in range(12):
solver.add(res[j] == enc[j])

if solver.check() == sat:
model = solver.model()
m_values = [model.evaluate(m[i]).as_long() for i in range(12)]
chars = [(v + 0x61) & 0xFF for v in m_values]
result_str = ''.join(chr(c) for c in chars)

print("找到解:", m_values)
print("对应的字符串:", result_str)
else:
print("无解")

找到解: [5, 0, 21, 24, 23, 22, 4, 10, 15, 15, 14, 0]

对应的字符串: favyxwekppoa

flag即为miniLCTF{favyxwekppoa}.