之前入门过python逆向,在比赛中发现这两个工具并不能反编译所有的pyinstaller打包的exe,特别是3.12之后的版本

关于ycb的两道py逆向

2025羊城杯初赛re 完整wp - 霍雅的博客

ez_py

其中一个附件是key.exe,用pyinstrator解包得到key.pyc

image-20251022113757708

python3.13反编译不完整

https://www.52pojie.cn/thread-2025482-1-1.html

Pylingual网站反编译的代码

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
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: key.py
# Bytecode version: 3.13.0rc3 (3571)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)

import ast
import types
import sys
o0o0o0 = [105084753, 3212558540, 351342182, 844102737, 2002504052, 356536456, 2463183122, 615034880, 1156203296]

def changli(o0o0o1, o0o0o2, o0o0o3):
o0o0o4 = 2269471011
o0o0o5 = o0o0o3 & 4294967295
o0o0o6 = (o0o0o3 >> 8 ^ 305419896) & 4294967295
o0o0o7 = (o0o0o3 << 4 ^ 2271560481) & 4294967295
o0o0o8 = (o0o0o3 >> 12 ^ 2882400000) & 4294967295
o0o0o9 = o0o0o1 & 4294967295
o0o0o10 = o0o0o2 & 4294967295
o0o0o11 = 0
for _ in range(32):
o0o0o11 = o0o0o11 + o0o0o4 & 4294967295
o0o0o9 = o0o0o9 + ((o0o0o10 << 4) + o0o0o5 ^ o0o0o10 + o0o0o11 ^ (o0o0o10 >> 4) + o0o0o6) & 4294967295
o0o0o10 = o0o0o10 + ((o0o0o9 << 4) + o0o0o7 ^ o0o0o9 + o0o0o11 ^ (o0o0o9 >> 4) + o0o0o8) & 4294967295
return (o0o0o9, o0o0o10)

def Shorekeeper(o0o0o12):
o0o0o13 = o0o0o12 >> 16
o0o0o14 = o0o0o12 & 65535
return (o0o0o13, o0o0o14)

def Kathysia(o0o0o15, o0o0o16):
return o0o0o15 << 16 | o0o0o16 + 0

