L3HCTF 2025 wp

MISC

Please Sign In

没看题目,全AI写的,直接出了

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
import torch
import json
from PIL import Image
from torchvision.models import shufflenet_v2_x1_0, ShuffleNet_V2_X1_0_Weights
from torchvision import transforms
import time

# 1. 加载与服务器相同的模型
print("[*] Loading model...")
feature_extractor = shufflenet_v2_x1_0(weights=ShuffleNet_V2_X1_0_Weights.IMAGENET1K_V1)
feature_extractor.fc = torch.nn.Identity()
feature_extractor.eval()

# 2. 加载目标特征向量
print("[*] Loading target embedding from embedding.json...")
with open("embedding.json", "r") as f:
target_embedding = torch.tensor(json.load(f), dtype=torch.float32)

# 3. 初始化一个随机图片作为起点
# ShuffleNetV2 通常使用 224x224 的输入尺寸
# 我们创建一个可训练的随机张量
print("[*] Initializing a random image...")
# 使用 .convert("RGB") 后的图像没有 alpha 通道,所以是3通道
# unsqueeze(0) 会在前面增加一个 batch 维度,所以是 (1, 3, H, W)
# 这里我们直接创建 (1, 3, 224, 224) 的张量
input_image = torch.randn(1, 3, 224, 224, requires_grad=True)

# 4. 设置优化器
optimizer = torch.optim.Adam([input_image], lr=0.1)

# 5. 迭代优化
print("[*] Starting optimization process...")
start_time = time.time()
for i in range(2000): # 增加迭代次数以获得更好的精度
# 清零梯度
optimizer.zero_grad()

# 计算当前图片的特征向量
current_embedding = feature_extractor(input_image)

# 计算损失 (MSE)
loss = torch.mean((current_embedding - target_embedding) ** 2)

# 反向传播
loss.backward()

# 更新图片
optimizer.step()

# 将像素值限制在 [0, 1] 范围内,这对于图像是有效的
with torch.no_grad():
input_image.clamp_(0, 1)

if (i + 1) % 10 == 0:
print(f"Iteration [{i+1}/1000], Loss: {loss.item():.10f}")

# 检查是否达到目标
# 将目标设置得更低,为服务器上的浮点差异留出余量
if loss.item() < 1e-7:
print(f"\n[*] Success! Target loss reached at iteration {i+1}.")
break
else:
print("\n[*] Max iterations reached. The loss may not be low enough.")

end_time = time.time()
print(f"[*] Optimization finished in {end_time - start_time:.2f} seconds.")

# 6. 保存生成的图片
print("[*] Saving the generated image to solution.png...")
# 将张量转换为 PIL Image
# 首先移除 batch 维度,然后调整维度顺序 (C, H, W) -> (H, W, C)
solution_tensor = input_image.squeeze(0).permute(1, 2, 0).detach().cpu().numpy()
# 将像素值从 [0, 1] 转换为 [0, 255]
solution_image_array = (solution_tensor * 255).astype('uint8')
solution_image = Image.fromarray(solution_image_array)
solution_image.save("solution.png")

print("\n[+] Done. You can now use 'solution.png' to submit to the server.")
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
import requests

# 目标服务器地址
urls = [
"http://1.95.8.146:50001/signin/",
"http://1.95.34.119:50001/signin/",
"http://43.138.2.216:50001/signin/"
]

# 要上传的图片文件
file_path = "solution.png"

for url in urls:
try:
with open(file_path, "rb") as f:
files = {"file": (file_path, f, "image/png")}
print(f"[*] Attacking {url}...")
response = requests.post(url, files=files, timeout=10)
response.raise_for_status() # 如果请求失败则引发异常

# 打印服务器返回的结果
print(f"[*] Server response from {url}:")
print(response.json())

except requests.exceptions.RequestException as e:
print(f"[!] An error occurred while attacking {url}: {e}")
except Exception as e:
print(f"[!] An unexpected error occurred: {e}")

image-20250714194746102

Re

TemporalParadox

这个做了挺久的,我觉得比android难一点,不应该比android分低吧…

一开始打不开说是什么错误的输入……查了下是libstc++6这个版本不匹配,修了快俩小时,没修好,不能运行,放弃了看下一题easyvm发现了libstc++6这个文件,试试直接复制到这个文件没想到就能跑了

程序的逻辑是在一个时间段内程序才会有输出啥的,看了下

1
2
start_time = 1751990400
end_time = 1752052051

转换一下是2025-07-09 00:00:00—2025-07-12 21:01:08

这里有一个也有一个时间的校验,只能在特定时间内会进行校验

image-20250714200439925

可以patch掉或者直接改自己系统时间

image-20250714200342030

发现疑似密文的串

image-20250714200224727

多次运行程序发现

query由多部份构成salt t r a b x y

其中salt是不变的

t就是你的时间戳 rabxy是根据你的时间戳随机生成的

image-20250714200538010

image-20250714201247339

这一块是最重要的一直循环生成随机数,注意这个条件也是循环的,一开始没注意到…

这样的话,我们在那个时间段爆破即可找出到底是哪个t

8a2fc1e9e2830c37f8a7f51572a640aa是个md5串

最后对query做md5与其比对就行

做sha1就是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
import hashlib
import time
from multiprocessing import Pool, Manager, cpu_count

class PRNG:
def __init__(self, seed):
self.state = seed if seed != 0 else 1

def xorshift32(self):
x = self.state
x ^= (x << 13) & 0xFFFFFFFF
x ^= (x >> 17) & 0xFFFFFFFF
x ^= (x << 5) & 0xFFFFFFFF
self.state = x
return x & 0x7FFFFFFF

def worker(start_t, end_t, salt, target_hash, result_queue):
for t in range(start_t, end_t):
if not result_queue.empty():
return # Stop if another worker found the result

prng = PRNG(t)
a, b, x, y = 0, 0, 0, 0 # Initialize variables

i = 0
while i < (prng.xorshift32()):
a = prng.xorshift32()
b = prng.xorshift32()
x = prng.xorshift32()
y = prng.xorshift32()
i += 1

r = prng.xorshift32()

query = f"salt={salt}&t={t}&r={r}&a={a}&b={b}&x={x}&y={y}"
print(query)
md5 = hashlib.md5(query.encode()).hexdigest()

if md5 == target_hash:
result_queue.put(query)
return

def test():
t = 1752326718
prng = PRNG(t)
a, b, x, y = 0, 0, 0, 0 # Initialize variables
i = 0
while i < (prng.xorshift32()):
a = prng.xorshift32()
b = prng.xorshift32()
x = prng.xorshift32()
y = prng.xorshift32()
i += 1
salt = "tlkyeueq7fej8vtzitt26yl24kswrgm5"

r = prng.xorshift32()
print(hex(i))
query = f"salt={salt}&t={t}&r={r}&a={a}&b={b}&x={x}&y={y}"
md5 = hashlib.md5(query.encode()).hexdigest()
print(query)
print(md5)

def solve_parallel():
# Final attempt with the wide time range and corrected logic.
start_time = 1751990400
end_time = 1752052051
target_hash = "8a2fc1e9e2830c37f8a7f51572a640aa"
salt = "tlkyeueq7fej8vtzitt26yl24kswrgm5"

num_processes = cpu_count()
chunk_size = (end_time - start_time) // num_processes

print(f"Target hash: {target_hash}")
print(f"Using salt: {salt}")
print(f"Searching from {start_time} to {end_time} using {num_processes} processes with the CORRECTED logic...")

manager = Manager()
result_queue = manager.Queue()

tasks = []
for i in range(num_processes):
chunk_start = start_time + i * chunk_size
chunk_end = chunk_start + chunk_size if i < num_processes - 1 else end_time
tasks.append((chunk_start, chunk_end, salt, target_hash, result_queue))

with Pool(processes=num_processes) as pool:
pool.starmap(worker, tasks)

if not result_queue.empty():
found_query = result_queue.get()
sha1_hash = hashlib.sha1(found_query.encode()).hexdigest()
flag = f"L3HCTF{{{sha1_hash}}}"
print(f"\n--- Found! ---")
print(f"Query string: {found_query}")
print(f"Flag: {flag}")
else:
print("\nFlag not found. The logic is likely correct now, but the time range might be off. Or the salt is different.")

if __name__ == "__main__":
# test()
solve_parallel()

ez_android

java层严重混淆是依托答辩啥也看不出来

直接so看,搜索wrong answer定位

得等IDA分析完,别着急,搜索完之后发现一大串,再点一下就出来了

image-20250714202000290

直接定位到221A70的这个greet函数

可以看到密文

image-20250714202121555

但别漏了还有三位

image-20250714202145086

写逆向脚本即可

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

# Key extracted from the disassembly
key = b'dGhpc2lzYWtleQWrong answer'
key_len = len(key)

# The encrypted data we want to reverse
# These are the QWORDs compared at the end of the greet function
encrypted_data = [
0x0A409663A025150C,
0x1FE106294065165C,
0xFC020A4C0E2C7290,
0x002A324F
]

# Reconstruct the full encrypted byte array
full_encrypted_data = bytearray()
for val in encrypted_data:
full_encrypted_data.extend(val.to_bytes(8, 'little'))

final_encrypted_bytes = full_encrypted_data[:27]

def decrypt(encrypted_bytes, key):
decrypted = bytearray(len(encrypted_bytes))
for i in range(len(encrypted_bytes)):
# Reverse the final XOR
i_1 = i - 14 if i >= 14 else i

# Reverse the rotation
rot = key[(i + 3) % 14] & 7
rotated_v11 = encrypted_bytes[i] ^ key[(i + 4) % 14]
v11 = ((rotated_v11 >> rot) | (rotated_v11 << (8 - rot))) & 0xFF

# Reverse the addition and XOR
idx1_val = (2 * i) | 1
idx1 = idx1_val - 14 * ((147 * idx1_val) >> 11)
idx1 %= key_len

plain_byte = (v11 - key[idx1]) & 0xFF
plain_byte ^= key[i_1]
decrypted[i] = plain_byte

return bytes(decrypted)

decrypted_flag = decrypt(final_encrypted_bytes, key)

print(f"Flag: {decrypted_flag.decode(errors='ignore')}")

Flag: L3HCTF{ez_rust_reverse_lol}

其它解法:直接爆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
inp = bytearray(27)
key = b"dGhpc2lzYWtleQ"
target = bytearray.fromhex("0c1525a06396400a5c1665402906e11f90722c0e4c0a02fc4f322a")
res = bytearray()

for i in range(27):
for j in range(256):
num = i - 14 if i >= 14 else i
temp = (2 * i) | 1
v11 = (key[temp - 14 * (147 * temp >> 11)] + (j ^ key[num])) & 0xFF
kidx = key[(i + 3) % 0xE] & 7
out = (key[(i + 4) % 0xE] ^ ((v11 << kidx) | (v11 >> (8 - kidx)))) & 0xFF
if out == target[i]:
res.append(j)
print(res)

终焉之门

这题逻辑很清晰,跟glsl有关,发现个base64

image-20250714202426772

多次base64解码发现

对其进行交叉引用意外发现:

image-20250729142734105

向上溯源发现:

image-20250729142804772

