1. PyArmor 在做的事:把“源码可读”变成“运行可用、理解困难”
PyArmor 的核心思路是:
- 发布产物仍然是
.py,可以“无缝替换”原脚本(大多数情况下)(GitHub) - 但
.py里不再是你的业务逻辑源码,而是一个引导壳(stub)+ 受保护载荷,执行时靠runtime 包加载与校验(pyarmor.readthedocs.io)
官方 README 概括得很直接:它支持脚本混淆、绑定机器/过期控制、以及更强的不可逆混淆能力(如重命名、部分函数转 C 编译)(GitHub)。
2. 产物结构:你发布出去的通常包含哪些东西?
用pyarmor gen生成后,典型目录长这样(示意):
dist/ your_pkg_or_scripts... pyarmor_runtime_000000/ # runtime 包(可独立放在任意路径) __init__.py pyarmor_runtime.* # 扩展模块/二进制组件(平台相关)关键点:
- runtime 包(例如
pyarmor_runtime_000000)可以放在任意路径,当成第三方包一样被导入;PyArmor 提供-i / --prefix / --use-runtime等机制来生成正确的导入方式(pyarmor.readthedocs.io) - PyArmor 还有“共享 runtime”的套路:先
pyarmor gen runtime生成统一 runtime,再在混淆时用--use-runtime PATH引用它,避免多个包重复携带 runtime(pyarmor.readthedocs.io)
3. 运行时机制:真正“护”的东西在哪里?
3.1 Runtime Key(运行时密钥/许可证信息)
PyArmor 的运行时加载并不是“直接跑”,它依赖runtime key:
- runtime key 通常**嵌在扩展模块
pyarmor_runtime**里,也可以使用--outer变成“外置文件”(pyarmor.readthedocs.io) - runtime key 可承载:过期时间、设备绑定、私有数据等;如果 key 不存在或无效,扩展模块就不会加载受保护脚本(pyarmor.readthedocs.io)
这就是 PyArmor 能做授权控制(过期/绑定)的根因:它把策略判断放到了 runtime 的“必须经过的一道门”。
3.2 Private / Restrict:让“被混淆的模块”更难被外部脚本调用/探测
--private:让 plain script / Python 解释器看不到混淆脚本的属性(更难 inspect)(pyarmor.readthedocs.io)--restrict:在 private 的基础上更进一步:包内可用、包外不可用;尤其适合发布 package 时防止外部随意 import 子模块(pyarmor.readthedocs.io)
官方教程里直接说明:--restrict会保证混淆模块只在包内可用,不能被普通脚本 import,也不能直接用解释器运行(pyarmor.readthedocs.io)。
4. “混淆方法”有哪些层次?(从轻到重)
PyArmor 的“混淆”不是一个按钮,而是一组可叠加的策略。你可以把它理解为:可用性优先 vs 安全性优先之间的滑杆。
4.1 模块级/函数级混淆强度:--obf-module+--obf-code+ wrap
在pyarmor gen中:
--obf-module <0,1>:是否做“整模块”层面的处理(默认 1)(pyarmor.readthedocs.io)--obf-code <0,1,2>:是否做“函数级”处理(默认 1)(pyarmor.readthedocs.io)--no-wrap:关闭 wrap 机制(wrap 影响函数进入/退出时的还原与再混淆策略)(pyarmor.readthedocs.io)
直觉:函数级越深、wrap 越积极,逆向者越难在运行中“稳定抓到明文逻辑”,但兼容性与性能成本也更高。
4.2 常量/字符串保护:--mix-str
--mix-str用于保护字符串常量(pyarmor.readthedocs.io)。
这是防止“grep 一下就看到 API key / URL / 协议字段”的常用手段(当然,敏感信息最佳实践仍是:放配置中心/密钥服务,不要硬编码)。
4.3 高阶不可逆混淆:RFT / BCC / Themida(偏商业增强)
man page 中--enable <jit,rft,bcc,themida>给出了可启用的特性集合(pyarmor.readthedocs.io)。
README 也强调了两类“不可逆”增强:
- 重命名函数/类/变量/参数(RFT 思路)
- 把部分 Python 函数转为 C 并编译为机器指令(BCC 思路)(GitHub)
建议写在博客里的态度:这些能力很强,但也更容易引入兼容性/构建复杂度问题——先用基础混淆跑通 CI/CD 与发布链路,再逐步加码。
5. 授权与交付:过期、绑定、外置 key(真正“商业化发布”常用)
5.1 过期 & 设备绑定
pyarmor gen支持:
-e/--expired设置过期(pyarmor.readthedocs.io)-b/--bind-device绑定设备(pyarmor.readthedocs.io)
5.2 外置 runtime key:--outer+pyarmor gen key
--outer启用外置 key 后,必须用pyarmor gen key生成外置 key 文件,否则运行会报缺 key (pyarmor.readthedocs.io)- 默认外置 key 名为
pyarmor.rkey,可用pyarmor cfg outer_keyname=...改名(pyarmor.readthedocs.io)
这套机制适合“交付包固定不变,授权文件按客户/机器下发”的商业模型。
6. 打包发布:PyInstaller / pack / 版本差异要注意
在 9.2 的 man page 里,pyarmor gen提供--pack <onefile|onedir|...|NAME.spec>来“混淆后再打包”,甚至支持传入.spec(pyarmor.readthedocs.io)。
但历史上 8.0 曾出现 “没有完全等价的 pack” 的讨论(维护者表示 8.0 主要提供 repack 处理 PyInstaller bundle)(GitHub)。
写博客时建议你点明:
- 如果团队里 PyArmor 版本不统一,打包链路可能不同
- 最稳妥的工程实践是:先只做混淆(不 pack),确认运行 OK,再叠加打包(社区维护者也类似建议先保证不打包可运行再排查)(GitHub)
7. 你必须告诉读者的“限制与坑”(不写这段博客不完整)
官方明确列出过一些关键差异/限制:
- 混淆脚本绑定 Python 主/次版本:例如用 Python 3.6 混淆的,必须跑在 3.6 上(pyarmor.readthedocs.io)
- 混淆脚本平台相关(不同 OS/架构需要对应 runtime 组件)(pyarmor.readthedocs.io)
- 某些 Python 特性会受影响:例如大量
inspect功能可能失效;尝试读取字节码/代码对象属性的第三方库可能不兼容(pyarmor.readthedocs.io)
这段内容非常重要:它能解释“为什么我混淆后某些框架/热更新/序列化工具崩了”。
8. 一套“可直接放进你博客”的实战命令清单(偏工程落地)
以下命令语义来自 PyArmor 9.2 文档的
pyarmor gen参数说明(pyarmor.readthedocs.io),你可以按项目裁剪。
8.1 混淆单文件 / 多文件
pyarmor gen main.py pyarmor gen main.py util.py8.2 混淆整个包(递归)
pyarmor gen -r src/mypkg8.3 把 runtime 放进包内(更“像一个正常包”)
pyarmor gen -r -i mypkg8.4 启用 restrict(包外不可 import 子模块)
pyarmor gen -r -i --restrict mypkg8.5 外置 key(授权文件下发场景)
pyarmor gen --outer -r -i mypkg pyarmor gen key -e30# 生成外置 key(示例:30 天)8.6 共享 runtime(多包复用)
pyarmor gen runtime -O build/runtime pyarmor gen --use-runtime build/runtime -r mypkg9. 试用版/许可:博客里最好也提一句
PyArmor 的许可条款页提到试用版的一些限制(例如 code object 大小限制、试用版混淆脚本不算“private”等)(pyarmor.dashingsoft.com)。
这对读者“为什么试用版达不到预期效果”很关键。
10. 结尾给一个“正确预期”的安全建议
你可以用这段做总结:
- PyArmor 适合保护:业务逻辑、算法实现、协议细节、反爬/反作弊策略、桌面工具分发
- 它不适合替代:服务端鉴权、密钥管理、后端核心逻辑(核心逻辑能放服务端就别下发到客户端)
- 最佳实践是:混淆 + 最小化下发逻辑 + 配置/密钥外置 + 授权策略(过期/绑定)+ CI 可重复构建