首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]巳时·绿光幽语 题解(Writeup)
发表于: 2026-8-11 12:41
3
[原创]巳时·绿光幽语 题解(Writeup)
阿捏利
1
2026-8-11 12:41
3
# 巳时·绿光幽语 题解(Writeup) ## 1. 题目信息 | 项目 | 内容 | | -------- | -------------------------------------------------------------------------------------------- | | 文件 | `1.exe`(约 7.4 MB,PyInstaller onefile 打包的 Python 3.13 程序) | | 运行提示 | `需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符)` | | 题目目标 | 求出 6 字符 key,还原(解密)`my_function` 的代码 | 程序运行后循环打印一段提示:需要还原的代码是 ```python def my_function(): print("恭喜成功!") ``` 要求输入 **6 个字符**的 key 来"还原代码"。输入正确的 key 后程序会调用还原出的 `my_function()` 并输出 `恭喜成功!`。 ## 2. 运行观察 ``` $ printf 'yb6tcr\n' | ./1.exe 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) 恭喜成功! ← 正确 key 的输出 (exit code 0) ``` 错误 key(或长度不等于 6 的输入)会反复重新提示,最多循环 20 次后以退出码 0 结束。 ## 3. 提取与三个"迷惑点" 用 `pyinstxtractor-ng` 提取 CArchive(21 个条目,PYZ 含 112 个模块)后,遇到三个反常现象: 1. **入口 `main.pyc` 是空壳**:反汇编后只有 ``` import sys sys.exit(0) ``` 一个"什么都干不了"的入口,与实际运行行为完全矛盾。 2. **PYZ 里 112 个模块全是标准库**:`email`、`importlib`、`json`、`logging`、`pathlib`、`urllib`、`zipfile` 等,没有任何业务模块;`base_library.zip` 内部逐模块扫描也全是标准库。 3. **提示字符串在任何地方都搜不到**:以 UTF-8 / GBK / UTF-16LE 三种编码对 1.exe 原始字节及全部提取文件做二进制搜索,`提示/还原/恭喜成功/请输入key` 全部 0 命中。说明提示文本是**运行时拼装/解密**出来的,而非明文存储。 > 排查顺序:CArchive 条目 → PYZ 模块 → base_library.zip → exe 原始字节(含资源段)→ 全部排除后,目标锁定在 **python313.dll**。 ## 4. 真正的入口:被改造的 python313.dll 在 `python313.dll` 中直接搜到了 `my_function` 的 UTF-8 字节,其周围是完整的 marshal 流。用 Python 3.14(可解 3.13 marshal v5)扫描定位到: ``` FOUND at 0x4d00e0 module filename: <frozen os> name: <module> ``` **结论:这份 python313.dll 是改造过的 CPython 运行时**——作者把 `os` 模块冻结(frozen)进 DLL,并在其中注入了挑战代码。PyInstaller 启动时 `import os` 即触发这段代码,因此: - `main.pyc` 的 `sys.exit(0)` 只是障眼法; - 提示文本、校验逻辑全部藏在 `<frozen os>` 里; - 字符串经 `XOR 0x55` 解密后以 UTF-8 输出到控制台(终端显示为 GBK),所以文件里没有明文。 ## 5. 算法还原(<frozen os></frozen> 模块级代码) 把 marshal 流导出并拼回 `.pyc`(补 3.13 魔数头 `f3 0d 0d 0a` + 12 字节 0),用 `pydisasm`(xdis)反汇编。模块级挑战代码在 os.py 正常定义之后(1185~1227 行),还原为 Python 伪码: ```python # ---- 注入的挑战代码(模块级) ---- x = 0 while x < 20: # 至多 20 次尝试 x += 1 # 1) 拼装并解密提示文本:8 段密文 XOR 0x55 → UTF-8 buffer = b''.join([ b'\xb3\xda\xc5\xb2\xf1\xef\xba\xe9\xd9\xbc\xc9\xd5\xbd\xf3\xd4\xbd', b'\xea\xcd\xb0\xdb\xca\xb2\xcf\xd1\xb1\xee\xf6\xb2\xf5\xd4X_', b'103u8,\n3 ;6!<:;}', b"|oX_\\%'<;!}w\xb3\xd4\xf8\xb0", b'\xc3\xc9\xb3\xdd\xc5\xb0\xdf\xca\xba\xe9\xd4w|X_\xbd', b'\xfa\xe2\xbd\xeb\xc6\xb0\xd0\xf0>0,\xbd\xea\xcd\xb0\xdb', b'\xca\xb1\xee\xf6\xb2\xf5\xd4\xba\xe9\xdd\xbd\xfa\xe2\xbd\xeb\xc6', b'\xb0\xd0\xf0c\xb1\xed\xff\xb0\xf8\xc2\xb2\xf9\xf3\xba\xe9\xdc', ]) w = b''.join((i ^ 85).to_bytes(1) for i in buffer) # XOR 0x55 print(w.decode('utf-8')) # 打印提示 key = input() # 读取 key if len(key) != 6: # 必须 6 字符 continue b_key = key.encode() # 6 字节异或密钥 # 2) 还原字节码:c(26B)XOR key → my_function.__code__.co_code c = b'\xecbmucryb6tcr*c\x03ucryb6tCr\x1eb' buf = b''.join((c[i] ^ b_key[i % len(b_key)]).to_bytes(1) for i in range(len(c))) my = my_function.__code__.replace(co_code=buf) # 3) 还原字符串常量:succ(15B)XOR key → co_consts[1](UTF-8) succ = b'\x9f\xe3\x9b\x91\xf5\xee\x9f\xea\xa6\x91\xe9\xed\x96\xde\xb7' buf = b''.join((succ[i] ^ b_key[i % len(b_key)]).to_bytes(1) for i in range(len(succ))) my = my.replace(co_consts=(None, buf.decode('utf-8'))) # 4) 替换并调用还原后的函数 my_function.__code__ = my my_function() break ``` 当前(混淆态)的 `my_function` 反汇编: ``` # Constants: 0: None 1: 666 # Names: 0: print 1188: RESUME 0 1189: LOAD_GLOBAL (NULL + print) LOAD_CONST (666) ← 混淆:把真实字符串换成了 666 CALL (1 positional) POP_TOP RETURN_CONST (None) ``` 即函数体就是 `print(consts[1])`,还原后 `consts[1]` 变为真实字符串。 ## 6. 求解 key(已知明文攻击) 异或是**可逆**的: ``` 明文[i] = 密文[i] XOR key[i % 6] ``` 而题目提示已经给出了还原目标代码 `def my_function(): print("恭喜成功!")`,所以 `co_consts[1]` 的明文就是 **`恭喜成功!`**,其 UTF-8 编码恰好是 15 字节,与 `succ`(15 字节)等长: ``` 恭喜成功! = E6 81 AD | E5 96 9C | E6 88 90 | E5 8A 9F | EF BC 81 ``` 于是直接由前 6 字节解出 key: ``` key[i] = succ[i] ^ plain[i] succ = 9F E3 9B 91 F5 EE ... plain = E6 81 AD E5 96 9C ... key = 79 62 36 74 63 72 → "yb6tcr" ``` ### 验证 用 `yb6tcr` 解 `succ` 全部 15 字节:`E6 81 AD E5 96 9C E6 88 90 E5 8A 9F EF BC 81` ✅ 与 `恭喜成功!` 完全一致。 再用它解 `c`(字节码): ``` ec^79=95( RESUME) 6d^36=5b(LOAD_GLOBAL) 2a^79=53(LOAD_CONST 1) 03^36=35(CALL 1) 43^63=20(POP_TOP) 1e^79=67(RETURN_CONST None) ``` 解出的 26 字节与函数骨架 `RESUME; LOAD_GLOBAL print; LOAD_CONST 1; CALL 1; POP_TOP; RETURN_CONST None` 完全吻合(混淆态与还原态字节码相同,仅常量不同)。 > 附:3.13 字节码对应关系(从反汇编反推):`0x53=LOAD_CONST`、`0x35=CALL`、`0x20=POP_TOP`、`0x67=RETURN_CONST`、`0x5b=LOAD_GLOBAL`、`0x95=RESUME`。 ## 7. 最终答案 ``` Key = yb6tcr ``` 实测(真实 EXE): ``` $ printf 'yb6tcr\n' | ./1.exe 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) 恭喜成功! (exit code 0) ```
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
阿捏利
1
9
发帖
10
回帖
124
RANK
关注
私信
他的文章
[原创]丑寅同墟·星海抉择WriteUp
21
[原创]亥子合辰·塔影迷楼 Writeup
52
[原创]申时·忆海倒带— WriteUp
77
[原创]巳时·绿光幽语 题解(Writeup)
3
[原创]# KCTF 2026 – Rosetta Calibration 题解(Writeup)
6
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部