def Phrolova(o0o0o17):
o0oA = 'Carlotta'
o0oB = ['o0oC', 'o0oD', 'o0oE', 'o0oF']
o0oG = []
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oH', ctx=ast.Store())], value=ast.Constant(305419896)))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oI', ctx=ast.Store())], value=ast.BinOp(ast.Name(id='o0oE', ctx=ast.Load()), ast.BitAnd(), ast.Constant(65535))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oJ', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oE', ctx=ast.Load()), ast.RShift(), ast.Constant(16)), ast.BitAnd(), ast.Constant(65535))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oK', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oE', ctx=ast.Load()), ast.BitXor(), ast.Name(id='o0oF', ctx=ast.Load())), ast.BitAnd(), ast.Constant(65535))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oL', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oE', ctx=ast.Load()), ast.RShift(), ast.Constant(8)), ast.BitXor(), ast.Name(id='o0oF', ctx=ast.Load())), ast.BitAnd(), ast.Constant(65535)))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oM', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oH', ctx=ast.Load()), ast.Mult(), ast.BinOp(ast.Name(id='o0oF', ctx=ast.Load()), ast.Add(), ast.Constant(1))), ast.BitAnd(), ast.Constant(4294967295))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oN', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.BinOp(ast.Name(id='o0oD', ctx=ast.Load()), ast.LShift(), ast.Constant(5)), ast.Add(), ast.Add(), ast.Constant(5)), ast.Add(), ast.Name(id='o0oJ', ctx=ast.Load()))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oP', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oC', ctx=ast.Load()), ast.Add(), ast.Name(id='o0oN', ctx=ast.Load())), ast.BitAnd(), ast.Constant(65535))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oN', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.BinOp(ast.Name(id='o0oP', ctx=ast.Load()), ast.LShift(), ast.Constant(5)), ast.Add(), ast.Add(), ast.Constant(5)), ast.Add(), ast.Name(id='o0oL', ctx=ast.Load()))))
o0oG.append(ast.Assign(targets=[ast.Name(id='o0oQ', ctx=ast.Store())], value=ast.BinOp(ast.BinOp(ast.Name(id='o0oD', ctx=ast.Load()), ast.Add(), ast.Name(id='o0oN', ctx=ast.Load())), ast.BitAnd(), ast.Constant(65535))))
o0oG.append(ast.Return(ast.Tuple(elts=[ast.Name(id='o0oP', ctx=ast.Load()), ast.Name(id='o0oQ', ctx=ast.Load())], ctx=ast.Load())))
o0oU = ast.FunctionDef(name=o0oA, args=ast.arguments(posonlyargs=[], args=[ast.arg(arg=a) for a in o0oB], kwonlyargs=[], kw_defaults=[], defaults=[]), body=o0oG, decorator_list=[])
o0oV = ast.parse('\ndef _tea_helper_func(a, b, c):\n magic1 = (a ^ b) & 0xDEADBEEF\n magic2 = (c << 3) | (a >> 5)\n return (magic1 + magic2 - (b & 0xCAFEBABE)) & 0xFFFFFFFF\n\ndef _fake_tea_round(x, y):\n return ((x * 0x9E3779B9) ^ (y + 0x12345678)) & 0xFFFFFFFF\n\n_tea_magic_delta = 0x9E3779B9 ^ 0x12345678\n_tea_dummy_keys = [0x1111, 0x2222, 0x3333, 0x4444]\n').body
o0oW = ast.Module(body=[o0oU] + o0oV, type_ignores=[])
ast.fix_missing_locations(o0oW)
o0oX = compile(o0oW, filename='<tea_obf_ast>', mode='exec')
o0oY = {}
exec(o0oX, o0oY)
if o0oA in o0oY:
o0o0o17[o0oA] = o0oY[o0oA]
return None
Phrolova(globals())

def shouan(o0o0o32):
raise ValueError('需要输入9个key') if len(o0o0o32)!= 9 else None

def jinhsi():
print('请输入9个数字:')
try:
o0o0o46 = input().strip()
if ',' in o0o0o46:
o0o0o42 = o0o0o46.split(',')
if len(o0o0o42)!= 9:
print('错误: 需要输入9个数')
return None
except Exception as o0o0o47:
print(f'发生错误: {o0o0o47}')
if __name__ == '__main__':
jinhsi()

貌似也不完整

