题目描述就四个字——"最好的语言",一开始猜测PHP,打开文件才发现是Python。本文将完整记录解密过程。
来源 Bugku
下载题目后得到一个文本文件re没有后缀,用文本打开一看,是一个类似XML格式的文本
magic 03f30d0a
moddate 6d4a695b (Tue Aug 7 15:29:49 2018)
<code>
<argcount> 0 </argcount>
<nlocals> 0</nlocals>
<stacksize> 5</stacksize>
<flags> 0040</flags>
<code>
6400006401006c00005a00006400006402006c01006d02005a0200016400
006401006c03005a03006400006401006c04005a04006403005a05006404
...
</code>
<names> ('base64', 'hashlib', 'md5', 'random', 'string', 'f', '_', '____', 'e', 'b64encode')</names>
<consts>
-1
None
('md5',)
'flag{*******}'
...
'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
</consts>
</code>
.......
看到头部有magic字段,这个应该是pyc文件生成;
pyc文件的结构:有magic number、常量表、变量名表、字节码等。但缺少关键的<dis>反汇编字段,应该被出题人删掉了!这就没法直接用工具反编译。
恢复内容
虽然没有反汇编,但文件里保留了完整的常量表和变量名。解题思路是:写一个简单的Python脚本;
有请AI上场,用同样的方式编译解析,然后反复对比、猜测、验证,逐步还原原始代码。
经过一番折腾,还原出的核心代码如下:
import base64
from hashlib import md5
import random
import string
f = 'flag{*******}'
def _(b):
__ = ''.join(random.sample(string.digits, 4)) # 随机4位数字密钥
___ = ''
for i in range(len(b)):
___ += chr(ord(b[i]) ^ ord(__[i % 4])) # 逐字符XOR加密
return ___
def ____(a):
___ = md5()
___.update(a)
return ___.digest() # 返回16字节MD5
# 核心:flag被切成三段,分别处理后拼接
e = _(f[:12]) + ____(f[12:19]) + _(f[19:])
print base64.b64encode(e)
输出结果:
U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU=
解密过程
第一步:Base64解码,分割三段
import base64
e = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
target = base64.b64decode(e)
before = target[:12] # 前12字节:_(f[:12])
md5_raw = target[12:28] # 中间16字节:____(f[12:19])
after = target[28:] # 后10字节:_(f[19:])
# 38字节,正好12+16+10
第二步:破解前12字节
由于flag开头必定是 flag{,我们可以用这个已知明文来反推密钥。XOR加密有个特点:如果知道明文和密文,就能直接算出密钥。
但这里我们采用更直接的方法——暴力枚举所有可能的4位数字密钥(0000-9999的不重复排列,共5040种),用每个密钥去解密前12字节,看哪个结果以 flag{ 开头:
from itertools import permutations
all_keys = [''.join(p) for p in permutations('0123456789', 4)]
def xor_crypt(data, key):
result = ''
for i in range(len(data)):
result += chr(data[i] ^ ord(key[i % 4]))
return result
for key in all_keys:
decrypted = xor_crypt(before, key)
if decrypted.startswith('flag{'):
print(f"密钥: {key}")
print(f"结果: {decrypted}")
break
运行结果:
密钥: 5914
结果: flag{PyC_1s_
这里就拿到了前12字符:flag{PyC_1s_

第三步:查询MD5
中间16字节是MD5的二进制结果,转成十六进制:
print(md5_raw.hex())
# 输出: 42eec54bb567824d4e4d0e7caaa8215e
拿到在线MD5破解网站一查,返回结果:613u21i
第四步:破解后10字节
后10字节的解密没有已知明文可以利用,但我们知道 flag 最后一位肯定是 }。同样暴力枚举所有密钥:
import string
for key in all_keys:
decrypted = xor_crypt(after, key)
if decrypted.endswith('}'):
valid = set(string.ascii_letters + string.digits + '_}')
if all(c in valid for c in decrypted):
print(f"密钥={key}: {decrypted}")

输出多个候选,挨个测试,最终发现是:
密钥=4813: _N0t_Hard}
第五步:拼接完整flag

前段: flag{PyC_1s_
中段: 613u21i
后段: _N0t_Hard}
完整: flag{PyC_1s_613u21i_N0t_Hard}
加密逻辑分析
flag: flag{PyC_1s_613u21i_N0t_Hard}
|________| |_____| |_________|
前12字符 中间7字符 剩余部分
| | |
_( ) ____( ) _( )
XOR加密 MD5哈希 XOR加密
| | |
12字节 16字节 10字节
\ | /
\ | /
拼接后Base64编码
|
38字节密文