利用Cython加密文件和项目

Python是一门解释型语言,当我们想让其他人运行我们的代码时,如果直接将.py源代码发送给他人,那么源代码将没有任何安全性可言,也就是任何一个人都可以打开源代码一看究竟,任何人都可以随意修改源代码。因此当我们想保护我们的源代码(算法保护)或者防止用户篡改源代码时,可以将Python源代码编译生成.pyd库文件或者.so库文件(Windows平台生成pyd文件,Linux生成so文件)。

事实上,只是我们表面上看Python是直接解释源代码,而实际上python解释器只会加载字节码。当我们import某个模块时,总是会在模块所在的目录创建一个__pycache__目录,里面存放着被加载模块的字节码文件。

Python有以下几种类型的文件:

  • py:Python控制台程序的源代码文件
  • pyw:Python带用户界面的源代码文件
  • pyx:Python包源文件
  • pyc:Python字节码文件(可通过逆向编译来得到源码)
  • pyo:Python优化后的字节码文件(可通过逆向编译来得到源码)
  • pyd:在Windows平台上Python的库文件(Python版DLL)
  • so:在Linux平台上是so文件

加密单个文件

编写setup.py文件

from setuptools import setup            # 可以使用setuptools中的setup
# from distutils.core import setup      # 也可以使用distutils.core中的setup
from Cython.Build import cythonize      

setup(ext_modules = cythonize(["model_extract.py"]), script_args=['build_ext', '--inplace'])

命令行运行文件,会在文件目录下生成.so或.pyd加密文件

python setup.py build_ext --inplace

加密整个项目

实际上setup中可以配置很多参数,进而可以满足我们对整个项目轻松进行加密。

import shutil
import os
from setuptools import setup
# from distutils.core import setup
from distutils.extension import Extension
from distutils.command.clean import clean
from Cython.Distutils import build_ext

MODULE_NAME = "mytest"   # 给项目名字
SRC_DIR = "src"          # 要加密代码的目录,个人倾向于把源码统一放在src文件夹下
IGNORE_FILES = ["__init__.py"]    # 一般不对该文件加密

# 对src文件夹下文件进行遍历,找到以py结尾的文件
def traverse_path(dir, files=None, folders=None, extension_list=('py',)):
    if folders is None:
        folders = []
    if files is None:
        files = []
    folders.append(dir)
    for file in os.listdir(dir):
        path = os.path.join(dir, file)
        if os.path.isfile(path):
            if '.' in path:
                _, extension = path.rsplit('.', 1)
                if extension.lower() in extension_list and file not in IGNORE_FILES:
                    files.append(path)
        elif os.path.isdir(path):
            traverse_path(path, files, folders, extension_list)


# ex_files参数可以支持不在src文件夹下的文件进行加密
def get_extensions(ex_files = []):   
    py_files = ex_files
    traverse_path(SRC_DIR, files=py_files)   # traverse_path函数对py_files进行更新
    ext_names = map(lambda x: x.replace(os.path.sep, '.')[:-3], py_files)

    def make_extension(ext_name):
        ext_path = ext_name.replace('.', os.path.sep) + '.py'
        return Extension(ext_name, [ext_path], include_dirs=['.'])

    extensions = map(lambda x: make_extension(x), ext_names)
    return list(extensions)


# ex_folders参数可以支持不在src文件夹下的文件进行加密
def get_packages(ex_folders = []): 
    folders = ex_folders
    traverse_path(SRC_DIR, folders=folders)     # traverse_path对folders进行更新
    packages = map(lambda x: x.replace('/', '.'), folders)
    return list(packages)


# 对加密后的py,pyc和c文件进行清除
class CleanCode(object):    

    def clean_build(self, distribution):
        clean_command = clean(distribution)
        clean_command.all = True
        clean_command.finalize_options()
        clean_command.run()

    def delete_source_code(self, target_dir):
        source_file_list = []
        traverse_path(target_dir, files=source_file_list, extension_list=('py', 'pyc', 'c'))
        for source_file in source_file_list:
            if os.path.basename(source_file) not in IGNORE_FILES:
                os.remove(source_file)

    def copy_so(self, build_path, source_code_path):
        self._copy_so(build_path, build_path, source_code_path)

    def _copy_so(self, target_dir, build_base_dir, target_base_dir):
        for file in os.listdir(target_dir):
            path = os.path.join(target_dir, file)
            if os.path.isfile(path) and path.endswith('.so'):
                new_path = path.replace(build_base_dir, target_base_dir)
                shutil.copyfile(path, new_path)
            elif os.path.isdir(path):
                self._copy_so(path, build_base_dir, target_base_dir)


# 继承Cython的build_ext类
class CustomBuildExt(build_ext, CleanCode): 

    def run(self):
        build_ext.run(self)

        source_code_path = SRC_DIR
        build_path = os.path.join(self.build_lib, SRC_DIR)
        self.copy_so(build_path, source_code_path)
        self.clean_build(self.distribution)
        self.delete_source_code(source_code_path)

ext_modules = get_extensions()

setup(
    name=MODULE_NAME,
    packages=get_packages(),
    ext_modules=ext_modules,
    cmdclass={'build_ext': CustomBuildExt}   # 自定义的CustomBuildExt
)

项目中用因为用到了Extension类,可能需要在ext_modules那一行后面加上下面这个代码:

ext_modules = get_extensions()
for e in ext_modules:
    e.cython_directives = {'language_level': "3"}

具体参考example

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值