调用两次解开试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
s = [0x75, 0x1B, 0x55, 0x0A, 0x17, 0x58, 0x21, 0x1A, 0x75, 0x6C, 0x5F, 0x67, 0x41, 0x52, 0x1F, 0x22, 0x33, 0x66, 0x6E, 0x03, 0x37, 0x3F, 0x03, 0x20, 0x27, 0x44, 0x22, 0x05, 0x35, 0x0D, 0x36, 0x26, 0x25, 0x5B, 0x4B, 0x22, 0x09, 0x13, 0x11, 0x65, 0x45, 0x75, 0x6E, 0x41, 0x3E, 0x39, 0x3A, 0x16, 0x35, 0x08, 0x0B, 0x08, 0x1E, 0x33, 0x19, 0x0A, 0x41, 0x7B, 0x44, 0x58, 0x7B, 0x66, 0x2A, 0x5C, 0x35, 0x0C, 0x14, 0x34, 0x20, 0x58, 0x33, 0x1D, 0x0B, 0x14, 0x6E, 0x65, 0x42, 0x77, 0x59, 0x78, 0x33, 0x39, 0x4F, 0x4C, 0x09, 0x27, 0x23, 0x1C, 0x20, 0x1F, 0x4C, 0x27, 0x39, 0x0F, 0x05, 0x06, 0x66, 0x6B, 0x54, 0x03, 0x30, 0x38, 0x2E, 0x1D, 0x3B, 0x0C, 0x19, 0x67, 0x42, 0x68, 0x7B, 0x6C, 0x38, 0x23, 0x3C, 0x07, 0x06, 0x1E, 0x44, 0x3B, 0x14, 0x05, 0x21, 0x2A, 0x33, 0x1D, 0x62, 0x79, 0x2D, 0x4D, 0x59, 0x5F, 0x26, 0x11, 0x3A, 0x09, 0x30, 0x04, 0x00, 0x3D, 0x11, 0x1D, 0x17, 0x6D, 0x76, 0x13, 0x4B, 0x5D, 0x39, 0x27, 0x2B, 0x3A, 0x27, 0x19, 0x5C, 0x19, 0x16, 0x23, 0x66, 0x49, 0x67, 0x47, 0x75, 0x57, 0x3C, 0x5E, 0x55, 0x20, 0x3F, 0x3F, 0x44, 0x6A, 0x41, 0x00, 0x78, 0x57, 0x34, 0x1F, 0x20, 0x07, 0x32, 0x34, 0x6E, 0x31, 0x0D, 0x05, 0x25, 0x07, 0x21, 0x46, 0x1B, 0x77, 0x2D, 0x4D, 0x5C, 0x19, 0x22, 0x12, 0x2D, 0x1C, 0x0A, 0x0F, 0x39, 0x3D, 0x11, 0x32, 0x03, 0x28, 0x08, 0x56, 0x58, 0x0A, 0x76, 0x4C, 0x3D, 0x19, 0x23, 0x28, 0x4C, 0x21, 0x4A, 0x26, 0x22, 0x51, 0x6E, 0x7B, 0x40, 0x6F, 0x77, 0x24, 0x30, 0x14, 0x31, 0x04, 0x06, 0x3D, 0x45, 0x56, 0x7A, 0x5B, 0x70, 0x5A, 0x24, 0x14, 0x05, 0x30, 0x01, 0x06, 0x42, 0x05, 0x27, 0x28, 0x3A, 0x0E, 0x02, 0x79, 0x76, 0x11, 0x21, 0x4B, 0x24, 0x29, 0x25, 0x59, 0x36, 0x07, 0x3E, 0x3E, 0x07, 0x35, 0x33, 0x42, 0x63, 0x6D, 0x5F, 0x73, 0x2B, 0x6D, 0x50, 0x1D, 0x30, 0x17, 0x0B, 0x26, 0x39, 0x7E, 0x03, 0x3D, 0x30, 0x62, 0x50, 0x05, 0x4D, 0x66, 0x38, 0x1A, 0x0D, 0x22, 0x05, 0x0F, 0x34, 0x68, 0x7F, 0x6C, 0x62, 0x43, 0x6A, 0x29, 0x23, 0x30, 0x20, 0x3C, 0x13, 0x66, 0x37, 0x27, 0x33, 0x35, 0x1B, 0x16, 0x76, 0x4D, 0x50, 0x3C, 0x73, 0x58, 0x0A, 0x23, 0x43, 0x36, 0x10, 0x37, 0x01, 0x3C, 0x27, 0x14, 0x37, 0x19, 0x15, 0x2C, 0x56, 0x59, 0x6C, 0x2E, 0x61, 0x64, 0x2F, 0x6C, 0x6B, 0x16, 0x27, 0x21, 0x3A, 0x47, 0x00, 0x43, 0x12, 0x22, 0x2E, 0x40, 0x66, 0x5C, 0x40, 0x7A, 0x00, 0x3D, 0x28, 0x30, 0x3F, 0x5E, 0x3D, 0x77, 0x59, 0x67, 0x67, 0x61, 0x72, 0x09, 0x40, 0x31, 0x04, 0x3D, 0x23, 0x5A, 0x19, 0x47, 0x00, 0x4F, 0x74, 0x12, 0x66, 0x77, 0x73, 0x15, 0x6E, 0x03, 0x3C, 0x19, 0x72, 0x17, 0x31, 0x27, 0x00, 0x06, 0x2E, 0x45, 0x61, 0x51, 0x71, 0x78, 0x48, 0x77, 0x6D, 0x4B, 0x15, 0x6A, 0x2B, 0x09, 0x72, 0x61, 0x36, 0x5E, 0x24, 0x25, 0x22, 0x4A, 0x3B, 0x39, 0x16, 0x78, 0x0F, 0x29, 0x2D, 0x23, 0x24, 0x3C, 0x22, 0x43, 0x23, 0x16, 0x20, 0x05, 0x21, 0x07, 0x11, 0x5E, 0x3F, 0x3B, 0x22, 0x77, 0x45, 0x77, 0x67, 0x5B, 0x01, 0x62, 0x6B, 0x5E, 0x3A, 0x4B, 0x39, 0x04, 0x54, 0x58, 0x09, 0x50, 0x27, 0x1A, 0x7D, 0x71, 0x66, 0x2C, 0x6B, 0x11, 0x50, 0x70, 0x76, 0x05, 0x02, 0x4F, 0x7E, 0x21, 0x00, 0x0A, 0x14, 0x00, 0x21, 0x08, 0x37, 0x00, 0x13, 0x17, 0x20, 0x5D, 0x52, 0x26, 0x22, 0x02, 0x5E, 0x36, 0x2C, 0x00, 0x6C, 0x19, 0x72, 0x68, 0x79, 0x47, 0x70, 0x77, 0x0A, 0x04, 0x10, 0x23, 0x34, 0x1D, 0x5A, 0x4C, 0x6E, 0x49, 0x77, 0x66, 0x66, 0x46, 0x3F, 0x03, 0x0C, 0x4B, 0x3A, 0x41, 0x69, 0x45, 0x74, 0x5E, 0x3B, 0x63, 0x68, 0x66, 0x50, 0x78, 0x7A, 0x3E, 0x1A, 0x32, 0x45, 0x35, 0x2A, 0x53, 0x68, 0x4B, 0x54, 0x6F, 0x47, 0x4B, 0x11, 0x15, 0x76, 0x31, 0x11, 0x13, 0x3D, 0x3F, 0x29, 0x00, 0x75, 0x56, 0x19, 0x77, 0x50, 0x6B, 0x61, 0x77, 0x50, 0x5C, 0x7A, 0x41, 0x43, 0x4C, 0x13, 0x1C, 0x0D, 0x2A, 0x2B, 0x6E, 0x7E, 0x07, 0x32, 0x79, 0x6A, 0x4D, 0x45, 0x58, 0x3C, 0x45, 0x7D, 0x34, 0x32, 0x13, 0x2D, 0x36, 0x11, 0x32, 0x38, 0x23, 0x35, 0x1A, 0x19, 0x38, 0x3B, 0x05, 0x3D, 0x20, 0x3C, 0x19, 0x5D, 0x43, 0x68, 0x67, 0x72, 0x5A, 0x77, 0x10, 0x5F, 0x15, 0x75, 0x10, 0x11, 0x69, 0x71, 0x78, 0x44, 0x3E, 0x0F, 0x46, 0x71, 0x18, 0x26, 0x09, 0x29, 0x05, 0x32, 0x66, 0x73, 0x52, 0x0D, 0x36, 0x29, 0x06, 0x36, 0x57, 0x1B, 0x0C, 0x3F, 0x03, 0x41, 0x5F, 0x3F, 0x42, 0x67, 0x2E, 0x6E, 0x66, 0x76, 0x73, 0x42, 0x66, 0x50, 0x53, 0x75, 0x4D, 0x11, 0x19, 0x39, 0x66, 0x30, 0x0A, 0x3D, 0x67, 0x19, 0x75, 0x42, 0x68, 0x76, 0x5A, 0x2A, 0x2B, 0x1F, 0x27, 0x32, 0x35, 0x02, 0x13, 0x3B, 0x19, 0x40, 0x33, 0x15, 0x42, 0x71, 0x59, 0x04, 0x41, 0x4C, 0x6B, 0x43, 0x76, 0x44, 0x54, 0x42, 0x66, 0x6E, 0x78, 0x21, 0x1C, 0x19, 0x2D, 0x35, 0x59, 0x7A, 0x43, 0x22, 0x37, 0x32, 0x16, 0x31, 0x0B, 0x67, 0x5C, 0x42, 0x67, 0x48, 0x53, 0x75, 0x10, 0x44, 0x73, 0x2D, 0x5C, 0x7A, 0x51, 0x71, 0x4E, 0x44, 0x73, 0x6D, 0x76, 0x50, 0x69, 0x74, 0x76, 0x00, 0x54, 0x12, 0x23, 0x7A, 0x41, 0x59, 0x4C, 0x4C, 0x41, 0x73, 0x68, 0x62, 0x6C, 0x76, 0x4A, 0x6A, 0x6B, 0x76, 0x76, 0x66, 0x79, 0x41, 0x66, 0x17, 0x27, 0x33, 0x35, 0x1B, 0x69, 0x32, 0x0C, 0x04, 0x26, 0x08, 0x42, 0x14, 0x7C, 0x48, 0x18, 0x44, 0x6B, 0x42, 0x34, 0x17, 0x2F, 0x35, 0x02, 0x1A, 0x04, 0x10, 0x1F, 0x01, 0x12, 0x28, 0x23, 0x0F, 0x6C, 0x6B, 0x5A, 0x66, 0x78, 0x75, 0x12, 0x54, 0x4B, 0x41, 0x76, 0x6A, 0x54, 0x75, 0x4C, 0x4C, 0x7A, 0x42, 0x36, 0x34, 0x31, 0x37, 0x5B, 0x61, 0x5D, 0x44, 0x67, 0x72, 0x68, 0x72, 0x4B, 0x15, 0x77, 0x42, 0x78, 0x71, 0x5A, 0x35, 0x53, 0x07, 0x0A, 0x74, 0x05, 0x7C, 0x5D, 0x73, 0x4E, 0x6E, 0x4A, 0x72, 0x4D, 0x72, 0x41, 0x74, 0x75, 0x44, 0x4F, 0x36, 0x3B, 0x7A, 0x51, 0x71, 0x78, 0x48, 0x77, 0x6D, 0x4B, 0x15, 0x6A, 0x76, 0x12, 0x58, 0x4B, 0x75, 0x11, 0x23, 0x38, 0x22, 0x4A, 0x30, 0x77, 0x5F, 0x78, 0x31, 0x3C, 0x34, 0x09, 0x21, 0x10, 0x32, 0x50, 0x22, 0x14, 0x0F, 0x41, 0x63, 0x1A, 0x22, 0x6C, 0x71, 0x5F, 0x76, 0x77, 0x58, 0x77, 0x76, 0x4B, 0x11, 0x72, 0x70, 0x74, 0x10, 0x1D, 0x76, 0x4D, 0x10, 0x58, 0x0D, 0x5F, 0x3A, 0x54, 0x34, 0x78, 0x51, 0x77, 0x12, 0x45, 0x11, 0x33, 0x3D, 0x33, 0x00, 0x0E, 0x22, 0x27, 0x37, 0x78, 0x7E, 0x1F, 0x3E, 0x37, 0x6D, 0x66, 0x7A, 0x59, 0x76, 0x12, 0x11, 0x67, 0x76, 0x4B, 0x11, 0x78, 0x45, 0x64, 0x62, 0x41, 0x72, 0x76, 0x2A, 0x03, 0x38, 0x34, 0x13, 0x3E, 0x00, 0x37, 0x32, 0x12, 0x3A, 0x35, 0x14, 0x42, 0x7C, 0x1B, 0x66, 0x0E, 0x76, 0x0C, 0x58, 0x40, 0x73, 0x53, 0x72, 0x72, 0x74, 0x4E, 0x6E, 0x78, 0x42, 0x66, 0x50, 0x78, 0x7A, 0x77, 0x54, 0x66, 0x45, 0x66, 0x7A, 0x53, 0x37, 0x19, 0x01, 0x35, 0x26, 0x50, 0x3B, 0x15, 0x76, 0x67, 0x54, 0x41, 0x79, 0x76, 0x6A, 0x54, 0x75, 0x4B, 0x19, 0x27, 0x68, 0x78, 0x72, 0x6C, 0x7A, 0x76, 0x7A, 0x41, 0x43, 0x4C, 0x44, 0x54, 0x44, 0x25, 0x2F, 0x3D, 0x33, 0x4E, 0x7A, 0x63, 0x5C, 0x4D, 0x10, 0x11, 0x72, 0x11, 0x75, 0x59, 0x73, 0x4B, 0x44, 0x78, 0x42, 0x3D, 0x40, 0x76, 0x76, 0x4E, 0x50, 0x77, 0x75, 0x66, 0x72, 0x75, 0x72, 0x4D, 0x54, 0x4A, 0x42, 0x67, 0x72, 0x13, 0x39, 0x1F, 0x75, 0x54, 0x75, 0x0D, 0x11, 0x3A, 0x25, 0x39, 0x07, 0x3C, 0x3E, 0x56, 0x30, 0x03, 0x37, 0x31, 0x6B, 0x4C, 0x24, 0x36, 0x13, 0x49, 0x68, 0x66, 0x6A, 0x49, 0x72, 0x12, 0x48, 0x77, 0x76, 0x4D, 0x15, 0x57, 0x76, 0x12, 0x6E, 0x53, 0x75, 0x05, 0x38, 0x27, 0x42, 0x24, 0x50, 0x4E, 0x75, 0x1E, 0x0C, 0x16, 0x2E, 0x2D, 0x0E, 0x1C, 0x3B, 0x33, 0x58, 0x0E, 0x4F, 0x78, 0x25, 0x45, 0x07, 0x73, 0x7A, 0x63, 0x77, 0x66, 0x79, 0x5A, 0x75, 0x4D, 0x48, 0x7A, 0x45, 0x4B, 0x7A, 0x48, 0x79, 0x5A, 0x66, 0x12, 0x17, 0x37, 0x07, 0x1F, 0x3D, 0x22, 0x2F, 0x2C, 0x33, 0x30, 0x03, 0x29, 0x7D, 0x1A, 0x07, 0x4B, 0x70, 0x67, 0x30, 0x59, 0x78, 0x4E, 0x2C, 0x6D, 0x68, 0x67, 0x48, 0x53, 0x75, 0x10, 0x44, 0x73, 0x76, 0x76, 0x7A, 0x51, 0x71, 0x4E, 0x44, 0x73, 0x6D, 0x34, 0x02, 0x2C, 0x35, 0x3D, 0x58, 0x3F, 0x41, 0x66, 0x7A, 0x53, 0x43, 0x66, 0x4C, 0x41, 0x73, 0x68, 0x62, 0x6C, 0x2B, 0x60, 0x6A, 0x6B, 0x76, 0x76, 0x66, 0x79, 0x41, 0x66, 0x44, 0x73, 0x72, 0x76, 0x13, 0x57, 0x25, 0x08, 0x50, 0x76, 0x67, 0x0B, 0x6E, 0x77, 0x43, 0x65, 0x44, 0x76, 0x42, 0x77, 0x58, 0x50, 0x76, 0x4D, 0x54, 0x0C, 0x6E, 0x4C, 0x7A, 0x53, 0x7A, 0x64, 0x72, 0x77, 0x41, 0x5A, 0x66, 0x78, 0x75, 0x12, 0x54, 0x4B, 0x41, 0x3F, 0x24, 0x00, 0x75, 0x0E, 0x4C, 0x67, 0x42, 0x27, 0x32, 0x35, 0x35, 0x5B, 0x05, 0x33, 0x05, 0x33, 0x33, 0x13, 0x7F, 0x46, 0x46, 0x27, 0x3F, 0x63, 0x5B, 0x5A, 0x76, 0x12, 0x54, 0x4F, 0x74, 0x12, 0x66, 0x77, 0x73, 0x4E, 0x6E, 0x4A, 0x72, 0x4D, 0x72, 0x08, 0x3A, 0x21, 0x44, 0x0E, 0x6D, 0x0C, 0x7A, 0x02, 0x25, 0x39, 0x0B, 0x3C, 0x12, 0x0F, 0x54, 0x3E, 0x37, 0x69, 0x55, 0x46, 0x26, 0x41, 0x17, 0x6D, 0x5C, 0x4A, 0x72, 0x77, 0x42, 0x78, 0x62, 0x68, 0x75, 0x4A, 0x6A, 0x6F, 0x76, 0x11, 0x76, 0x55, 0x74, 0x1F, 0x3A, 0x08, 0x31, 0x5A, 0x15, 0x31, 0x37, 0x23, 0x19, 0x0C, 0x25, 0x1B, 0x1A, 0x79, 0x0D, 0x74, 0x0D, 0x1D, 0x37, 0x4D, 0x6E, 0x58, 0x06, 0x0A, 0x44, 0x54, 0x75, 0x78, 0x4C, 0x77, 0x41, 0x11, 0x50, 0x70, 0x76, 0x4C, 0x44, 0x4F, 0x76, 0x66, 0x4C, 0x37, 0x21, 0x09, 0x2F, 0x01, 0x6D, 0x66, 0x7A, 0x59, 0x76, 0x12, 0x11, 0x67, 0x76, 0x4B, 0x11, 0x78, 0x45, 0x64, 0x3F, 0x6B, 0x58, 0x76, 0x79, 0x57, 0x79, 0x77, 0x58, 0x41, 0x44, 0x76, 0x66, 0x53, 0x41, 0x25, 0x05, 0x1A, 0x32, 0x66, 0x77, 0x06, 0x6C, 0x67, 0x58, 0x4B, 0x73, 0x11, 0x69, 0x58, 0x74, 0x4E, 0x6E, 0x78, 0x42, 0x66, 0x0B, 0x52, 0x7A, 0x77, 0x54, 0x66, 0x45, 0x66, 0x7A, 0x53, 0x75, 0x4B, 0x44, 0x74, 0x6D, 0x4B, 0x11, 0x15, 0x3F, 0x29, 0x00, 0x41, 0x3B, 0x76, 0x77, 0x54, 0x26, 0x1F, 0x58, 0x39, 0x09, 0x07, 0x36, 0x2D, 0x2E, 0x37, 0x01, 0x4C, 0x4E, 0x1F, 0x14, 0x29, 0x5F, 0x4C, 0x6E, 0x6E, 0x76, 0x4E, 0x62, 0x79, 0x76, 0x4D, 0x10, 0x11, 0x72, 0x11, 0x75, 0x59, 0x73, 0x4B, 0x0D, 0x36, 0x16, 0x66, 0x2B, 0x76, 0x6B, 0x4E, 0x03, 0x23, 0x34, 0x25, 0x39, 0x0A, 0x36, 0x0C, 0x00, 0x0B, 0x39, 0x6A, 0x7F, 0x09, 0x27, 0x36, 0x6E, 0x3F, 0x75, 0x10, 0x11, 0x69, 0x71, 0x78, 0x44, 0x77, 0x41, 0x12, 0x71, 0x57, 0x76, 0x4A, 0x66, 0x41, 0x24, 0x32, 0x2F, 0x11, 0x09, 0x19, 0x2E, 0x08, 0x26, 0x53, 0x33, 0x24, 0x26, 0x46, 0x1E, 0x2A, 0x76, 0x0F, 0x6E, 0x1A, 0x3B, 0x18, 0x7E, 0x32, 0x42, 0x7B, 0x4D, 0x53, 0x37, 0x44, 0x43, 0x7D, 0x6D, 0x66, 0x71, 0x58, 0x7A, 0x67, 0x19, 0x75, 0x42, 0x75, 0x76, 0x15, 0x7A, 0x68, 0x50, 0x63, 0x35, 0x34, 0x3C, 0x1B, 0x3E, 0x56, 0x62, 0x7A, 0x45, 0x4B, 0x7A, 0x48, 0x79, 0x5A, 0x66, 0x41, 0x43, 0x76, 0x44, 0x09, 0x68, 0x4C, 0x6E, 0x78, 0x72, 0x4B, 0x50, 0x79, 0x76, 0x11, 0x7A, 0x4B, 0x6D, 0x67, 0x32, 0x18, 0x26, 0x0B, 0x6E, 0x67, 0x54, 0x7D, 0x62, 0x53, 0x75, 0x10, 0x44, 0x73, 0x76, 0x76, 0x7A, 0x51, 0x71, 0x4E, 0x44, 0x28, 0x47, 0x76, 0x50, 0x69, 0x74, 0x76, 0x43, 0x15, 0x41, 0x66, 0x7A, 0x53, 0x43, 0x66, 0x4C, 0x41, 0x73, 0x2A, 0x2D, 0x23, 0x3A, 0x4A, 0x25, 0x20, 0x76, 0x6B, 0x66, 0x2D, 0x13, 0x33, 0x01, 0x68, 0x58, 0x76, 0x50, 0x16, 0x76, 0x4D, 0x50, 0x67, 0x73, 0x11, 0x44, 0x77, 0x43, 0x65, 0x44, 0x76, 0x42, 0x31, 0x17, 0x02, 0x76, 0x45, 0x1D, 0x19, 0x10, 0x4C, 0x33, 0x53, 0x67, 0x64, 0x62, 0x6C, 0x41, 0x13, 0x66, 0x64, 0x75, 0x03, 0x42, 0x50, 0x41, 0x3F, 0x61, 0x5F, 0x7C, 0x66, 0x4C, 0x7A, 0x42, 0x74, 0x66, 0x74, 0x76, 0x10, 0x7A, 0x77, 0x44, 0x67, 0x72, 0x68, 0x72, 0x4B, 0x4E, 0x5D, 0x42, 0x78, 0x71, 0x5A, 0x76, 0x12, 0x54, 0x4F, 0x74, 0x12, 0x66, 0x77, 0x73, 0x4E, 0x6E, 0x4A, 0x72, 0x4D, 0x72, 0x41, 0x3D, 0x33, 0x44, 0x47, 0x3E, 0x45, 0x3B, 0x12, 0x3A, 0x07, 0x0C, 0x36, 0x39, 0x0A, 0x6E, 0x23, 0x0B, 0x12, 0x59, 0x56, 0x75, 0x19, 0x29, 0x3F, 0x26, 0x02, 0x37, 0x25, 0x39, 0x31, 0x1F, 0x68, 0x78, 0x4A, 0x78, 0x7F, 0x7F, 0x18, 0x5C, 0x55, 0x74, 0x4C, 0x6E, 0x49, 0x72, 0x11, 0x6A, 0x75, 0x76, 0x77, 0x58, 0x77, 0x76, 0x4B, 0x11, 0x72, 0x70, 0x74, 0x10, 0x46, 0x76, 0x67, 0x10, 0x58, 0x44, 0x11, 0x6E, 0x54, 0x75, 0x78, 0x4C, 0x77, 0x41, 0x11, 0x50, 0x70, 0x76, 0x4C, 0x44, 0x4F, 0x76, 0x66, 0x4C, 0x75, 0x73, 0x4C, 0x21, 0x01, 0x76, 0x51, 0x7A, 0x1F, 0x37, 0x5E, 0x42, 0x22, 0x6D, 0x4B, 0x3B, 0x78, 0x45, 0x64, 0x62, 0x41, 0x72, 0x76, 0x79, 0x57, 0x79, 0x77, 0x58, 0x41, 0x44, 0x76, 0x66, 0x53, 0x41, 0x66, 0x44, 0x49, 0x77, 0x66, 0x66, 0x51, 0x24, 0x08, 0x19, 0x00, 0x68, 0x11, 0x43, 0x58, 0x74, 0x4E, 0x6E, 0x78, 0x42, 0x66, 0x50, 0x78, 0x7A, 0x77, 0x54, 0x66, 0x45, 0x66, 0x7A, 0x53, 0x75, 0x4B, 0x44, 0x29, 0x47, 0x4B, 0x11, 0x15, 0x76, 0x67, 0x54, 0x41, 0x79, 0x76, 0x6A, 0x54, 0x75, 0x4B, 0x19, 0x7A, 0x42, 0x25, 0x58, 0x6C, 0x7A, 0x76, 0x7A, 0x41, 0x43, 0x4C, 0x44, 0x54, 0x44, 0x66, 0x6E, 0x6E, 0x76, 0x4E, 0x62, 0x2F, 0x33, 0x1F, 0x54, 0x58, 0x31, 0x45, 0x75, 0x44, 0x73, 0x04, 0x0F, 0x78, 0x5D, 0x66, 0x7B, 0x76, 0x6C, 0x4E, 0x5D, 0x66, 0x6E, 0x4C, 0x72, 0x75, 0x72, 0x4D, 0x54, 0x4A, 0x42, 0x67, 0x72, 0x5A, 0x77, 0x4B, 0x75, 0x15, 0x75, 0x10, 0x43, 0x2C, 0x25, 0x2D, 0x16, 0x39, 0x5A, 0x38, 0x71, 0x57, 0x76, 0x4A, 0x66, 0x41, 0x77, 0x66, 0x6E, 0x52, 0x42, 0x66, 0x37, 0x63, 0x58, 0x12, 0x48, 0x77, 0x76, 0x4D, 0x15, 0x57, 0x76, 0x12, 0x6E, 0x53, 0x75, 0x0F, 0x37, 0x20, 0x07, 0x66, 0x41, 0x4B, 0x6F, 0x67, 0x58, 0x57, 0x6D, 0x66, 0x71, 0x58, 0x7A, 0x67, 0x19, 0x75, 0x42, 0x75, 0x2D, 0x3F, 0x7A, 0x68, 0x50, 0x63, 0x77, 0x66, 0x79, 0x5A, 0x75, 0x4D, 0x48, 0x7A, 0x45, 0x4B, 0x7A, 0x48, 0x30, 0x14, 0x32, 0x41, 0x00, 0x76, 0x59, 0x54, 0x11, 0x32, 0x2F, 0x3B, 0x39, 0x34, 0x14, 0x38, 0x22, 0x50, 0x01, 0x46, 0x60, 0x34, 0x21, 0x24, 0x6E, 0x64, 0x6E, 0x76, 0x42, 0x67, 0x48, 0x53, 0x75, 0x10, 0x44, 0x73, 0x76, 0x76, 0x7A, 0x51, 0x71, 0x4E, 0x0D, 0x35, 0x6D, 0x7E, 0x13, 0x69, 0x69, 0x6B, 0x43, 0x05, 0x48, 0x66, 0x33, 0x03, 0x43, 0x7B, 0x4C, 0x14, 0x3A, 0x26, 0x36, 0x64, 0x37, 0x18, 0x2D, 0x62, 0x6D, 0x5C, 0x66, 0x79, 0x41, 0x66, 0x44, 0x73, 0x72, 0x76, 0x50, 0x16, 0x76, 0x4D, 0x50, 0x67, 0x73, 0x11, 0x06, 0x25, 0x06, 0x24, 0x0F, 0x6D, 0x68, 0x77, 0x58, 0x50, 0x76, 0x4D, 0x54, 0x57, 0x44, 0x4C, 0x7A, 0x53, 0x7A, 0x39, 0x58, 0x5D, 0x41, 0x5A, 0x66, 0x78, 0x75, 0x12, 0x54, 0x4B, 0x41, 0x76, 0x6A, 0x54, 0x31, 0x09, 0x0A, 0x3B, 0x17, 0x38, 0x32, 0x6E, 0x5C, 0x10, 0x7A, 0x77, 0x44, 0x67, 0x72, 0x68, 0x72, 0x4B, 0x15, 0x77, 0x42, 0x78, 0x71, 0x5A, 0x76, 0x44, 0x11, 0x1D, 0x30, 0x5B, 0x25, 0x23, 0x73, 0x53, 0x6E, 0x5F, 0x62, 0x5D, 0x69, 0x6B, 0x74, 0x75, 0x44, 0x4F, 0x6D, 0x11, 0x7A, 0x51, 0x71, 0x78, 0x48, 0x77, 0x6D, 0x4B, 0x15, 0x6A, 0x24, 0x57, 0x0C, 0x1E, 0x27, 0x5F, 0x71, 0x5C, 0x76, 0x4A, 0x72, 0x77, 0x42, 0x78, 0x62, 0x68, 0x28, 0x60, 0x40, 0x6F, 0x76, 0x11, 0x76, 0x55, 0x74, 0x4C, 0x6E, 0x00, 0x22, 0x1A, 0x77, 0x67, 0x6D, 0x5D, 0x58, 0x77, 0x76, 0x4B, 0x4C, 0x58, 0x70, 0x74, 0x10, 0x1D, 0x20, 0x08, 0x42, 0x1C, 0x0D, 0x52, 0x3A, 0x54, 0x68, 0x78, 0x59, 0x67, 0x50, 0x0A, 0x7A, 0x2D, 0x5C, 0x00, 0x00]
s[0] ^= 0x56
xor = b"Vm0xd1NtUXlWa1pPVldoVFlUSlNjVlZyV21GVk1XeDBaRVYwYWxadVFsaFdiWFF3VmxkS1IxTnNXbFpXZWtFeFZsUkdTMk15VGtaYVJtUk9ZbXRLZVZacldtdFNNVnBYVm01R1UySkdXbFJVVnpWUFRURmtjbGRzWkU5U01IQXdWa2QwVjFaWFNrbFJiR2hWVm5wV2NsUlVSbFpsUmxwMFQxZG9UbUV5ZHpCWFYzUmhZekZhYzFacVdtbFNXRkpYV1ZkMGQyUnNVbGhsU0dSVVZqQndSMVpITVc5aFZscFlaSHBLVjJKVVFYaFdSRVp6VmpGS1dWcEdVbWxpVmtwdlZsZDRWazFXU2tkaVJtUllZbTFTV0ZWdGRHRk5WbXQzV2toT2FWSnNjRmRaTUdoM1ZqQXhWMk5JV2xkU1JVVjRWbXBHUjJOV1VuTlNiR1JUVWxWVk1RPT0="
for i in range(1, 2318):
s[i] ^= xor[i%460]
print("".join(map(chr, s)))

