使用py_compile模块将py文件转为pyc文件,容易被反编译成py文件,且反编译后的代码几乎和源代码一样。
在线代码混淆网站:https://pyob.oxyry.com/
虽然混淆后的代码乍一眼看上去好像头绪很大看不出来,但是静下来细看也就是改了一些变量和加了一些空格啥的,变量几乎都是O0OO0O0OOOOOOOO0O这种变量名,其中重要的方法逻辑还是能看出来。
保护 Python 脚本的工具,能够加密 Python 脚本,保护运行时刻的 Python 代码不被泄露,设置加密脚本的有效期限,绑定加密脚本到硬盘、网卡等硬件设备。
最大缺点:要钱!
windows下一般用pyinstaller、py2exe等工具打包,经测试,如果依赖包本身就比较大,比如含torch等,打包后的exe文件也会比较大。
网上还有一款打包工具Nuitka,可以打包win和Linux,具体用法不作介绍,有需要可以自行了解。
将.py编译为.c文件,再将.c文件编译为.so(Linux)或者.pyd(Windows),运行时可以被python解释器识别。
缺点:听说不支持一小部分代码,目前还未碰到;环境改变需要重新编译。
pip install Cython
我测试的版本为 0.29.34 。
compile.py
from distutils.core import setup
from Cython.Build import cythonize
import os
import shutil
# 需要加密的文件列表
encrypt_py_files = ['demo/demo_xxxx.py','utils/objdetector.py']
for encrypt_py_file in encrypt_py_files:
# 获取文件名,不带后缀
filename_without_extension = os.path.basename(encrypt_py_file).replace('.py','')
# 转成so文件
setup(ext_modules=cythonize(encrypt_py_file,language_level = "3"))
# 删除生成的c文件
os.remove(encrypt_py_file.replace('.py','.c'))
# 删除原py文件,注意备份
# os.remove(encrypt_py_file)
# 去build文件夹寻找对应的so文件
for root, dirs, files in os.walk('build'):
for f in files:
file_path = os.path.join(root, f)
# 如果是so文件并且文件名和需加密的py文件名一致
if f.endswith('so') and f.split('.')[0] == filename_without_extension:
# 将so文件移动到原py文件目录下并重命名
shutil.move(file_path, encrypt_py_file.replace('.py','.so'))
else:
# 其他文件一律删除
os.remove(file_path)
可以将该python脚本放在项目的最外层目录下,我已经按照需要编写好了处理逻辑。
简单解释下:执行setup后,会在当前目录创建一个build目录,里面的lib开头的文件夹下会存放编译好的so文件,需要将so文件移动至原py文件的目录下,并且文件名称需要和py文件名称一致,此外还会生成一些.c和.o文件,统统删掉。
2024.01.11补充:
有些Linux环境下,可能会产生编译信息:
Compiling multi_rtsp_detect.py because it changed.
[1/1] Cythonizing demo_xxxx.py
/home/ubuntu/.conda/envs/pytorch/lib/python3.8/site-packages/setuptools/dist.py:771: UserWarning: Usage of dash-separated 'description-file' will not be supported in future versions. Please use the underscore name 'description_file' instead
warnings.warn(
/home/ubuntu/.conda/envs/pytorch/lib/python3.8/site-packages/setuptools/config/setupcfg.py:508: SetuptoolsDeprecationWarning: The license_file parameter is deprecated, use license_files instead.
warnings.warn(msg, warning_class)
running build_ext
building 'demo_xxxx' extension
gcc -pthread -B /home/ubuntu/.conda/envs/pytorch/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/ubuntu/.conda/envs/pytorch/include/python3.8 -c demo_xxxx.c -o build/temp.linux-x86_64-cpython-38/demo_xxxx.o
gcc -pthread -shared -B /home/ubuntu/.conda/envs/pytorch/compiler_compat -L/home/ubuntu/.conda/envs/pytorch/lib -Wl,-rpath=/home/ubuntu/.conda/envs/pytorch/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-cpython-38/demo_xxxx.o -o build/lib.linux-x86_64-cpython-38/demo_xxxx.cpython-38-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-cpython-38/demo_xxxx.cpython-38-x86_64-linux-gnu.so ->
已经自动把so文件拷贝到原py文件目录,但是名称没改。
python compile.py build_ext --inplace
1)直接在终端运行:
python -c "from demo.demo_xxxx import main;main()"
2)使用python文件运行:
run.py
from demo_xxxx import main
main()
python demo/run.py
错误1:
ImportError: dynamic module does not define module export function (PyInit_demo_xxxx)
原因:so文件的文件名跟py文件名不一致
错误2:
SyntaxError: Non-UTF-8 code starting with '\xcd' in file demo/demo_xxxx.so on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
原因:使用了命令 python demo/demo_xxxx.so 来运行so文件会导致该错误,应选择正确的运行方式。
2024.01.11补充:
错误3:
其实也不算Cython错误,顺便记录下。如果保护的python文件中有用到多进程方法,则在启动该python文件方法时,需要在main()方法内启动。
错误包含信息:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
在main方法下启动:
if __name__ == '__main__':
方法名()