首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]KCTF 2026 第二题:巳时·绿光幽语
发表于: 2026-8-11 13:34
140
[原创]KCTF 2026 第二题:巳时·绿光幽语
mb_dcvvjyqc
2026-8-11 13:34
140
# KCTF 2026 第二题:巳时·绿光幽语 —— PyInstaller 皮囊下的 frozen 模块隐藏逻辑 > 一道"还原代码"题:6 字符 key 还原一段被加密的 Python 字节码。程序问你要 key,却把答案藏在了谁都想不到的地方——Python 解释器自带的 `<frozen os>` 模块尾部。本文记录完整分析链与三个实战发现,全部步骤可复现。 ## 0. 结论先行 - **key:`yb6tcr`**(提交可用 `flag{yb6tcr}`) - 验证: ``` $ printf 'yb6tcr\n' | wine64 luozi.exe 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) 恭喜成功! ← 还原后的代码真正执行了 ``` ## 1. 样本画像 | 项目 | 值 | |---|---| | 大小 | 7,438,179 字节 | | SHA256 | F6B192F9EE87C4AD2BC8681291CA428CF8E98FEC26FA5D7B82C6E791A7A90EAD | | 类型 | 64 位 PE Console | | 打包 | PyInstaller onefile + Python 3.13 | 运行即循环提示: ``` 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) ``` ## 2. 踩坑一:main.pyc 是纯摆设 PyInstaller 解包(pyinstxtractor-ng)后,反编译入口 `main.pyc`: ```python import sys sys.exit(0) ``` 几乎什么都没做。常规思路直接撞墙——**真逻辑不在任何普通脚本里**。这是本题第一层伪装:业务代码不在 Python 源码层,而在更底层。 ## 3. 踩坑二:字符串全军覆没 对文件做 GBK/UTF-8/UTF-16 全变体字符串搜索,`提示`、`恭喜成功`、`请输入key` 全部落空。**提示文本不是明文存储**——它是加密的。 此时两个方向: - 静态:从加密数据反推算法(提示文本 105 字节,XOR 变体试遍无果) - 动态:运行时内存里必然有解密后的明文 ## 4. 动态调试的三重地狱(wine 环境) 在 wine + gdb 下跟进这个程序,连续踩坑: 1. **反调试**:gdb attach 即静默退出。`IsDebuggerPresent` 两处调用(0x14000d94b / 0x14001d637),patch 副本为 `mov eax,0; nop x4` 绕过 2. **断点永不触发**:程序阻塞在 `read()`(ReadFile 内部)时,断点设在 call 指令已错过;`_PyEval_EvalFrameDefault` 只在帧**首次进入**时触发,attach 时程序早已卡在帧内等输入 3. **静态 CRT 假象**:ReadFile/WriteFile 调用链全在 wine DLL,exe 帧不在栈上——误判"主逻辑在 C 层" 一个绕开 stdin 的实用技巧:**gdb 直接模拟 read 返回**——`frame 2`(libc read 帧)→ 写缓冲区 → `return (long)7`,程序就认为读到了任意 key。这比管道喂 stdin 稳定得多。 ## 5. 关键突破:`<frozen os>` 尾部藏了个"程序" 真正该断的是 **Python C-API**。对 python313.dll 的导出下断: ``` PyRun_SimpleStringFlags / PyRun_StringFlags / Py_CompileStringExFlags PyMarshal_ReadObjectFromString / PyEval_EvalCode / _PyEval_EvalFrameDefault PyImport_ExecCodeModule ``` 逐个 dump 执行到的 code 对象后,`<frozen os>` 模块的 `co_names` 尾部混进了一串不属于标准库 os 的名字: ``` x, my_function, join, buffer, w, i, b, to_bytes, print, decode, input, key, len, encode, b_key, c, buf, range, __code__, my, replace, succ, Exception, e ``` `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。 ## 6. 提示文本:8 段 16 字节 chunk,XOR 0x55 在 frozen os 数据区(python313.dll 内,docstring 之前)找到连续 8 个 16 字节的 `TYPE_STRING` 常量,每段之后跟着 `int 85`(=0x55)和字符串 `"utf-8"`: ``` 0x4dad17: 73 10 00 00 00 b3 da c5 b2 f1 ef ba e9 d9 bc c9 d5 bd f3 d4 bd 0x4dad2c: 73 10 00 00 00 ea cd b0 db ca b2 cf d1 b1 ee f6 b2 f5 d4 58 5f ...共 8 段... ``` 解密脚本: ```python import struct d = open('python313.dll','rb').read() pos = 0x4dad15 # tuple 头 (29 08) chunks = [] for _ in range(d[pos+1]): ln = struct.unpack_from('<I', d, pos+2)[0] chunks.append(d[pos+6:pos+6+ln]) pos += 6 + ln full = b''.join(bytes(b ^ 0x55 for b in c) for c in chunks) print(full.decode('utf-8')) # 提示,需要还原的代码\r\ndef my_function():\r\n\tprint("恭喜成功!")\r\n请输入key还原代码(请输入6个字符) ``` ## 7. 核心:key 推导(已知明文攻击) `<frozen os>` 的 co_consts 里还有一个 26 字节 bytes 常量: ``` 密文 c = ec626d7563727962367463722a63037563727962367443721e62 ``` 它的作用:**用输入的 6 字符 key 做重复 XOR,解密出 `my_function` 的目标字节码**,然后 `code.replace(co_code=解密结果, co_consts=(None, 成功串))` 替换并执行。 目标字节码用同版本 Python 3.13 编译得到: ```python compile("def my_function():\n print(666)", "<x>", "exec") # co_code = 95005b0100000000000000005301350100000000000020006700 ``` ```python def find_period(data): for n in range(1, len(data)+1): if all(data[i] == data[i % n] for i in range(len(data))): return n return len(data) key_stream = bytes(a ^ b for a, b in zip(cipher, plain_code)) key = key_stream[:find_period(key_stream)] # yb6tcryb6tcryb6tcryb6tcryb → yb6tcr ``` 成功字符串同步验证:`9fe39b91f5ee9feaa691e9ed96deb7 XOR yb6tcr = "恭喜成功!"`。 ## 8. 实战发现:CACHE 00 占位泄露 key(零明文攻击) 这是本题最漂亮的一点——**其实不需要知道目标字节码**。 Python 3.12+ 字节码的指令带固定 CACHE 槽位(全 0x00): ``` RESUME 0 LOAD_GLOBAL 1 (print) ← 后跟 8 字节 CACHE LOAD_CONST 1 ← 后跟 1 字节 CACHE CALL 1 ← 后跟 6 字节 CACHE POP_TOP ← 后跟 1 字节 CACHE RETURN_CONST 0 ``` 重复 key XOR 下,`明文 0x00 ^ key[i%6] = key[i%6]`——**密文中与 CACHE 对应的字节就是 key 循环的原样**: ``` 密文: ec 62 6d 75 63 72 79 62 36 74 63 72 2a 63 03 75 63 72 79 62 36 74 43 72 1e 62 ^ b ^ ^ c r y b 6 t c r ^ ^ ^ ^ c r y b 6 t ^ r ^ b 泄露: bcryb6tcrcryb6trb → 按 6 字节周期对齐 → y b 6 t c r ``` 密文第 6–17 字节 `cryb6tcryb6t` 直接就是 key 的重复——**key 明晃晃嵌在密文里**,因为字节码的 CACHE 位全是 0。知道这个规律,光看密文就能拼出 key,连 Python 版本都不用猜。 ## 9. 完整防御链回顾 | 层 | 手段 | 击破方式 | |---|---|---| | 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 占位泄露 | ## 10. 复现脚本(solve.py) ```python #!/usr/bin/env python3 def find_period(data): for n in range(1, len(data)+1): if all(data[i] == data[i % n] for i in range(len(data))): return n return len(data) cipher = bytes.fromhex('ec626d7563727962367463722a63037563727962367443721e62') plain = bytes.fromhex('95005b0100000000000000005301350100000000000020006700') key = bytes(a ^ b for a, b in zip(cipher, plain))[:find_period(bytes(a ^ b for a, b in zip(cipher, plain)))] print("key =", key.decode()) succ = bytes.fromhex('9fe39b91f5ee9feaa691e9ed96deb7') print("success =", bytes(succ[i] ^ key[i % len(key)] for i in range(len(succ))).decode()) ``` ``` key = yb6tcr success = 恭喜成功! ```
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于
2026-8-11 14:14 被mb_dcvvjyqc编辑 ,原因: 修改笔误
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_dcvvjyqc
9
发帖
2
回帖
40
RANK
关注
私信
他的文章
[原创]KCTF 2026 第二题:巳时·绿光幽语
140
[原创]一串反引号,十层突破:n1ctf-2018-easy_harder_php 从备份泄露到 MySQL 提权读 flag
3232
[原创]在正则的缝隙里跳舞:BabyFirst 限制型命令注入的完整攻破与复现
1485
[原创]hitcon-2016-babytrick 解题报告
3471
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部