京麒CTF 2025 热身赛 wp

re1

rust,缺少符号表,很难看

image-20250517104329967

使用bindiff恢复符号表,首先需要配置rust环境

Rust语言开发环境搭建详细教程_rust环境搭建-CSDN博客

image-20250517121241854

参考rust恢复符号表 | clev1L’s blog

恢复了一点点吧

根据可疑字符串定位到

image-20250517121351115

image-20250517121357513

关键部分

image-20250517152354130

v29是

1
v29 = *(_WORD *)v16;

将内存地址 v16 处的前两个字节解释为一个 unsigned short(即 16 位无符号整数),赋值给 v29

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

enc = [0x0, 0xA1, 0xFB, 0x53, 0x1C, 0xFA, 0xF0, 0x1B, 0x6, 0x40, 0xD4, 0x8C,
0x16, 0xF4, 0x90, 0x27, 0x42, 0xB9, 0x8B, 0xF, 0x2, 0xD7, 0x31, 0xB7,
0x26, 0x12, 0x6, 0x7E, 0xAE, 0xDF, 0xDA, 0x68, 0xAF, 0x35, 0xCC, 0xB7,
0xB0, 0xD0, 0x9A, 0x59, 0x2B, 0xB] # length = 42

def rol(val, r_bits, max_bits=8):
return ((val << r_bits) & (2**max_bits - 1)) | (val >> (max_bits - r_bits))

def decrypt(ciphertext):
prefix = b"fl"
result = bytearray(len(ciphertext))
v29 = int.from_bytes(prefix[:2], "little")
for i in range(len(ciphertext)):
v33 = ((v29 >> 2) ^ (v29 >> 3) ^ (v29 >> 1)) & 0xFFFF
next_v29 = ((v29 >> 1) | (v33 << 15)) & 0xFFFF
v34 = rol(v29 & 0xFF, 4)
v35 = ((4 * (v34 & 0x33)) | ((v34 >> 2) & 0x33)) & 0xFF
xor_val = (i + ((2 * (v35 & 0x55)) | ((v35 >> 1) & 0x55))) & 0xFF
result[i] = ciphertext[i] ^ xor_val
v29 = next_v29
return bytes(result)

ciphertext = bytes(enc)
plaintext = decrypt(ciphertext)
print(plaintext.decode(errors='ignore'))

仔细解释下

1
2
next_v29 = ((v29 >> 1) | (v33 << 15)) & 0xFFFF
v34 = rol(v29 & 0xFF, 4)

为什么是v29 & 0XFF,因为rol是byte,而v33的byte是v29

image-20250517165345969

flag{1c98572d-7f7b-4fbf-8750-4a2986c695ce}

re2

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
# v8: shuffle 掩码(reverse)
shuffle_mask = list(reversed(range(16)))

# v9: 每字节加法掩码
add_mask = bytes([
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10
])

# 加密的目标密文
ciphertext = b"cge87k?9<>?@=pss393=>;8@:Cp@DAuH"

def decrypt_block(block: bytes) -> bytes:
# 先减去加法掩码
subtracted = bytes((b - m) & 0xFF for b, m in zip(block, add_mask))
# 再反转(inverse shuffle)
return bytes(subtracted[i] for i in shuffle_mask)

# 解密两个 block
plaintext = b""
for i in range(0, len(ciphertext), 16):
block = ciphertext[i:i+16]
plaintext += decrypt_block(block)

# 输出 flag
print(f"flag{{{plaintext.decode()}}}")

GPT速通

flag{cdb0444318e24beb8f374e9181599072}

re!!!

IOS逆向

image-20250517184812914

看着像密文

image-20250517184947497

image-20250517185049438

image-20250517185105569

rc4 –> base64 –> obfusecateString –> transformString.

先看最后一个

image-20250517190752379

根据 n190 的大小分三种情况(单字节、两字节、三字节或四字节)

逆一下:

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
def decrypt_specialized(hex_str: str) -> str:
# 1. 清理输入:去掉空白
hex_str = ''.join(hex_str.split())
# 2. 验证长度为偶数
if len(hex_str) % 2 != 0:
raise ValueError(f"Invalid hex string length {len(hex_str)}, must be even.")
# 3. 从十六进制解码为字节
data = bytes.fromhex(hex_str)
# 4. 按 UTF-8 解码得到中间混淆字符串
transformed = data.decode('utf-8')

