-
-
[原创]巳时·绿光幽语 WP
-
发表于: 2天前 84
-
这是一个 PyInstaller 打包的 Python 逆向题。常规解包后入口脚本看起来只是 sys.exit(0),真正的逻辑被藏进了打包自带的 python313.dll 的 frozen os 模块里;最终从动态 patch my_function.__code__ 的逻辑中恢复出 6 字符 key:
看到样本是 题目.exe,并且 IDA 里大量出现 PYINSTALLER_*、PYZ archive、_pyi_main_co 等字符串,所以第一反应是:这不是普通 PE 逻辑,核心 Python 代码应该在 PyInstaller CArchive/PYZ 里。
我先解析文件末尾 PyInstaller cookie:
TOC 里比较关键的条目是:
继续解 PYZ.pyz,发现里面全是标准库模块,没有业务模块。入口 main 也很短,常量和名字只有:
按 Python 3.13 opcode 表反汇编后,基本就是:
这一步得到的结论是:如果只看 PyInstaller 解包结果,题目像是空的。
直接运行程序后,发现它并不是立刻退出,而是反复输出:
这说明普通 main 入口不是全部逻辑。接下来我想的是:既然 CArchive/PYZ 没业务代码,而程序确实执行了提示逻辑,那代码可能藏在:
我搜索 my_function、恭喜、还原 等字符串,发现 my_function 不在 main 或 PYZ,而在解出的:
附近还能看到 marshalled code object 的痕迹,以及可疑常量:
这时基本确定:题目改了 Python 运行时 DLL,而不是常规地把业务脚本放进 PyInstaller archive。
PyInstaller 启动时会 import 很多标准库模块,尤其是 os。所以我去解析 python313.dll 的导出符号,看到这些 frozen module 表:
用 pefile 解 _PyImport_FrozenStdlib,按 CPython frozen module 结构:
列出模块后发现:
my_function 的地址正好落在 frozen os 模块的 code blob 范围里。也就是说:程序启动 import os 时,就执行了被篡改过的 frozen os 模块尾部逻辑。
我把这段 frozen os marshal 数据抽出来:
然后加上 Python 3.13 pyc header,用 xdis 反汇编:
在 frozen os 的顶层 code 尾部,能看到新增的逻辑。核心流程如下:
如果 key 错了,解出来的 co_code 或 co_consts 不合法,就会触发异常,继续下一轮循环。
这里的关键观察是:成功字符串明显应该是提示里的:
succ 是它被 6 字节 key 循环 XOR 后的密文。所以可以直接用已知明文恢复 key。
完整求解脚本如下:
输出:
xor stream 呈现 6 字节周期:
因此 key 为:
把 key 输入程序:
得到:
验证成功。