首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]巳时·绿光幽语 WP
发表于: 2026-8-11 14:35
113
[原创]巳时·绿光幽语 WP
aut0run
1
2026-8-11 14:35
113
## Summary 这是一个 PyInstaller 打包的 Python 逆向题。常规解包后入口脚本看起来只是 `sys.exit(0)`,真正的逻辑被藏进了打包自带的 `python313.dll` 的 frozen `os` 模块里;最终从动态 patch `my_function.__code__` 的逻辑中恢复出 6 字符 key: ```text yb6tcr ``` ## 完整思路 ### 1. 先按 PyInstaller 常规流程解包 看到样本是 `题目.exe`,并且 IDA 里大量出现 `PYINSTALLER_*`、`PYZ archive`、`_pyi_main_co` 等字符串,所以第一反应是:这不是普通 PE 逻辑,核心 Python 代码应该在 PyInstaller CArchive/PYZ 里。 我先解析文件末尾 PyInstaller cookie: ```text cookie offset = 7438091 pkg_start = 345600 pyver = 313 pylib = python313.dll ``` TOC 里比较关键的条目是: ```text s main z PYZ.pyz b python313.dll b base_library.zip ``` 继续解 `PYZ.pyz`,发现里面全是标准库模块,没有业务模块。入口 `main` 也很短,常量和名字只有: ```python co_names = ('sys', 'exit') co_consts = (0, None) ``` 按 Python 3.13 opcode 表反汇编后,基本就是: ```python import sys sys.exit(0) ``` 这一步得到的结论是:如果只看 PyInstaller 解包结果,题目像是空的。 ### 2. 运行样本,推翻“空壳入口”的判断 直接运行程序后,发现它并不是立刻退出,而是反复输出: ```text 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) ``` 这说明普通 `main` 入口不是全部逻辑。接下来我想的是:既然 CArchive/PYZ 没业务代码,而程序确实执行了提示逻辑,那代码可能藏在: 1. PyInstaller bootloader 原生代码里; 2. 额外资源/附加数据里; 3. 打包进去的 Python 运行时或 frozen 标准库里。 我搜索 `my_function`、`恭喜`、`还原` 等字符串,发现 `my_function` 不在 `main` 或 `PYZ`,而在解出的: ```text pyi_out/python313.dll ``` 附近还能看到 marshalled code object 的痕迹,以及可疑常量: ```text bmucryb6tcr*c ucryb6tCr co_code co_consts ``` 这时基本确定:题目改了 Python 运行时 DLL,而不是常规地把业务脚本放进 PyInstaller archive。 ### 3. 定位 modified frozen `os` PyInstaller 启动时会 import 很多标准库模块,尤其是 `os`。所以我去解析 `python313.dll` 的导出符号,看到这些 frozen module 表: ```text _PyImport_FrozenBootstrap _PyImport_FrozenStdlib _PyImport_FrozenTest PyImport_FrozenModules ``` 用 `pefile` 解 `_PyImport_FrozenStdlib`,按 CPython frozen module 结构: ```c struct _frozen { const char *name; const unsigned char *code; int size; int is_package; }; ``` 列出模块后发现: ```text 009 'os' code=1804d1ae0 size=47420 ispkg=0 <-- covers my_function ``` `my_function` 的地址正好落在 frozen `os` 模块的 code blob 范围里。也就是说:程序启动 import `os` 时,就执行了被篡改过的 frozen `os` 模块尾部逻辑。 我把这段 frozen `os` marshal 数据抽出来: ```text frozen_os.marshal ``` 然后加上 Python 3.13 pyc header,用 `xdis` 反汇编: ```bash python -m xdis.disasm frozen_os.pyc > frozen_os.xdis.txt ``` ### 4. 还原注入逻辑 在 frozen `os` 的顶层 code 尾部,能看到新增的逻辑。核心流程如下: ```python x = 0 while x < 20: def my_function(): print(666) x += 1 buffer = b''.join([...]) w = b'' for i in buffer: b = i ^ 85 w += b.to_bytes(1) print(w.decode('utf-8')) key = input() if len(key) != 6: continue b_key = key.encode() c = b'\xecbmucryb6tcr*c\x03ucryb6tCr\x1eb' buf = b'' for i in range(len(c)): buf += (c[i] ^ b_key[i % len(b_key)]).to_bytes(1) my = my_function.__code__ my = my.replace(co_code=buf) succ = b'\x9f\xe3\x9b\x91\xf5\xee\x9f\xea\xa6\x91\xe9\xed\x96\xde\xb7' buf = b'' for i in range(len(succ)): buf += (succ[i] ^ b_key[i % len(b_key)]).to_bytes(1) my = my.replace(co_consts=(None, buf.decode('utf-8'))) my_function.__code__ = my my_function() return ``` 如果 key 错了,解出来的 `co_code` 或 `co_consts` 不合法,就会触发异常,继续下一轮循环。 这里的关键观察是:成功字符串明显应该是提示里的: ```text 恭喜成功! ``` `succ` 是它被 6 字节 key 循环 XOR 后的密文。所以可以直接用已知明文恢复 key。 ### 5. 求 key 完整求解脚本如下: ```python succ = bytes.fromhex("9fe39b91f5ee9feaa691e9ed96deb7") plain = "\u606d\u559c\u6210\u529f\uff01".encode("utf-8") stream = bytes(a ^ b for a, b in zip(succ, plain)) key = stream[:6] print("xor stream:", stream) print("key:", key.decode()) enc_code = b"\xecbmucryb6tcr*c\x03ucryb6tCr\x1eb" code = bytes(enc_code[i] ^ key[i % len(key)] for i in range(len(enc_code))) const = bytes(succ[i] ^ key[i % len(key)] for i in range(len(succ))) print("decrypted co_code:", code.hex()) print("decrypted const:", const.decode("utf-8")) ``` 输出: ```text xor stream: b'yb6tcryb6tcryb6' key: yb6tcr decrypted co_code: 95005b0100000000000000005301350100000000000020006700 decrypted const: 恭喜成功! ``` `xor stream` 呈现 6 字节周期: ```text yb6tcr yb6tcr yb6 ``` 因此 key 为: ```text yb6tcr ``` ### 6. 验证 把 key 输入程序: ```powershell 'yb6tcr' | .\题目.exe ``` 得到: ```text 提示,需要还原的代码 def my_function(): print("恭喜成功!") 请输入key还原代码(请输入6个字符) 恭喜成功! ``` 验证成功。 ## Flag / Key ```text yb6tcr ``` ## 复盘 这题最容易走偏的地方是:PyInstaller 解包结果确实显示 `main` 基本为空,`PYZ` 也没有业务模块。如果只停在这里,会误以为样本被剥离或入口异常。 真正的突破点来自动态运行结果和静态结果矛盾:程序明明输出了业务提示,但解包的 Python 脚本没有这些字符串。沿着这个矛盾去搜 `my_function`,才发现字符串在 `python313.dll`。再结合 PyInstaller 启动流程,会想到 frozen 标准库模块可能被改,最后定位到 frozen `os`。 本题核心不是复杂算法,而是隐藏位置:把校验逻辑塞进 Python 运行时 DLL 的 frozen `os` 模块,让常规 PyInstaller 解包流程看不到真正业务代码
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
aut0run
雪 币:
2217
活跃值:
(692)
能力值:
( LV5,RANK:60 )
在线值:
发帖
5
回帖
11
粉丝
13
关注
私信
aut0run
1
2
楼
写错了
最后于
2026-8-25 20:01 被aut0run编辑 ,原因:
2026-8-21 22:12
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
aut0run
1
5
发帖
11
回帖
60
RANK
关注
私信
他的文章
[原创]libDexHelper.so 全面分析
4303
[原创]某加密企业版 Frida 检测绕过
5072
[原创]巳时·绿光幽语 WP
113
[原创]KCTF2026 第六题:酉时·书院迷局 设计思路
75
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部