TGCTF 2025 WriteUp

12k 词

个人感觉reverse题目里面解数最少的index反而不是最阴间的,起码没有conforand阴间

在这里仅给出我个人解出的题目的WriteUp.

Crypto方向:

费克特尔:

直接上factordb分解n即可

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *
c=670610235999012099846283721569059674725712804950807955010725968103642359765806
n=810544624661213367964996895060815354972889892659483948276203088055391907479553
e=65537

n = 113 * 18251 * 2001511 * 214168842768662180574654641 * 916848439436544911290378588839845528581
phin = 112 * 18250 * 2001510 * 214168842768662180574654640 * 916848439436544911290378588839845528580
d = inverse(e, phin)
m = pow(c, d, n)
print(long_to_bytes(m).decode())

TGCTF{f4888_6abdc_9c2bd_9036bb}.

宝宝rsa:

前半段爆破e,后半段直接开方

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
import gmpy2
from Crypto.Util.number import *
from sympy import nextprime

p1 = 8362851990079664018649774360159786938757293294328116561219351503022492961843907118845919317399785168488103775809531198339213009936918460080250107807031483
q1 = 8312546034426788223492083178829355192676175323324230533451989649056072814335528263136523605276378801682321623998646291206494179416941978672637426346496531
c1 = 39711973075443303473292859404026809299317446021917391206568511014894789946819103680496756934914058521250438186214943037578346772475409633145435232816799913236259074769958139045997486622505579239448395807857034154142067866860431132262060279168752474990452298895511880964765819538256786616223902867436130100322
n1 = p1 * q1
phin1 = (p1 - 1) * (q1 - 1)
e = 65537
while e <= 2 ** 19:
try:
d1 = inverse(e, phin1)
m1 = pow(c1, d1, n1)
flag1 = long_to_bytes(m1)
if flag1[:6] == b'TGCTF{':
break
print(e, flag1[:6])
except:
pass
e = nextprime(e)
print(flag1.decode(),end="")
c2 = 51380982170049779703682835988073709896409264083198805522051459033730166821511419536113492522308604225188048202917930917221
m2, tag = gmpy2.iroot(c2,3)
print(long_to_bytes(m2).decode())

TGCTF{!!3xP_Is_Sm@ll_But_D@ng3r0}.

AAAAAAAA·真·签到:

注意到是变位凯撒,偏移量为-1,0,1…且仅对大写字母生效,编写脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def decrypt_flag(encrypted_flag):
decrypted_flag = []
offset = -1
for char in encrypted_flag:
if 'A' <= char <= 'Z':
new_char = chr(((ord(char) - 65 + offset) % 26) + 65)
decrypted_flag.append(new_char)
else:
decrypted_flag.append(char)
offset += 1
return ''.join(decrypted_flag)
encrypted_flag = "UGBRC{RI0G!O04_5C3_OVUI_DV_MNTB}"
decrypted_flag = decrypt_flag(encrypted_flag)
print("Decrypted Flag:", decrypted_flag)

TGCTF{WO0O!Y04_5R3_GOOD_AT_MOVE}.

mm不躲猫猫:

多组nc:

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
from gmpy2 import *
from Crypto.Util.number import *

def extract(file_path):
n_list = []
c_list = []
with open(file_path, 'r') as file:
for line in file:
line = line.strip()
if line.startswith("n ="):
n_value = line.split("=")[1].strip()
n_list.append(int(n_value))
elif line.startswith("c ="):
c_value = line.split("=")[1].strip()
c_list.append(int(c_value))
return n_list, c_list

file_path = "task-mm.txt"
n, c = extract(file_path)

e = 65537

for i in range(len(n)):
for j in range(len(n)):
if(i != j):
if(gcd(n[i], n[j]) != 1):
p = gcd(n[i], n[j])
q = n[i] // p
phin = (p-1)*(q-1)
d = inverse(e, phin)
m = pow(c[i], d, n[i])
print(long_to_bytes(m))

TGCTF{ExcePt10n4lY0u_Fl4gF0rY0u_555b0nus}.

Misc:

next is the end:

编写脚本提取嵌套文件夹中的文件:

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
import os
import zipfile

def add_long_path_prefix(path):
if not path.startswith("\\\\?\\"):
return "\\\\?\\" + os.path.abspath(path)
return path

def extract_all_files_flat(zip_path, output_dir):
zip_path = add_long_path_prefix(zip_path)
output_dir = add_long_path_prefix(output_dir)
os.makedirs(output_dir, exist_ok=True)

with zipfile.ZipFile(zip_path, 'r') as zip_ref:
for member in zip_ref.infolist():
if member.is_dir():
continue
file_name = os.path.basename(member.filename)

if not file_name:
continue
output_path = os.path.join(output_dir, file_name)
counter = 1
while os.path.exists(output_path):
name, ext = os.path.splitext(file_name)
output_path = os.path.join(output_dir, f"{name}_{counter}{ext}")
counter += 1
with zip_ref.open(member) as source, open(output_path, 'wb') as target:
target.write(source.read())

print(f"提取文件: {output_path}")

def main():
zip_file = "next_is_the_end.zip"
output_directory = "misc-next/"

if not os.path.exists(zip_file):
print("压缩包路径不存在,请检查后重试。")
return

if not os.path.exists(output_directory):
os.makedirs(output_directory, exist_ok=True)

print("开始解压...")
extract_all_files_flat(zip_file, output_directory)
print("解压完成!所有文件已提取到:", output_directory)

if __name__ == "__main__":
main()

flag{so_great!}.

你的运气是好是坏?:

无脑猜114514,居然一下子就猜对了

但是我想问问出题人,这样没活烂整真的好么?不知道114514这个梗的就要被枪毙么?我也不是没在群里见过有人死活猜不到的,希望以后不要这么出了,没必要拿一个没意义的大粪来凑题目数量。

这是啥o_o:

发现是一个GIF,提取帧间隔得到:[840, 710, 670, 840, 700, 1230, 890, 1110, 1170, 950, 990, 970, 1170, 1030, 1040, 1160, 950, 1170, 1120, 950, 1190, 1050, 1160, 1040, 950, 1160, 1050, 1090, 1010, 330, 1250],除以10得到flag:TGCTF{You_caught_up_with_time!}.

你能发现图中的秘密吗?:

查看图片,发现没有额外数据块,考虑隐写,用zsteg得到:

1
2
3
imagedata           .. text: "{|{@C@M@L"
b1,r,lsb,xy .. text: "key=i_1ove_y0u"
b4,bgr,msb,xy .. file: MPEG ADTS, layer I, v2, Monaural

打开压缩包,发现一个pdf和一个png,其中pdf用ps打开,发现有一个隐藏图层,得到flag2:flag2=_attentive_and_conscientious},png文件最后有一个比别的IDAT块都要大的数据块,单独提取得到一个图片,发现其宽度是375的整数倍时好像有模糊不清的英文,爆破遍历宽高发现为1125时是正确的,flag1:flag{you_are_so,拼合得到flag{you_are_so_attentive_and_conscientious}.

Reverse方向:

Base64:

查看base64函数:

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
_BYTE *__fastcall base64(__int64 a1)
{
v2 = -1i64;
strcpy(v30, "GLp/+Wn7uqX8FQ2JDR1c0M6U53sjBwyxglmrCVdSThAfEOvPHaYZNzo4ktK9iebI");
do
++v2;
while ( *(_BYTE *)(a1 + v2) );
v3 = 0i64;
v4 = (int)v2 / 3;
if ( (_DWORD)v2 == 3 * ((int)v2 / 3) )
{
v5 = 0;
v6 = 4 * v4;
}
else if ( (int)v2 % 3 == 1 )
{
v5 = 1;
v6 = 4 * v4 + 4;
}
else if ( (int)v2 % 3 == 2 )
{
v5 = 2;
v6 = 4 * v4 + 4;
}
else
{
v5 = v31;
v6 = v31;
}
v7 = v6;
v8 = v6 + 1i64;
if ( v6 == -1i64 )
v8 = -1i64;
v9 = malloc(v8);
if ( (int)v2 - v5 > 0 )
{
v10 = a1 + 2;
v11 = ((int)v2 - v5 - 1i64) / 3ui64 + 1;
do
{
v3 += 4i64;
v12 = *(unsigned __int8 *)(v10 - 2) >> 2;
v10 += 3i64;
v13 = v12 + 24;
v14 = v13 - 64;
if ( v13 <= 0x40 )
v14 = v13;
v9[v3 - 4] = v30[v14];
v15 = ((*(unsigned __int8 *)(v10 - 4) >> 4) | (16 * (*(_BYTE *)(v10 - 5) & 3))) - 40;
if ( ((*(unsigned __int8 *)(v10 - 4) >> 4) | (16 * (*(_BYTE *)(v10 - 5) & 3u))) + 0x18 <= 0x40 )
v15 = ((*(unsigned __int8 *)(v10 - 4) >> 4) | (16 * (*(_BYTE *)(v10 - 5) & 3))) + 0x18;
v9[v3 - 3] = v30[v15];
v16 = ((*(unsigned __int8 *)(v10 - 3) >> 6) | (4 * (*(_BYTE *)(v10 - 4) & 0xF))) - 40;
if ( ((*(unsigned __int8 *)(v10 - 3) >> 6) | (4 * (*(_BYTE *)(v10 - 4) & 0xFu))) + 0x18 <= 0x40 )
v16 = ((*(unsigned __int8 *)(v10 - 3) >> 6) | (4 * (*(_BYTE *)(v10 - 4) & 0xF))) + 0x18;
v9[v3 - 2] = v30[v16];
v17 = (*(_BYTE *)(v10 - 3) & 0x3F) - 40;
if ( (*(_BYTE *)(v10 - 3) & 0x3Fu) + 24 <= 0x40 )
v17 = (*(_BYTE *)(v10 - 3) & 0x3F) + 24;
v9[v3 - 1] = v30[v17];
--v11;
}
while ( v11 );
}
v18 = v5 - 1;
if ( !v18 )
{
v25 = (*(unsigned __int8 *)((int)v2 + a1 - 1) >> 2) - 40;
if ( (*(unsigned __int8 *)((int)v2 + a1 - 1) >> 2) + 24 <= 0x40u )
v25 = (*(unsigned __int8 *)((int)v2 + a1 - 1) >> 2) + 24;
v9[v7 - 4] = v30[v25];
v26 = *(_BYTE *)((int)v2 + a1 - 1) & 3;
*(_WORD *)&v9[v7 - 2] = 0x3D3D;
v27 = 16 * v26 + 24;
v28 = v27 - 64;
if ( v27 <= 0x40 )
v28 = v27;
v9[v7 - 3] = v30[v28];
goto LABEL_37;
}
if ( v18 != 1 )
{
LABEL_37:
v9[v7] = 0;
return v9;
}
v19 = a1 + (int)v2;
v20 = (*(unsigned __int8 *)(v19 - 2) >> 2) - 40;
if ( (*(unsigned __int8 *)(v19 - 2) >> 2) + 24 <= 0x40u )
v20 = (*(unsigned __int8 *)(v19 - 2) >> 2) + 24;
v9[v7 - 4] = v30[v20];
v21 = ((*(unsigned __int8 *)(v19 - 1) >> 4) | (16 * (*(_BYTE *)(v19 - 2) & 3))) - 40;
if ( ((*(unsigned __int8 *)(v19 - 1) >> 4) | (16 * (*(_BYTE *)(v19 - 2) & 3u))) + 24 <= 0x40 )
v21 = ((*(unsigned __int8 *)(v19 - 1) >> 4) | (16 * (*(_BYTE *)(v19 - 2) & 3))) + 24;
v9[v7 - 3] = v30[v21];
v22 = *(_BYTE *)(v19 - 1) & 0xF;
*(_WORD *)&v9[v7 - 1] = 61;
v23 = 4 * v22 + 24;
v24 = 4 * v22 - 40;
if ( v23 <= 0x40 )
v24 = v23;
v9[v7 - 2] = v30[v24];
return v9;
}

换表base64,又多了一些额外的加减逻辑,编写脚本解密:

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
import base64

custom_base64_table = "GLp/+Wn7uqX8FQ2JDR1c0M6U53sjBwyxglmrCVdSThAfEOvPHaYZNzo4ktK9iebI"
reverse_table = {char: index for index, char in enumerate(custom_base64_table)}

def reverse_transform(value):
if value - 24 >= 0:
return value - 24
elif value + 40 <= 0x40:
return value + 40
else:
return value + 64

def custom_base64_decode(encoded_str):
six_bit_blocks = []
for char in encoded_str:
if char == '=':
continue
mapped_index = reverse_table[char]
original_index = reverse_transform(mapped_index)
six_bit_blocks.append(original_index)

decoded_bytes = bytearray()
buffer = 0
bits_left = 0
for block in six_bit_blocks:
buffer = (buffer << 6) | block
bits_left += 6
if bits_left >= 8:
decoded_bytes.append((buffer >> (bits_left - 8)) & 0xFF)
bits_left -= 8

return bytes(decoded_bytes)

encoded_str = "AwLdOEVEhIWtajB2CbCWCbTRVsFFC8hirfiXC9gWH9HQayCJVbB8CIF="
decoded_data = custom_base64_decode(encoded_str)
print(decoded_data)

HZNUCTF{ad162c-2d94-434d-9222-b65dc76a32}.

水果忍者:

是unity引擎游戏,用dnspy打开assembly-csharp.dll,在gamemanager找到:

1
2
3
4
5
6
7
8
// Token: 0x04000015 RID: 21
private static readonly string encryptionKey = "HZNUHZNUHZNUHZNU";

// Token: 0x04000016 RID: 22
private static readonly string iv = "0202005503081501";

// Token: 0x04000017 RID: 23
private static readonly string encryptedHexData = "cecadff28e93aa5d6f65128ae33e734d3f47b4b8a050d326c534a732d51b96e2a6a80dca0d5a704a216c2e0c3cc6aaaf";

AES解密得到HZNUCTF{de20-70dd-4e62-b8d0-06e}.

XTEA:

查看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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
__int64 i; // rcx
int v5; // eax
int v6; // edi
char v8[32]; // [rsp+0h] [rbp-20h] BYREF
char v9; // [rsp+20h] [rbp+0h] BYREF
int v10[9]; // [rsp+24h] [rbp+4h] BYREF
void *Block; // [rsp+48h] [rbp+28h]
void *v12; // [rsp+68h] [rbp+48h]
int v13[15]; // [rsp+88h] [rbp+68h] BYREF
int j; // [rsp+C4h] [rbp+A4h]
int k; // [rsp+E4h] [rbp+C4h]
int m; // [rsp+104h] [rbp+E4h]
int v17; // [rsp+1D4h] [rbp+1B4h]

v3 = &v9;
for ( i = 66i64; i; --i )
{
*(_DWORD *)v3 = 0xCCCCCCCC;
v3 += 4;
}
sub_1400113A2((__int64)&unk_1400220A7);
srand(0x7E8u);
checkdebugger(); //如果有,设置为0x7E9
printf("Welcome to HZNUCTF!!!\n");
printf("Plz input the cipher:\n");
v10[0] = 0;
if ( (unsigned int)scanf("%d", v10) == 1 ) //输入的cipher作为delta
{
printf("Plz input the flag:\n");
Block = malloc(0x21ui64);
v12 = malloc(0x10ui64);
if ( (unsigned int)scanf("%s", (const char *)Block) == 1 ) //输入flag
{
for ( j = 0; j < 32; j += 4 )
{
v17 = *((char *)Block + j + 3) | (*((char *)Block + j + 2) << 8) | (*((char *)Block + j + 1) << 16) | (*((char *)Block + j) << 24); //大端序
v13[j / 4] = v17;
}
keygen((__int64)v12); //密钥是随机数
for ( k = 0; k < 7; ++k ) // 循环加密
XTEA((unsigned int)v10[0], &v13[k], &v13[k + 1], v12);
for ( m = 0; m < 8; ++m )
{
if ( v13[m] != cipher[m] )
{
printf("wrong_wrong!!!");
exit(1);
}
}
printf("Congratulation!!!");
free(Block);
free(v12);
v5 = 0;
}
else
{
printf("Invalid input.\n");
free(Block);
free(v12);
v5 = 1;
}
}
else
{
printf("Invalid input.\n");
v5 = 1;
}
v6 = v5;
sub_14001133E(v8, &unk_14001AD40);
return v6;
}

