基于Cython和内置distutils库,实现python源码加密(非混淆模式)

Posted flywuya

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Cython和内置distutils库,实现python源码加密(非混淆模式)相关的知识,希望对你有一定的参考价值。

起因

python本身只能做混淆,不能加密,多年的商业软件开发经验导致有某种“洁癖”欲望,将py编译打包

尝试

  • pyinstaller原理是freeze打包pyc文件,利用工具可完美逆行出源码
  • 各种混淆脚本,版本兼容很差,配置繁琐
  • cython 常规使用只能编译单个特殊模块

解决

反复尝试摸索后,还是利用了cython和distutils库,自动化识别并转换py到c源码并编译,放出源码供大家参考

""" 
利用cython和distutils编译py到pyd[so] 注意安装cython及本地平台对应编译器
http://flywuya.cnblogs.com/
 """
import os
import shutil
from distutils.core import setup
from distutils.command.build_ext import build_ext
from Cython.Build import cythonize

BUILD_CONFIG = {
    'SupportExt': ['.py', '.pyx'],
    'CopyOnlyFile': ['__main__.py', '__init__.py'],
    'CopyOnlyDir': ['assets'],
    'IgnoreDir': ['dist', 'build', '__pycache__'],
}


def copy_tree(src, dst):
    """ not like shutil.copytree, dst can be exists  """
    assert os.path.exists(src)
    assert os.path.isdir(src)
    os.makedirs(dst, exist_ok=True)

    for fn in os.listdir(src):
        s = os.path.join(src, fn)
        t = os.path.join(dst, fn)
        if os.path.isfile(s):
            shutil.copy2(s, t)
        elif os.path.isdir(s):
            copy_tree(s, t)


def build_module(source_file, dst_dir, tmp_dir):
    """ cythonize && build ext """
    assert os.path.isfile(source_file)
    assert not os.path.isabs(source_file)
    assert os.path.exists(dst_dir)
    os.makedirs(tmp_dir, exist_ok=True)

    build_cython = os.path.join(tmp_dir, 'build.cython')
    build_temp = os.path.join(tmp_dir, 'build.temp')
    build_lib = dst_dir

    ext_modules = cythonize(
        source_file,
        build_dir=build_cython,
        language_level=3,
    )

    class build_here(build_ext):
        def initialize_options(self):
            super().initialize_options()
            self.build_temp = build_temp
            self.build_lib = build_lib
    setup(
        ext_modules=ext_modules,
        script_args=['build_ext'],
        cmdclass=dict(build_ext=build_here)
    )


def build_modules(source_dir, dst_dir, tmp_dir):
    """ scan && build modules in source_dir """
    assert os.path.exists(source_dir)
    assert not os.path.isabs(source_dir)
    assert not os.path.isabs(dst_dir)
    os.makedirs(dst_dir, exist_ok=True)

    for root, dirs, files in os.walk(source_dir):
        rel_pth = root[len(source_dir)+1:]

        for ignore in BUILD_CONFIG['IgnoreDir']:
            if ignore in dirs:
                dirs.remove(ignore)

        for dn in dirs:
            if dn in BUILD_CONFIG['CopyOnlyDir']:
                copy_tree(
                    os.path.join(root, dn),
                    os.path.join(dst_dir, rel_pth, dn)
                )
                dirs.remove(dn)

        for fn in files:
            _, ext = os.path.splitext(fn)
            os.makedirs(
                os.path.join(dst_dir, rel_pth),
                exist_ok=True
            )
            if fn in BUILD_CONFIG['CopyOnlyFile']:
                shutil.copy2(
                    os.path.join(root, fn),
                    os.path.join(dst_dir, rel_pth, fn)
                )
            elif ext.lower() in BUILD_CONFIG['SupportExt']:
                build_module(
                    os.path.join(root, fn),
                    dst_dir,
                    os.path.join(tmp_dir, rel_pth),
                )
            else:
                shutil.copy2(
                    os.path.join(root, fn),
                    os.path.join(dst_dir, rel_pth, fn)
                )


if __name__ == "__main__":

    # 这里填写要编译的目录
    tasks = [
        'app',
    ]

    others = [
        'requirements.txt',
        'packages',
    ]

    BUILD_CONFIG['CopyOnlyFile'].extend(['settings.py'])

    for task in tasks:
        build_modules(
            task,
            os.path.join('dist', task),
            os.path.join('build', task),
        )

    for other in others:
        if os.path.isfile(other):
            bn = os.path.basename(other)
            shutil.copy2(other, os.path.join('dist', bn))
        elif os.path.isdir(other):
            bn = os.path.basename(other)
            copy_tree(other, os.path.join('dist', bn))

以上是关于基于Cython和内置distutils库,实现python源码加密(非混淆模式)的主要内容,如果未能解决你的问题,请参考以下文章

python安装pyffmpeg,cython报错:ImportError: No module named Cython.Distutils

如何手动编译使用 C++ 的 Cython 代码?

Cython - 如何使用引用正确调用操作员

使用 pyximport 时如何设置 Cython 编译器标志?

使用 distutils 构建基于 ctypes 的 C 库

Cython & C++:通过引用传递