-
-
[原创]KCTF 2026 第二题:巳时·绿光幽语
-
发表于: 2天前 49
-
一道"还原代码"题:6 字符 key 还原一段被加密的 Python 字节码。程序问你要 key,却把答案藏在了谁都想不到的地方——Python 解释器自带的 <frozen os> 模块尾部。本文记录完整分析链与三个实战发现,全部步骤可复现。
运行即循环提示:
PyInstaller 解包(pyinstxtractor-ng)后,反编译入口 main.pyc:
几乎什么都没做。常规思路直接撞墙——真逻辑不在任何普通脚本里。这是本题第一层伪装:业务代码不在 Python 源码层,而在更底层。
对文件做 GBK/UTF-8/UTF-16 全变体字符串搜索,提示、恭喜成功、请输入key 全部落空。提示文本不是明文存储——它是加密的。
此时两个方向:
在 wine + gdb 下跟进这个程序,连续踩坑:
一个绕开 stdin 的实用技巧:gdb 直接模拟 read 返回——frame 2(libc read 帧)→ 写缓冲区 → return (long)7,程序就认为读到了任意 key。这比管道喂 stdin 稳定得多。
真正该断的是 Python C-API。对 python313.dll 的导出下断:
逐个 dump 执行到的 code 对象后,<frozen os> 模块的 co_names 尾部混进了一串不属于标准库 os 的名字:
input/key/replace/succ/__code__——一眼就是"读 key → XOR 解密 → 替换 code 对象 → 执行"。业务逻辑被整体塞进了 os 模块的 module 级代码末尾,这就是为什么静态搜索和普通反编译全部失效——它伪装成了标准库的一部分。
反汇编确认结构是 while x < 20: try: ... except: continue(RERAISE/PUSH_EXC_INFO/CHECK_EXC_MATCH 异常处理模式),循环里每次重新定义 my_function(print 666),读 key,长度不等于 6 就 continue。
在 frozen os 数据区(python313.dll 内,docstring 之前)找到连续 8 个 16 字节的 TYPE_STRING 常量,每段之后跟着 int 85(=0x55)和字符串 "utf-8":
解密脚本:
<frozen os> 的 co_consts 里还有一个 26 字节 bytes 常量:
它的作用:用输入的 6 字符 key 做重复 XOR,解密出 my_function 的目标字节码,然后 code.replace(co_code=解密结果, co_consts=(None, 成功串)) 替换并执行。
目标字节码用同版本 Python 3.13 编译得到:
成功字符串同步验证:9fe39b91f5ee9feaa691e9ed96deb7 XOR yb6tcr = "恭喜成功!"。
这是本题最漂亮的一点——其实不需要知道目标字节码。
Python 3.12+ 字节码的指令带固定 CACHE 槽位(全 0x00):
重复 key XOR 下,明文 0x00 ^ key[i%6] = key[i%6]——密文中与 CACHE 对应的字节就是 key 循环的原样:
密文第 6–17 字节 cryb6tcryb6t 直接就是 key 的重复——key 明晃晃嵌在密文里,因为字节码的 CACHE 位全是 0。知道这个规律,光看密文就能拼出 key,连 Python 版本都不用猜。
| 项目 | 值 |
|---|---|
| 大小 | 7,438,179 字节 |
| SHA256 | F6B192F9EE87C4AD2BC8681291CA428CF8E98FEC26FA5D7B82C6E791A7A90EAD |
| 类型 | 64 位 PE Console |
| 打包 | PyInstaller onefile + Python 3.13 |
| 层 | 手段 | 击破方式 |
|---|---|---|
| 1 | PyInstaller onefile,main.pyc 为 sys.exit(0) 空壳 | 跟进子进程,断 Python C-API |
| 2 | 业务逻辑伪装进 <frozen os> 模块尾部 |
逐帧 dump code 对象,看 co_names 尾部异常 |
| 3 | 提示文本分块 XOR 0x55 | 明文 UTF-8 XOR 0x55 反向搜 DLL,定位 chunk 区 |
| 4 | 目标字节码 XOR 循环 key | 已知明文攻击 + CACHE 占位泄露 |
$ printf 'yb6tcr\n' | wine64 luozi.exe
提示,需要还原的代码
def my_function():
print("恭喜成功!")
请输入key还原代码(请输入6个字符)
恭喜成功! ← 还原后的代码真正执行了
提示,需要还原的代码
def my_function():
print("恭喜成功!")
请输入key还原代码(请输入6个字符)