发现有一个原版大端序XTEA加密,其中delta要自己输入,cipher根据rand生成,动态调试绕过debugger得到密钥[6648,4542,2449,13336],猜测delta还是0x9E3779B9,编写脚本解密:

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
def encrypt(dwordenc, dwordkey, delta):
rounds = 32
for j in range(0, len(dwordenc) - 1):
m1 = dwordenc[j]
m2 = dwordenc[j + 1]
ttl = 0
for _ in range(rounds):
m1 = (m1 + ((dwordkey[ttl & 3] + ttl) ^ (m2 + ((m2 >> 5) ^ (m2 << 4))))) & 0xFFFFFFFF
ttl = (ttl - delta) & 0xFFFFFFFF
m2 = (m2 + ((dwordkey[(ttl >> 11) & 3] + ttl) ^ (m1 + ((m1 >> 5) ^ (m1 << 4))))) & 0xFFFFFFFF
# print(hex(m1), hex(m2))
dwordenc[j] = m1
dwordenc[j + 1] = m2
return dwordenc

def decrypt(dwordenc, dwordkey, delta):
rounds = 32
for j in range(len(dwordenc) - 2, -1, -1):
m1 = dwordenc[j]
m2 = dwordenc[j + 1]
ttl = (-delta * rounds) & 0xFFFFFFFF
for _ in range(rounds):
m2 = (m2 - ((dwordkey[(ttl >> 11) & 3] + ttl) ^ (m1 + ((m1 >> 5) ^ (m1 << 4))))) & 0xFFFFFFFF
ttl = (ttl + delta) & 0xFFFFFFFF
m1 = (m1 - ((dwordkey[ttl & 3] + ttl) ^ (m2 + ((m2 >> 5) ^ (m2 << 4))))) & 0xFFFFFFFF
dwordenc[j] = m1
dwordenc[j + 1] = m2
return dwordenc

# 测试加密
key = [0x19F8, 0x11BE, 0x991, 0x3418]
m = [0x31323334, 0x35363738, 0x31323334, 0x35363738]
delta = 12345678
n = encrypt(m, key, delta)
for i in range(len(n)):
print(hex(n[i]),end=", ")
print()
c = decrypt(n, key, delta)
for i in range(len(c)):
print(hex(c[i]),end=", ")
print()

# 解密
enc = [0x8CCB2324, 0x09A7741A, 0xFB3C678D, 0xF6083A79,
0xF1CC241B, 0x39FA59F2, 0xF2ABE1CC, 0x17189F72]
delta = 0x9E3779B9
flag = decrypt(enc, key, delta)
for i in range(len(flag)):
print(hex(flag[i]),end=", ")
print()

输出:0x485a4e55, 0x4354467b, 0x6165362d, 0x39663537, 0x2d346237, 0x342d6234, 0x32332d39, 0x3865627d

HZNUCTF{ae6-9f57-4b74-b423-98eb}.

