因为python是作为一种动态语言,所以如果要将python代码进行加密其实是一件很难的事情。但听说2018年python官方还是公布了一些针对现有python代码比较好的加密方式,本篇对此作出一些简单的总结。
简单来说,pyc文件就是Python的字节码文件,我们都知道Python是一种全平台的解释性语言,全平台其实就是Python文件在经过解释器解释之后(或者称为编译)生成的pyc文件可以在多个平台下运行,这样同样也可以隐藏源代码。
pyc文件只有在文件被当成模块导入时才会生成。也就是说,Python解释器认为,只有import进行的模块才需要被重用。 生成pyc文件的好处显而易见,当我们多次运行程序时,不需要重新对该模块进行重新的解释。另外其实当我们运行程序的时候就自动生成了pyc文件,下面就来详细介绍。
当我们之前有启动项目成功时,python自动就会创建在每一级有调用到文件的目录一个叫_pycache_的文件夹当成缓存,那么之后启动的速度会极大的提升。
但自动生成的缓存文件夹中,我们需要运用Linux命令 mv * ../
将编译的pyc文件弄到上一级,然后用rename批量改名,并且每一个文件目录都需要运行相同的命令,这样感觉太过于麻烦,所以这里就需要引入其它方法了。
利用compileall和py_compile来预编译python代码:
这两个从某种意义上是互通的,一般anaconda预装了这两个东西,原装python的话不太清楚,所以我们可以直接用,语法格式为:
python -m py_compile file.py #把单个.py文件编译为字节码文件
python -m py_compile /path/to/src/ #批量生成字节码文件,/path/to/src/是包含.py文件名的路径
python -m compileall file.py #把单个.py文件编译为字节码文件
python -m compileall /path/to/src/ #批量生成字节码文件,/path/to/src/是包含.py文件名的路径
上面的py_compile针对文件夹是会有一些问题,但理论上这种语法应该是可以的。我们用compileall对一个还未运行过的项目进行compile,当跑完程序后,会在所有可以运行的文件下自动生成编译pyc文件:
但Windows比较坑的是,当我在上面运行完命令后,发现在我项目文件夹下多了一个_pycache_文件夹,这就和上面的一样了,然后我回到Linux服务器跑,则不会出现这种问题。所以建议最好在Linux环境下编译,Windows感觉有点莫名其妙,我猜它是直接去把代码运行了一遍,提前生成了pyc文件,目录格式为:
|---test.py
|---test2.py
|---test3.py
|---__pycache__
|----test.cpython-36.pyc
|----test2.cpython-36.pyc
|----test3.cpython-36.pyc
然后我们还可以写成一个脚本对项目文件进行编译加密:
# 批量编译py文件
import compileall
compileall.compile_dir("/usr/to/src")
sudo find /usr/to/src -name "*.py" | xargs rm -rf # 删除该目录下所有的py文件
对于compileall更详细的参数以及命令解析可以看下面的链接:https://docs.python.org/3/library/compileall.html
与上面的编译类似,我们可以写一个脚本文件,然后对某一个文件进行编译,但在此之前,我们需要先下载Cpython模块,步骤为:
# 前提
pip intall Cpython
# 脚本文件
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = 'Hello world app',
ext_modules = cythonize("test.py"),
)
然后我们就退回到目录下运行命令就会生成pyd文件:
python setup.py build_ext --inplace
另外这个是基于Windows环境下编译成pyd文件,如果是linux,那么就是so文件了。
而pyo文件其实更简单,就是上面pyc命令的改版:
python -O -m py_compile file.py
python -O -m py_compile /path/to/src/
python -O -m compileall file.py
python -O -m compileall /path/to/src/
或者
python -OO -m py_compile file.py
python -OO -m py_compile /path/to/src/
python -OO -m compileall file.py
python -OO -m compileall /path/to/src/
这里可以推荐一个在线网站:https://tool.lu/pyc/ ,当我们提交一个pyc文件时,它会自动反编译出一个py文件出来:
当然,不太清楚这个网站到底是怎样进行反编译的,不过确实有效,另外就是另一个python的包了:uncompyle
我们可以pip安装:
pip install uncompyle
安装成功后help一下,如果有下述命令与信息那么就表明安装成功。
Usage:
uncompyle6 [OPTIONS]... [ FILE | DIR]...
uncompyle6 [--help | -h | --V | --version]
Examples:
uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library
Options:
-o <path> output decompiled files to this path:
if multiple input files are decompiled, the common prefix
is stripped from these names and the remainder appended to
<path>
uncompyle6 -o /tmp bla/fasel.pyc bla/foo.pyc
-> /tmp/fasel.pyc_dis, /tmp/foo.pyc_dis
uncompyle6 -o /tmp bla/fasel.pyc bar/foo.pyc
-> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
uncompyle6 -o /tmp /usr/lib/python1.5
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
--compile | -c <python-file>
attempts a decompilation after compiling <python-file>
-d print timestamps
-p <integer> use <integer> number of processes
-r recurse directories looking for .pyc and .pyo files
--fragments use fragments deparser
--verify compare generated source with input byte-code
--verify-run compile generated source, run it and check exit code
--weak-verify compile generated source
--linemaps generated line number correspondencies between byte-code
and generated source output
--encoding <encoding>
use <encoding> in generated source according to pep-0263
--help show this message
Debugging Options:
--asm | -a include byte-code (disables --verify)
--grammar | -g show matching grammar
--tree | -t include syntax tree (disables --verify)
--tree++ add template rules to --tree when possible
Extensions of generated files:
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
+ '_unverified' successfully decompile but --verify failed
+ '_failed' decompile failed (contact author for enhancement)
安装好后的可执行文件名叫uncompyle6,根据上面的提示,我们需要输入uncompyle6 models.pyc > models.py
将models.pyc反编译成py文件
uncompile -o . *.pyc
将当前文件夹中所有的pyc文件反编译成后缀名为.pyc_dis的源文件
反编译后的效果可以说很理想,如果你的代码格式符合PEP8规范的要求,那就基本和源来的文件一样,不过各种注释就没有了。
最后改的代码还没有运行(没有生成pyc)的,就真的丢了,不过不多,再写一遍吧!
其实不仅仅只有这一种反编译的uncompyle,还有很多比如说uncompyle2, decompyle2, DePython, unpyc, uncompyle, pycdc,我们都可以进行相关的破解,它们相对应的pyo、pyd文件的破解形式,我们还可以对这些进行二次加密,这里限于篇幅与使用情况,我就不一一叙述了。
网上加密是有很多地方的,不同于转成pyc的二进制加密,这种多了一些模糊层,比如说下面这个网站,注释基本也看不出端倪,如果文件不多的话可以看看。
这种看起来是二进制的加密方式,但我将一个文件在这个网站编译后,再用上面的uncompile,发现并没有什么太大的作用,但这样我就不知道是基于什么情况进行的加密的,破解难度会稍微大一点。
这里是看到知乎的一篇文章提到了改变python编译器的方式,因为主流是Cpython,所以可以针对某些地方进行修改,达到代码在运行的同时就达到了加密的效果,并且这里用的是RSA加密,算是目前最流行也是比较难破解的几种加密方式之一。
上述两图的详细地址为如何保护你的 Python 代码 (二)—— 定制 Python 解释器
这篇文章写得有点艰辛,可能整体的流程安排有些乱,主要是最近感冒影响了博文进度,导致中途断断续续,不过自己正在恢复,另外,可能里面还有些小错误,后期会修改,希望看完本篇博文或多或少有所收获。
参考与推荐: