PyArmor核心保护在于字节码加密、运行时动态解密及机器/时间绑定,而非简单重命名;其混淆后的.py仅为引导壳,真实逻辑藏于加密.pyc中,由pyarmor_runtime加载执行。

pyarmor 是目前最接近“开箱即用”的选择,但它不是万能的——混淆后仍可被有经验者逐步还原,真正起作用的是它对运行时环境的绑定(如机器码、时间锁)和字节码加密层。单纯靠重命名变量或插入无用代码,对现代反编译工具(如 uncompyle6、decompyle3)基本无效。
为什么直接用 ast 模块做重命名容易翻车
很多人从 ast.NodeTransformer 入手写混淆器,以为改掉 FunctionDef.name 和 Name.id 就完事了,结果运行报错或逻辑错乱。常见坑包括:
-
self、cls这类隐式参数名不能重命名,否则实例方法失效 - 同名但不同作用域的变量(比如全局
config和函数内config)被映射到同一个随机名,引发覆盖 - 没过滤
keyword.kwlist,把return或def也当普通标识符替换了 - 字符串里硬编码的函数名(如
getattr(obj, "process"))不会被 AST 触及,混淆后调用直接失败
pyarmor 的真实保护边界在哪
它实际做了三件事:源码转字节码 → 字节码加密 → 运行时动态解密加载。关键点是:
- 混淆后的
.py文件只是壳,真正逻辑在加密的.pyc里,import时由pyarmor_runtime解密执行 - 默认不加密字符串常量,需显式加
--advanced 2才启用字符串加密 - 机器绑定靠检查
/sys/class/dmi/id/product_uuid(Linux)或WMIC(Windows),虚拟机或容器里可能不稳定 - 它不处理
eval()、exec()或importlib.util.spec_from_file_location动态加载的模块
哪些场景下混淆反而会暴露更多线索
混淆不是越“乱”越好。以下操作会让逆向者更快定位核心逻辑:
- 所有函数都缩成单字母名(如
a()、b()),反而凸显出“这里一定有猫腻”,因为正常项目不会这么极端 - 在关键验证函数前后插入大量
pass或空if False:,这种模式本身就成了识别锚点 - 用固定种子生成随机名(如
random.seed(42)),导致不同版本混淆结果一致,方便比对差异 - 混淆后保留原注释或日志字符串(如
print("DEBUG: checking license")),等于主动交出入口线索
Cython 扩展模块、只暴露 Python 接口的方式——混淆只是第一道门,门后有没有更硬的墙,才决定别人到底能不能走进来。


















