第一届OpenHarmony CTF专题赛RE题解

easyre

abc-decompiler反编译如下

image-20250628103130983

.abcD的效果好像更好,对比下

image-20250628103737456

image-20250628103925761

magic为base64解出来:4d4b84c22f434cc8cda6dd4703af89dab83

hint1在index文件中

image-20250628104139822

自解密部分,这块实际上就是首先+上自己的长度,然后反转,逐字符-i在反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def reverseStr(s):
return s[::-1]

def decode_hint1(hint1):
# Step 1: 构造 r10
hint_len = len(hint1)
r10 = ''.join([chr(ord(c) + hint_len) for c in hint1])

# Step 2: reverse r10
rev_r10 = reverseStr(r10)

# Step 3: 构造 r43
r43 = ''.join([chr(ord(rev_r10[i]) - i) for i in range(len(rev_r10))])

# Step 4: reverse r43 得到最终 hint1 参数
final_hint1 = reverseStr(r43)
return final_hint1

# 输入
input_hint1 = "tlfr`llakodZbjW_aR"
# 输出结果
print(decode_hint1(input_hint1))

universityofoxford

arkts

魔改RC4->RSA->换表Base64,Gemini速通

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

def custom_base64_decode(encoded_str):
custom_alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/'
standard_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

decode_map = {char: standard_alphabet[custom_alphabet.find(char)] for char in custom_alphabet}

decoded_base64_str = ""
for char in encoded_str:
if char == '=':
decoded_base64_str += '='
else:
decoded_base64_str += decode_map[char]
reverse_map = {v: k for k, v in zip(standard_alphabet, custom_alphabet)}

original_string = ""
for char in encoded_str:
if char in reverse_map:
original_string += reverse_map[char]
else:
original_string += char # for '=' or other characters


# Let's implement the full reverse chain.
import base64
# 1. Reverse character substitution
standard_b64_str = ""
for char in encoded_str.replace('=', ''):
standard_b64_str += standard_alphabet[custom_alphabet.find(char)]

# The padding might be lost, let's add it back if needed.
padding = len(encoded_str) - len(encoded_str.rstrip('='))
standard_b64_str += '=' * padding

# 2. Base64 decode
try:
decoded_bytes = base64.b64decode(standard_b64_str)
except Exception as e:
# The padding might be tricky. Let's try to fix it.
missing_padding = len(standard_b64_str) % 4
if missing_padding:
standard_b64_str += '='* (4 - missing_padding)
decoded_bytes = base64.b64decode(standard_b64_str)

# 3. Bytes to string (number)
return decoded_bytes.decode('utf-8')

def mod_pow(base, exp, mod):
res = 1
base %= mod
while exp > 0:
if exp % 2 == 1:
res = (res * base) % mod
base = (base * base) % mod
exp //= 2
return res

def mod_inverse(a, m):
return pow(a, -1, m)

def rsa_decrypt(cipher_arr):
N = 75067
# p=271, q=277
phi_N = 270 * 276 # 74520
e = 7
d = mod_inverse(e, phi_N) # 42583

decrypted = []
for c in cipher_arr:
decrypted.append(mod_pow(c, d, N))
return decrypted

def rc4_encrypt(key, data):
S = list(range(256))
j = 0
key_bytes = key.encode('utf-8')

# KSA (Key-Scheduling Algorithm)
for i in range(256):
j = (j + S[i] + key_bytes[i % len(key_bytes)]) % 256
S[i], S[j] = S[j], S[i]

# PRGA (Pseudo-Random Generation Algorithm)
i = 0
j = 0
output = bytearray()
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
output.append(char ^ k)

return output

# The JS RC4 is not standard. It has a bug/feature.
# Let's re-implement the JS RC4 exactly.
def rc4_encrypt_js(key, data_str):
p1 = key
p2 = data_str
r0 = list(range(256))
r23 = 0
r24 = 0
while r24 < 256:
r33 = r23 % len(p1)
r37 = (r23 + r0[r23] + ord(p1[r33])) % 256
temp = r0[r24]
r0[r24] = r0[r37]
r0[r37] = temp

r23 = r37
r24 += 1

