1.5. 定制和扩展

Pyarmor 提供了 加密插件 和运行 脚本补丁 分别来扩展功能。

1.5.1. 使用加密插件修正运行辅助包的依赖项

在 8.2 版本加入.

在使用 Dawin 的设备中,如果 Python 没有安装在标准路径,那么运行加密脚本的时候可能会因为找不到依赖的 Python 动态库而出现装载错误。

如果需要运行加密脚本的环境在这种设备,那么在加密脚本的时候需要修正运行辅助包的依赖库位置。

首先查看一些运行辅助包中动态库 pyarmor_runtime.so 的依赖项:

$ otool -L dist/pyarmor_runtime_000000/pyarmor_runtime.so

dist/pyarmor_runtime_000000/pyarmor_runtime.so:

    pyarmor_runtime.so (compatibility version 0.0.0, current version 1.0.0)
    ...
    @rpath/lib/libpython3.9.dylib (compatibility version 3.9.0, current version 3.9.0)
    ...

如果 客户设备 上面没有 @rpath/lib/libpython3.9.dylib ,而是 @rpath/lib/libpython3.9.so ,那么加密脚本无法被装载。

这时候可以通过插件来修正这个问题,首先创建一个插件脚本 .pyarmor/conda.py:

__all__ = ['CondaPlugin']

class CondaPlugin:

    def _fixup(self, target):
        from subprocess import check_call
        check_call('install_name_tool -change @rpath/lib/libpython3.9.dylib @rpath/lib/libpython3.9.so %s' % target)
        check_call('codesign -f -s - %s' % target)

    @staticmethod
    def post_runtime(ctx, source, target, platform):
        if platform.startswith('darwin.'):
            print('using install_name_tool to fix %s' % target)
            self._fixup(target)

启用这个插件脚本,然后重新加密脚本:

$ pyarmor cfg plugins + "conda"
$ pyarmor gen foo.py

请根据具体的环境修改上面的插件脚本以满足需要。

参见

加密插件

1.5.2. 使用脚本补丁绑定脚本到 Docker

在 8.2 版本加入.

假设我们要把脚本 app.py 绑定运行在两个 Docker 上面,它们的 id 分别是 docker-a1docker-b2

那么,首先创建一个 脚本补丁 .pyarmor/hooks/app.py

def _pyarmor_check_docker():
    cid = None
    with open("/proc/self/cgroup") as f:
        for line in f:
            if line.split(':', 2)[1] == 'name=systemd':
                cid = line.strip().split('/')[-1]
                break

    docker_ids = __pyarmor__(0, None, b'keyinfo', 1).decode('utf-8')
    if cid is None or cid not in docker_ids.split(','):
        raise RuntimeError('license is not for this machine')

_pyarmor_check_docker()

然后加密脚本,同时把 Docker 的信息存储到 运行密钥 中:

$ pyarmor gen --bind-data "docker-a1,docker-b2" app.py

运行加密脚本以验证其效果,可以增加一些 print 语句在脚本补丁中进行调试。

1.5.3. 使用其他网络时间服务来检查脚本有效期

在 8.2 版本加入.

默认情况下 Pyarmor 是请求 NTP 服务器来验证加密脚本的有效期,如果 NTP 端口没有开放,也可以通过 脚本补丁 使用其他网络时间服务器来进行验证。

首先创建脚本补丁 .pyarmor/hooks/foo.py

def _pyarmor_check_worldtime(host, path):
    from http.client import HTTPSConnection
    expired = __pyarmor__(1, None, b'keyinfo', 1)
    conn = HTTPSConnection(host)
    conn.request("GET", path)
    res = conn.getresponse()
    if res.code == 200:
        data = res.read()
        s = data.find(b'"unixtime":')
        n = data.find(b',', s)
        current = int(data[s+11:n])
        if current > expire:
            raise RuntimeError('license is expired')
     else:
         raise RuntimeError('got network time failed')
_pyarmor_check_worldtime('worldtimeapi.org', '/api/timezone/Europe/Paris')

然后加密脚本,有效期的设置使用本地时间:

$ pyarmor gen -e .30 foo.py

这样就可以使用定制的代码检查网络时间。