result_chars = []
# 5. 逐字符逆向还原
for i, ch in enumerate(transformed):
# 偶数位置使用密钥 0xEF,奇数位置使用 0xBE
key = 0xEF if (i % 2 == 0) else 0xBE
code = ord(ch)
# ASCII 范围时先减 1 再异或,否则直接异或
if code <= 0x7F:
n190 = code - 1
else:
n190 = code
orig_cp = n190 ^ key
result_chars.append(chr(orig_cp))

# 6. 拼接并返回结果
return ''.join(result_chars)

if __name__ == '__main__':
# 示例:将下面替换为你的密文 hex 字符串
cipher_hex = (
"c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28f"
"c2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c28"
"0c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282"
"c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c25631"
"0c299c3946931c291c3917a2e470b632a7811730f65c385"
)
try:
plaintext = decrypt_specialized(cipher_hex)
print(plaintext)
except Exception as e:
print("解密失败:", e)

接下来那部分:

这段是核心混淆逻辑:

  • 将当前字符值加上迭代器 v6(动态偏移);
  • 再加一个固定偏移 0xDEADBEEF
  • 然后取低字节;
  • 如果结果非负,取负值再取反(取补码)形成非线性变换。
1
2
3
4
if ((v11 & 0xFFFFFF80) != 0)
v13 = (((v11 & 0x3F) << 8) | ((unsigned int)v11 >> 6)) + 33217;
else
v13 = v11 + 1;

这是根据字符值决定如何处理的分支:

  • 如果是高位字符(非 ASCII),执行位运算变换并加上 33217。
  • 否则简单地加一。
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
import base64

def decrypt_specialized(hex_str: str):
hex_str = ''.join(hex_str.split())
if len(hex_str) % 2 != 0:
raise ValueError(f"Invalid hex string length {len(hex_str)}, must be even.")
data = bytes.fromhex(hex_str)
transformed = data.decode('utf-8')
result_chars = []
for i, ch in enumerate(transformed):
key = 0xEF if (i % 2 == 0) else 0xBE
code = ord(ch)
if code <= 0x7F:
n190 = code
else:
n190 = code
orig_cp = n190 ^ key
result_chars.append(chr(orig_cp))

# 6. 拼接并返回结果
return result_chars

if __name__ == '__main__':
hex_enc = "c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"
cipher_hex = (
"c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"
)
try:
plaintext = decrypt_specialized(cipher_hex)
flag_base64 = ''
for i in range(len(plaintext)):
flag_base64 += chr((ord(plaintext[i]) - i - 0xDEADBEEF) & 0xFF)
base64.b64decode(flag_base64).decode()
except Exception as e:
print("解密失败:", e)

adbe2979358798308c911dd4647a2f6a13c042cc324597e1edbc089a9e9015fba9

还有个RC4,key找了一圈发现几个init

image-20250517200405650

跟进下

image-20250517200426974

处理下大小端序的问题

Haruhikage

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

def decrypt_specialized(hex_str: str):
hex_str = ''.join(hex_str.split())
if len(hex_str) % 2 != 0:
raise ValueError(f"Invalid hex string length {len(hex_str)}, must be even.")
data = bytes.fromhex(hex_str)
transformed = data.decode('utf-8')
result_chars = []
for i, ch in enumerate(transformed):
key = 0xEF if (i % 2 == 0) else 0xBE
code = ord(ch)
if code <= 0x7F:
n190 = code
else:
n190 = code
orig_cp = n190 ^ key
result_chars.append(chr(orig_cp))

# 6. 拼接并返回结果
return result_chars

def rc4_decrypt(data: bytes, key: str) -> bytes:
# KSA (Key Scheduling Algorithm)
S = list(range(256))
j = 0
key_bytes = key.encode('utf-8')
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 = j = 0
out = bytearray()
for byte 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]
out.append(byte ^ K)
return bytes(out)

if __name__ == '__main__':
hex_enc = "c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"
cipher_hex = (
"c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"
)
try:
plaintext = decrypt_specialized(cipher_hex)
flag_base64 = ''
for i in range(len(plaintext)):
flag_base64 += chr((ord(plaintext[i]) - i - 0xDEADBEEF) & 0xFF)
rc4_enc = base64.b64decode(flag_base64)
rc4_enc_bytes = bytes.fromhex(rc4_enc.decode())
key = 'Haruhikage'
decrypted = rc4_decrypt(rc4_enc_bytes, key)
print(decrypted)
except Exception as e:
print("解密失败:", e)

flag{N4nd3_H4ruhik4g3_Y4tt4n0?!!}

image-20250517202557093