python - 源码保护 - 编译成so文件
一、python源码保护(.pyd/.so加密)
Python 作为一门动态语言和脚本语言,运行通过它编写的程序,并不需要进行静态编译和打包的过程,对其代码进行加密是一件很麻烦、复杂和困难的事情。
Cython是一个编程语言,它通过类似Python的语法来编写C扩展并可以被Python调用。能够将Python+C混合编码的.pyx脚本转换为C代码,主要用于优化Python脚本性能或Python调用C函数库。
基于它的原理,可以得到一种代码加密的思路:将 .py/.pyx 编译为 .c 文件,再将 .c 文件编译为 .so(Unix) 或 .pyd(Windows),这样得到的文件更难反编译。
-
优点
(1) 生成的二进制 .so 或 .pyd 文件难以破解
(2) 同时带来了性能提升 -
不足
(1) 兼容性稍差,对于不同版本的操作系统,可能需要重新编译
(2) 虽然支持大多数 Python 代码,但如果一旦发现部分代码不支持,完善成本较高
(3) 将编写的.py文件编译成.pyd/.so文件需要为其编写setup文件
二、编译单个文件
运行python3 setup.py build_ext --inplace
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(["test"]))
三、编译整个工程
编译整个工程,在脚本的目录下,生成与原工程目录结构相同的工程(build)
# -* -coding: UTF-8 -* -
__author__ = 'xzf'
"""
执行前提:
系统安装python-devel 和 gcc
Python安装cython
编译某个文件夹:
project_name: 文件名
生成结果:
脚本根目录 build 下
"""
import os, shutil
from distutils.core import setup
from Cython.Build import cythonize
def get_all_file(folder_path):
file_list = []
for foldername, subfolders, filenames in os.walk(folder_path):
for filename in filenames:
file_path = os.path.join(foldername, filename)
file_list.append(file_path)
return file_list
def clean_c_file(file_list):
for file in file_list:
ext = os.path.splitext(file)[1]
if ext == ".c":
os.remove(file)
def clean_py_and_c_file(file_list):
for file in file_list:
ext = os.path.splitext(file)[1]
if ext == ".py" or ext == ".c":
os.remove(file)
def find_so(compile_path, org_name):
all_file = os.listdir(compile_path)
for file in all_file:
if ".so" in file and org_name in file:
return file
if __name__ == "__main__":
print("-----------------编译开始------------------")
# 当前路径
curr_dir = os.path.abspath('.')
# project 路径
project_name = "project"
parentpath = os.path.abspath(project_name)
# 编译路径
compile_path = curr_dir + "/build"
print("-----------------复制工程------------------")
# 复制工程的所有文件
shutil.rmtree(compile_path)
shutil.copytree(parentpath, compile_path)
if os.path.exists(compile_path + "/tmp"):
shutil.rmtree(compile_path + "/tmp")
os.mkdir(compile_path + "/tmp")
file_list = get_all_file(compile_path)
print("-----------------开始遍历------------------")
for file in file_list:
setup(ext_modules=cythonize([file]), script_args=["build_ext",
"-b", compile_path,
"-t", "/tmp"
])
path_name, file_name = os.path.split(file)
print("\n当前路径名:", path_name)
print("当前文件名:", file_name)
so_file = find_so(compile_path, file_name.split(".")[0])
print("编译生成的so文件:", so_file)
new_file_so_name = file_name.split(".")[0] + ".so"
print("文件重命名:", new_file_so_name)
os.rename(compile_path + "/" + so_file,
compile_path + "/" + new_file_so_name)
print("移动文件:", compile_path + "/" + new_file_so_name)
shutil.move(compile_path + "/" + new_file_so_name,
path_name + "/" + new_file_so_name)
print()
clean_py_and_c_file(get_all_file(compile_path))
if os.path.exists(compile_path + "/tmp"):
shutil.rmtree(compile_path + "/tmp")
print("-----------------编译结果------------------")
print(file_list)
print(get_all_file(compile_path))
print("-----------------编译结束------------------")