r45 = bytearray(len(p2))
r56 = 0
r57 = 0
r58 = 0
while r56 < len(p2):
r66 = (r58 + 1) % 256
r70 = (r57 + r0[r66]) % 256
# Another weird swap `r0[r66] = r0[r70]; r0[r70] = r0[r66];`
# Let's assume it's a swap.
temp = r0[r66]
r0[r66] = r0[r70]
r0[r70] = temp

# The encryption part is also weird: `(p2.charCodeAt(r56) + r0[(r0[r66] + r0[r70]) % 256]) % 256`
# It's addition, not XOR. So decryption is subtraction.
keystream_val = r0[(r0[r66] + r0[r70]) % 256]
r45[r56] = (ord(p2[r56]) - keystream_val + 256) % 256
r56 += 1
r57 = r70
r58 = r66
return r45.decode('utf-8')

def rc4_decrypt_js(key, data_bytes):
p1 = key
p2 = data_bytes
r0 = list(range(256))
r23 = 0
r24 = 0
while r24 < 256:
r33 = r23 % len(p1)
r37 = (r23 + r0[r23] + ord(p1[r33])) % 256
# Assuming a real swap was intended
r0[r24], r0[r37] = r0[r37], r0[r24]
r23 = r37
r24 += 1

r45 = bytearray(len(p2))
r56 = 0
r57 = 0
r58 = 0
while r56 < len(p2):
r66 = (r58 + 1) % 256
r70 = (r57 + r0[r66]) % 256
# Assuming a real swap was intended
r0[r66], r0[r70] = r0[r70], r0[r66]

# Decryption is subtraction
keystream_val = r0[(r0[r66] + r0[r70]) % 256]
r45[r56] = (p2[r56] - keystream_val + 256) % 256
r56 += 1
r57 = r70
r58 = r66
return r45

if __name__ == '__main__':
target_cipher = ['ndG5nZa=', 'nte3ndK=', 'nJy2nJi=', 'mtK0mJG=', 'nde5mZK=', 'mtiWnda=', 'ntq1nZm=', 'mZG0mJq=', 'nJe4ma==', 'nJG4mW==', 'mJa0mZG=', 'mty1mte=', 'mtu3odq=', 'nJyZmJy=', 'nJeWody=', 'mJy1ntm=', 'ntaWody=', 'ma==', 'ntqYodK=', 'ndK2nJm=', 'nJyZndq=', 'ntaWody=', 'ndGYndi=', 'nJG4mW==', 'mJu5mG==', 'mtiYmda=', 'mZmWnde=', 'mteXndC=', 'ndqXndm=', 'mte1mZi=', 'mJy5ntq=', 'mZC4mtC=', 'mJe4nW==', 'nJC3odu=', 'ndyXmdK=', 'ndG5nZa=', 'ndaZnZa=', 'mtK0nJa=']
secret_key = 'OHCTF2026'

# Step 1: Decode from customBase64 to get RSA ciphertexts (as numbers)
rsa_cipher_nums = [int(custom_base64_decode(c)) for c in target_cipher]

# Step 2: Decrypt RSA
rc4_encrypted_codes = rsa_decrypt(rsa_cipher_nums)

# Step 3: Decrypt RC4
# The result of RSA decrypt is a list of char codes. This is the RC4 encrypted data.
rc4_encrypted_bytes = bytes(rc4_encrypted_codes)

# The JS RC4 is non-standard. Let's use the direct implementation.
flag_bytes = rc4_decrypt_js(secret_key, rc4_encrypted_bytes)

print(f"Flag: {flag_bytes.decode('utf-8')}")

我自己写的:

image-20250628170406117

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

enc = ["ndG5nZa=", "nte3ndK=", "nJy2nJi=", "mtK0mJG=", "nde5mZK=", "mtiWnda=", "ntq1nZm=", "mZG0mJq=", "nJe4ma==",
"nJG4mW==", "mJa0mZG=", "mty1mte=", "mtu3odq=", "nJyZmJy=", "nJeWody=", "mJy1ntm=", "ntaWody=", "ma==",
"ntqYodK=", "ndK2nJm=", "nJyZndq=", "ntaWody=", "ndGYndi=", "nJG4mW==", "mJu5mG==", "mtiYmda=", "mZmWnde=",
"mteXndC=", "ndqXndm=", "mte1mZi=", "mJy5ntq=", "mZC4mtC=", "mJe4nW==", "nJC3odu=", "ndyXmdK=", "ndG5nZa=",
"ndaZnZa=", "mtK0nJa="]

