1.3. 基础教程
内容
本教程仅适用于 Pyarmor 8.0+,使用下面的命令来查看版本信息:
$ pyarmor --version
如果 Pyarmor 的版本小于 8,那么请选择相应版本的文档(在页面的左下角可以选择版本),或者升级 Pyarmor 到正确版本。
在整个教程中,使用的例子结构如下:
project/
├── foo.py
├── queens.py
└── joker/
├── __init__.py
├── queens.py
└── config.json
Pyarmor 使用 pyarmor gen 加密不同的脚本,它提供了丰富的选项,以满足不同应用关于性能和安全方面的各种需求。
这里仅仅介绍的是常用的一些功能,关于完整的命令选项请查阅 命令手册
1.3.1. 调试模式和跟踪日志
当加密过程出现问题的时候,检查控制台的输出日志可以帮助发现问题所在,而使用选项 -d
启用调试模式会打印更多的信息帮助发现错误:
$ pyarmor -d gen foo.py
跟踪日志用来记录那些函数被 Pyarmor 使用什么方式进行了保护,它通过下面的方式启用:
$ pyarmor cfg enable_trace=1
启用之后,每一次执行命令 pyarmor gen 都会生成一个跟踪日志文件 .pyarmor/pyarmor.trace.log
记录相关的保护信息。例如:
$ pyarmor gen foo.py
$ cat .pyarmor/pyarmor.trace.log
trace.co foo:1:<module>
trace.co foo:5:hello
trace.co foo:9:sum2
trace.co foo:12:main
每一行开头的 trace.co
表示是默认的加密模式,后面的函数名称表示该函数使用默认的方式进行加密。
使用下面的方式禁用跟踪日志:
$ pyarmor cfg enable_trace=0
1.3.2. 使用更多的选项加密脚本
对于脚本,可以使用这些选项来增加安全性:
$ pyarmor gen --enable-jit --mix-str --assert-call --private foo.py
选项 --enable-jit
通过使用动态指令生成技术处理某些敏感数据来增加安全性。
选项 --mix-str
1 能够加密脚本中所有长度大于 8 的字符串。
选项 --assert-call
能够确保加密脚本中的函数不会被替换。
选项 --private
能够确保加密脚本的属性不能直接被 Python 解释器直接导入查看。
例如,
data = "abcdefgxyz"
def fib(n):
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
if __name__ == '__main__':
fib(n)
字符串常量 abcdefgxyz
和函数 fib
会被以如下方式进行保护
data = __mix_str__(b"******")
def fib(n):
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
if __name__ == '__main__':
__assert_call__(fib)(n)
如果函数 fib
是加密函数,那么 __assert_call__(fib)
返回原来的函数,否则抛出保护异常。
为了查看那些函数和字符串被保护,可以启用并检查跟踪日志:
$ pyarmor cfg enable_trace=1
$ pyarmor gen --mix-str --assert-call fib.py
$ cat .pyarmor/pyarmor.trace.log
trace.assert.call fib:10:'fib'
trace.mix.str fib:1:'abcxyz'
trace.mix.str fib:9:'__main__'
trace.co fib:1:<module>
trace.co fib:3:fib
1.3.3. 使用更多的选项加密包
如果是加密 Python 包 ,使用其他两个选项:
$ pyarmor gen --enable-jit --mix-str --assert-call --assert-import --restrict joker/
选项 --assert-import
可以检查导入的模块,确保是没有被替换的加密模块。
选项 --restrict
可以确保加密模块只能在加密脚本内部使用,而不能被外部脚本导入。
默认情况下 __init__.py
中定义的函数和名称是可以被外部脚本使用的,其他模块定义的函数和名称是不能被外部脚本使用。让我们创建一个测试脚本 dist/a.py
来验证一下
import joker
print('import joker OK')
from joker import queens
print('import joker.queens OK')
运行这个测试脚本:
$ cd dist
$ python a.py
... import joker OK
... RuntimeError: unauthorized use of script
如果需要导出包中的其他模块,要么不使用选项 --restrict
,要么单独配置模块 joker.queens
不使用约束模式:
$ pyarmor cfg -p joker.queens restrict_module=0
再次加密和测试一下,这次应该可以正常运行:
$ pyarmor gen --restrict joker/
$ cd dist/
$ python a.py
... import joker OK
... import joker.queens
1.3.4. 拷贝数据文件
很多包都有数据文件,并且运行的时候需要这些数据文件,但是默认情况下不会被 Pyarmor 拷贝到输出路径。
但是 Pyarmor 提供了三种方法可以解决这个问题
在加密之前先把整个包全部拷贝到输出目录,然后加密脚本,这样加密脚本仅仅覆盖原来的
.py
文件,而数据文件保留不变:$ mkdir dist/joker $ cp -a joker/* dist/joker $ pyarmor gen -O dist -r joker/
通过配置选项,让 Pyarmor 自动拷贝所有数据文件:
$ pyarmor cfg data_files=* $ pyarmor gen -O dist -r joker/
如果只需要拷贝 *.yaml
和 *.json
文件,使用下面的配置命令:
$ pyarmor cfg data_files="*.yaml *.json"
自己编写 加密插件 来拷贝需要的数据文件
1.3.5. 周期性检查运行密钥
使用下面的命令生成的加密脚本,运行的时候会每隔一个小时对运行密钥进行一次检查:
$ pyarmor gen --period 1 foo.py
1.3.6. 绑定加密脚本到多个设备
使用选项 -b
多次可以绑定加密脚本到多个设备。
例如,两台设备 A 和 B,以太网地址分别是 66:77:88:9a:cc:fa
和 f8:ff:c2:27:00:7f
,使用下面的命令绑定加密脚本到这两台设备:
$ pyarmor gen -b "66:77:88:9a:cc:fa" -b "f8:ff:c2:27:00:7f" foo.py
1.3.7. 使用外部文件存放运行密钥
在加密脚本的时候指定使用外部密钥:
$ pyarmor gen --outer foo.py
在这种情况下,加密后的脚本无法直接运行:
$ python dist/foo.py
需要先使用 pyarmor gen key 创建一个外部密钥:
$ pyarmor gen key -e 3
上面的命令会生成外部密钥文件 dist/pyarmor.rkey
,拷贝这个文件到运行辅助包:
$ cp dist/pyarmor.rkey dist/pyarmor_runtime_000000/
这样可以正常运行加密脚本 dist/foo.py
:
$ python dist/foo.py
让我们在生成一个新的运行密钥文件,存放在另外一个目录:
$ pyarmor gen key -O dist/key2 -e 10
$ ls dist/key2/pyarmor.rkey
拷贝这个新文件到运行辅助包去替换原来的:
$ cp dist/key2/pyarmor.rkey dist/pyarmor_runtime_000000/
外部运行密钥必须至少包含一个约束条件,要么是有效期,要么是设备信息。
外部运行密钥的名称默认是 pyarmor.rkey
外部运行密钥也可以存放在其他路径,请参阅 外部密钥 中的说明
1.3.8. 如何本地化错误信息
运行错误信息可以使用本地语言进行替换定制,例如加密脚本过期之后可以提示用户自己设定的错误信息。
首先在当前目录创建子目录 .pyarmor
,然后在其中创建文件 messages.cfg
:
$ mkdir .pyarmor
$ vi .pyarmor/messages.cfg
编辑这个文件。这是一个 .ini
格式的文件,按照自己的需要修改 error_N
后面对应的错误信息
[runtime.message]
error_1 = 脚本许可证已经过期
error_2 = 脚本许可证不可用于当前设备
error_3 = 缺少运行许可文件
error_4 = 非法使用脚本
error_5 = 脚本不支持当前 Python 版本
error_6 = 脚本不支持当前系统
error_7 = 加密模块的数据格式不正确
error_8 = 加密函数的数据格式不正确
然后需要重新加密脚本:
$ pyarmor gen foo.py
如果你想所有的运行密钥错误都显示同样的错误信息,而其他类型的错误还是使用默认值, 那么,修改成为下面的样子
[runtime.message]
error_1 = 未授权使用脚本
error_2 = 未授权使用脚本
然后重新加密脚本使之生效。
1.3.9. 生成可独立运行的加密脚本
这里的打包是指生成可以在没有 Python 环境独立运行的可执行文件。
Pyarmor 需要使用 PyInstaller 打包好的可执行文件,请首先安装 PyInstaller:
$ pip install pyinstaller
1.3.9.1. 单个可执行文件模式
在 8.5.4 版本加入.
生成一个单独的可执行文件直接使用下面的命令即可:
$ pyarmor gen --pack onefile foo.py
这个命令会加密 foo.py
,然后加密和 foo.py
同目录的,并且被引用到的其他模块或者包,最后调用 PyInstaller 把加密脚本打包成为一个单独的可执行文件 dist/foo
:
$ ls dist/foo
$ dist/foo
需要注意的是对于脚本引用到系统模块,以及任何不在当前目录下的模块和包,都没有进行加密处理。
重要
在命令行中的脚本 foo.py
必须是没有加密的,加密的脚本是无法直接打包的
1.3.9.2. 单个目录模式
在 8.5.4 版本加入.
生成单个目录模式的包直接使用下面的命令即可:
$ pyarmor gen --pack onedir foo.py
这个命令基本和上面的命令类似,只是在最后调用 PyInstaller 的时候传入的是单个目录的模式。查看最后的输出目录:
$ ls dist/foo
$ dist/foo/foo
1.3.9.3. 使用 spec 文件打包加密脚本
在 8.5.8 版本加入.
如果已经有现成的 spec 文件能够成功打包没有加密的脚本,例如:
$ pyinstaller foo.spec
$ dist/foo
可以直接把 foo.spec
传递给 --pack
,来自动打包加密脚本。例如:
$ pyarmor gen --pack foo.spec -r foo.py joker/
$ dist/foo
在这种模式无法自动加密依赖的脚本,需要人工在命令行指出需要加密的所有脚本,否则不会被加密。
如果打包出现问题,或者需要使用更多打包方面的功能,请参阅 详解可独立运行的加密脚本