s = [0x75, 0x1B, 0x55, 0x0A, 0x17, 0x58, 0x21, 0x1A, 0x75, 0x6B, 0x5F, 0x67, 0x6B, 0x3B, 0x53, 0x34, 0x33, 0x0A, 0x0D, 0x01, 0x33, 0x66, 0x3F, 0x7D, 0x32, 0x40, 0x2C, 0x46, 0x22, 0x45, 0x7A, 0x0A, 0x3B, 0x5D, 0x5E, 0x33, 0x3E, 0x18, 0x45, 0x3D, 0x15, 0x6C, 0x23, 0x4D, 0x30, 0x7A, 0x2D, 0x5E, 0x53, 0x5D, 0x0D, 0x0F, 0x0D, 0x30, 0x29, 0x01, 0x0C, 0x66, 0x02, 0x05, 0x38, 0x27, 0x32, 0x13, 0x22, 0x04, 0x15, 0x0E, 0x68, 0x3B, 0x43, 0x17, 0x21, 0x1A, 0x6E, 0x2E, 0x07, 0x25, 0x44, 0x78, 0x3C, 0x25, 0x15, 0x21, 0x26, 0x29, 0x36, 0x1C, 0x27, 0x50, 0x6E, 0x5E, 0x20, 0x0A, 0x45, 0x07, 0x76, 0x15, 0x1B, 0x15, 0x71, 0x30, 0x26, 0x1B, 0x34, 0x1F, 0x19, 0x3B, 0x4B, 0x78, 0x29, 0x46, 0x7A, 0x76, 0x7A, 0x41, 0x05, 0x00, 0x0B, 0x15, 0x10, 0x66, 0x3D, 0x6E, 0x6B, 0x4E, 0x31, 0x30, 0x38, 0x45, 0x51, 0x18, 0x69, 0x3B, 0x75, 0x59, 0x73, 0x4B, 0x02, 0x34, 0x0D, 0x27, 0x3E, 0x76, 0x35, 0x4E, 0x4D, 0x77, 0x36, 0x29, 0x21, 0x7D, 0x33, 0x44, 0x4F, 0x60, 0x42, 0x67, 0x72, 0x5A, 0x25, 0x0E, 0x21, 0x40, 0x27, 0x5E, 0x11, 0x24, 0x30, 0x2C, 0x56, 0x7F, 0x02, 0x1E, 0x71, 0x5A, 0x25, 0x46, 0x66, 0x12, 0x7B, 0x66, 0x2D, 0x5B, 0x59, 0x4C, 0x37, 0x63, 0x58, 0x44, 0x0D, 0x34, 0x64, 0x4D, 0x5D, 0x16, 0x25, 0x5A, 0x66, 0x05, 0x30, 0x0F, 0x64, 0x73, 0x12, 0x6F, 0x50, 0x08, 0x5F, 0x4D, 0x58, 0x57, 0x6D, 0x36, 0x71, 0x45, 0x7A, 0x31, 0x5C, 0x36, 0x50, 0x7D, 0x32, 0x5A, 0x2E, 0x60, 0x00, 0x6F, 0x77, 0x30, 0x3C, 0x19, 0x67, 0x45, 0x5A, 0x6B, 0x57, 0x5C, 0x74, 0x59, 0x75, 0x5A, 0x7E, 0x50, 0x4D, 0x67, 0x53, 0x5D, 0x4B, 0x6A, 0x6E, 0x3C, 0x3D, 0x1F, 0x58, 0x29, 0x7A, 0x11, 0x2C, 0x0E, 0x2E, 0x75, 0x79, 0x48, 0x67, 0x58, 0x77, 0x78, 0x57, 0x6B, 0x48, 0x41, 0x6D, 0x03, 0x4A, 0x60, 0x61, 0x7F, 0x73, 0x58, 0x6A, 0x64, 0x44, 0x73, 0x6D, 0x76, 0x02, 0x2C, 0x20, 0x23, 0x11, 0x5B, 0x41, 0x20, 0x28, 0x12, 0x00, 0x32, 0x44, 0x12, 0x3A, 0x26, 0x6A, 0x3C, 0x7F, 0x4A, 0x60, 0x6B, 0x62, 0x65, 0x71, 0x6C, 0x59, 0x68, 0x51, 0x67, 0x67, 0x65, 0x59, 0x0D, 0x5C, 0x10, 0x7A, 0x4D, 0x35, 0x5D, 0x0B, 0x36, 0x17, 0x65, 0x0A, 0x39, 0x0B, 0x24, 0x1D, 0x58, 0x20, 0x08, 0x17, 0x45, 0x44, 0x1C, 0x73, 0x53, 0x21, 0x4E, 0x72, 0x77, 0x41, 0x5A, 0x30, 0x3D, 0x36, 0x00, 0x54, 0x02, 0x41, 0x6B, 0x6A, 0x12, 0x39, 0x03, 0x03, 0x28, 0x4A, 0x24, 0x6F, 0x6F, 0x5C, 0x10, 0x7A, 0x77, 0x44, 0x31, 0x37, 0x2B, 0x60, 0x4B, 0x53, 0x77, 0x5F, 0x78, 0x37, 0x08, 0x37, 0x51, 0x00, 0x47, 0x24, 0x1B, 0x7D, 0x5D, 0x73, 0x4E, 0x6E, 0x4A, 0x24, 0x08, 0x31, 0x53, 0x74, 0x20, 0x44, 0x52, 0x6D, 0x57, 0x7A, 0x5B, 0x71, 0x3E, 0x48, 0x7D, 0x6D, 0x43, 0x06, 0x64, 0x66, 0x12, 0x55, 0x4B, 0x67, 0x1F, 0x7A, 0x76, 0x7C, 0x4A, 0x34, 0x7E, 0x59, 0x52, 0x48, 0x68, 0x75, 0x4A, 0x6A, 0x3D, 0x33, 0x45, 0x23, 0x07, 0x3A, 0x4C, 0x23, 0x00, 0x2A, 0x19, 0x40, 0x75, 0x76, 0x77, 0x58, 0x77, 0x76, 0x4B, 0x11, 0x3F, 0x39, 0x2C, 0x18, 0x59, 0x39, 0x19, 0x18, 0x55, 0x55, 0x1F, 0x7E, 0x54, 0x7E, 0x78, 0x5E, 0x79, 0x51, 0x11, 0x5A, 0x70, 0x3E, 0x0D, 0x17, 0x07, 0x7E, 0x2F, 0x4C, 0x7E, 0x73, 0x1A, 0x2B, 0x09, 0x64, 0x44, 0x6A, 0x57, 0x66, 0x1E, 0x11, 0x77, 0x78, 0x5B, 0x18, 0x71, 0x49, 0x64, 0x24, 0x41, 0x7F, 0x76, 0x2F, 0x12, 0x3A, 0x65, 0x50, 0x51, 0x4A, 0x66, 0x6A, 0x53, 0x51, 0x68, 0x54, 0x40, 0x7E, 0x6A, 0x4C, 0x13, 0x76, 0x4D, 0x58, 0x4B, 0x73, 0x11, 0x69, 0x58, 0x74, 0x4E, 0x6E, 0x3C, 0x0D, 0x32, 0x58, 0x75, 0x6B, 0x79, 0x44, 0x66, 0x4E, 0x66, 0x68, 0x5D, 0x65, 0x4B, 0x4E, 0x74, 0x25, 0x0A, 0x42, 0x5D, 0x7E, 0x2E, 0x54, 0x4A, 0x79, 0x20, 0x2F, 0x17, 0x67, 0x43, 0x08, 0x74, 0x52, 0x74, 0x72, 0x7C, 0x74, 0x66, 0x73, 0x48, 0x4F, 0x4C, 0x02, 0x54, 0x49, 0x66, 0x38, 0x2B, 0x35, 0x5C, 0x6A, 0x68, 0x78, 0x5D, 0x1C, 0x11, 0x62, 0x1F, 0x65, 0x50, 0x7A, 0x47, 0x44, 0x2D, 0x4C, 0x3E, 0x63, 0x7A, 0x5C, 0x4E, 0x50, 0x77, 0x75, 0x66, 0x72, 0x75, 0x72, 0x00, 0x1D, 0x12, 0x4A, 0x23, 0x3D, 0x0E, 0x7F, 0x46, 0x64, 0x1B, 0x65, 0x10, 0x1A, 0x69, 0x63, 0x76, 0x54, 0x77, 0x4B, 0x12, 0x39, 0x16, 0x25, 0x02, 0x6E, 0x08, 0x77, 0x6D, 0x6E, 0x04, 0x07, 0x25, 0x78, 0x41, 0x62, 0x1C, 0x58, 0x7B, 0x76, 0x5C, 0x1B, 0x47, 0x7F, 0x1B, 0x62, 0x53, 0x33, 0x4C, 0x7B, 0x73, 0x14, 0x23, 0x13, 0x41, 0x7D, 0x5D, 0x56, 0x47, 0x61, 0x66, 0x60, 0x56, 0x6A, 0x6E, 0x10, 0x79, 0x68, 0x75, 0x76, 0x15, 0x7A, 0x68, 0x50, 0x63, 0x77, 0x66, 0x79, 0x5A, 0x75, 0x09, 0x07, 0x2E, 0x4D, 0x46, 0x6B, 0x46, 0x69, 0x5A, 0x6D, 0x41, 0x51, 0x78, 0x54, 0x54, 0x48, 0x66, 0x26, 0x39, 0x21, 0x03, 0x58, 0x30, 0x76, 0x1A, 0x7A, 0x1D, 0x28, 0x24, 0x63, 0x51, 0x64, 0x40, 0x7E, 0x7A, 0x42, 0x76, 0x46, 0x43, 0x7C, 0x19, 0x48, 0x73, 0x30, 0x76, 0x77, 0x51, 0x27, 0x0B, 0x07, 0x61, 0x65, 0x67, 0x5E, 0x79, 0x78, 0x76, 0x52, 0x1B, 0x51, 0x6F, 0x73, 0x5F, 0x43, 0x33, 0x42, 0x19, 0x7A, 0x64, 0x48, 0x6C, 0x76, 0x4A, 0x6A, 0x6B, 0x76, 0x76, 0x66, 0x2C, 0x4F, 0x3F, 0x6E, 0x73, 0x72, 0x76, 0x50, 0x1F, 0x76, 0x47, 0x50, 0x77, 0x7D, 0x04, 0x44, 0x7C, 0x43, 0x75, 0x4A, 0x63, 0x59, 0x5D, 0x05, 0x7A, 0x5C, 0x1B, 0x1B, 0x1E, 0x00, 0x4C, 0x37, 0x12, 0x33, 0x2A, 0x7A, 0x7E, 0x41, 0x01, 0x4C, 0x78, 0x75, 0x12, 0x54, 0x1D, 0x04, 0x35, 0x78, 0x54, 0x20, 0x3F, 0x05, 0x20, 0x07, 0x74, 0x7B, 0x74, 0x20, 0x55, 0x39, 0x65, 0x4C, 0x76, 0x60, 0x70, 0x62, 0x45, 0x05, 0x7B, 0x42, 0x60, 0x61, 0x4A, 0x78, 0x02, 0x5D, 0x54, 0x5E, 0x12, 0x66, 0x77, 0x73, 0x18, 0x2B, 0x09, 0x60, 0x4D, 0x27, 0x17, 0x74, 0x68, 0x44, 0x08, 0x21, 0x6E, 0x1C, 0x03, 0x30, 0x3F, 0x2B, 0x38, 0x22, 0x19, 0x51, 0x64, 0x2E, 0x4B, 0x58, 0x44, 0x75, 0x44, 0x19, 0x3F, 0x2C, 0x0F, 0x69, 0x5D, 0x68, 0x78, 0x62, 0x68, 0x75, 0x0C, 0x26, 0x20, 0x37, 0x45, 0x76, 0x07, 0x35, 0x18, 0x27, 0x06, 0x72, 0x0C, 0x6A, 0x20, 0x05, 0x3E, 0x02, 0x32, 0x78, 0x13, 0x11, 0x7D, 0x70, 0x21, 0x63, 0x54, 0x2C, 0x08, 0x1E, 0x01, 0x5F, 0x3B, 0x6E, 0x54, 0x75, 0x78, 0x1A, 0x32, 0x02, 0x03, 0x50, 0x24, 0x23, 0x1A, 0x44, 0x52, 0x76, 0x33, 0x1A, 0x75, 0x7E, 0x4C, 0x7E, 0x44, 0x63, 0x57, 0x50, 0x73, 0x76, 0x12, 0x11, 0x67, 0x30, 0x07, 0x5E, 0x39, 0x11, 0x64, 0x26, 0x04, 0x35, 0x24, 0x3C, 0x12, 0x79, 0x6A, 0x58, 0x0F, 0x0B, 0x3F, 0x35, 0x16, 0x49, 0x30, 0x01, 0x0A, 0x65, 0x6E, 0x32, 0x5A, 0x3B, 0x08, 0x58, 0x41, 0x73, 0x01, 0x67, 0x49, 0x78, 0x4E, 0x3A, 0x2D, 0x14, 0x68, 0x08, 0x78, 0x70, 0x77, 0x00, 0x33, 0x13, 0x68, 0x23, 0x5A, 0x7C, 0x50, 0x6E, 0x74, 0x6D, 0x4B, 0x11, 0x41, 0x23, 0x31, 0x5A, 0x18, 0x79, 0x7C, 0x77, 0x54, 0x64, 0x45, 0x09, 0x7A, 0x4D, 0x78, 0x20, 0x2D, 0x2E, 0x3F, 0x35, 0x5A, 0x69, 0x4C, 0x44, 0x54, 0x44, 0x32, 0x3B, 0x38, 0x76, 0x44, 0x7F, 0x79, 0x04, 0x02, 0x44, 0x19, 0x20, 0x50, 0x31, 0x10, 0x32, 0x05, 0x17, 0x70, 0x4A, 0x22, 0x2F, 0x31, 0x24, 0x0B, 0x15, 0x77, 0x78, 0x66, 0x62, 0x7B, 0x67, 0x44, 0x54, 0x40, 0x42, 0x70, 0x60, 0x4A, 0x79, 0x5B, 0x75, 0x1E, 0x75, 0x01, 0x09, 0x79, 0x7F, 0x68, 0x4D, 0x7E, 0x5A, 0x38, 0x71, 0x57, 0x76, 0x4A, 0x32, 0x14, 0x21, 0x68, 0x37, 0x52, 0x48, 0x7B, 0x6A, 0x1B, 0x33, 0x46, 0x01, 0x38, 0x6D, 0x67, 0x3F, 0x57, 0x76, 0x12, 0x6E, 0x15, 0x39, 0x03, 0x37, 0x27, 0x42, 0x20, 0x02, 0x16, 0x24, 0x18, 0x1D, 0x19, 0x2E, 0x3F, 0x71, 0x45, 0x7A, 0x74, 0x17, 0x60, 0x59, 0x5F, 0x76, 0x15, 0x7A, 0x68, 0x16, 0x2F, 0x38, 0x27, 0x2D, 0x5A, 0x34, 0x00, 0x18, 0x36, 0x0C, 0x1F, 0x2F, 0x0C, 0x3C, 0x5A, 0x7B, 0x41, 0x52, 0x66, 0x4A, 0x44, 0x59, 0x4C, 0x6E, 0x78, 0x72, 0x4B, 0x16, 0x35, 0x39, 0x50, 0x2E, 0x4B, 0x3E, 0x37, 0x34, 0x1C, 0x31, 0x4E, 0x73, 0x76, 0x16, 0x2E, 0x05, 0x16, 0x75, 0x1A, 0x44, 0x62, 0x78, 0x63, 0x61, 0x7B, 0x5B, 0x4E, 0x44, 0x73, 0x6D, 0x22, 0x05, 0x3F, 0x7A, 0x2E, 0x43, 0x1E, 0x5C, 0x66, 0x29, 0x1A, 0x0D, 0x6E, 0x18, 0x14, 0x25, 0x66, 0x3B, 0x6C, 0x7C, 0x4A, 0x2C, 0x39, 0x33, 0x27, 0x33, 0x3C, 0x0F, 0x25, 0x1D, 0x73, 0x79, 0x76, 0x03, 0x46, 0x33, 0x08, 0x14, 0x6E, 0x73, 0x1E, 0x44, 0x36, 0x0E, 0x35, 0x08, 0x3F, 0x16, 0x22, 0x1C, 0x15, 0x6D, 0x67, 0x54, 0x57, 0x44, 0x4C, 0x2E, 0x06, 0x2C, 0x6A, 0x2B, 0x77, 0x4A, 0x47, 0x66, 0x2B, 0x3C, 0x5C, 0x5C, 0x1F, 0x14, 0x20, 0x64, 0x0C, 0x75, 0x46, 0x4C, 0x3C, 0x10, 0x31, 0x37, 0x21, 0x33, 0x5E, 0x39, 0x2E, 0x44, 0x6D, 0x72, 0x79, 0x7C, 0x5E, 0x15, 0x7C, 0x42, 0x2B, 0x21, 0x1F, 0x33, 0x56, 0x5D, 0x4F, 0x7B, 0x12, 0x6E, 0x36, 0x3E, 0x1E, 0x22, 0x03, 0x26, 0x18, 0x36, 0x04, 0x74, 0x7F, 0x44, 0x5F, 0x63, 0x04, 0x73, 0x4A, 0x5B, 0x52, 0x48, 0x77, 0x6D, 0x4B, 0x43, 0x2F, 0x35, 0x01, 0x58, 0x08, 0x3A, 0x5D, 0x25, 0x24, 0x67, 0x4A, 0x6F, 0x77, 0x14, 0x3D, 0x21, 0x7B, 0x7D, 0x5A, 0x64, 0x77, 0x7A, 0x11, 0x66, 0x5B, 0x60, 0x40, 0x6E, 0x59, 0x7C, 0x08, 0x63, 0x6E, 0x5C, 0x77, 0x58, 0x77, 0x76, 0x1D, 0x54, 0x31, 0x63, 0x74, 0x53, 0x52, 0x3A, 0x02, 0x42, 0x4A, 0x44, 0x0C, 0x6E, 0x02, 0x30, 0x3B, 0x5F, 0x7F, 0x51, 0x1F, 0x44, 0x7C, 0x76, 0x5C, 0x4A, 0x58, 0x7A, 0x66, 0x5D, 0x7B, 0x63, 0x45, 0x75, 0x60, 0x76, 0x4C, 0x7A, 0x59, 0x20, 0x57, 0x52, 0x74, 0x76, 0x08, 0x5E, 0x34, 0x0A, 0x36, 0x71, 0x41, 0x6F, 0x76, 0x2F, 0x12, 0x3A, 0x64, 0x50, 0x50, 0x4A, 0x66, 0x6A, 0x53, 0x51, 0x68, 0x52, 0x45, 0x77, 0x76, 0x68, 0x07, 0x7F, 0x56, 0x72, 0x4B, 0x73, 0x11, 0x69, 0x0E, 0x31, 0x0D, 0x7D, 0x78, 0x01, 0x29, 0x1C, 0x37, 0x28, 0x63, 0x54, 0x7B, 0x45, 0x30, 0x3F, 0x10, 0x66, 0x43, 0x54, 0x7A, 0x7B, 0x47, 0x11, 0x04, 0x78, 0x77, 0x58, 0x41, 0x69, 0x78, 0x7C, 0x5D, 0x6E, 0x61, 0x33, 0x7A, 0x42, 0x78, 0x72, 0x3A, 0x3F, 0x35, 0x69, 0x41, 0x0F, 0x0D, 0x1D, 0x11, 0x16, 0x77, 0x6E, 0x73, 0x76, 0x03, 0x2B, 0x21, 0x7E, 0x0E, 0x5F, 0x5D, 0x3D, 0x43, 0x64, 0x55, 0x73, 0x08, 0x0B, 0x34, 0x0D, 0x34, 0x78, 0x7A, 0x76, 0x3D, 0x58, 0x7A, 0x65, 0x68, 0x61, 0x79, 0x72, 0x5D, 0x5A, 0x58, 0x4E, 0x67, 0x7A, 0x0E, 0x22, 0x1D, 0x75, 0x1F, 0x75, 0x62, 0x5E, 0x3D, 0x79, 0x2A, 0x05, 0x33, 0x08, 0x53, 0x3F, 0x04, 0x7E, 0x47, 0x73, 0x4F, 0x67, 0x6F, 0x67, 0x5B, 0x4C, 0x3E, 0x63, 0x40, 0x69, 0x38, 0x48, 0x77, 0x76, 0x4D, 0x43, 0x12, 0x35, 0x01, 0x6E, 0x1F, 0x34, 0x15, 0x33, 0x21, 0x50, 0x66, 0x4D, 0x53, 0x38, 0x04, 0x00, 0x5F, 0x2E, 0x29, 0x3D, 0x17, 0x28, 0x74, 0x15, 0x75, 0x01, 0x3A, 0x3A, 0x5A, 0x28, 0x7C, 0x5C, 0x63, 0x04, 0x6E, 0x74, 0x4A, 0x7B, 0x5E, 0x44, 0x7A, 0x55, 0x45, 0x68, 0x44, 0x79, 0x52, 0x32, 0x14, 0x15, 0x76, 0x4E, 0x54, 0x30, 0x29, 0x3A, 0x70, 0x20, 0x0A, 0x14, 0x30, 0x37, 0x5F, 0x29, 0x43, 0x60, 0x72, 0x7F, 0x49, 0x7C, 0x47, 0x67, 0x78, 0x1A, 0x6E, 0x41, 0x48, 0x5F, 0x10, 0x44, 0x73, 0x76, 0x20, 0x3F, 0x12, 0x62, 0x4E, 0x02, 0x3A, 0x23, 0x37, 0x1C, 0x0A, 0x3B, 0x3A, 0x0C, 0x47, 0x41, 0x7B, 0x7A, 0x1E, 0x0A, 0x3E, 0x44, 0x0D, 0x32, 0x31, 0x27, 0x3E, 0x67, 0x46, 0x6A, 0x27, 0x37, 0x2F, 0x23, 0x2B, 0x53, 0x6A, 0x44, 0x00, 0x7A, 0x66, 0x5E, 0x03, 0x7A, 0x4D, 0x5D, 0x77, 0x7D, 0x02, 0x48, 0x77, 0x17, 0x30, 0x12, 0x78, 0x1B, 0x7E, 0x51, 0x4B, 0x5C, 0x67, 0x54, 0x57, 0x44, 0x4C, 0x3C, 0x01, 0x3B, 0x23, 0x11, 0x38, 0x0D, 0x15, 0x34, 0x78, 0x68, 0x12, 0x02, 0x0E, 0x02, 0x62, 0x62, 0x12, 0x3C, 0x02, 0x0D, 0x36, 0x21, 0x3B, 0x2A, 0x3B, 0x24, 0x1C, 0x7A, 0x66, 0x4A, 0x77, 0x7B, 0x73, 0x58, 0x16, 0x3F, 0x00]
s[0] ^= 0x56
xor = b"Vm0xd1NtUXlWa1pPVldoVFlUSlNjVlZyV21GVk1XeDBaRVYwYWxadVFsaFdiWFF3VmxkS1IxTnNXbFpXZWtFeFZsUkdTMk15VGtaYVJtUk9ZbXRLZVZacldtdFNNVnBYVm01R1UySkdXbFJVVnpWUFRURmtjbGRzWkU5U01IQXdWa2QwVjFaWFNrbFJiR2hWVm5wV2NsUlVSbFpsUmxwMFQxZG9UbUV5ZHpCWFYzUmhZekZhYzFacVdtbFNXRkpYV1ZkMGQyUnNVbGhsU0dSVVZqQndSMVpITVc5aFZscFlaSHBLVjJKVVFYaFdSRVp6VmpGS1dWcEdVbWxpVmtwdlZsZDRWazFXU2tkaVJtUllZbTFTV0ZWdGRHRk5WbXQzV2toT2FWSnNjRmRaTUdoM1ZqQXhWMk5JV2xkU1JVVjRWbXBHUjJOV1VuTlNiR1JUVWxWVk1RPT0="
for i in range(1, 1760):
s[i] ^= xor[i%460]
print("".join(map(chr, 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
#version 430 core

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) buffer OpCodes { int opcodes[]; };
layout(std430, binding = 2) buffer CoConsts { int co_consts[]; };
layout(std430, binding = 3) buffer Cipher { int cipher[16]; };
layout(std430, binding = 4) buffer Stack { int stack_data[256]; };
layout(std430, binding = 5) buffer Out { int verdict; };

const int MaxInstructionCount = 1000;

void main()
{
if (gl_GlobalInvocationID.x > 0) return;

uint ip = 0u;
int sp = 0;
verdict = -233;

while (ip < uint(MaxInstructionCount))
{
int opcode = opcodes[int(ip)];
int arg = opcodes[int(ip)+1];

switch (opcode)
{
case 2:
stack_data[sp++] = co_consts[arg];
break;
case 7:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a + b;
break;
}
case 8:
{
int a = stack_data[--sp];
int b = stack_data[--sp];
stack_data[sp++] = a - b;
break;
}
case 14:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a ^ b;
break;
}

case 15:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = int(a == b);
break;
}

case 16:
{
bool ok = true;
for (int i = 0; i < 16; i++)
{
if (stack_data[i] != (cipher[i] - 20))
{
ok = false;
break;
}
}
verdict = ok ? 1 : -1;
return;
}

case 18:
{
int c = stack_data[--sp];
if (c == 0) ip = uint(arg);
break;
}

default:
verdict = 500;
return;
}

ip+=2;
}
verdict = 501;
}

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
#version 330