def custom_base64_decode(custom_b64_str):
custom_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
standard_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
trans_table = str.maketrans(custom_chars, standard_chars)
standard_b64_str = custom_b64_str.translate(trans_table)
decoded_bytes = base64.b64decode(standard_b64_str)
return decoded_bytes

decoded_numbers = [int(custom_base64_decode(i).decode()) for i in enc]

def rsa_decrypt(c):
q = 277
p = 271
e = 7
n = 75067
d = libnum.invmod(e, (p - 1) * (q - 1))
return pow(c, d, n)

decoded_numbers = [rsa_decrypt(i) for i in decoded_numbers]

def rc4_decrypt_custom(key, encrypted_bytes) -> str:
S = list(range(256))
j = 0

# KSA:Key Scheduling Algorithm
for i in range(256):
j = (j + S[j] + ord(key[j % len(key)])) % 256
S[i], S[j] = S[j], S[i]

# 解密流程
i = 0
j = 0
result = ''
for byte in encrypted_bytes:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
plain = (byte - K + 256) % 256
result += chr(plain)
return result

key = "OHCTF2026"
key = [ord(i) for i in key]

flag = rc4_decrypt_custom("OHCTF2026", decoded_numbers)
print("Recovered plaintext:", flag)

Secret

DevStudio打开看了下,有个手势验证,跟之前一样,先分析顶层代码

找一圈没找到相关的加密或调用,最后在lock头部发现了一个so的调用,直接去so里看

image-20250630193150447

看字符串没什么头绪,好在方法不多一个个看下去发现了一个Base64一个SM4还发现了疑似校验函数

image-20250630194316036

在init_proc_(uint *)方法里发现了tea家族的算法

image-20250630194732970

这个init函数在校验那个函数得到调用这样就理的清了

key就是what不过要注意端绪转化,这是个xxtea加密,很明显有论粗round

1
key = [0x0000000B, 0x0000002D, 0x0000000E, 0x0001BF52]

enc

1
v = [0xeb159b69, 0x71efca1b, 0x91c9c6c6, 0x957af873, 0xd3deab9, 0x27894343, 0x61d6415b, 0x1f80fed8,0xdf62f1d9]
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
def decrypt_block(data: list[int], n,key: list[int]) -> list[int]:
assert len(data) == 9
assert len(key) == 4
delta = 0x9E3779B9
rounds = 6 + 52 // n
sum = (delta * rounds) & 0xFFFFFFFF
v = data.copy()
x = v[0]
for _ in range(rounds):
e = (sum >> 2) & 3
for i in range(n - 1, 0, -1):
z = v[i - 1]
t1 = (z >> 5) ^ ((x << 2) & 0xFFFFFFFF)
t2 = (x >> 3) ^ ((z << 4) & 0xFFFFFFFF)
t3 = sum ^ x
t4 = key[(i & 3) ^ e] ^ z
v[i] = (v[i] - ((t1 + t2) ^ (t3 + t4))) & 0xFFFFFFFF
x = v[i]
z = v[n - 1]
t1 = (z >> 5) ^ ((x << 2) & 0xFFFFFFFF)
t2 = (x >> 3) ^ ((z << 4) & 0xFFFFFFFF)
t3 = sum ^ x
t4 = key[e] ^ z
v[0] = (v[0] - ((t1 + t2) ^ (t3 + t4))) & 0xFFFFFFFF
x = v[0]
sum = (sum - delta) & 0xFFFFFFFF
return v

# === 输入 ===
enc = [
0xeb159b69, 0x71efca1b, 0x91c9c6c6, 0x957af873,
0x0d3deab9, 0x27894343, 0x61d6415b, 0x1f80fed8, 0xdf62f1d9
]
key = [0x0000000B, 0x0000002D, 0x0000000E, 0x0001BF52]

plaintext = decrypt_block(enc, len(enc),key)