所以使用 xdis——“跨 Python 反汇编器”生成字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
decompile_pyc_313.py
将 Python 3.13 版本的 .pyc 文件反编译为汇编字节码并保存到文本文件中
"""

import sys
import marshal
import dis
import struct
import io
from contextlib import redirect_stdout


def load_code_object(pyc_path):
"""
读取 .pyc 文件,返回反序列化后的 code object。
兼容 Python 3.13 的 PYC 文件头格式。
"""
with open(pyc_path, "rb") as f:
magic = f.read(4) # 魔数
bitfield = f.read(4) # Bitfield(包含优化模式、hash-based 信息等)

# Python 3.7+ pyc 格式在这里开始存储 hash 或 timestamp
# 在 3.13 中文件头更复杂,我们尝试跳过 8 或 16 字节
data = f.read()
try:
code = marshal.loads(data)
except Exception:
# 尝试跳过 timestamp / hash 部分
data = data[8:]
try:
code = marshal.loads(data)
except Exception:
data = data[16:]
code = marshal.loads(data)
return code


def disassemble_recursive(code, out, indent=0):
"""
递归反汇编 code 对象及其子对象(函数、闭包、类)。
"""
indent_str = " " * indent
out.write(f"{indent_str}Disassembly of code object: {code.co_name}\n")
out.write(f"{indent_str}{'-' * 60}\n")
dis.dis(code, file=out)
out.write("\n")

# 遍历常量,递归反汇编子函数
for const in code.co_consts:
if isinstance(const, type(code)):
disassemble_recursive(const, out, indent + 2)


def decompile_pyc(pyc_path, output_path=None):
"""
主函数:将 pyc 反汇编并写入文件。
"""
if output_path is None:
output_path = pyc_path + ".dis.txt"

code = load_code_object(pyc_path)

with open(output_path, "w", encoding="utf-8") as out:
out.write(f"[+] Disassembly of {pyc_path}\n")
out.write("=" * 60 + "\n\n")
disassemble_recursive(code, out)

print(f"[+] 汇编字节码已保存到: {output_path}")


if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"用法: python {sys.argv[0]} <file.pyc> [output.txt]")
sys.exit(1)

pyc_path = sys.argv[1]
output_path = sys.argv[2] if len(sys.argv) >= 3 else None
decompile_pyc(pyc_path, output_path)

python3.13环境下运行得到字节码

接下来就到了AI时间,ds解出正确的key

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
def changli_inv(L, R, o0o0o3):
# 逆向changli函数
o0o0o4 = 2269471011
o0o0o5 = o0o0o3 & 0xFFFFFFFF
o0o0o6 = ((o0o0o3 >> 8) ^ 305419896) & 0xFFFFFFFF
o0o0o7 = ((o0o0o3 << 4) ^ 2271560481) & 0xFFFFFFFF
o0o0o8 = ((o0o0o3 >> 12) ^ 2882400000) & 0xFFFFFFFF

# 生成轮密钥
keys = []
o0o0o11 = 0
for _ in range(32):
o0o0o11 = (o0o0o11 + o0o0o4) & 0xFFFFFFFF
keys.append(o0o0o11)

# 逆向Feistel网络
for i in range(31, -1, -1):
o0o0o11 = keys[i]
# 逆向右半部分
R_prev = (R - (((L << 4) + o0o0o7) ^ (L + o0o0o11) ^ ((L >> 4) + o0o0o8))) & 0xFFFFFFFF
# 逆向左半部分
L_prev = (L - (((R_prev << 4) + o0o0o5) ^ (R_prev + o0o0o11) ^ ((R_prev >> 4) + o0o0o6))) & 0xFFFFFFFF
L, R = L_prev, R_prev

return L, R

def Carlotta_inv(output_P, output_Q, o0oE, o0oF):
# 逆向Carlotta函数
o0oH = 305419896
o0oI = o0oE & 0xFFFF
o0oJ = (o0oE >> 16) & 0xFFFF
o0oK = (o0oE ^ o0oF) & 0xFFFF
o0oL = ((o0oE >> 8) ^ o0oF) & 0xFFFF
o0oM = (o0oH * (o0oF + 1)) & 0xFFFFFFFF

# 第二轮逆向
o0oN2 = (((output_P << 5) + o0oK) ^ (output_P + o0oM) ^ ((output_P >> 5) + o0oL)) & 0xFFFFFFFF
o0oD = (output_Q - o0oN2) & 0xFFFF

# 第一轮逆向
o0oN1 = (((o0oD << 5) + o0oI) ^ (o0oD + o0oM) ^ ((o0oD >> 5) + o0oJ)) & 0xFFFFFFFF
o0oC = (output_P - o0oN1) & 0xFFFF

return o0oC, o0oD

def find_original_input():
# 目标输出
target = [105084753, 3212558540, 351342182, 844102737, 2002504052, 356536456, 2463183122, 615034880, 1156203296]

# 步骤1: 逆向changli操作链
# 从后往前逆向changli操作
state = target.copy()

# 逆向8轮changli操作
for i in range(7, -1, -1):
L_prev, R_prev = changli_inv(state[i], state[i+1], 2025)
state[i] = L_prev
state[i+1] = R_prev

# 步骤2: 逆向Carlotta和Shorekeeper操作
original_input = []
for j in range(9):
# 从state[j]中提取Carlotta的输出
output_P = (state[j] >> 16) & 0xFFFF
output_Q = state[j] & 0xFFFF

# Carlotta函数的参数
o0oE = j + 2025 # 索引 + 2025
o0oF = j * j # 索引的平方

# 逆向Carlotta得到Shorekeeper的输出
o0oC, o0oD = Carlotta_inv(output_P, output_Q, o0oE, o0oF)

# 合并得到原始输入
original_val = (o0oC << 16) | o0oD
original_input.append(original_val)

return original_input

# 计算原始key
original_key = find_original_input()
print("原始key:", original_key)

# 验证结果
def verify_solution(input_key):
# 重新实现shouan函数来验证
if len(input_key) != 9:
raise ValueError("需要输入9个key")

o0o0o35 = []
for o0o0o49, o0o0o34 in enumerate(input_key):
o0o0o33 = o0o0o49 * o0o0o49

# Shorekeeper操作
o0o0o13 = o0o0o34 >> 16
o0o0o14 = o0o0o34 & 0xFFFF

# Carlotta操作
o0oE = o0o0o49 + 2025
o0oF = o0o0o33

o0oH = 305419896
o0oI = o0oE & 0xFFFF
o0oJ = (o0oE >> 16) & 0xFFFF
o0oK = (o0oE ^ o0oF) & 0xFFFF
o0oL = ((o0oE >> 8) ^ o0oF) & 0xFFFF
o0oM = (o0oH * (o0oF + 1)) & 0xFFFFFFFF

o0oN = (((o0o0o14 << 5) + o0oI) ^ (o0o0o14 + o0oM) ^ ((o0o0o14 >> 5) + o0oJ)) & 0xFFFFFFFF
o0oP = (o0o0o13 + o0oN) & 0xFFFF

o0oN = (((o0oP << 5) + o0oK) ^ (o0oP + o0oM) ^ ((o0oP >> 5) + o0oL)) & 0xFFFFFFFF
o0oQ = (o0o0o14 + o0oN) & 0xFFFF

o0o0o40 = (o0oP << 16) | o0oQ
o0o0o35.append(o0o0o40)

o0o0o41 = []
for i in range(8):
L, R = changli(o0o0o35[i], o0o0o35[i+1], 2025)
o0o0o35[i] = L
o0o0o35[i+1] = R
o0o0o41.append(L)

o0o0o41.append(o0o0o35[8])
return o0o0o41

# 重新定义changli函数用于验证
def changli(o0o0o1, o0o0o2, o0o0o3):
o0o0o4 = 2269471011
o0o0o5 = o0o0o3 & 0xFFFFFFFF
o0o0o6 = ((o0o0o3 >> 8) ^ 305419896) & 0xFFFFFFFF
o0o0o7 = ((o0o0o3 << 4) ^ 2271560481) & 0xFFFFFFFF
o0o0o8 = ((o0o0o3 >> 12) ^ 2882400000) & 0xFFFFFFFF
o0o0o9 = o0o0o1 & 0xFFFFFFFF
o0o0o10 = o0o0o2 & 0xFFFFFFFF
o0o0o11 = 0

for _ in range(32):
o0o0o11 = (o0o0o11 + o0o0o4) & 0xFFFFFFFF
o0o0o9 = (o0o0o9 + (((o0o0o10 << 4) + o0o0o5) ^ (o0o0o10 + o0o0o11) ^ ((o0o0o10 >> 4) + o0o0o6))) & 0xFFFFFFFF
o0o0o10 = (o0o0o10 + (((o0o0o9 << 4) + o0o0o7) ^ (o0o0o9 + o0o0o11) ^ ((o0o0o9 >> 4) + o0o0o8))) & 0xFFFFFFFF

return (o0o0o9, o0o0o10)

# 验证结果
print("验证结果:", verify_solution(original_key))
print("目标输出:", [105084753, 3212558540, 351342182, 844102737, 2002504052, 356536456, 2463183122, 615034880, 1156203296])
print("验证通过:", verify_solution(original_key) == [105084753, 3212558540, 351342182, 844102737, 2002504052, 356536456, 2463183122, 615034880, 1156203296])

key是1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789

另一个文件src打开很明显的pyarmor,但是PY头被删了,补上PY000000

https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot

命令python shot.py "D:\download\2025-ycb\附件\src"

image-20251027090251931

再用之前的key解出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
cipher = [
1473, 3419, 9156, 1267, 9185, 2823, 7945, 618, 7036, 2479,
5791, 1945, 4639, 1548, 3634, 3502, 2433, 1407, 1263, 3354,
9274, 1085, 8851, 3022, 8031, 734, 6869, 2644, 5798, 1862,
4745, 1554, 3523, 3631, 2512, 1499, 1221, 3226, 9237
]

fuck_key = [1234, 5678, 9123, 4567, 8912, 3456, 7891, 2345, 6789]

# 计算 __
__ = [k % 0xFF for k in fuck_key] # 0xFF is 255

# 定义 init 函数
def init(key, key_len):
var0 = 0
box = list(range(256))
for i in range(256):
var0 = (var0 + box[i] + key[i % key_len]) % 256
box[i], box[var0] = box[var0], box[i]
return box

# 定义 make 函数
def make(box):
var2 = 0
var0 = 0
result = []
for i in range(256):
var2 = (var2 + 1) % 256
var0 = (var0 + box[var2]) % 256
box[var2], box[var0] = box[var0], box[var2]
index = (box[var2] + box[var0] + i % 23) % 256
result.append(box[index])
return result

# 生成密钥流
key_bytes = bytes(__)
box_init = init(key_bytes, len(key_bytes))
key_stream = make(box_init)

# 现在解密
flag_list = []
for i in range(len(cipher)):
if i % 2 == 0:
_ = fuck_key[i % 9]
else:
_ = (fuck_key[i % 9] * 2) % 0xFFF # 0xFFF is 4095
plain_byte = cipher[i] ^ (key_stream[i] + _)
flag_list.append(plain_byte)

# 将flag_list转换为字节串
flag_bytes = bytes(flag_list)
print(flag_bytes)

re2

plus.py很明显被混淆了,试版本发现是3.9

用脚本把加法算出来

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


def simplify_additions_in_file(input_file, output_file):
# 读取原始代码
with open(input_file, "r", encoding="utf-8") as f:
code = f.read()

# 匹配类似 int(7 + 7 + 9 + 3) 这样的表达式
pattern = re.compile(r"int\(([\d+\s+]+)\)")

def replace_expr(match):
expr = match.group(1)
try:
# 计算加法的实际结果
result = eval(expr)
return f"int({result})"
except Exception:
return match.group(0) # 出错就原样返回

# 执行替换
optimized_code = re.sub(pattern, replace_expr, code)

# 保存优化后的结果
with open(output_file, "w", encoding="utf-8") as f:
f.write(optimized_code)

print(f"已优化完成,结果保存到 {output_file}")


# 使用示例
simplify_additions_in_file("plus.py", "optimized.py")

再优化一下换行

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
#!/usr/bin/env python3
# optimize_and_prettify.py
# 用途:将单行混淆 Python 脚本拆行并把 int(...) 加法计算成数字
# 说明:保守替换——只计算由纯数字构成的 int(...) 加法片段,遇到空 int() 或非纯数字的 int(...) 会保留原样。

import re
import shutil
import sys
from pathlib import Path

def optimize_int_sums(text: str) -> str:
"""
将连续的 int(NUM)+int(NUM)+... 替换成它们的和(数字字符串)。
只匹配完全由 int(decimal) 构成的序列;不会触碰空的 int() 或含变量的 int(x)。
"""
# 匹配类似 int(123) + int(456) + int(7)
sum_pattern = re.compile(r'(?:int\(\d+\)(?:\s*\+\s*int\(\d+\))+)')
def sum_repl(m):
nums = re.findall(r'int\((\d+)\)', m.group(0))
total = sum(map(int, nums))
return str(total)
text = sum_pattern.sub(sum_repl, text)

# 再将单独的 int(123) -> 123 (但不改 int())
text = re.sub(r'\bint\((\d+)\)\b', r'\1', text)
return text

def split_statements_to_lines(text: str) -> str:
"""
将以分号分隔的语句拆成多行,保留 ; 如果它在字符串常量内部则比较复杂——
这里采取保守方法:基于简单分割 `;`,并修复连续 ; 带来的空行。
"""
# 先把 Windows/Mac 换行标准化
text = text.replace('\r\n', '\n').replace('\r', '\n')

# 尝试智能拆分:把每个顶级分号作为换行位置
# 这里用简单分割(对大多数混淆脚本是足够的,因为代码里少用字符串包含分号)
parts = [p.strip() for p in text.split(';')]
# 移除完全空的段
parts = [p for p in parts if p != '']
# 每行以分号结尾(保持语句终止),最后一行如果本来没有分号就不加
lines = []
for i, p in enumerate(parts):
# 如果末尾是注释(# ...),已经在 p 中;我们保留注释
# 对于最后一段:如果原文以分号结尾我们也会保留分号;这里检测比较难,保持每行加分号可以不影响执行
# 但为了更自然,我们不在最后一行添加分号
if i < len(parts) - 1:
lines.append(p + ';')
else:
lines.append(p)
return '\n'.join(lines)

def prettify(text: str) -> str:
"""
主流程:先做数值简化,再拆行;这样可保证像 int(1)+int(2) 这种被合并后再换行。
"""
t = optimize_int_sums(text)
t = split_statements_to_lines(t)
# 额外的清理:去掉多余空行
t = re.sub(r'\n\s*\n+', '\n\n', t)
return t

def backup_file(path: Path):
bak = path.with_suffix(path.suffix + '.bak')
shutil.copy2(path, bak)
print(f"Backup created: {bak}")

def process_file(in_path: Path, out_path: Path = None):
if not in_path.exists():
raise FileNotFoundError(in_path)
if out_path is None:
out_path = in_path.with_name(in_path.stem + '_optimized' + in_path.suffix)

backup_file(in_path)
text = in_path.read_text(encoding='utf-8', errors='ignore')
new_text = prettify(text)
out_path.write_text(new_text, encoding='utf-8')
print(f"Optimized written to: {out_path}")

if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python optimize_and_prettify.py <input_file.py> [<output_file.py>]")
sys.exit(1)
inp = Path(sys.argv[1])
out = Path(sys.argv[2]) if len(sys.argv) > 2 else None
try:
process_file(inp, out)
except Exception as e:
print("Error:", e)
sys.exit(2)

image-20251027111318593

运行会报错

然后跟着wp

image-20251027112042227

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

# 加密函数(已知)
def encrypt(data, key):
return bytes([(b * 40 + (b ^ key)) & 0xFF for b in data])

# 解密函数:枚举明文字节 0-255,找满足加密后等于密文字节的 p
def decrypt_byte(c, key):
for p in range(256):
if (p * 40 + (p ^ key)) & 0xFF == c:
return p
return None

def decrypt(data, key):
res = []
for c in data:
p = decrypt_byte(c, key)
if p is None:
return None
res.append(p)
return bytes(res)

# 题目给出的 base64(可能是正确密文)
b64_str = "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI="
cipher = base64.b64decode(b64_str)

# 尝试所有 key 解密
for key in range(256):
plain = decrypt(cipher, key)
if plain and plain.startswith(b'DASCTF{') and all(32 <= x < 127 for x in plain):
print(f"Key: {key:02x}")
print(f"Flag: {plain.decode()}")
break
else:
print("No valid flag found for any key.")

各种版本的python环境

conda

image-20251027114148015

venv

创建:python3.13 -m venv venv313

激活:Windows:venv313\Scripts\activate Linux:source venv313/bin/activate

退出:deactivate

验证版本:python -V