#define S(a,b,t) smoothstep(a,b,t)

uniform float time;

out vec4 fragColor;

mat2 Rot(float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c);
}

vec2 hash(vec2 p) {
p = vec2(dot(p, vec2(2127.1, 81.17)), dot(p, vec2(1269.5, 283.37)));
return fract(sin(p) * 43758.5453);
}

float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);

return mix(
mix(dot(-1.0 + 2.0 * hash(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
dot(-1.0 + 2.0 * hash(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
mix(dot(-1.0 + 2.0 * hash(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
dot(-1.0 + 2.0 * hash(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x),
u.y
) * 0.5 + 0.5;
}

void main() {
vec2 uSize = vec2(1280.0, 800.0);
vec2 uv = gl_FragCoord.xy / uSize;

float ratio = uSize.x / uSize.y;
vec2 tuv = uv - 0.5;

float degree = noise(vec2(time * 0.1, tuv.x * tuv.y));
tuv.y *= 1.0 / ratio;
tuv *= Rot(radians((degree - 0.5) * 720.0 + 180.0));
tuv.y *= ratio;

float frequency = 3.5;
float amplitude = 10.0;
float speed = time * 1.5;

tuv.x += sin(tuv.y * frequency + speed) / amplitude;
tuv.y += sin(tuv.x * frequency * 1.5 + speed) / (amplitude * 0.5);

vec3 color1 = vec3(0.8, 0.4, 0.9);
vec3 color2 = vec3(0.4, 0.7, 1.0);
vec3 color3 = vec3(1.0, 0.6, 0.4);
vec3 color4 = vec3(0.6, 1.0, 0.6);

vec3 layer1 = mix(color1, color2, S(-0.3, 0.2, (tuv * Rot(radians(-5.0))).x));
vec3 layer2 = mix(color3, color4, S(-0.3, 0.2, (tuv * Rot(radians(-5.0))).x));
vec3 finalColor = mix(layer1, layer2, S(0.5, -0.3, tuv.y));

fragColor = vec4(finalColor, 1.0);
}

发现这两份代码

追溯了下发现是在一开始这里初始化的

image-20250729143544491

或者直接

image-20250729144047174

跟进变量即可发现代码

image-20250729144111731

其实这块的逻辑是初始化

image-20250729144150570

1
2
3
4
5
layout(std430, binding = 0) buffer OpCodes  { int opcodes[]; };
layout(std430, binding = 2) buffer CoConsts { int co_consts[]; };
layout(std430, binding = 3) buffer Cipher { int cipher[16]; };
layout(std430, binding = 4) buffer Stack { int stack_data[256]; };
layout(std430, binding = 5) buffer Out { int verdict; };

image-20250729150958551

image-20250729151015751

如图:

image-20250729151138901

程序首先初始化一个窗口并设置 OpenGL 上下文。然后,它调用 initialize_gl_functions 来加载所有必需的 OpenGL 函数指针,例如 glBufferData 。加载 GPU 数据:程序多次调用 load_data_to_gpu 函数,将数据加载到 GPU 内存中的缓冲区。gpu_vm_opcodes :一个包含自定义虚拟机操作码的数组。gpu_vm_constants :一个包含虚拟机使用的常量的数组。其他用于存储用户输入、密码检查结果和中间计算的缓冲区。

密码验证:程序会将括号内的 32 个十六进制字符转换为 16 个字节的数据。然后,这 16 个字节的数据被加载到 GPU 的一个特定缓冲区中,作为密码提交给虚拟机进行验证。

GPU 虚拟机执行:

  • 程序设置并启动一个计算着色器。
  • 这个计算着色器实现了一个自定义的虚拟机,它会执行 gpu_vm_opcodes 中的指令,使用 gpu_vm_constants 中的常量,并根据用户输入的密码数据进行计算。

记录下idapython的使用(这个总是问gpt,还是积累下代码吧)

第一份是批量数据转化,第二份是数据导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import idc

start = 0x140093020 # 起始地址
count = 16 # 要定义多少个 dd

for i in range(count):
idc.create_data(start + i * 4, idc.FF_DWORD, 4, idc.BADADDR)

import ida_bytes

start = 0x140093020 # 起始地址
count = 16 # 要读取多少个 dword(4 字节)

output = []

for i in range(count):
ea = start + i * 4
val = ida_bytes.get_dword(ea)
output.append(val)

# 打印为 Python 列表形式
print(output)

重写下逻辑,看看这个opcode到底干啥了

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
enc = [243, 130, 6, 509, 336, 56, 178, 222, 346, 407, 156, 471, 110, 40, 326, 151]

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

con = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00]

stack_data = [0] * 0x100
verdict = -233
ip = 0
sp = 0

inp = bytes.fromhex("12345678123456781234567812345678")
for i in range(16):
con[i] = inp[i]

def push(arg):
global stack_data, sp
stack_data[sp] = arg
sp += 1

def pop():
global stack_data, sp
sp -= 1
return stack_data[sp]

while ip < len(opcodes):
opcode = opcodes[ip]
arg = opcodes[ip+1]
if opcode == 2:
print(f"stack[{sp}] = con[{arg}]")
push(con[arg])
elif opcode == 7:
b = pop()
a = pop()
print(f"stack[{sp}] = stack[{sp}] + stack[{sp + 1}]")
push(a + b)
elif opcode == 8:
a = pop()
b = pop()
print(f"stack[{sp}] = stack[{sp + 1}] - stack[{sp}]")
push(a - b)
elif opcode == 14:
b = pop()
a = pop()
print(f"stack[{sp}] = stack[{sp}] ^ stack[{sp + 1}]")
push(a ^ b)
elif opcode == 15:
b = pop()
a = pop()
tmp = True if a == b else False
# print(f"cmp stack[{sp}] = {hex(a)}, stack[{sp+1}] = {hex(b)}\tpush stack[{sp}], res = {tmp}")
push(1 if tmp == True else 0)
elif opcode == 16:
tmp = True
for i in range(16):
# print(f"cmp stk[{i}] = {hex(stack_data[i])}, cipher[{i}] = {hex(enc[i]-20)}")
if (stack_data[i] != (enc[i]-20)):
tmp = False
tag = 1 if tmp == True else -1
break
elif opcode == 18:
c = pop()
print(f"pop stack[{sp+1}] = {hex(c)}")
if c == 0:
print(f"jz to {hex(c)}")
ip = arg
else:
print(f"jnz denied")
else:
tag = 500
break
ip += 2
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
stack[0] = con[0]
stack[1] = con[1]
stack[2] = con[0]
stack[1] = stack[1] ^ stack[2]
stack[2] = con[16]
stack[1] = stack[2] - stack[1]
stack[2] = con[2]
stack[3] = con[1]
stack[2] = stack[2] ^ stack[3]
stack[3] = con[17]
stack[2] = stack[3] - stack[2]
stack[3] = con[3]
stack[4] = con[2]
stack[3] = stack[3] ^ stack[4]
stack[4] = con[18]
stack[3] = stack[3] + stack[4]
stack[4] = con[4]
stack[5] = con[3]
stack[4] = stack[4] ^ stack[5]
stack[5] = con[19]
stack[4] = stack[4] + stack[5]
stack[5] = con[5]
stack[6] = con[4]
stack[5] = stack[5] ^ stack[6]
stack[6] = con[20]
stack[5] = stack[6] - stack[5]
stack[6] = con[6]
stack[7] = con[5]
stack[6] = stack[6] ^ stack[7]
stack[7] = con[21]
stack[6] = stack[6] + stack[7]
stack[7] = con[7]
stack[8] = con[6]
stack[7] = stack[7] ^ stack[8]
stack[8] = con[22]
stack[7] = stack[7] + stack[8]
stack[8] = con[8]
stack[9] = con[7]
stack[8] = stack[8] ^ stack[9]
stack[9] = con[23]
stack[8] = stack[8] + stack[9]
stack[9] = con[9]
stack[10] = con[8]
stack[9] = stack[9] ^ stack[10]
stack[10] = con[24]
stack[9] = stack[9] + stack[10]
stack[10] = con[10]
stack[11] = con[9]
stack[10] = stack[10] ^ stack[11]
stack[11] = con[25]
stack[10] = stack[10] + stack[11]
stack[11] = con[11]
stack[12] = con[10]
stack[11] = stack[11] ^ stack[12]
stack[12] = con[26]
stack[11] = stack[11] + stack[12]
stack[12] = con[12]
stack[13] = con[11]
stack[12] = stack[12] ^ stack[13]
stack[13] = con[27]
stack[12] = stack[13] - stack[12]
stack[13] = con[13]
stack[14] = con[12]
stack[13] = stack[13] ^ stack[14]
stack[14] = con[28]
stack[13] = stack[14] - stack[13]
stack[14] = con[14]
stack[15] = con[13]
stack[14] = stack[14] ^ stack[15]
stack[15] = con[29]
stack[14] = stack[14] + stack[15]
stack[15] = con[15]
stack[16] = con[14]
stack[15] = stack[15] ^ stack[16]
stack[16] = con[30]
stack[15] = stack[16] - stack[15]

看了下逻辑,可知con前16个就是输入,后16起到key的作用,然后分为两种加密。

简单解释下,当我们要对stack1做处理,我们需要用到密文1和密文2,密文1异或密文2=stack1这时的stack2只是一个临时空间,后面要存入key1,stack1=key1-stack1

1
2
3
4
5
6
stack[0] = con[0]
stack[1] = con[1]
stack[2] = con[0]
stack[1] = stack[1] ^ stack[2]
stack[2] = con[16]
stack[1] = stack[2] - stack[1]
  1. 密文=密钥[i-1]-(明文[i]^明文[i-1])
  2. 密文=(明文[i]^明文[i-1])+密钥[i-1]

那么很清晰了,我们把opcode归成两类0和1

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
enc = [243, 130, 6, 509, 336, 56, 178, 222, 346, 407, 156, 471, 110, 40, 326, 151]

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

con = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00]

stack_data = [0] * 0x100
verdict = -233
ip = 0
sp = 0

inp = bytes.fromhex("12345678123456781234567812345678")
for i in range(16):
con[i] = inp[i]

def push(arg):
global stack_data, sp
stack_data[sp] = arg
sp += 1

def pop():
global stack_data, sp
sp -= 1
return stack_data[sp]

while ip < len(opcodes):
opcode = opcodes[ip]
arg = opcodes[ip+1]
if opcode == 2:
print(f"stack[{sp}] = con[{arg}]")
push(con[arg])
elif opcode == 7:
b = pop()
a = pop()
print(f"stack[{sp}] = stack[{sp}] + stack[{sp + 1}]")
push(a + b)
elif opcode == 8:
a = pop()
b = pop()
print(f"stack[{sp}] = stack[{sp + 1}] - stack[{sp}]")
push(a - b)
elif opcode == 14:
b = pop()
a = pop()
print(f"stack[{sp}] = stack[{sp}] ^ stack[{sp + 1}]")
push(a ^ b)
elif opcode == 15:
b = pop()
a = pop()
tmp = True if a == b else False
# print(f"cmp stack[{sp}] = {hex(a)}, stack[{sp+1}] = {hex(b)}\tpush stack[{sp}], res = {tmp}")
push(1 if tmp == True else 0)
elif opcode == 16:
tmp = True
for i in range(16):
# print(f"cmp stk[{i}] = {hex(stack_data[i])}, cipher[{i}] = {hex(enc[i]-20)}")
if (stack_data[i] != (enc[i]-20)):
tmp = False
tag = 1 if tmp == True else -1
break
elif opcode == 18:
c = pop()
print(f"pop stack[{sp+1}] = {hex(c)}")
if c == 0:
print(f"jz to {hex(c)}")
ip = arg
else:
print(f"jnz denied")
else:
tag = 500
break
ip += 2

op = [1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,0]
enc = [i - 20 for i in enc]
key = [0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00]

for i in range(1,16):
if op[i] == 0:
enc[i] = key[i - 1] - enc[i]
else:
enc[i] = enc[i] - key[i - 1]
enc[i] ^= enc[i - 1]

result = ''.join(f"{enc[i]:02x}" for i in range(16))
print('D3L{' + result + '}')

L3HCTF{df9d4ba41258574ccb7155b9d01f5c58}

其他方法:爆破

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
opc = [0x02, 0x00, 0x02, 0x01, 0x02, 0x00, 0x0E, 0x00, 0x02, 0x10, 0x08, 0x00, 0x02, 0x02, 0x02, 0x01, 0x0E, 0x00, 0x02, 0x11, 0x08, 0x00, 0x02, 0x03, 0x02, 0x02, 0x0E, 0x00, 0x02, 0x12, 0x07, 0x00, 
0x02, 0x04, 0x02, 0x03, 0x0E, 0x00, 0x02, 0x13, 0x07, 0x00, 0x02, 0x05, 0x02, 0x04, 0x0E, 0x00, 0x02, 0x14, 0x08, 0x00, 0x02, 0x06, 0x02, 0x05, 0x0E, 0x00, 0x02, 0x15, 0x07, 0x00, 0x02, 0x07,
0x02, 0x06, 0x0E, 0x00, 0x02, 0x16, 0x07, 0x00, 0x02, 0x08, 0x02, 0x07, 0x0E, 0x00, 0x02, 0x17, 0x07, 0x00, 0x02, 0x09, 0x02, 0x08, 0x0E, 0x00, 0x02, 0x18, 0x07, 0x00, 0x02, 0x0A, 0x02, 0x09,
0x0E, 0x00, 0x02, 0x19, 0x07, 0x00, 0x02, 0x0B, 0x02, 0x0A, 0x0E, 0x00, 0x02, 0x1A, 0x07, 0x00, 0x02, 0x0C, 0x02, 0x0B, 0x0E, 0x00, 0x02, 0x1B, 0x08, 0x00, 0x02, 0x0D, 0x02, 0x0C, 0x0E, 0x00,
0x02, 0x1C, 0x08, 0x00, 0x02, 0x0E, 0x02, 0x0D, 0x0E, 0x00, 0x02, 0x1D, 0x07, 0x00, 0x02, 0x0F, 0x02, 0x0E, 0x0E, 0x00, 0x02, 0x1E, 0x08, 0x00, 0x10, 0x00, 0x02, 0x10, 0x02, 0x11, 0x0F, 0x00,
0x12, 0x54, 0x02, 0x1F, 0x01, 0x00, 0x03, 0x01]

enc = [0x000000F3, 0x00000082, 0x00000006, 0x000001FD, 0x00000150, 0x00000038, 0x000000B2, 0x000000DE, 0x0000015A, 0x00000197, 0x0000009C, 0x000001D7, 0x0000006E, 0x00000028, 0x00000146, 0x00000097]
con = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x00]

inp = bytearray(16)
for leng in range(16):
for cand in range(256):
inp[leng] = cand

stk = [0] * 0x100
tag = -233
ip = 0
sp = 0

for i in range(16):
con[i] = inp[i]

res = [0]*16

def push(arg):
global stk, sp
stk[sp] = arg
sp += 1

def pop():
global stk, sp
sp -= 1
return stk[sp]

while ip < len(opc):
opcode = opc[ip]
arg = opc[ip+1]
if opcode == 2:
push(con[arg])
elif opcode == 7:
b = pop()
a = pop()
push(a + b)
elif opcode == 8:
a = pop()
b = pop()
push(a - b)
elif opcode == 14:
b = pop()
a = pop()
push(a ^ b)
elif opcode == 15:
b = pop()
a = pop()
tmp = True if a == b else False
push(1 if tmp == True else 0)
elif opcode == 16:
tmp = True
for i in range(16):
if (stk[i] != (enc[i]-20)):
tmp = False
res[i] = 0
else:
res[i] = 1
tag = 1 if tmp == True else -1
break
elif opcode == 18:
c = pop()
if c == 0:
ip = arg
else:
pass
else:
tag = 500
break
ip += 2

if res[leng] == 1:
break

print("L3HCTF{"+inp.hex()+"}")

大致看了下,这个是通过res位来确定该位是否爆破成功的,仔细理解下,有点稍微难解释

easyvm

虚拟机逆向

可以看L3HCTF WP | Liv’s blog大佬的博客

首先学到个新的标记下:ida打开先设置Options->Compiler… [Compiler: Visual C++],而非gnu

如果这是一个Windows 平台的程序,使用 Microsoft Visual Studio 编译的 .exe 文件,一般应设置为:visual C++

定位到如下位置:

image-20250819214718924

image-20250819214840461

可以跟进到sub_1400107E0,发现一些运算符号

image-20250819220139781

可以下个给这几处附加脚本

image-20250819223609600

1
2
3
4
5
auto eax = GetRegValue("eax");
auto rbp = GetRegValue("rbp");
auto v1 = Dword(rbp + 16);
auto add1 = Dword(rbp + 20);
Message("%X = %X + %X;\n",eax,v1,add1);
1
2
3
4
5
auto eax = GetRegValue("eax");
auto rbp = GetRegValue("rbp");
auto vA8 = Dword(rbp + 0xC0 - 0xA8);
auto vA4 = Dword(rbp + 0xC0 - 0xA4);
Message("%X = %X - %X;\n",eax, vA8, vA4);
1
2
3
4
5
auto eax = GetRegValue("eax");
auto rbp = GetRegValue("rbp");
auto vA0 = Dword(rbp + 0xC0 - 0xA0);
auto v9C = Dword(rbp + 0xC0 - 0x9C);
Message("%X = %X / %X;\n",eax, vA0, v9C);
1
2
3
4
5
auto rbp = GetRegValue("rbp");
auto eax = GetRegValue("eax");
auto v98 = Dword(rbp + 0xC0 - 0x98);
auto v94 = Dword(rbp + 0xC0 - 0x94);
Message("%X = %X / %X \n", eax, v98, v94);
1
2
3
4
5
auto rbp = GetRegValue("rbp");
auto eax = GetRegValue("eax");
auto v90 = Dword(rbp + 0xC0 - 0x90);
auto v8C = Dword(rbp + 0xC0 - 0x8C);
Message("%X = %X mod %X \n", eax, v90, v8C);
1
2
3
4
5
auto rbp = GetRegValue("rbp");
auto eax = GetRegValue("eax");
auto v88 = Dword(rbp + 0xC0 - 0x88);
auto v84 = Dword(rbp + 0xC0 - 0x84);
Message("%X = %X << %X \n", eax, v88, v84);
1
2
3
4
5
auto rbp = GetRegValue("rbp");
auto eax = GetRegValue("eax");
auto v80 = Dword(rbp + 0xC0 - 0x80);
auto v7C = Dword(rbp + 0xC0 - 0x7C);
Message("%X = %X >> %X \n", eax, v80, v7C);
1
2
3
4
5
auto rbp = GetRegValue("rbp");
auto eax = GetRegValue("eax");
auto v78 = Dword(rbp + 0xC0 - 0x78);
auto v74 = Dword(rbp + 0xC0 - 0x74);
Message("%X = %X ^ %X \n", eax, v78, v74);

输入11112222333344445555666677778888测一下,下面那个是摘出的一部分,我是搜索31313131定位到又有异或加法处观察的

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
91919190 = 32323232 << 3 
36FD3D5D =91919190 + A56BABCD;
32323232 =32323232 + 0;
32323232 =32323232 + 0;
4CF0F6F = 32323232 ^ 36FD3D5D
3232323 = 32323232 >> 4
3232322 =3232323 + FFFFFFFF;
7EC2C4D = 4CF0F6F ^ 3232322
391D5D7E =7EC2C4D + 31313131;

11223344 =0 + 11223344;

E47575F8 = 391D5D7E << 2
E47575F7 = E47575F8 + FFFFFFFF;
4A3F90C2 = 391D5D7E + 11223344;
F60D7FC3 = 4A3F90C2 + ABCDEF01;
12780A34 = F60D7FC3 ^ E47575F7
1C8EAEB = 391D5D7E >> 5
A73496B8 = 1C8EAEB + A56BABCD;
B54C9C8C = 12780A34 ^ A73496B8
E77ECEBE = B54C9C8C + 32323232;

3F = 40 - 1;
3BF675F0 = E77ECEBE << 3
E16221BD =3BF675F0 + A56BABCD;
F8A10202 =E77ECEBE + 11223344;
F8A10202 =F8A10202 + 0;
19C323BF = F8A10202 ^ E16221BD
E77ECEB = E77ECEBE >> 4
E77ECEA =E77ECEB + FFFFFFFF;
17B4CF55 = 19C323BF ^ E77ECEA
50D22CD3 =17B4CF55 + 391D5D7E;
22446688 =11223344 + 11223344;
4348B34C = 50D22CD3 << 2
4348B34B =4348B34C + FFFFFFFF;
7316935B =50D22CD3 + 22446688;
1EE4825C =7316935B + ABCDEF01;
5DAC3117 = 1EE4825C ^ 4348B34B
2869166 = 50D22CD3 >> 5
A7F23D33 =2869166 + A56BABCD;
FA5E0C24 = 5DAC3117 ^ A7F23D33
E1DCDAE2 =FA5E0C24 + E77ECEBE;
3E = 3F - 1;
EE6D710 = E1DCDAE2 << 3
B45282DD =EE6D710 + A56BABCD;
421416A =E1DCDAE2 + 22446688;
421416A =421416A + 0;
B073C3B7 = 421416A ^ B45282DD
E1DCDAE = E1DCDAE2 >> 4
E1DCDAD =E1DCDAE + FFFFFFFF;
BE6E0E1A = B073C3B7 ^ E1DCDAD
F403AED =BE6E0E1A + 50D22CD3;
336699CC =22446688 + 11223344;
3D00EBB4 = F403AED << 2
3D00EBB3 =3D00EBB4 + FFFFFFFF;
42A6D4B9 =F403AED + 336699CC;
EE74C3BA =42A6D4B9 + ABCDEF01;
D3742809 = EE74C3BA ^ 3D00EBB3
7A01D7 = F403AED >> 5
A5E5ADA4 =7A01D7 + A56BABCD;
769185AD = D3742809 ^ A5E5ADA4
586E608F =769185AD + E1DCDAE2;
3D = 3E - 1;

可以发现11223344一直在+,有移位操作可能与tea相关,轮次是0x40,可能是个变种tea

我们拆开分析下

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
A += ((B << 3) + key[?]) ^ (B + key[?] + sum)  ^ ((B >> 4) + key[?])
A += (((B << 3) + key[1]) ^ (B + Sum + key[0]) ^ ((B >> 4) + key[2]));
91919190 = 32323232 << 3
36FD3D5D =91919190 + A56BABCD;
32323232 =32323232 + 0;
32323232 =32323232 + 0;
4CF0F6F = 32323232 ^ 36FD3D5D
3232323 = 32323232 >> 4
3232322 =3232323 + FFFFFFFF;
7EC2C4D = 4CF0F6F ^ 3232322
391D5D7E =7EC2C4D + 31313131;

11223344 =0 + 11223344; sum += delta

B += (((A << 2) + key[?]) ^ (A + Sum + key[?]) ^ ((A >> 5) + key[?]));这里的key并没有顺序,我们可以规定一个
B += (((A << 2) + key[2]) ^ (A + Sum + key[3]) ^ ((A >> 5) + key[1]));
E47575F8 = 391D5D7E << 2
E47575F7 = E47575F8 + FFFFFFFF;
4A3F90C2 = 391D5D7E + 11223344;
F60D7FC3 = 4A3F90C2 + ABCDEF01;
12780A34 = F60D7FC3 ^ E47575F7
1C8EAEB = 391D5D7E >> 5
A73496B8 = 1C8EAEB + A56BABCD;
B54C9C8C = 12780A34 ^ A73496B8
E77ECEBE = B54C9C8C + 32323232;

3F = 40 - 1;

3BF675F0 = E77ECEBE << 3
E16221BD =3BF675F0 + A56BABCD;
F8A10202 =E77ECEBE + 11223344; (看出来是 + 了一个 sum)
F8A10202 =F8A10202 + 0; (这里也是一个key不过是0)
19C323BF = F8A10202 ^ E16221BD
E77ECEB = E77ECEBE >> 4
E77ECEA =E77ECEB + FFFFFFFF;
17B4CF55 = 19C323BF ^ E77ECEA
50D22CD3 =17B4CF55 + 391D5D7E;

22446688 =11223344 + 11223344;

4348B34C = 50D22CD3 << 2
4348B34B =4348B34C + FFFFFFFF;
7316935B =50D22CD3 + 22446688;
1EE4825C =7316935B + ABCDEF01;
5DAC3117 = 1EE4825C ^ 4348B34B
2869166 = 50D22CD3 >> 5
A7F23D33 =2869166 + A56BABCD;
FA5E0C24 = 5DAC3117 ^ A7F23D33
E1DCDAE2 =FA5E0C24 + E77ECEBE;

可以总结为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
uint32_t key[]{ 0x0, 0xA56BABCD, 0xFFFFFFFF, 0xABCDEF01 };

uint32_t Delta = 0x11223344;

DWORD Sum = 0;

DWORD A = Input[0];
DWORD B = Input[1];

for (int i = 0; i < 0x40; i++)
{
A += (((B << 3) + key[1]) ^ (B + Sum + key[0]) ^ ((B >> 4) + key[2]));

Sum += Delta;

B += (((A << 2) + key[2]) ^ (A + Sum + key[3]) ^ ((A >> 5) + key[1]));
}

Input[0] = A;
Input[1] = B;

需要注意一点,观察输出数据可以发现不同组加密之间共用一个Sum值,也就是第一组数据加密后的Sum值会作为下一组数据加密的初始Sum值,所以解密的时候要注意Sum值。

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

using namespace std;

void decrypt(uint32_t* v, int i) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x11223344;
uint32_t sum = delta * 0x40 * i;
uint32_t key[4]{ 0x0, 0xA56BABCD, 0xFFFFFFFF, 0xABCDEF01 };
for (int i = 0; i < 0x40; i++)
{
v1 -= (((v0 << 2) + key[2]) ^ (v0 + sum + key[3]) ^ ((v0 >> 5) + key[1]));
sum -= delta;
v0 -= (((v1 << 3) + key[1]) ^ (v1 + sum + key[0]) ^ ((v1 >> 4) + key[2]));
}
v[0] = v0; v[1] = v1;
}

int main() {
uint32_t enc[8] = { 0x877A62A6, 0x6A55F1F3, 0xAE194847, 0xB1E643E7, 0xA94FE881, 0x9BC8A28A, 0xC4CFAA9F, 0xF1A00CA1, };
for (int i = 0; i < 32; i += 8)
{
decrypt((uint32_t*)((uint8_t*)enc + i), (i + 8) / 8);
}
printf("L3HCTF{%.32s}\n", enc);
printf("L3HCTF{");
for (int i = 0; i < 8; i++) printf("%.4s", &enc[i]);
printf("}\n");
return 0;
}

L3HCTF{9c50d10ba864bedfb37d7efa4e110bf2}

obfuscate

函数不多直接手动翻几个看看发现1E80和1250两个略复杂,有移位操作,可能是加密或初始化的地方,但是没有发现交叉引用,调试看看。

调试半天都会退出,太深了也没找到是哪退出的,之前在handler发现了exit,看看引用。

image-20250820225554697

image-20250820225734570

直接改成retn

继续调试其实也能找到退出的地方

image-20250820231155888

image-20250820234959722

输入长度为32

没找出来在哪调用的那两个函数,直接下断点调试,250没看出来实际作用,E80跟进后发现a2a3是前8位密文

看起来像tea

image-20250821095721347

实在太丑了这个代码

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

// 精简后的轮函数伪代码
uint64_t round_function(uint32_t *key, uint64_t block, uint32_t *extra)
{
uint32_t L = (uint32_t)(block & 0xFFFFFFFF); // 低32位
uint32_t R = (uint32_t)((block >> 32) & 0xFFFFFFFF);// 高32位
uint32_t tmp;

// 主循环:0xC = 12,共 13 轮
for (int i = 0; i <= 12; i++)
{
// Step1: 取查表值
uint32_t t1 = *(uint32_t*)( (uint64_t)key[0] + 4*(2*i) );
uint32_t t2 = *(uint32_t*)( (uint64_t)key[0] + 4*(2*i+1) );

// Step2: 混合运算 (ARX风格)
uint32_t fL = t1
+ (( (L & 0xC3577130) | (~L & 0x3CA88ECF))
^ ((R & 0xC3577130) | (~R & 0x3CA88ECF))) << L;

uint32_t fR = t2
+ ((~L & R) | (L & ~R)) >> (32 - L);

// Step3: Feistel-like 交换
tmp = L;
L = fL ^ R; // 新左半
R = fR ^ tmp; // 新右半
}

// 合并结果
return ((uint64_t)R << 32) | L;
}

结合GPT分析

1
2
A = *(_DWORD *)(*(_QWORD *)v24 + 4LL * (unsigned int)(2 * *(_DWORD *)v28))
+ -(~(((*(_DWORD *)B & 0xAE4094B7 | ~*(_DWORD *)B & 0x51BF6B48) ^ (*(_DWORD *)A & 0xAE4094B7 | ~*(_DWORD *)A & 0x51BF6B48)) >> (32 - *(_DWORD *)v27)) & ~((~*(_DWORD *)B & *(_DWORD *)A | *(_DWORD *)B & ~*(_DWORD *)A) << *(_DWORD *)B))
1
2
3
(~*(_DWORD *)B & *(_DWORD *)A | *(_DWORD *)B & ~*(_DWORD *)A)是*(_DWORD *)B ^ *(_DWORD *)A
((~*(_DWORD *)B & *(_DWORD *)A | *(_DWORD *)B & ~*(_DWORD *)A) << *(_DWORD *)B)) 就是 (B ^ A) << B
(B ^ A) >> (32 - B)
1
A = *(_DWORD *)(*(_QWORD *)v24 + 4LL * (unsigned int)(2 * *(_DWORD *)v28)) + -(~(((*(_DWORD *)B & 0xAE4094B7 | ~*(_DWORD *)B & 0x51BF6B48) ^ (*(_DWORD *)A & 0xAE4094B7 | ~*(_DWORD *)A & 0x51BF6B48)) >> (32 - B)) & ~(B ^ A << B))

到这里其实还能继续分析,交给GPT即可,但是中间用IDA9开了下发现直接更清晰……

image-20250821112126992

服了…

再分析下:

1
2
3
4
5
6
7
8
9
10
11
12
A = *(_DWORD *)(*(_QWORD *)v13 + 4LL * (unsigned int)(2 * *v17)) + -(~((B ^ A) >> (32 - B)) & ~((B ^ A) << B)) - 1;
A = key[2*i] + -(~((B ^ A) >> (32 - B)) & ~((B ^ A) << B)) - 1;
~a & ~b 等价于 ~(a | b)(德摩根律)
- ~( (X >> (32 - B)) | (X << B) )
(~Y) 等于 ~(~Y) + 1 = Y + 1
A = key[2*i] + (((B ^ A) >> (32 - B)) | ((B ^ A) << B));
后面那个同理
B = Key[2 * i + 1] + (((B ^ A) >> (32 - A)) | ((B ^ A) << A));
最后
第一部分 A & ~B → A 是 1 且 B 是 0 → 输出 1
第二部分 ~A & B → A 是 0 且 B 是 1 → 输出 1
两部分用 OR 连接 → 输出 1 当且仅当 A ≠ B

因此有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Encrypt_(uint32_t* Input)
{
unsigned int Key[26] = {
0x122F2C9C, 0xE3BCCAE7, 0xD0FFC0F2, 0xD9A12544, 0x8A27992F, 0x55B1B935, 0x9110B161, 0x92811564,
0x5CE9B359, 0x77C79A51, 0x4265527A, 0x8AB57C4B, 0x11529FA4, 0x9D9F63FF, 0xA970B936, 0xC8EABA0D,
0x9A0EB4AA, 0xB0BC6E7F, 0x9784B100, 0x70DCD3AE, 0x6057A44E, 0x89187658, 0xE00098A8, 0x45773540,
0xF9374F1A, 0x913FA548
};

uint32_t A = Input[0] + Key[0];
uint32_t B = Input[1] + Key[1];

for (int i = 1; i <= 0xC; i++)
{
A = Key[2 * i] + (((B ^ A) >> (32 - B)) | ((B ^ A) << B));

B = Key[2 * i + 1] + (((B ^ A) >> (32 - A)) | ((B ^ A) << A));

A ^= B;
}

Input[0] = A;
Input[1] = B;
}

密文

image-20250821142232764

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
#include <iostream>
#include <string>

void decrypt(uint32_t* Input)
{
unsigned int Key[26] = {
0x122F2C9C, 0xE3BCCAE7, 0xD0FFC0F2, 0xD9A12544, 0x8A27992F, 0x55B1B935, 0x9110B161, 0x92811564,
0x5CE9B359, 0x77C79A51, 0x4265527A, 0x8AB57C4B, 0x11529FA4, 0x9D9F63FF, 0xA970B936, 0xC8EABA0D,
0x9A0EB4AA, 0xB0BC6E7F, 0x9784B100, 0x70DCD3AE, 0x6057A44E, 0x89187658, 0xE00098A8, 0x45773540,
0xF9374F1A, 0x913FA548
};

uint32_t A = Input[0];
uint32_t B = Input[1];

for (int i = 0xC; i >= 1; i--){
A ^= B;

B -= Key[2 * i + 1];
B = ((B << (32 - A)) | (B >> A));
B ^= A;

A -= Key[2 * i];
A = ((A << (32 - B)) | (A >> B));
A ^= B;
}

Input[0] = A - Key[0];
Input[1] = B - Key[1];
}

int main()
{
unsigned int enc[8] = {
0xF2A1BB1B, 0x21877CE9, 0x0AFD378A, 0xBC811A94, 0xAAE31E40, 0x3FD82E73, 0x4271B884, 0x398B35CC
};
for (int i = 0; i < 32; i += 8) decrypt((uint32_t*)((uint8_t*)enc + i));
printf("L3HCTF{%.32s}\n", enc);
return 0;
}
// L3HCTF{5fd277be39046905ef6348ba89131922}

snake

具体解法有很多

2025 L3HCTF SU WriteUp - su-team

L3HCTF WP | Liv’s blog调试方法

关键代码在sub_59BAC0patch掉限制条件即可

image-20250821222314671

image-20250821223151370

L3HCTF{ad4d5916-9697-4219-af06-014959c2f4c9}