print("Decrypted uint32 block:")
for i, val in enumerate(plaintext):
print(f"[{i}] = 0x{val:08X} ({val})")

解出来得到134507286

输入后发现还要输入图片,回到Arkts层看看,其实感觉跟Base64和SM4有关,不然也不会放在这

image-20250630210029213

image-20250630210046713

就是把媒体文件做base64encode保存到bb.txt中

bb.txt在做某种处理

image-20250630210315642

iterate32很明显的SM4的密钥生成了,注意有魔改

image-20250630210810604

enc在如下部分:

image-20250701093947618

Base64然后tohex即可

image-20250701100145397

key不太好找:

image-20250701100111289

写SM4的时候端绪搞了很长时间服了

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
S_BOX = [
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
]

FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]

def wd_to_byte(wd, bys):
bys.extend([(wd >> i) & 0xff for i in range(24, -1, -8)])

def bys_to_wd(bys):
ret = 0
for i in range(4):
bits = 24 - i * 8
ret |= (bys[i] << bits)
return ret

# 大端序使用
def s_box(wd):
ret = []
for i in range(0, 4):
byte = (wd >> (24 - i * 8)) & 0xff
row = byte >> 4
col = byte & 0x0f
index = (row * 16 + col)
ret.append(S_BOX[index])
return bys_to_wd(ret)


def rotate_left(wd, bit):
return (wd << bit & 0xffffffff) | (wd >> (32 - bit))


def Linear_transformation(wd):
return wd ^ rotate_left(wd, 2) ^ rotate_left(wd, 10) ^ rotate_left(wd, 18) ^ rotate_left(wd, 24)

# 小端序使用
def functionB(b):
a = [
(b >> 24) & 0xFF,
(b >> 16) & 0xFF,
(b >> 8) & 0xFF,
b & 0xFF
]
return (
S_BOX[a[0]] << 24 |
S_BOX[a[1]] << 16 |
S_BOX[a[2]] << 8 |
S_BOX[a[3]]
)

def Tx(k1, k2, k3, ck):
xor = k1 ^ k2 ^ k3 ^ ck
t = s_box(xor)
return t ^ rotate_left(t, 13) ^ rotate_left(t, 23)


def T(x1, x2, x3, rk):
t = x1 ^ x2 ^ x3 ^ rk
t = s_box(t)
return t ^ rotate_left(t, 2) ^ rotate_left(t, 10) ^ rotate_left(t, 18) ^ rotate_left(t, 24)

def key_extend(MK):
keys = [FK[i] ^ MK[i] for i in range(4)]
RK = []
for i in range(32):
t = keys[i] ^ Tx(keys[i + 1], keys[i + 2], keys[i + 3], CK[i])
keys.append(t)
RK.append(t)
return RK

def getRK(MK):
K = [MK[i] ^ FK[i] for i in range(4)]
RK = []
for i in range(32):
tmp = K[i % 4] ^ Tx(K[(i + 1) % 4] , K[(i + 2) % 4] , K[(i + 3) % 4] , CK[i])
K[(i + 4) % 4] = tmp
RK.append(tmp)
return RK

def R(x0, x1, x2, x3):
x0 &= 0xffffffff
x1 &= 0xffffffff
x2 &= 0xffffffff
x3 &= 0xffffffff
s = f"{x3:08x}{x2:08x}{x1:08x}{x0:08x}"
return s

def decrypt_block(X, RK):
X = X[:]
for i in range(32):
tmp = T(X[(i + 1) % 4] ,X[(i + 2) % 4] , X[(i + 3) % 4] , RK[31 - i])
X[(i + 4) % 4] = X[i % 4] ^ tmp ^ 0x9E3779B9
return X[::-1]

def u32_to_bytes(val):
return val.to_bytes(4, byteorder='big')

if __name__ == '__main__':
MK = [0xE52BCC34, 0x1F1B5B18, 0x5F1ED75A, 0xF108FE7F]
rk = key_extend(MK)
print(rk)
result = b''
for i in range(0, len(enc), 4):
block = enc[i:i+4]
decrypted = decrypt_block(block, rk)
for word in decrypted:
result += u32_to_bytes(word)

print(result.decode())

总算研究透了

image-20250701114905122

上传一下

image-20250701115551219

可以解出来了