一般情况下,写完的代码如果需要拿出去用,是不希望给对方源代码的,这个大家应该都懂,我们一般是给对方提供一个可执行文件或者便于调用的SO库等,那么今天我们来使用PyInstaller
进行打包,看一下效果。
说一下系统环境: Ubuntu 18.04
打包代码
先看一段利用numpy
进行矩阵乘法计算为例:
1 | #!/bin/python3 |
现在我们开始安装PyInstaller
, 关于PyInstaller
的选项含义可以去官方文档查看:
1 | pip install Pyinstaller |
这里我选择的是onefile
模式:
1 | pyinstaller --onefile calc.py |
可以看到在输出了一系列信息后,已经打包成功,新增了两个文件夹build
和dist
,我们需要的可执行文件就在dist
文件夹中。同时在上面的输出信息中,提示我这里是不支持UPX
的,如果想要可执行文件尽可能小的话,可以进行配置:
https://github.com/upx/upx
为了保证可用,现在将刚生成的可执行文件拷贝到另一台机器(我这里是台裸机),分别用file
和ldd
看一下calc的信息:
1 | file calc |
1 | ldd calc |
验证执行:
1 | chmod +x calc |
可以看到的确是可以成功执行的,calc在执行的时候,会在tmp目录下面创建一个_MEIXXXXX的文件夹,程序结束之后会删掉。
不安全因素
上面的效果看起来极好,就一个可执行文件,不需要安装相应的依赖包等等,现在来一起来看看其不安全在哪儿?PyInstaller
安装以后,提供了一个工具pyi-archive_viewer
,使用此命令可以检查使用PyInstaller
构建的任何存档(PYZ
或PKG
)或任何可执行文件(.exe
文件或ELF
或COFF
二进制文件)的内容:
1 | pyi-archive_viewer dist/calc |
在上面这一系列输出中可以看出环境下安装的一些依赖,比如我安装的numpy
,还有两个极为重要的文件:calc
、PYZ-00.pyz
。这两个东西,跟源代码息息相关。
验证不安全因素
继续操作,通过pyi-archive_viewer
提取calc
可执行文件为pyc
文件:
1 | ? x calc |
现在得到一个calc.pyc
文件,都已经拿到了pyc
文件了,那么如果源代码没经过混淆或者其他处理的话,就直接可以得到一毛一样的源码。
接下来安装超级著名的反编译工具uncompyle6
:
1 | pip install uncompyle6 |
直接执行:
1 | uncompyle6 calc.pyc |
会发现报错了,这是因为在pyc
文件中缺少了相应的magic,怎么获取magic
呢?
Python代码在执行的时候会生成一个__pycache__
目录,里面会有calc.cpython-37.pyc
文件,现在分别用hexdump查看两个文件:
1 | hexdump -C __pycache__/calc.cpython-37.pyc |
1 | hexdump -C calc.pyc |
可以发现,在两个文件的靠前部分的内容基本一致,calc.pyc
与calc.cpython-37.pyc
相比缺了第一行,缺的这一行就是所谓的版本magic
,直接对calc.pyc
进行编辑,将calc.cpython-37.pyc
第一行的magic
加上即可。这里要注意下,Python的不同版本magic
是不同的。
那么再次执行uncompyle6
,就可以获得源码了:
1 | uncompyle6 calc.pyc |
由此可见,想使用PyInstaller
作为隐藏源码的方式还是不安全的,对反编译来说只是稍微有一点点门槛罢了,那么咋提高一些呢,我想是可以结合Cython
去做这件事的。
参考链接
1、https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.matmul.html
2、https://pyinstaller.readthedocs.io/en/stable/usage.html