A-A+

Pyarmor 加密你的python程序

2024年09月23日 15:59 汪洋大海 暂无评论 共2687字 (阅读148 views次)

首先调用 PyInstaller 将脚本打包成为单独的可执行文件或者打包到一个目录,然后把打包生成的可执行文件通过选项 --pack 传递给 pyarmor gen 来实现:

$ pyinstaller foo.py
$ pyarmor gen --pack dist/foo/foo foo.py

Pyarmor 首先加密脚本,并把它们被存放到 .pyarmor/pack/dist ,然后进行下面的额外处理:

  • 提取可执行文件中内容到一个临时目录 .pyarmor/pack/
  • 使用加密脚本替换临时目录中同名的未加密脚本
  • 把加密脚本的 运行辅助文件 增加到临时目录中
  • 根据把临时目录中所有内容重新生成可执行文件,并替换原来的可执行文件

需要注意的是,在这种方式下面,使用 PyInstaller 6.0+ 打包生成的可执行文件无法被正确处理,只能使用低版本 PyInstaller 进行打包。

重要

只有命令行列出的脚本会被加密,如果需要加密其他脚本或者子目录,在命令行列出它们。例如:

$ pyarmor gen --pack dist/foo/foo -r *.py dir1 dir2 ...

1

 

4.3.2. 自己动手打包加密脚本

如果使用上面的方式出现问题,或者打包好的可执行文件出现执行错误,那么请使用下面的方式自己打包加密脚本。

这需要你了解 如何使用 PyInstaller 和 如何使用 spec file ,如果还不知道如何使用,请点击链接学习相关知识。

下面我们使用上面的例子来说明如何手动打包加密脚本

  • 首先使用 Pyarmor 加密这个脚本,把需要加密的脚本和目录全部在主脚本后面列出来,也可以使用其他加密选项,但是不要使用 -i 或者 --prefix :
    $ cd project
    $ pyarmor gen -O obfdist -r foo.py util.py joker/
    

    检查加密脚本执行是否正常:

    $ python obfdist/foo.py
    
  • 然后使用下面的命令生成生成 foo.spec :
    $ pyi-makespec --onefile foo.py
    

    检查是否可以正确打包:

    $ pyinstaller foo.spec
    $ dist/foo
    
  • 接着修改 foo.spec ,插入下面的补丁代码到 pyz = PYZ 所在的行之前,这一步是重点
# Pyarmor patch start:

srcpath = ''
obfpath = 'obfdist'

def apply_pyarmor_patch(srcpath, obfpath):

    from PyInstaller.compat import is_win, is_cygwin
    extname = 'pyarmor_runtime' + ('.pyd' if is_win or is_cygwin else '.so')

    from glob import glob
    rtpkg = glob(os.path.join(obfpath, '*', extname))
    if len(rtpkg) != 1:
        raise RuntimeError('No runtime package found')
    rtpkg = os.path.basename(os.path.dirname(rtpkg[0]))

    extpath = os.path.join(rtpkg, extname)

    if hasattr(a.pure, '_code_cache'):
        code_cache = a.pure._code_cache
    else:
        from PyInstaller.config import CONF
        code_cache = CONF['code_cache'].get(id(a.pure))

    # Make sure both of them are absolute paths
    src = os.path.normcase(os.path.abspath(srcpath))
    obf = os.path.abspath(obfpath)
    n = len(src) + 1

    count = 0
    for i in range(len(a.scripts)):
        if os.path.normcase(a.scripts[i][1]).startswith(src):
            x = os.path.join(obf, a.scripts[i][1][n:])
            if os.path.exists(x):
                a.scripts[i] = a.scripts[i][0], x, a.scripts[i][2]
                count += 1
    if count == 0:
        raise RuntimeError('No obfuscated script found')

    for i in range(len(a.pure)):
        if os.path.normcase(a.pure[i][1]).startswith(src):
            x = os.path.join(obf, a.pure[i][1][n:])
            if os.path.exists(x):
                code_cache.pop(a.pure[i][0], None)
                a.pure[i] = a.pure[i][0], x, a.pure[i][2]

    a.pure.append((rtpkg, os.path.join(obf, rtpkg, '__init__.py'), 'PYMODULE'))
    a.binaries.append((extpath, os.path.join(obf, extpath), 'EXTENSION'))

apply_pyarmor_patch(srcpath, obfpath)

# Pyarmor patch end.

# Before this line
# pyz = PYZ(...)
  • 最后直接使用打过补丁的 foo.spec 来打包,使用选项 --clean 避免补丁因为缓存的文件而失效:
    $ pyinstaller --clean foo.spec
    

请根据你的具体情况,做如下修改

  • 使用实际目录设置 srcpath ,相对路径即可,在这个例子,是当前路径,直接设置为空字符串
  • 使用实际目录设置 obfpath ,相对路径即可,在这个例子中,加密目录是 obfdist

如何验证打包进去的是加密脚本

在主脚本以及导入的其他包和模块增加一些调试语句进行判断,例如

print('this is __pyarmor__', __pyarmor__)

如果不是加密脚本,这个语句会报错,只有在加密脚本中才能正常打印。

 

备注

选项 -i 或者 --prefix 会导致运行辅助包无法找到
其他 PyInstaller 的选项也可以在这里使用

文章来源:https://pyarmor.readthedocs.io/zh/latest/topic/repack.html#id11

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

给我留言