蛇年的本命语言:

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
from collections import Counter
print('Welcome to HZNUCTF!!!')
print('Plz input the flag:')
inp = input()
cinp = Counter(inp)
o1 = ''.join((lambda .0: for oidx in .0:
str(cinp[oidx]))(inp))
print('ans1: ', '', **('end',))
print(o1)
if o1 != '111111116257645365477364777645752361':
print('wrong_wrong!!!')
exit(1)
i1 = ''
for oidx in inp:
if cinp[oidx] > 0:
i1 += oidx + str(cinp[oidx])
cinp[oidx] = 0
i2 = (lambda .0: [ord(oidx) for oidx in .0])(i1)
im = [
7 * i2[0] == 504,
9 * i2[0] - 5 * i2[1] == 403,
(2 * i2[0] - 5 * i2[1]) + 10 * i2[2] == 799,
3 * i2[0] + 8 * i2[1] + 15 * i2[2] + 20 * i2[3] == 2938,
(5 * i2[0] + 15 * i2[1] + 20 * i2[2] - 19 * i2[3]) + 1 * i2[4] == 2042,
(7 * i2[0] + 1 * i2[1] + 9 * i2[2] - 11 * i2[3]) + 2 * i2[4] + 5 * i2[5] == 1225,
11 * i2[0] + 22 * i2[1] + 33 * i2[2] + 44 * i2[3] + 55 * i2[4] + 66 * i2[5] - 77 * i2[6] == 7975,
((21 * i2[0] + 23 * i2[1] + 3 * i2[2] + 24 * i2[3] - 55 * i2[4]) + 6 * i2[5] - 7 * i2[6]) + 15 * i2[7] == 229,
(2 * i2[0] + 26 * i2[1] + 13 * i2[2] + 0 * i2[3] - 65 * i2[4]) + 15 * i2[5] + 29 * i2[6] + 1 * i2[7] + 20 * i2[8] == 2107,
(10 * i2[0] + 7 * i2[1] + -9 * i2[2] + 6 * i2[3] + 7 * i2[4] + 1 * i2[5] + 22 * i2[6] + 21 * i2[7] - 22 * i2[8]) + 30 * i2[9] == 4037,
(15 * i2[0] + 59 * i2[1] + 56 * i2[2] + 66 * i2[3] + 7 * i2[4] + 1 * i2[5] - 122 * i2[6]) + 21 * i2[7] + 32 * i2[8] + 3 * i2[9] - 10 * i2[10] == 4950,
(((13 * i2[0] + 66 * i2[1] + 29 * i2[2] + 39 * i2[3] - 33 * i2[4]) + 13 * i2[5] - 2 * i2[6]) + 42 * i2[7] + 62 * i2[8] + 1 * i2[9] - 10 * i2[10]) + 11 * i2[11] == 12544,
(((23 * i2[0] + 6 * i2[1] + 29 * i2[2] + 3 * i2[3] - 3 * i2[4]) + 63 * i2[5] - 25 * i2[6]) + 2 * i2[7] + 32 * i2[8] + 1 * i2[9] - 10 * i2[10]) + 11 * i2[11] - 12 * i2[12] == 6585,
((((223 * i2[0] + 6 * i2[1] - 29 * i2[2] - 53 * i2[3] - 3 * i2[4]) + 3 * i2[5] - 65 * i2[6]) + 0 * i2[7] + 36 * i2[8] + 1 * i2[9] - 15 * i2[10]) + 16 * i2[11] - 18 * i2[12]) + 13 * i2[13] == 6893,
((((29 * i2[0] + 13 * i2[1] - 9 * i2[2] - 93 * i2[3]) + 33 * i2[4] + 6 * i2[5] + 65 * i2[6] + 1 * i2[7] - 36 * i2[8]) + 0 * i2[9] - 16 * i2[10]) + 96 * i2[11] - 68 * i2[12]) + 33 * i2[13] - 14 * i2[14] == 1883,
(((69 * i2[0] + 77 * i2[1] - 93 * i2[2] - 12 * i2[3]) + 0 * i2[4] + 0 * i2[5] + 1 * i2[6] + 16 * i2[7] + 36 * i2[8] + 6 * i2[9] + 19 * i2[10] + 66 * i2[11] - 8 * i2[12]) + 38 * i2[13] - 16 * i2[14]) + 15 * i2[15] == 8257,
((((23 * i2[0] + 2 * i2[1] - 3 * i2[2] - 11 * i2[3]) + 12 * i2[4] + 24 * i2[5] + 1 * i2[6] + 6 * i2[7] + 14 * i2[8] - 0 * i2[9]) + 1 * i2[10] + 68 * i2[11] - 18 * i2[12]) + 68 * i2[13] - 26 * i2[14]) + 15 * i2[15] - 16 * i2[16] == 5847,
(((((24 * i2[0] + 0 * i2[1] - 1 * i2[2] - 15 * i2[3]) + 13 * i2[4] + 4 * i2[5] + 16 * i2[6] + 67 * i2[7] + 146 * i2[8] - 50 * i2[9]) + 16 * i2[10] + 6 * i2[11] - 1 * i2[12]) + 69 * i2[13] - 27 * i2[14]) + 45 * i2[15] - 6 * i2[16]) + 17 * i2[17] == 18257,
((((25 * i2[0] + 26 * i2[1] - 89 * i2[2]) + 16 * i2[3] + 19 * i2[4] + 44 * i2[5] + 36 * i2[6] + 66 * i2[7] - 150 * i2[8] - 250 * i2[9]) + 166 * i2[10] + 126 * i2[11] - 11 * i2[12]) + 690 * i2[13] - 207 * i2[14]) + 46 * i2[15] + 6 * i2[16] + 7 * i2[17] - 18 * i2[18] == 12591,
(((((5 * i2[0] + 26 * i2[1] + 8 * i2[2] + 160 * i2[3] + 9 * i2[4] - 4 * i2[5]) + 36 * i2[6] + 6 * i2[7] - 15 * i2[8] - 20 * i2[9]) + 66 * i2[10] + 16 * i2[11] - 1 * i2[12]) + 690 * i2[13] - 20 * i2[14]) + 46 * i2[15] + 6 * i2[16] + 7 * i2[17] - 18 * i2[18]) + 19 * i2[19] == 52041,
((((((29 * i2[0] - 26 * i2[1]) + 0 * i2[2] + 60 * i2[3] + 90 * i2[4] - 4 * i2[5]) + 6 * i2[6] + 6 * i2[7] - 16 * i2[8] - 21 * i2[9]) + 69 * i2[10] + 6 * i2[11] - 12 * i2[12]) + 69 * i2[13] - 20 * i2[14] - 46 * i2[15]) + 65 * i2[16] + 0 * i2[17] - 1 * i2[18]) + 39 * i2[19] - 20 * i2[20] == 20253,
(((((((45 * i2[0] - 56 * i2[1]) + 10 * i2[2] + 650 * i2[3] - 900 * i2[4]) + 44 * i2[5] + 66 * i2[6] - 6 * i2[7] - 6 * i2[8] - 21 * i2[9]) + 9 * i2[10] - 6 * i2[11] - 12 * i2[12]) + 69 * i2[13] - 2 * i2[14] - 406 * i2[15]) + 651 * i2[16] + 2 * i2[17] - 10 * i2[18]) + 69 * i2[19] - 0 * i2[20]) + 21 * i2[21] == 18768,
(((((555 * i2[0] - 6666 * i2[1]) + 70 * i2[2] + 510 * i2[3] - 90 * i2[4]) + 499 * i2[5] + 66 * i2[6] - 66 * i2[7] - 610 * i2[8] - 221 * i2[9]) + 9 * i2[10] - 23 * i2[11] - 102 * i2[12]) + 6 * i2[13] + 2050 * i2[14] - 406 * i2[15]) + 665 * i2[16] + 333 * i2[17] + 100 * i2[18] + 609 * i2[19] + 777 * i2[20] + 201 * i2[21] - 22 * i2[22] == 111844,
(((((((1 * i2[0] - 22 * i2[1]) + 333 * i2[2] + 4444 * i2[3] - 5555 * i2[4]) + 6666 * i2[5] - 666 * i2[6]) + 676 * i2[7] - 660 * i2[8] - 22 * i2[9]) + 9 * i2[10] - 73 * i2[11] - 107 * i2[12]) + 6 * i2[13] + 250 * i2[14] - 6 * i2[15]) + 65 * i2[16] + 39 * i2[17] + 10 * i2[18] + 69 * i2[19] + 777 * i2[20] + 201 * i2[21] - 2 * i2[22]) + 23 * i2[23] == 159029,
(((520 * i2[0] - 222 * i2[1]) + 333 * i2[2] + 4 * i2[3] - 56655 * i2[4]) + 6666 * i2[5] + 666 * i2[6] + 66 * i2[7] - 60 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 73 * i2[11] + 1007 * i2[12] + 7777 * i2[13] + 2500 * i2[14] + 6666 * i2[15] + 605 * i2[16] + 390 * i2[17] + 100 * i2[18] + 609 * i2[19] + 99999 * i2[20] + 210 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24] == 2762025,
((((1323 * i2[0] - 22 * i2[1]) + 333 * i2[2] + 4 * i2[3] - 55 * i2[4]) + 666 * i2[5] + 666 * i2[6] + 66 * i2[7] - 660 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 100 * i2[12] + 777 * i2[13] + 2500 * i2[14] + 6666 * i2[15] + 605 * i2[16] + 390 * i2[17] + 100 * i2[18] + 609 * i2[19] + 9999 * i2[20] + 210 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] == 1551621,
(((((777 * i2[0] - 22 * i2[1]) + 6969 * i2[2] + 4 * i2[3] - 55 * i2[4]) + 666 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 100 * i2[12] + 777 * i2[13] + 250 * i2[14] + 666 * i2[15] + 65 * i2[16] + 90 * i2[17] + 100 * i2[18] + 609 * i2[19] + 999 * i2[20] + 21 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] - 26 * i2[26] == 948348,
((((((97 * i2[0] - 22 * i2[1]) + 6969 * i2[2] + 4 * i2[3] - 56 * i2[4]) + 96 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 90 * i2[17] + -2 * i2[18] + 609 * i2[19] + 0 * i2[20] + 21 * i2[21] + 2 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] - 26 * i2[26]) + 27 * i2[27] == 777044,
(((((177 * i2[0] - 22 * i2[1]) + 699 * i2[2] + 64 * i2[3] - 56 * i2[4] - 96 * i2[5] - 66 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 0 * i2[17] + -2 * i2[18] + 69 * i2[19] + 0 * i2[20] + 21 * i2[21] + 222 * i2[22] + 23 * i2[23] - 224 * i2[24]) + 25 * i2[25] - 26 * i2[26]) + 27 * i2[27] - 28 * i2[28] == 185016,
((((((77 * i2[0] - 2 * i2[1]) + 6 * i2[2] + 6 * i2[3] - 96 * i2[4] - 9 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 0 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 0 * i2[17] + -2 * i2[18] + 9 * i2[19] + 0 * i2[20] + 21 * i2[21] + 222 * i2[22] + 23 * i2[23] - 224 * i2[24]) + 26 * i2[25] - -58 * i2[26]) + 27 * i2[27] - 2 * i2[28]) + 29 * i2[29] == 130106]
if all(im):
print('Congratulation!!!')
else:
print('wrong_wrong!!!')

先用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
41
42
43
44
45
from z3 import *
i2 = [Int(f'i2_{i}') for i in range(30)]
solver = Solver()
im = [
7 * i2[0] == 504,
9 * i2[0] - 5 * i2[1] == 403,
(2 * i2[0] - 5 * i2[1]) + 10 * i2[2] == 799,
3 * i2[0] + 8 * i2[1] + 15 * i2[2] + 20 * i2[3] == 2938,
(5 * i2[0] + 15 * i2[1] + 20 * i2[2] - 19 * i2[3]) + 1 * i2[4] == 2042,
(7 * i2[0] + 1 * i2[1] + 9 * i2[2] - 11 * i2[3]) + 2 * i2[4] + 5 * i2[5] == 1225,
11 * i2[0] + 22 * i2[1] + 33 * i2[2] + 44 * i2[3] + 55 * i2[4] + 66 * i2[5] - 77 * i2[6] == 7975,
((21 * i2[0] + 23 * i2[1] + 3 * i2[2] + 24 * i2[3] - 55 * i2[4]) + 6 * i2[5] - 7 * i2[6]) + 15 * i2[7] == 229,
(2 * i2[0] + 26 * i2[1] + 13 * i2[2] + 0 * i2[3] - 65 * i2[4]) + 15 * i2[5] + 29 * i2[6] + 1 * i2[7] + 20 * i2[8] == 2107,
(10 * i2[0] + 7 * i2[1] + -9 * i2[2] + 6 * i2[3] + 7 * i2[4] + 1 * i2[5] + 22 * i2[6] + 21 * i2[7] - 22 * i2[8]) + 30 * i2[9] == 4037,
(15 * i2[0] + 59 * i2[1] + 56 * i2[2] + 66 * i2[3] + 7 * i2[4] + 1 * i2[5] - 122 * i2[6]) + 21 * i2[7] + 32 * i2[8] + 3 * i2[9] - 10 * i2[10] == 4950,
(((13 * i2[0] + 66 * i2[1] + 29 * i2[2] + 39 * i2[3] - 33 * i2[4]) + 13 * i2[5] - 2 * i2[6]) + 42 * i2[7] + 62 * i2[8] + 1 * i2[9] - 10 * i2[10]) + 11 * i2[11] == 12544,
(((23 * i2[0] + 6 * i2[1] + 29 * i2[2] + 3 * i2[3] - 3 * i2[4]) + 63 * i2[5] - 25 * i2[6]) + 2 * i2[7] + 32 * i2[8] + 1 * i2[9] - 10 * i2[10]) + 11 * i2[11] - 12 * i2[12] == 6585,
((((223 * i2[0] + 6 * i2[1] - 29 * i2[2] - 53 * i2[3] - 3 * i2[4]) + 3 * i2[5] - 65 * i2[6]) + 0 * i2[7] + 36 * i2[8] + 1 * i2[9] - 15 * i2[10]) + 16 * i2[11] - 18 * i2[12]) + 13 * i2[13] == 6893,
((((29 * i2[0] + 13 * i2[1] - 9 * i2[2] - 93 * i2[3]) + 33 * i2[4] + 6 * i2[5] + 65 * i2[6] + 1 * i2[7] - 36 * i2[8]) + 0 * i2[9] - 16 * i2[10]) + 96 * i2[11] - 68 * i2[12]) + 33 * i2[13] - 14 * i2[14] == 1883,
(((69 * i2[0] + 77 * i2[1] - 93 * i2[2] - 12 * i2[3]) + 0 * i2[4] + 0 * i2[5] + 1 * i2[6] + 16 * i2[7] + 36 * i2[8] + 6 * i2[9] + 19 * i2[10] + 66 * i2[11] - 8 * i2[12]) + 38 * i2[13] - 16 * i2[14]) + 15 * i2[15] == 8257,
((((23 * i2[0] + 2 * i2[1] - 3 * i2[2] - 11 * i2[3]) + 12 * i2[4] + 24 * i2[5] + 1 * i2[6] + 6 * i2[7] + 14 * i2[8] - 0 * i2[9]) + 1 * i2[10] + 68 * i2[11] - 18 * i2[12]) + 68 * i2[13] - 26 * i2[14]) + 15 * i2[15] - 16 * i2[16] == 5847,
(((((24 * i2[0] + 0 * i2[1] - 1 * i2[2] - 15 * i2[3]) + 13 * i2[4] + 4 * i2[5] + 16 * i2[6] + 67 * i2[7] + 146 * i2[8] - 50 * i2[9]) + 16 * i2[10] + 6 * i2[11] - 1 * i2[12]) + 69 * i2[13] - 27 * i2[14]) + 45 * i2[15] - 6 * i2[16]) + 17 * i2[17] == 18257,
((((25 * i2[0] + 26 * i2[1] - 89 * i2[2]) + 16 * i2[3] + 19 * i2[4] + 44 * i2[5] + 36 * i2[6] + 66 * i2[7] - 150 * i2[8] - 250 * i2[9]) + 166 * i2[10] + 126 * i2[11] - 11 * i2[12]) + 690 * i2[13] - 207 * i2[14]) + 46 * i2[15] + 6 * i2[16] + 7 * i2[17] - 18 * i2[18] == 12591,
(((((5 * i2[0] + 26 * i2[1] + 8 * i2[2] + 160 * i2[3] + 9 * i2[4] - 4 * i2[5]) + 36 * i2[6] + 6 * i2[7] - 15 * i2[8] - 20 * i2[9]) + 66 * i2[10] + 16 * i2[11] - 1 * i2[12]) + 690 * i2[13] - 20 * i2[14]) + 46 * i2[15] + 6 * i2[16] + 7 * i2[17] - 18 * i2[18]) + 19 * i2[19] == 52041,
((((((29 * i2[0] - 26 * i2[1]) + 0 * i2[2] + 60 * i2[3] + 90 * i2[4] - 4 * i2[5]) + 6 * i2[6] + 6 * i2[7] - 16 * i2[8] - 21 * i2[9]) + 69 * i2[10] + 6 * i2[11] - 12 * i2[12]) + 69 * i2[13] - 20 * i2[14] - 46 * i2[15]) + 65 * i2[16] + 0 * i2[17] - 1 * i2[18]) + 39 * i2[19] - 20 * i2[20] == 20253,
(((((((45 * i2[0] - 56 * i2[1]) + 10 * i2[2] + 650 * i2[3] - 900 * i2[4]) + 44 * i2[5] + 66 * i2[6] - 6 * i2[7] - 6 * i2[8] - 21 * i2[9]) + 9 * i2[10] - 6 * i2[11] - 12 * i2[12]) + 69 * i2[13] - 2 * i2[14] - 406 * i2[15]) + 651 * i2[16] + 2 * i2[17] - 10 * i2[18]) + 69 * i2[19] - 0 * i2[20]) + 21 * i2[21] == 18768,
(((((555 * i2[0] - 6666 * i2[1]) + 70 * i2[2] + 510 * i2[3] - 90 * i2[4]) + 499 * i2[5] + 66 * i2[6] - 66 * i2[7] - 610 * i2[8] - 221 * i2[9]) + 9 * i2[10] - 23 * i2[11] - 102 * i2[12]) + 6 * i2[13] + 2050 * i2[14] - 406 * i2[15]) + 665 * i2[16] + 333 * i2[17] + 100 * i2[18] + 609 * i2[19] + 777 * i2[20] + 201 * i2[21] - 22 * i2[22] == 111844,
(((((((1 * i2[0] - 22 * i2[1]) + 333 * i2[2] + 4444 * i2[3] - 5555 * i2[4]) + 6666 * i2[5] - 666 * i2[6]) + 676 * i2[7] - 660 * i2[8] - 22 * i2[9]) + 9 * i2[10] - 73 * i2[11] - 107 * i2[12]) + 6 * i2[13] + 250 * i2[14] - 6 * i2[15]) + 65 * i2[16] + 39 * i2[17] + 10 * i2[18] + 69 * i2[19] + 777 * i2[20] + 201 * i2[21] - 2 * i2[22]) + 23 * i2[23] == 159029,
(((520 * i2[0] - 222 * i2[1]) + 333 * i2[2] + 4 * i2[3] - 56655 * i2[4]) + 6666 * i2[5] + 666 * i2[6] + 66 * i2[7] - 60 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 73 * i2[11] + 1007 * i2[12] + 7777 * i2[13] + 2500 * i2[14] + 6666 * i2[15] + 605 * i2[16] + 390 * i2[17] + 100 * i2[18] + 609 * i2[19] + 99999 * i2[20] + 210 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24] == 2762025,
((((1323 * i2[0] - 22 * i2[1]) + 333 * i2[2] + 4 * i2[3] - 55 * i2[4]) + 666 * i2[5] + 666 * i2[6] + 66 * i2[7] - 660 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 100 * i2[12] + 777 * i2[13] + 2500 * i2[14] + 6666 * i2[15] + 605 * i2[16] + 390 * i2[17] + 100 * i2[18] + 609 * i2[19] + 9999 * i2[20] + 210 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] == 1551621,
(((((777 * i2[0] - 22 * i2[1]) + 6969 * i2[2] + 4 * i2[3] - 55 * i2[4]) + 666 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 220 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 100 * i2[12] + 777 * i2[13] + 250 * i2[14] + 666 * i2[15] + 65 * i2[16] + 90 * i2[17] + 100 * i2[18] + 609 * i2[19] + 999 * i2[20] + 21 * i2[21] + 232 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] - 26 * i2[26] == 948348,
((((((97 * i2[0] - 22 * i2[1]) + 6969 * i2[2] + 4 * i2[3] - 56 * i2[4]) + 96 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 90 * i2[17] + -2 * i2[18] + 609 * i2[19] + 0 * i2[20] + 21 * i2[21] + 2 * i2[22] + 23 * i2[23] - 24 * i2[24]) + 25 * i2[25] - 26 * i2[26]) + 27 * i2[27] == 777044,
(((((177 * i2[0] - 22 * i2[1]) + 699 * i2[2] + 64 * i2[3] - 56 * i2[4] - 96 * i2[5] - 66 * i2[6]) + 96 * i2[7] - 60 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 0 * i2[17] + -2 * i2[18] + 69 * i2[19] + 0 * i2[20] + 21 * i2[21] + 222 * i2[22] + 23 * i2[23] - 224 * i2[24]) + 25 * i2[25] - 26 * i2[26]) + 27 * i2[27] - 28 * i2[28] == 185016,
((((((77 * i2[0] - 2 * i2[1]) + 6 * i2[2] + 6 * i2[3] - 96 * i2[4] - 9 * i2[5] - 6 * i2[6]) + 96 * i2[7] - 0 * i2[8] - 20 * i2[9]) + 99 * i2[10] + 3 * i2[11] + 10 * i2[12] + 707 * i2[13] + 250 * i2[14] + 666 * i2[15] + -9 * i2[16] + 0 * i2[17] + -2 * i2[18] + 9 * i2[19] + 0 * i2[20] + 21 * i2[21] + 222 * i2[22] + 23 * i2[23] - 224 * i2[24]) + 26 * i2[25] - -58 * i2[26]) + 27 * i2[27] - 2 * i2[28]) + 29 * i2[29] == 130106
]

for equation in im:
solver.add(equation)

if solver.check() == sat:
m = solver.model()
result = [m.evaluate(i2[i]) for i in range(len(i2))]
print("Solution:", result)
else:
print("No solution found")

输出[72, 49, 90, 49, 78, 49, 85, 49, 67, 49, 84, 49, 70, 49, 123, 49, 97, 54, 100, 50, 55, 53, 102, 55, 45, 52, 54, 51, 125, 49],即为H1Z1N1U1C1T1F1{1a6d275f7-463}1,再解出现次数:

1
2
3
4
5
6
7
8
9
10
11
12
lists = [['H', 1], ['Z', 1], ['N', 1], ['U', 1], ['C', 1], ['T', 1], ['F', 1], ['{', 1], ['a', 6], ['d', 2], ['7', 5], ['f', 7], ['-', 4], ['6', 3], ['}', 1]]
count = b'111111116257645365477364777645752361'
result = bytearray(b'')
for j in range(len(count)):
candidate = int(count[j] - 48)
for i in range(len(lists)):
if lists[i][1] == candidate:
result.append(ord(lists[i][0]))
if lists[i][1] == 1:
del lists[i]
break
print(result.decode())

HZNUCTF{ad7fa-76a7-ff6a-fffa-7f7d6a}.

randomsystem:

发现有很多花,去花,查看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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int v4; // eax
int v6; // [esp-8h] [ebp-78Ch]
char v7; // [esp+0h] [ebp-784h]
char v8; // [esp+0h] [ebp-784h]
char v9; // [esp+0h] [ebp-784h]
char v10; // [esp+0h] [ebp-784h]
char v11; // [esp+0h] [ebp-784h]
int m; // [esp+250h] [ebp-534h]
int k; // [esp+25Ch] [ebp-528h]
unsigned int v14; // [esp+268h] [ebp-51Ch]
int j; // [esp+274h] [ebp-510h]
char v16; // [esp+283h] [ebp-501h]
int i; // [esp+298h] [ebp-4ECh]
int v18[34]; // [esp+2A4h] [ebp-4E0h] BYREF
char Str[20]; // [esp+32Ch] [ebp-458h] BYREF
int v20[66]; // [esp+340h] [ebp-444h] BYREF
_OWORD v21[16]; // [esp+448h] [ebp-33Ch] BYREF
char v22[264]; // [esp+550h] [ebp-234h] BYREF
char Destination[96]; // [esp+658h] [ebp-12Ch] BYREF
char v24; // [esp+6B8h] [ebp-CCh] BYREF
char Source[76]; // [esp+6C0h] [ebp-C4h] BYREF
int v26; // [esp+70Ch] [ebp-78h] BYREF
int v27; // [esp+710h] [ebp-74h]
int v28; // [esp+714h] [ebp-70h]
int v29; // [esp+718h] [ebp-6Ch]
char v30; // [esp+71Ch] [ebp-68h]
int v31[4]; // [esp+728h] [ebp-5Ch] BYREF
char v32[72]; // [esp+738h] [ebp-4Ch] BYREF
int savedregs; // [esp+784h] [ebp+0h] BYREF

checkdebuggerjustmycode(&unk_41E0A9);
v26 = 0;
v27 = 0;
v28 = 0;
v29 = 0;
v30 = 0;
j_memset(v22, 0, 0x100u);
j_memset(v21, 0, sizeof(v21));
strcpy(Str, "KeYkEy!!"); //密钥
v20[0] = 376;
v20[1] = 356;
//8*8矩阵
v20[63] = 151;
printf("Welcome to HZNUCTF!!!\n", v7);
printf("Enter something: \n", v8);
scanf("%64s", (char)v32);
Func1(v32, v31); //两个加密函数
Func2(v31[0], v31[1], &v26);
if ( (char)v26 == 0x35 //第一个验证
&& SBYTE1(v26) == 0x32
&& SBYTE2(v26) == 0x36
&& SHIBYTE(v26) == 0x35
&& (char)v27 == 0x35
&& SBYTE1(v27) == 0x36
&& SBYTE2(v27) == 0x36
&& SHIBYTE(v27) == 0x35
&& (char)v28 == 0x35
&& SBYTE1(v28) == 0x32
&& SBYTE2(v28) == 0x36
&& SHIBYTE(v28) == 0x35
&& (char)v29 == 0x35
&& SBYTE1(v29) == 0x33
&& SBYTE2(v29) == 0x36
&& SHIBYTE(v29) == 0x35 )
{

查看func1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
_DWORD *__cdecl func1(int a1, _DWORD *a2)
{
_DWORD *result; // eax
__int64 v3; // rax
int i; // [esp+D8h] [ebp-8h]

checkdebuggerjustmycode(&unk_9EE0A9);
result = a2;
*a2 = 0;
a2[1] = 0;
for ( i = 0; i < 64; ++i )
{
v3 = shl(63 - i, 0);
HIDWORD(v3) |= a2[1];
*a2 |= v3;
a2[1] = HIDWORD(v3);
result = (_DWORD *)(i + 1);
}
return result;
}

观察汇编发现了一些没有反编译出来的逻辑,比如cmp 输入值, “1”,经过分析这是提取64位输出值中的二进制,将其写入64个二进制位(1个qword)的过程,接着查看func2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl func2(__int64 a1, int a2)
{
int result; // eax
int v3; // ecx
int i; // [esp+DCh] [ebp-8h]

result = checkdebuggerjustmycode(&unk_9EE0A9);
for ( i = 15; i >= 0; --i )
{
v3 = (7 * (((unsigned __int8)a1 & 0xFu) > 9)) | a1 & 0xF | 0x30;
*(_BYTE *)(i + a2) = v3;
LOBYTE(v3) = 4;
a1 = shr (v3, HIDWORD(a1));
result = i - 1;
}
return result;
}

同理发现这是一个类似于to hex的功能,只不过是基于ascii 0x30~0x3F的,那么目标值就是5265566552655365,还原得到要输入的值为0101001001100101010101100110010101010010011001010101001101100101,代入验证发现正确,继续往后看:

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
    printf("good_job!!!\n", v9);
printf("So, Plz input the flag:\n", v10);
scanf("%73s", (char)&v24);
strncpy_s(Destination, 0x41u, Source, 0x40u); //提取中间64位
sub_41127B();
srand(Seed); //2025
sub_41127B();
j_memset(v18, 0, 0x80u); //这里用随机数结合if not in list then append的方式生成了一个32项的sbox
for ( i = 0; i < 32; ++i )
{
do
{
rand();
v4 = sub_41127B() % 32;
v16 = 1;
for ( j = 0; j < i; ++j )
{
if ( v18[j] == v4 )
{
v16 = 0;
break;
}
}
}
while ( !v16 );
v18[i] = v4;
}
Func3(Destination, v18); //64字节用sbox进行打乱
Func4(v22, Destination); //填充矩阵到v22
Func5(&v26, Str); //把target从十六进制按每两个字符为一个字节转为字符密钥“ReVeReSe”
Func6(matrix, v22, v21); //将01矩阵与输入值对应的ascii矩阵相乘并输出矩阵
v14 = 0;
for ( k = 0; k < 8; ++k )
{
for ( m = 0; m < 8; ++m )
{
*((_DWORD *)&v21[2 * k] + m) ^= Str[v14 % j_strlen(Str)]; //异或
++v14;
}
}
if ( Func7 (v21, v20) == 1 ) //矩阵比较
printf("Congratulation!!!\n", v11);
}
else
{
printf("wrong_wrong!!!\n", v9);
}
sub_41120D(&savedregs, &dword_412D48, 0, v3);
return v6;
}

编写脚本同构加密过程,有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
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
import numpy as np

def func1(m):
return int(m.decode(), 2)

def func2(m):
result = []
for i in range(15, -1, -1):
n = (m >> (i * 4)) & 0xF
c = chr(n + 0x30)
result.append(c)
return "".join(result)

def denc1(n):
b = bin(n)[2:]
b = b.zfill(64)
return b.encode()

def denc2(s):
result = 0
for i, c in enumerate(s):
h = ord(c) - 0x30
result |= h << ((15 - i) * 4)
return result

# 测试加密
binm = bytearray(b"1111111100000000111111110000000011111111000000001111111100000000")
print(binm.decode())
intm = func1(binm)
print(intm)
hexm = func2(intm)
print(hexm)

# 解密
target = "5265566552655365"
print(target)
inttar = denc2(target)
print(inttar)
bintar = denc1(inttar)
print(bintar.decode())

# flag部分
sbox = [27, 26, 25, 23, 28, 1, 6, 10,
20, 7, 15, 14, 31, 18, 19, 21,
9, 30, 22, 24, 8, 2, 29, 3,
12, 11, 17, 16, 0, 13, 5, 4]

key = bytearray(b"KeYkEy!!")

mat = [376, 356, 169, 501, 277, 329, 139, 342,
380, 365, 162, 258, 381, 339, 347, 307,
263, 359, 162, 484, 310, 333, 346, 339,
150, 194, 175, 344, 158, 250, 128, 175,
158, 173, 152, 379, 158, 292, 130, 365,
197, 20, 197, 161, 198, 10, 207, 244,
202, 14, 204, 176, 193, 255, 35, 7,
158, 181, 145, 353, 153, 357, 246, 151]

m01 = [1, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 1, 0, 1,
0, 1, 0, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1]

def func3(m, sbox):
for i in range(len(m) // 2):
m[i], m[len(m) - sbox[i] - 1] = m[len(m) - sbox[i] - 1], m[i]
return m

def denc3(m, sbox):
for i in range(len(m) // 2 - 1, -1, -1):
m[i], m[len(m) - sbox[i] - 1] = m[len(m) - sbox[i] - 1], m[i]
return m

def func4(m):
tmp = m.decode()
n = [None] * len(m)
for i in range(len(tmp)):
n[i] = ord(tmp[i])
return n

def denc4(m):
n = bytearray(m)
return n

def func5(target, key):
for i in range(0, len(target), 2):
h = int(target[i])
l = int(target[i + 1])
b = (h << 4) | l
key[i // 2] = b

def func6(m1, m2):
m3 = [0] * 64
for i in range(8):
for j in range(8):
for k in range(8):
m3[8 * i + j] += m2[8 * k + j] * m1[8 * i + k]
return m3

def funcx(mat, key):
index = 0
for k in range(8):
for m in range(8):
mat[8 * k + m] ^= key[index % len(key)]
index += 1
return mat

def func7(m, mat):
try:
assert m == mat
except:
print("AssertionError")

# 测试加密
chrf = bytearray(b"0000000011111111000000001111111100000000111111110000000011111111")
print(chrf.decode())
fn3f = func3(chrf, sbox)
print(fn3f.decode())
fn4f = func4(fn3f)
print(fn4f)
func5(target, key)
print(key)
print(key.decode())
mf6 = func6(m01, fn4f)
print(mf6)
mxk = funcx(mf6, key)
print(mxk)
func7(mxk, mat)

# 解密
dex = funcx(mat,key)
print(dex)
Af = np.array(m01)
A = Af.reshape((8, 8))
Cf = np.array(dex)
C = Cf.reshape((8, 8))
Ai = np.linalg.inv(A)
B = np.dot(Ai, C)
Bf = B.flatten()
bex = [int(i) for i in Bf]
print(bex)
flag = bytearray(denc3(bex, sbox))
print(f"HZNUCTF{{{flag.decode()}}}")

HZNUCTF{3zfb899ac5c256d-7a8r59f0tccd-4fa6b8vfd111-a44ffy4r0-6dce5679da58}.

conforand:

查看main,发现有严重的ollvm,将函数整体复制到源代码,找到其中的有效执行流并包裹上打印,修掉报错,执行程序,得到main函数大致执行流:

1
2
3
4
5
6
init(v155, (const char *)v3, v4);
printf("Welcome to HZNUCTF!!!\n");
printf("Plz input the flag:\n");
scanf("%s", v154);
rc4((__int64)v157, 0x2AuLL, (__int64)v155, 9LL);
printf("%x ", v163);

可以看出是一个rc4加密,密文以十六进制形式输出,flag长度为2A,密钥长度为9,尝试解析init函数:

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
__int64 __fastcall init(char *a1, const char *a2, __int64 a3)
{
__int64 result; // rax
char *v4; // [rsp+18h] [rbp-18h]
unsigned int v5; // [rsp+24h] [rbp-Ch]
char *dest; // [rsp+28h] [rbp-8h]

v5 = 0x3D7A06FB;
v4 = a1;
while ( 1 )
{
result = v5;
if ( v5 == 0xAEE1317C )
break;
if ( v5 == 0x3D7A06FB )
{
dest = v4;
v5 = 0x6AF64EC7;
}
else
{
((void (__fastcall *)(char *, const char *, __int64))sub_5s5s5s)(a1, a2, a3);
a1 = dest;
a2 = (const char *)(unsigned int)"JustDoIt!";
v5 = 0xAEE1317C;
strcpy(dest, a2);
}
}
return result;
}

找到了9字节密钥JustDoIt!,查看sub_5s5s5s,发现了以下几行:

1
2
3
*v205 = time(0LL);
seed = *v205;
srand(seed);

说明有某个东西会基于时间的随机数生成,继续往下查看rc4函数,找了很久发现了这些rc4特征,将其有关联的变量重命名并查找xref,尽可能地恢复出所有调用:
323~325行:

1
2
3
*sbox_j = sbox__i_sbox_j % 257;
sbox[*sbox_i] = sbox[*sbox_j];
sbox__j = &sbox[*sbox_i];

339~341行:

1
2
3
sboxi = *sbox_i;
v149 = 257;
*sbox_i = (sboxi - 746900618 + 746900619) % 257;

365~366行:

1
2
sbox_i = sboxinited;
sbox_j = &sboxinited[-4];

372行:

1
v244 = v242 < lenm_3;

384~403行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
v113 = sbox__i_sbox_j;
v114 = sbox__i_sbox_j;
v115 = sbox__i_sbox_j;
v116 = sbox__i_sbox_j;
v117 = sbox__i_sbox_j;
v118 = sbox__i_sbox_j;
v119 = sbox__i_sbox_j;
v131 = 257;
v130 = sbox__i_sbox_j;
*sbox_j = sbox__i_sbox_j % 257;
sbox[*sbox_i] = sbox[*sbox_j];
v129 = v119;
v128 = v113;
v127 = v118;
v126 = v114;
v125 = v115;
v124 = v116;
v123 = v117;
v122 = sbox_i;

408行:

1
v242 = *sboxinited_1;

438~445行:

1
2
3
4
5
6
7
inited = init_sbox((__int64)sbox_1, key_3);
*sbox_i = 0;
*sbox_j = 0;
*sbox_t = 0;
*sboxinited_1 = 0LL;
*sboxinited_1 = 0LL;
sboxinited[3] = inited;

580行:

1
if ( v244 )

587~596行:

1
2
3
4
5
6
7
sboxinit = init_sbox((__int64)sbox_1, key_3);
*sbox_i = 0;
*sbox_j = 0;
*sbox_t = 0;
*sboxinited_1 = 0LL;
*sboxinited_1 = 0LL;
sboxinit_1 = sboxinit;

634~678行:

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
sbox[*sbox_j] = *sbox__j;
sbox__i_sbox__j = (unsigned __int8)sbox[*sbox_j] + (unsigned __int8)sbox[*sbox_i];
v134 = -1;
*sbox_t = sbox__i_sbox__j % 257;
v65 = *(unsigned __int8 *)(message_2 + *sboxinited_1);
sbox__t = sbox[*sbox_t];
v67 = (v134 ^ 0x7E050B95 | ~v134 & 0x7E050B95) ^ ((v134 ^ 0x7E050B95) & ((v134 ^ 0xD7FABE8 | 0xD7FABE8) & ~(~(v65 & ~v65) | ~((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581))) | ((v134 ^ 0xD7FABE8) & v65 & ~v65 | ~(v65 & ~v65) & 0xD7FABE8) ^ ((v134 ^ 0xD7FABE8) & ((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581)) | ~((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581)) & 0xD7FABE8)) | ~((v134 ^ 0xD7FABE8 | 0xD7FABE8) & ~(~(v65 & ~v65) | ~((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581))) | ((v134 ^ 0xD7FABE8) & v65 & ~v65 | ~(v65 & ~v65) & 0xD7FABE8) ^ ((v134 ^ 0xD7FABE8) & ((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581)) | ~((v134 ^ 0xDDA7B581 | ~v134 & 0xDDA7B581) ^ ((v134 ^ 0xDDA7B581) & (unsigned __int8)v65 | ~v65 & 0xDDA7B581)) & 0xD7FABE8)) & 0x7E050B95);
v68 = ~v67 & 0x5F51A4D1 | (v134 ^ 0x5F51A4D1) & v67;
v69 = v68 & (v134 ^ 0x5F51A4D1 ^ v68);
LOBYTE(v67) = ~(_BYTE)v134 & v65 | ~*(_BYTE *)(message_2 + *sboxinited_1);
v70 = ((v134 ^ 0xFA) & ((v134 ^ 0x56 | ~(_BYTE)v134 & 0x56) ^ ((v134 ^ 0x56) & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) & 0x56)) | ~((v134 ^ 0x56 | ~(_BYTE)v134 & 0x56) ^ ((v134 ^ 0x56) & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) & 0x56)) & 0xFA) ^ ((v134 ^ 0xFA) & v67 | ~(_BYTE)v67 & 0xFA);
v71 = (v134 ^ 0xFA | 0xFA) & ~(~((v134 ^ 0x56 | ~(_BYTE)v134 & 0x56) ^ ((v134 ^ 0x56) & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) & 0x56)) | ~(_BYTE)v67) | v70;
LOBYTE(v67) = sbox__t & ~sbox__t ^ (~(_BYTE)v134 & sbox__t | ~sbox__t) | sbox__t & ~sbox__t & (~(_BYTE)v134 & sbox__t | ~sbox__t);
LOBYTE(v68) = (v134 ^ 0x92 | 0x92) & ~(~(_BYTE)v67 | ~(((v134 ^ 0xD2) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xD2) ^ ((v134 ^ 0xD2) & v67 | ~(_BYTE)v67 & 0xD2)));
LOBYTE(v65) = (v134 ^ 0xA8 | ~(_BYTE)v134 & 0xA8) ^ ((v134 ^ 0xA8) & sbox__t | ~sbox__t & 0xA8);
v72 = (v134 ^ 0xA1 | 0xA1) & ~(~(~(_BYTE)v134 & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA)) | ~(_BYTE)v65) | ((v134 ^ 0xA1) & (~(_BYTE)v134 & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA)) | ~(~(_BYTE)v134 & (((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA) | ~(((v134 ^ 0xEA) & (~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) | ~(~(_BYTE)v134 & 0xD1 | v134 ^ 0xD1) & 0xEA) ^ 0xEA)) & 0xA1) ^ ((v134 ^ 0xA1) & v65 | ~(_BYTE)v65 & 0xA1);
v73 = (v134 ^ 0x98 | 0x98) & ~(~((~(_BYTE)v134 & 0xD0 | v134 ^ 0xD0) ^ 0xD0 | ~(_BYTE)v134 & 0xD0 | (v134 ^ 0xD0) & 0xD0) | ~(~(_BYTE)v134 & v72 | ~v72));
v74 = (v134 ^ 0xB3272901 | ~v134 & 0xB3272901) ^ ((v134 ^ 0xB3272901) & v69 | ~v69 & 0xB3272901);
LOBYTE(v67) = ~(_BYTE)v134 & (~(_BYTE)v134 & v71 | ~v71) & (~((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D) ^ 0x2E | ((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D)) & 0x2E) ^ (~(_BYTE)v134 & v71 | ~v71)) | ~((~(_BYTE)v134 & v71 | ~v71) & (~((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D) ^ 0x2E | ((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D)) & 0x2E) ^ (~(_BYTE)v134 & v71 | ~v71)));
v75 = (v134 ^ 0x4D | 0x4D) & ~(v134 ^ 0x11 | ~((v134 ^ 1 | ((v134 & 1) == 0)) ^ ((v134 ^ 1) & v69 | ((v69 & 1) == 0))));
LOBYTE(v69) = (v134 ^ 0x7E | 0x7E) & ~(~((v134 ^ 0xF3 | ~(_BYTE)v134 & 0xF3) ^ ((v134 ^ 0xF3) & 0x11 | (v134 ^ 0x11) & 0xF3)) | ~(_BYTE)v69);
v76 = (~(_BYTE)v134 & v71 | ~v71) & (~((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D) ^ 0x2E | ((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D)) & 0x2E) ^ (~(_BYTE)v134 & v71 | ~v71)) & (~((v134 ^ 0xF3 | ~(_BYTE)v134 & 0xF3) ^ ((v134 ^ 0xF3) & 0x11 | (v134 ^ 0x11) & 0xF3)) ^ (~(_BYTE)v134 & v71 | ~v71) & (~((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D) ^ 0x2E | ((v134 ^ 0x8D | ~(_BYTE)v134 & 0x8D) ^ ((v134 ^ 0x8D) & 0x2E | (v134 ^ 0x2E) & 0x8D)) & 0x2E) ^ (~(_BYTE)v134 & v71 | ~v71)));
v133 = v74;
v77 = (v134 ^ 0xF0 | 0xF0) & ~(~(_BYTE)v69 | ~v75) | ((v134 ^ 0xF0) & v69 | ~(_BYTE)v69 & 0xF0) ^ ((v134 ^ 0xF0) & v75 | ~v75 & 0xF0);
v78 = (v134 ^ 0xAB | 0xAB) & ~(~v76 | ~(v67 & (v134 ^ 0x11 ^ v67))) | ((v134 ^ 0xAB) & v76 | ~v76 & 0xAB) ^ ((v134 ^ 0xAB) & v67 & (v134 ^ 0x11 ^ v67) | ~(v67 & (v134 ^ 0x11 ^ v67)) & 0xAB);
LOBYTE(v67) = (v134 ^ 0x5B | 0x5B) & ~(~((v134 ^ 0xD4 | 0xD4) & ~(~((v134 ^ 0xF3 | ~(_BYTE)v134 & 0xF3) ^ ((v134 ^ 0xF3) & 0x11 | (v134 ^ 0x11) & 0xF3)) | v134 ^ 0x11) | ((v134 ^ 0xD4) & ((v134 ^ 0xF3 | ~(_BYTE)v134 & 0xF3) ^ ((v134 ^ 0xF3) & 0x11 | (v134 ^ 0x11) & 0xF3)) | ~((v134 ^ 0xF3 | ~(_BYTE)v134 & 0xF3) ^ ((v134 ^ 0xF3) & 0x11 | (v134 ^ 0x11) & 0xF3)) & 0xD4) ^ ((v134 ^ 0xD4) & 0x11 | (v134 ^ 0x11) & 0xD4)) | ~(~(_BYTE)v134 & (v67 ^ v133 | v67 & v133) | ~(v67 ^ v133 | v67 & v133)));
LOBYTE(v69) = v67 ^ ((v134 ^ 0x52) & v78 | ~v78 & 0x52) ^ ((v134 ^ 0x52) & v77 | ~v77 & 0x52) | v67 & (((v134 ^ 0x52) & v78 | ~v78 & 0x52) ^ ((v134 ^ 0x52) & v77 | ~v77 & 0x52));
LOBYTE(v74) = (v134 ^ 0x4D | ~(_BYTE)v134 & 0x4D) ^ ((v134 ^ 0x4D) & v68 | ~(_BYTE)v68 & 0x4D);
LOBYTE(v67) = ~(_BYTE)v134 & v73 | ~v73;
LOBYTE(v68) = (v134 ^ 0xD9 | 0xD9) & ~(~((v134 ^ 0x92 | ~(_BYTE)v134 & 0x92) ^ ((v134 ^ 0x92) & 0x4F | (v134 ^ 0x4F) & 0x92)) | ~(_BYTE)v68);
v79 = (v134 ^ 0x48 | 0x48) & ~(v134 ^ 0x4F | ~(_BYTE)v67);
v80 = v73 & (~((v134 ^ 0x92 | ~(_BYTE)v134 & 0x92) ^ ((v134 ^ 0x92) & 0x4F | (v134 ^ 0x4F) & 0x92)) ^ v73);
v81 = (v134 ^ 0x9C | 0x9C) & ~(~v80 | ~v79) | ((v134 ^ 0x9C) & v80 | ~v80 & 0x9C) ^ ((v134 ^ 0x9C) & v79 | ~v79 & 0x9C);
v82 = ((v134 ^ 0xEF) & v81 | ~v81 & 0xEF) ^ ((v134 ^ 0xEF) & (v68 ^ v74 & (v134 ^ 0x4F ^ v74) | v68 & v74 & (v134 ^ 0x4F ^ v74)) | ~(v68 ^ v74 & (v134 ^ 0x4F ^ v74) | v68 & v74 & (v134 ^ 0x4F ^ v74)) & 0xEF);
v83 = (v134 ^ 0x65) & v67 | ~(_BYTE)v67 & 0x65;
LOBYTE(v74) = ~(_BYTE)v134 & ((v134 ^ 0x65 | 0x65) & ~(~(_BYTE)v67 | ~(_BYTE)v74) | v83 ^ ((v134 ^ 0x65) & v74 | ~(_BYTE)v74 & 0x65)) | ~((v134 ^ 0x65 | 0x65) & ~(~(_BYTE)v67 | ~(_BYTE)v74) | v83 ^ ((v134 ^ 0x65) & v74 | ~(_BYTE)v74 & 0x65));
LOBYTE(v67) = v74 & (~((v134 ^ 0x92 | ~(_BYTE)v134 & 0x92) ^ ((v134 ^ 0x92) & 0x4F | (v134 ^ 0x4F) & 0x92) ^ 0x4F | ((v134 ^ 0x92 | ~(_BYTE)v134 & 0x92) ^ ((v134 ^ 0x92) & 0x4F | (v134 ^ 0x4F) & 0x92)) & 0x4F) ^ v74);
LOBYTE(v68) = (v134 ^ 0xDC | 0xDC) & ~(~(_BYTE)v67 | ~v82) | ((v134 ^ 0xDC) & v67 | ~(_BYTE)v67 & 0xDC) ^ ((v134 ^ 0xDC) & v82 | ~v82 & 0xDC);
v84 = (v134 ^ 0x77 | 0x77) & ~(~(~(_BYTE)v134 & v69 | ~(_BYTE)v69) | v134 ^ 0x30);
LOBYTE(v69) = (v134 ^ 0x4A | 0x4A) & ~(~(~(_BYTE)v134 & 0x30 | v134 ^ 0x30) | ~(_BYTE)v69);
LOBYTE(v67) = ~((v134 ^ 0x61 | ~(_BYTE)v134 & 0x61) ^ ((v134 ^ 0x61) & v68 | ~(_BYTE)v68 & 0x61));
v85 = v68 & (~(~(_BYTE)v134 & 0x30 | v134 ^ 0x30) ^ v68);
LOBYTE(v74) = v69 ^ v84 | v69 & v84;
v86 = v85 ^ (v134 ^ 0x8C | 0x8C) & ~(v134 ^ 0x30 | v67) | v85 & (v134 ^ 0x8C | 0x8C) & ~(v134 ^ 0x30 | v67);
*(_BYTE *)(message_2 + *sboxinited_1) = ~v86 & v74 | ~(_BYTE)v74 & v86;

721行:

1
sbox__i_sbox_j = (unsigned __int8)sbox[*sbox_i] + *sbox_j;

729~736行:

1
2
3
4
5
6
7
8
sbox_t = sboxinited;
sboxinited_1 = (__int64 *)&sboxinited[-4];
message_2 = message_1;
lenm_2 = lenm_1;
key_2 = key_1;
lenkey_2 = lenkey_1;
sbox_1 = sbox;
key_3 = key_1;

775行:

1
v247 = *sboxinited_1;

783行:

1
*sboxinited_1 = v248;

将这些行打上断点调试,恢复出PRGA的执行流:

Init_sbox → 初始化I, j, t等参数为0 → 在密文长度内循环{i自增模257 → j = si + j模257 → si = sj → sj = si → t = si + sj模257 → 对明文和st进行操作(结合动调发现还是异或不变)}

再观察KSA部分,也用上面的方法恢复出执行流:

初始化各种数据(包括一个额外的s盒) → 初始化第一个s盒为1~257 → 初始化第二个s盒为密钥的当前索引取模密钥长度再异或(推测)rand值,接下来在257轮循环内:{j = 当前的j与两个s盒的加和取模257 → 交换第一个s盒的第ij项}

动态调试注意到这个rand值只获取了一次,随后便被一直使用,所以不需要爆破时间戳,仅需爆破异或值即可,综上,结合所给密文,编写同构加解密脚本并爆破:

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
def rc4(m, key, xor):
sbox1 = [0] * 257
sbox2 = [0] * 257
for i in range(257):
sbox1[i] = i & 0xFF
sbox2[i] = (key[i % len(key)] ^ xor) & 0xFF
j = 0
for i in range(257):
j = (j + sbox1[i] + sbox2[i]) % 257
sbox1[i], sbox1[j] = sbox1[j], sbox1[i]
i = 0
j = 0
for k in range(len(m)):
i = (i + 1) % 257
j = (j + sbox1[i]) % 257
sbox1[i] = sbox1[j]
m[k] ^= sbox1[(2 * sbox1[i]) % 257]
return bytearray(m)

encflag = [0x83, 0x1e, 0x9c, 0x48, 0x7a, 0xfa, 0xe8, 0x88, 0x36, 0xd5, 0x0a, 0x08, 0xf6, 0xa7,
0x70, 0x0f, 0xfd, 0x67, 0xdd, 0xd4, 0x3c, 0xa7, 0xed, 0x8d, 0x51, 0x10, 0xce, 0x6a,
0x9e, 0x56, 0x57, 0x83, 0x56, 0xe7, 0x67, 0x9a, 0x67, 0x22, 0x24, 0x6e, 0xcd, 0x2f]

key = b"JustDoIt!"

for xor in range(0xFF):
flag = rc4(encflag.copy(), key, xor)
if flag.startswith(b"HZNUCTF{"):
print(hex(xor))
print(flag.decode())
break

HZNUCTF{489b88-1305-411e-b1f4-88a3070a73}.

exchange:

先脱upx,发现可以运行了,查看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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
__int64 i; // rcx
char v6[32]; // [rsp+0h] [rbp-20h] BYREF
char v7; // [rsp+20h] [rbp+0h] BYREF
char *Source; // [rsp+28h] [rbp+8h]
char Destination[44]; // [rsp+48h] [rbp+28h] BYREF
char Str1[36]; // [rsp+74h] [rbp+54h] BYREF
char *v11; // [rsp+98h] [rbp+78h]
char v12[100]; // [rsp+C0h] [rbp+A0h] BYREF
int j; // [rsp+124h] [rbp+104h]
unsigned __int8 v14; // [rsp+144h] [rbp+124h]
unsigned __int8 v15; // [rsp+164h] [rbp+144h]
char v16[32]; // [rsp+184h] [rbp+164h] BYREF
char v17[32]; // [rsp+1A4h] [rbp+184h] BYREF
char v18; // [rsp+1C4h] [rbp+1A4h]

v3 = &v7;
for ( i = 114i64; i; --i )
{
*(_DWORD *)v3 = 0xCCCCCCCC;
v3 += 4;
}
sub_7FF76FC713CA((__int64)&unk_7FF76FC860A6);
Source = (char *)malloc(0x2Aui64);
printf("Welcome to HZNUCTF!!!\n");
printf("Plz input the flag:\n");
scanf("%s", Source);
strncpy_s(Destination, 9ui64, Source, 8ui64);
strncpy_s(Str1, 2ui64, Source + 40, 1ui64);
if ( !j_strcmp(Destination, "HZNUCTF{") && !j_strcmp(Str1, "}") )
{
v11 = (char *)malloc(0x24ui64);
strncpy_s(v11, 0x24ui64, Source + 8, 0x20ui64);
memset(v12, 0, 0x41ui64);
for ( j = 0; j < 32; j += 2 )
{
v14 = 0;
v15 = 0;
memset(v16, 0, 5ui64);
memset(v17, 0, 3ui64);
v14 = v11[j];
sub_7FF76FC71393((__int64)v16, "%x", v14); //flag包裹的32字符被按ascii读取,0=30, 1=31 … 8=38, 9=39, a=41, b=42, c=43, d=44, e=45, f=46
v15 = v11[j + 1];
sub_7FF76FC71393((__int64)v17, "%x", v15);
j_strcat(v16, v17);
v18 = v16[1];
v16[1] = v16[2];
v16[2] = v18;
j_strcat(v12, v16); //每两字符对应的4字节进行了交换,3046→3406,abcd→acbd
}
check((__int64)v12, (__int64)Destination); //v12是64个字节,destination是HZNUCTF{
printf("Congratulation!!!\n");
free(v11);
}
free(Source);
sub_7FF76FC7135C(v6, &unk_7FF76FC7D390);
return 0;
}

发现会按ascii读取flag包裹的内容,并每2字节进行了交换,查看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
27
28
29
30
31
__int64 __fastcall sub_7FF76FC77F30(int a1, int a2)
{
char *v2; // rdi
__int64 i; // rcx
int v4; // edx
char v6[32]; // [rsp+0h] [rbp-30h] BYREF
char v7; // [rsp+30h] [rbp+0h] BYREF
char Src[96]; // [rsp+40h] [rbp+10h] BYREF
char v9[64]; // [rsp+A0h] [rbp+70h] BYREF
int j; // [rsp+F4h] [rbp+C4h]

v2 = &v7;
for ( i = 58i64; i; --i )
{
*(_DWORD *)v2 = 0xCCCCCCCC;
v2 += 4;
}
sub_7FF76FC713CA((__int64)&unk_7FF76FC860A6);
j_memcpy(v9, Src, sizeof(v9));
LOBYTE(v4) = 8;
sub_7FF76FC713B6(a2, v4, a1, (int)Src, 8); //这是一个魔改DES
for ( j = 0; j < 64; ++j )
{
if ( (unsigned __int8)Src[j] != dword_7FF76FC80000[j] ) //密文
{
printf("wrong_wrong!!!\n");
exit(1);
}
}
return sub_7FF76FC7135C(v6, &unk_7FF76FC7D1F0);
}

发现了一个DES加密,密钥为HZNUCTF{,查看该DES:

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
__int64 __fastcall DES_0(__int64 key, char a2, const void *m, void *out, unsigned int a5)
{
char *v5; // rdi
__int64 i; // rcx
__int64 v7; // rax
__int64 v8; // rdi
char v10[32]; // [rsp+0h] [rbp-20h] BYREF
char v11; // [rsp+20h] [rbp+0h] BYREF
char v12[280]; // [rsp+30h] [rbp+10h] BYREF
char key_1[32]; // [rsp+148h] [rbp+128h] BYREF
char v14[204]; // [rsp+168h] [rbp+148h] BYREF
char v15; // [rsp+234h] [rbp+214h]

v5 = &v11;
for ( i = 90i64; i; --i )
{
*(_DWORD *)v5 = 0xCCCCCCCC;
v5 += 4;
}
checkfordebuggerjustmycode(byte_7FF76FC860A6);
if ( a5 && key && out && m )
{
if ( a2 == 8 || a2 == 16 )
{
j_memmove(out, m, (int)(8 * a5));
v15 = a2;
if ( a2 == 8 ) //DES
{
j_memcpy(key_1, (const void *)key, 8ui64);
Func1((__int64)v12, (__int64)key_1);
Func2((__int64)v12, (__int64)out, a5);
}
else
{
if ( v15 != 16 ) //3DES
{
v7 = 0xFFFFFFFFi64;
goto LABEL_18;
}
j_memcpy(key_1, (const void *)key, 8ui64);
j_memcpy(v14, (const void *)(key + 8), 8ui64);
Func1((__int64)v12, (__int64)key_1);
Func2((__int64)v12, (__int64)out, a5);
Func1((__int64)v12, (__int64)v14);
Func3((__int64)v12, (__int64)out, a5);
Func1((__int64)v12, (__int64)key_1);
Func2((__int64)v12, (__int64)out, a5);
}
v7 = 0i64;
goto LABEL_18;
}
v7 = 0xFFFFFFFFi64;
}
else
{
v7 = 0xFFFFFFFFi64;
}
LABEL_18:
v8 = v7;
sub_7FF76FC7135C(v10, &unk_7FF76FC7D120);
return v8;
}

经过观察发现该DES是被魔改的,但是由于下面是3DES,所以Func3一定是解密函数,不妨把上面的DES的call Func2 patch为call Func3(毕竟参数都一样),E8 FC EF FF FF改为E8 BD ED FF FF,成功patch为Func3,进行动态调试,将密文patch进v12,观察执行后的字段:333936147332632923d96353321d3345636826d26314621d3349330463126348,可以注意到成功解密,换位后得到33393164733262392d396533312d343566382d626134612d3439303461326438,fromhex得到391ds2b9-9e31-45f8-ba4a-4904a2d8,flag:HZNUCTF{391ds2b9-9e31-45f8-ba4a-4904a2d8}.

index:

发现是wasm文件,但是前四字节的magic被改成了大写,改回小写后用ghidra解析:

1
2
3
4
5
6
undefined4 export::main(undefined4 param1,undefined4 param2)
{
undefined4 uVar1;
uVar1 = main_0();
return uVar1;
}

查看main0(函数名称已手动恢复):

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
undefined4 main_0(void)
{
printf(s_Welcome_to_HZNUCTF!!!_ram_000100ae,0);
printf(s_Plz_input_the_key:_ram_00010051,0);
local_70 = &local_40;
scanf(0x10026,&local_70);
iVar1 = strcmp(&local_40,s_TGCTF404_ram_00011020); //TGCTF404
if (iVar1 == 0) {
xor51(&local_40); //每个字节异或0x51
printf(s_Ok,_let’s_go!!!_ram_0001007a,0);
printf(s_Plz_input_the_flag:_ram_00010065,0);
local_80[0] = &local_30;
scanf(0x10026,local_80);
iVar1 = strlen(&local_30); //长度为32
if (iVar1 == 0x20) {
encrypt1(&local_30,&local_60); //第一个加密
for (local_64 = 0; local_64 < 8; local_64 = local_64 + 1) {
encrypt2((int)&local_60 + local_64 * 4,&local_40); //一次加密4字节
}
for (local_68 = 0; local_68 < 8; local_68 = local_68 + 1) {
for (local_6c = 0; local_6c < 4; local_6c = local_6c + 1) {
if ((uint)*(byte *)((int)&local_60 + local_6c + local_68 * 4) !=
DWORD_ARRAY_ram_00010fa0[local_68 * 4 + local_6c]) { //与密文比较
printf(s_wrong_wrong!!!_ram_00010042,0);
return 0;
}
}
}
printf(s_Congratulation!!!_ram_0001008b,0);
}
else {
printf(s_wrong_wrong!!!_ram_00010042,0);
}
}
else {
printf(s_wrong_wrong!!!_ram_0001009e,0);
}
return 0;
}

发现是首先读取输入存储在v0+64,然后与TGCTF404进行比较,如果相等,进入异或0x51的函数:

1
2
3
4
5
6
7
8
9
10
11
void xor51(int param1)
{
int iVar1;
int local_c;

iVar1 = strlen(param1);
for (local_c = 0; local_c < iVar1; local_c = local_c + 1) {
*(byte *)(param1 + local_c) = *(byte *)(param1 + local_c) ^ 0x51;
}
return;
}

发现是将TGCTF404循环异或0x51,继续分析main0,接下来输入flag,存储在80,且要求长度为32,接下来进行了一个类似于randomize的函数,将打乱的结果存储在32,循环用encrypt2和异或后的key进行加密,之后将密文与0x10fa0位置的dword密文进行比较,输出结果,先提取密文,得到841c6bf74922d642507b42f446a98362d13280426a10a3f2e2b80b76b0dc0251,继续分析加密逻辑,先查看encrypt1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void encrypt1(int param1,int param2)
{
int local_14;
int local_10;
int local_c;

local_c = 0;
unnamed_function_15(0x194);
unnamed_function_8(param1,0x20);
for (local_10 = 0; local_10 < 8; local_10 = local_10 + 1) {
for (local_14 = 0; local_14 < 4; local_14 = local_14 + 1) {
*(undefined *)(param2 + local_10 * 4 + local_14) = *(undefined *)(param1 + local_c);
local_c = local_c + 1;
}
}
return;
}
1
2
3
4
5
void unnamed_function_15(int param1)
{
uRam00011208 = (ulonglong)(param1 - 1);
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void unnamed_function_8(int param1,int param2)
{
undefined uVar1;
int iVar2;
byte local_11;
int local_c;

if (1 < param2) {
for (local_c = 0; local_c < param2 + -1; local_c = local_c + 1) {
iVar2 = unnamed_function_16();
iVar2 = local_c + iVar2 / (0x7fffffff / (param2 - local_c) + 1);
uVar1 = *(undefined *)(param1 + iVar2);
*(undefined *)(param1 + iVar2) = *(undefined *)(param1 + local_c);
*(undefined *)(param1 + local_c) = uVar1;
}
}
return;
}
1
2
3
4
5
uint unnamed_function_16(void)
{
lRam00011208 = lRam00011208 * 0x5851f42d4c957f2d + 1;
return (uint)((ulonglong)lRam00011208 >> 0x21);
}

发现是将输入的明文随机打乱,随机数种子为0x194(404),再分析encrypt2,发现这个函数是被循环调用的,一次只加密了输入值的一个dword,循环8次,查看encrypt2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void encrypt2(int param1,int param2)
{
int iVar1;
uint uVar2;
int local_1c;
byte local_15;
byte local_9;

iVar1 = (int)*(char *)(param2 + iRam00011200) >> 4;
uVar2 = (int)*(char *)(param2 + iRam00011200) & 0xf;
iRam00011200 = iRam00011200 + 1;
encrypt3(ARRAY_ram_00010ea0,(int)(char)ARRAY_ram_00010da0[uVar2 + iVar1 * 0x10]); //带入SM4的sbox和密钥的全局索引
for (local_1c = 0; local_1c < 4; local_1c = local_1c + 1) {
*(byte *)(param1 + local_1c) = *(byte *)(param1 + local_1c) ^ ARRAY_ram_00010ea0[iVar1 * 0x10 + local_1c * 0x11 + uVar2 ]; //异或低字节+循环索引*0x11
*(byte *)(param1 + local_1c) = *(byte *)(param1 + local_1c) ^ s_TGCTF404_ram_00011020[local_1 c];
}
return;
}

其中0x10ea0的位置是SM4的S盒,0x10da0的位置是AES的S盒,继续查看encrypt3:

1
2
3
4
5
6
7
8
9
10
void encrypt3(int param1,undefined4 param2)
{
int local_c;
byte local_5;
for (local_c = 0; local_c < 0x100; local_c = local_c + 1) {
*(byte *)(param1 + local_c) = *(byte *)(param1 + local_c) ^ (byte)param2;
*(undefined *)(param1 + local_c) = *(undefined *)(param1 + local_c);
}
return;
}

encrypt3对SM4的S盒进行异或AES的S盒的指定索引的值,构成新的S盒,回到encrypt2,发现是将字符异或新的S盒的指定索引,然后再异或被异或后的密钥,综上所述已经分析完毕,编写脚本解密:

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
AES_sbox = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01,
0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D,
0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4,
0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7,
0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E,
0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB,
0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C,
0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C,
0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A,
0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3,
0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A,
0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E,
0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9,
0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99,
0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

SM4_sbox = [0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6,
0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05, 0x2B, 0x67, 0x9A, 0x76,
0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86,
0x06, 0x99, 0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A,
0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62, 0xE4, 0xB3,
0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA,
0x75, 0x8F, 0x3F, 0xA6, 0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73,
0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB,
0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35, 0x1E, 0x24, 0x0E, 0x5E,
0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21,
0x78, 0x87, 0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52,
0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, 0xEA, 0xBF,
0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE,
0xF9, 0x61, 0x15, 0xA1, 0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34,
0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29,
0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, 0xD5, 0xDB, 0x37, 0x45,
0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C,
0x5B, 0x51, 0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F,
0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8, 0x0A, 0xC1,
0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12,
0xB8, 0xE5, 0xB4, 0xB0, 0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96,
0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE,
0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48]

sbox = SM4_sbox.copy()

key = bytearray(b"TGCTF404")

# 还原密钥再次加密
for i in range(len(key)):
key[i] ^= 0x51

# 还原被加密的SM4的sbox
for i in range(len(key)):
for j in range(0x100):
SM4_sbox[j] ^= AES_sbox[key[i]] # 异或密钥索引8次

# 密文
cipher = bytearray.fromhex("841c6bf74922d642507b42f446a98362d13280426a10a3f2e2b80b76b0dc0251")
result = bytearray(32)

for i in range(7, -1, -1):
enc = cipher[i * 4 : (i + 1) * 4] # 4字节
for j in range(4):
enc[j] ^= key[j]
enc[j] ^= SM4_sbox[j * 0x11 + key[i]] # key的字节
for k in range(0x100): # 解密一层sbox
SM4_sbox[k] ^= AES_sbox[key[i]]
for l in range(4):
result[4 * i + l] = enc[l] ^ 0x51
print(bytearray([enc[l] ^ 0x51]).decode(),end="")
print()
assert SM4_sbox == sbox
print(result)
dec = result.copy()
# 加密回去以验证
for i in range(8):
m = result[i * 4 : (i + 1) * 4]
for k in range(0x100): # 加密
SM4_sbox[k] ^= AES_sbox[key[i]]
for l in range(4):
m[l] ^= 0x51
for j in range(4):
m[j] ^= SM4_sbox[j * 0x11 + key[i]] # key的字节
m[j] ^= key[j]
result[i * 4 : (i + 1) * 4] = m
print(result)

assert result == cipher

得到中间密文Z49H539c{–6}d4888bTUCf8NeFe–e9,继续解密一阶段的洗牌算法,编写脚本同构加密过程:

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
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

void randd(unsigned char* array, int length) {
if (length > 1) {
for (int i = 0; i < length; i++) {
int j = i + rand() / (0x7FFF / (length - i) + 1);
uint8_t temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}

void encrypt1(unsigned char* param1, unsigned char* param2) {
int local_c = 0;
srand(0x194);
randd(param1, 32);

for (int i = 0; i < 8; i++) {
for (int j = 0; j < 4; j++) {
param2[i * 4 + j] = param1[local_c++];
}
}
}

int main() {
unsigned char input[33] = "123456ABCDEFGHiJKLMNOPQRSTUVWXYZ";
unsigned char output[32] = { 0 };
unsigned char original[32];
memcpy(original, input, 32);
encrypt1(input, output);

printf("before -> after:\n");
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 32; j++) {
if (output[j] == original[i]) {
printf("input[%2d] ('%c') -> output[%2d] ('%c')\n", i, original[i], j, output[j]);
break;
}
}
}

printf("\nafter:\n");
for (int i = 0; i < 32; i++) {
printf("%c", (output[i] >= 32 && output[i] <= 126) ? output[i] : '.');
}
printf("\n");

return 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
before -> after:
input[ 0] ('1') -> output[ 3] ('1')
input[ 1] ('2') -> output[ 0] ('2')
input[ 2] ('3') -> output[24] ('3')
input[ 3] ('4') -> output[20] ('4')
input[ 4] ('5') -> output[21] ('5')
input[ 5] ('6') -> output[19] ('6')
input[ 6] ('A') -> output[26] ('A')
input[ 7] ('B') -> output[ 8] ('B')
input[ 8] ('C') -> output[22] ('C')
input[ 9] ('D') -> output[17] ('D')
input[10] ('E') -> output[31] ('E')
input[11] ('F') -> output[15] ('F')
input[12] ('G') -> output[ 9] ('G')
input[13] ('H') -> output[13] ('H')
input[14] ('i') -> output[25] ('i')
input[15] ('J') -> output[23] ('J')
input[16] ('K') -> output[ 4] ('K')
input[17] ('L') -> output[10] ('L')
input[18] ('M') -> output[14] ('M')
input[19] ('N') -> output[11] ('N')
input[20] ('O') -> output[30] ('O')
input[21] ('P') -> output[28] ('P')
input[22] ('Q') -> output[ 6] ('Q')
input[23] ('R') -> output[27] ('R')
input[24] ('S') -> output[ 1] ('S')
input[25] ('T') -> output[ 5] ('T')
input[26] ('U') -> output[29] ('U')
input[27] ('V') -> output[18] ('V')
input[28] ('W') -> output[ 2] ('W')
input[29] ('X') -> output[ 7] ('X')
input[30] ('Y') -> output[16] ('Y')
input[31] ('Z') -> output[12] ('Z')

after:
2SW1KTQXBGLNZHMFYDV645CJ3iARPUOE

编写脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

int main() {

char output[33] = "Z49H539c{--6}d4888bTUCf8NeFe--e9";
char input[33] = {0};

int mapping[32] = {
3, 0, 24, 20, 21, 19, 26, 8,
22, 17, 31, 15, 9, 13, 25, 23,
4, 10, 14, 11, 30, 28, 6, 27,
1, 5, 29, 18, 2, 7, 16, 12
};

for (int i = 0; i < 32; i++) {
input[i] = output[mapping[i]];
}
input[32] = '\0';

printf("%s\n", input);

return 0;
}

HZNUCTF{f898-de85-46e-9e43-b9c8}.

总结:

本次比赛费了很大劲终于把re ak了…

对三道压轴题的评价:

conforand的ollvm是真的

exchange的patch call解密函数实际上是非预期,不过魔改DES我是真逆不太动(x)

index还好,换个工具解析就行,就是随机数部分有点不稳定,直接用反编译出来的貌似不太行,用库函数反而出了…