Python/ target=_blank class=infotextkey>Python是一种解释型语言,但是与JAVAscript这种纯脚本语言不同,python提供了一种编译成字节码运行的方法,编译之后就得到pyc文件,这点和java编译成class文件再用jvm解释运行很类似,但是与java不同的是,python编译字节码不是一个强制的操作,事实上,编译是一个自动的过程,一般不会在意它的存在。
编译成字节码可以节省加载模块的时间,提高效率。除了效率之外,字节码的形式也增加了反向工程的难度,可以保护源代码。这个只是一定程度上的保护,反编译还是可以的。
我们编写两个py脚本
mylib.py:包含一个函数,打印一行文字.
def keyFun():
print("keyFun is running")
main.py:程序运行入口,调用mylib种的keyFun函数
from mylib import keyFun
if __name__ == "__main__":
keyFun()
编译所有文件,在脚本目录执行以下命令:
python -m compileall .
可以看到生成了相对应的两个pyc文件
编译生成pyc文件
此时执行main.cpython-38.pyc会提示找不到mylib模块,需要将文件名中的.cpython-38删掉.
执行pyc文件
反编译pyc的工具很多,我用的是python3.8,这里介绍几种可以反编译python3.8的工具.
本文作为演示,使用在线网站反编译mylib.py,可以看到下图反编译代码与实际代码一模一样.
反编译pyc结果
我们使用pyinstaller将main.py打包成exe文件
pyinstaller -F main.py
pyinstaller打包exe并执行
反编译pyinstaller打包的exe需要用到pyinstxtractor(
https://github.com/extremecoders-re/pyinstxtractor).
将main.exe复制到pyinstxtractor文件夹,执行python pyinstxtractor.py main.exe
python pyinstxtractor.py main.exe
反编译exe
可以看到pyinstxtractor已经提示入口文件为main.pyc.我们反编译main.pyc就可以看到pyc引入可哪些模块,这个例子可以从反编译代码中看到引入了mylib模块,再接着反编译mylib.pyc就可以了.
从上面的反编译pyc文件结果可以看出,pyc很容易就被反编译,无法保护我们的代码.这里我们介绍使用Cython将python文件编译成pyd文件的方法.
首先安装Cython(Anaconda自带Cython的话不需要安装)
pip install Cython
在mylib.py所在目录新建build_pyd.py文件
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize([
"mylib.py"
]),
)
#1.执行 python build_pyd.py build_ext --inplace
#2.再把.cp38-win_amd64删掉 python renamepyd_file.py
执行python build_pyd.py build_ext --inplace.将会为mylib.py生成对应的.c文件和.pyd文件
Cython生成pyd文件
与上文提到的pyc文件无法直接执行一样,pyd文件也需要删除文件名中的.cp38-win_amd64.这样main.py才能找到对应的mylib.pyd.
import os
lists = os.listdir("./")
for item in lists:
try:
if ".cp38-win_amd64.pyd" in item:
# 重命名文件
fileName = item.replace("cp38-win_amd64.", "")
files = os.rename(item, fileName)
except Exception as e:
print(e)
执行main.py,此时main.py引用的是编译后的mylib.pyd.如果修改了mylib.py中的代码,需要删除pyd文件后调试,不然不会看到改动后的效果.
使用上文中的方法将python文件编译为pyd文件后,再用pynstaller打包,这时候我们反编译就只能看到pyd文件了,要想破解pyd文件就需要使用汇编级别的破解技术,如果你的代码需要别人这样去破解的话,那恭喜你了,哈哈.
需要注意的是,编译为pyd再用pyinstaller打包,可能会出现模块无法被打包进去的情况,这时候需要编辑spec文件,将mylib模块添加到hiddenimports中.
a = Analysis(['main.py'],
pathex=['E:\playground\decompiletest'],
binaries=[],
datas=[],
//这里引入mylib模块
hiddenimports=['mylib'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)