如何正确保护Python代码,不是Pyinstaller

引言

在开发 Python 软件或者脚本时,为了保护 Python 代码不被盗用或篡改,我们需要借助一些工具来保护我们的源代码。通常情况下,我们可能会用 Pyinstaller 来保护我们的代码,并且将代码打包成可以在任何电脑上运行的单个文件。

但是,Pyinstaller 打包后的程序,只是将源代码编译成了 pyc 文件,pyc 文件和 Java 的 class 文件类似,是可以进行反编译的,而且还原度还挺高,所以不太符合我们的需求。所以这里介绍一款可以完美保护我们的代码的工具:PyArmor。

1. PyArmor 概述

Pyarmor 是一个用于加密和保护 Python 脚本的工具。它能够在运行时刻保护 Python 脚本代码不被泄露,设置加密后脚本的使用期限,绑定加密脚本到硬盘、网卡等硬件设备。它支持多种 Python 版本和平台,包括 Windows、Linux 和 macOS。

功能特点

  • 无缝替换: 加密后的脚本依然是一个有效的 .py 文件,在大多数情况下可以直接替换原来的 .py 脚本,而不影响脚本的使用。
  • 均衡加密: 提供了丰富的加密选项来平衡安全性和性能,能够满足大多数应用对安全性和性能的要求。
  • 不可逆加密: 能够直接重命名源代码中的函数,类,方法,变量和参数。
  • 转换成为 C 代码: 能够把模块中部分函数转换成为 C 代码,然后使用高优化选项直接编译 C 代码为机器指令来保护 Python 函数
  • 限制加密脚本的使用范围: 可以绑定加密脚本到指定的设备或者设置加密脚本的有效期
  • Themida 保护: 使用 Themida 保护加密脚本(仅 Windows 平台可用)

2. 安装方法

PyArmor 的安装很简单,使用 pip 安装即可。或者说使用你自己的依赖管理工具,我这里使用 poetry。

1
2
3
poetry add PyArmor
# 或者使用 pip
pip install PyArmor

3. 主要功能

3.1 代码加密

代码加密是 PyArmor 的核心功能之一,它将 Python 源代码转换为加密格式,防止他人修改。加密后的代码在执行的时候会自动解密,不影响软件原有的功能。

来看一下加密的例子,先写一个示例程序。

1
2
3
4
5
6
7
8
9
10
11
import requests

r = requests.get('https://www.baidu.com')
print(r.status_code)
print(r.text[:200])

a = [a for a in range(10)]
print(a)
a.reverse()
print(a)

1
pyarmor gen test7.py

运行以上命令开始加密脚本,得到下面的结果:

image-20241105171518913

运行加密后的 test7.py,得到以下结果:

1
2
3
4
5
200
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link re
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

查看加密后的 test7.py

image-20241105203000169

__init__.py内容,还生成了一个 so 文件,pyarmor_runtime.so,有点类似于 cython 的编译结果,必须带有这个文件加密的代码才能运行。

image-20241105203015809

3.2 过期时间

除了以上的加密功能, PyArmor还可以设置过期时间,只要在打包的时候加 -e 参数即可。

1
pyarmor g -e 2024-11-04 test7.py

第一次运行,正常输出,等一天后再运行,程序报错了。

1
2
3
4
5
6
Traceback (most recent call last):
File "/Users/xx/Downloads/xx/code/test/dist/test7.py", line 2, in <module>
from pyarmor_runtime_000000 import __pyarmor__
File "/Users/xx/Downloads/xx/code/test/dist/pyarmor_runtime_000000/__init__.py", line 2, in <module>
from .pyarmor_runtime import __pyarmor__
RuntimeError: this license key is expired (1:11086)

也可以设置使用远程服务器的时间

1
2
pyarmor cfg nts=pool.ntp.org
pyarmor g -e 2024-11-04 test7.py

不过这里有一个问题,http 协议的时间服务器,有可能会遇到劫持问题,哈哈哈,逆向搞多了,突然想到这个问题,不知道官方有没有解决方案。

3.3 绑定到本机电脑

可以使用一下命令将程序绑定到特定的 MAC 地址。

1
pyarmor g -b '00:16:3e:35:19:3d' test7.py 

由于这不是我电脑的 MAC 地址,运行报错。

1
2
3
4
5
6
Traceback (most recent call last):
File "/Users/b/Downloads/work/code/test/dist/test7.py", line 2, in <module>
from pyarmor_runtime_000000 import __pyarmor__
File "/Users/b/Downloads/work/code/test/dist/pyarmor_runtime_000000/__init__.py", line 2, in <module>
from .pyarmor_runtime import __pyarmor__
RuntimeError: this license key is not for this machine (1:10235)

3.4 注意事项

运行加密脚本需要一个 扩展模块 pyarmor_runtime ,它在运行辅助包 pyarmor_runtime_000000 目录下面。

使用二进制的扩展模块意味着加密脚本需要有为各个平台的预编译的扩展模块 pyarmor_runtime ,所以加密脚本

  • 只能运行在那些已经有预编译扩展模块的平台,所有支持的平台请参考 生成加密脚本的环境
  • 只能使用相同版本 CPython interpreter 解释器来运行,例如使用 Python 3.8 加密的脚本,无法被 Python 3.9 运行
  • 一般不能被第三方解释器,例如 PyPy, IronPython 或者 Jython 等来运行

结论

有了这个工具,后续我们在将开发的代码发给其他人时,可以用PyArmor 来将代码加密后发给对方,同时还可以配置过期时间和本地 MAC 地址或者序列号绑定,不用担心我们的代码被用户拿去篡改或者被用于其他用途了。

更多更强大的功能需要付费,但是一般情况下,免费版本已经足够我们使用了,付费版本如果有需求的小伙伴可以参考下表自行购买。

功能特征试用版基础版专家版集团版管线版
大脚本 / 混淆字符串常量 1YYYY
BCC / RFT / FLY 模式 2YYY
离线加密 3Y
最多构建设备数 41001002000
本地运行不受限制的 Docker 5Y
使用在 CI/CD pipeline 6YYY

本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注:qrcode
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!
本文链接
https://blog.d77.xyz/archives/b0786c18.html