背景
Python 是一种解释型语言,没有编译过程,发布程序的同时就相当于公开了源码,这也是其作为开源语言的一个特性。但在某些场景下,我们的源码是不想被别人看到的,例如开发商业软件。
加密方式
参考链接:
如何给Python代码进行加密
Python 源码混淆与加密
1、代码混淆
2、发行.pyc 文件
3、使用 py2exe、pyinstaller打包为exe文件
4、使用PyArmor加密脚本
5、使用pyconcrete加密为pye文件
6、使用 Cython将py文件生成为pyd加密文件
编译与反编译过程
本文采用第6种方法,即使用 Cython将py文件生成为pyd加密文件.
正常情况下:
py文件编译为exe文件过程
py—>(pyinstaller包)---->exe文件
反编译过程
exe文件—>(pyinstxtractor包)—>pyc文件—>(uncompyle6包)---->py源码
加密情况下
py文件编译为exe文件过程
py—>(Cypthon)—>c文件和pyd文件–>(pyinstaller包)---->exe文件
中间生成的pyd文件是难以反编译的
本文用到的工具
python 3.8.5
pycharm
visual studio 2022(需要C编译器)
pyinstaller 5.1
Cython 0.29.30
pyinstxtractor.py https://sourceforge.net/projects/pyinstallerextractor/
python3打包为exe文件
这里有个Hello.py文件
Hello.py文件内容如下:
from tkinter import *
# 这是一段TK测试代码
class ChangeLabelDemo:
def __init__(self):
window = Tk()
window.title = "改变labeldemo" #窗口标题
frame1 = Frame(window)
frame1.pack()
self.lb1 = Label(frame1, text="Programming is fun")
self.lb1.pack()
frame2 = Frame(window)
frame2.pack()
label = Label(frame2, text="输入")
self.msg = StringVar()
entry = Entry(frame2, textvariable=self.msg)
btChangeText = Button(frame2, text="改变text", command=self.processButton)
self.v1 = StringVar()
rbRed = Radiobutton(frame2, text="Red", bg="red", variable=self.v1, value="R", command=self.processRadiobutton)
rbYellow = Radiobutton(frame2, text="Yellow", bg="yellow", variable=self.v1, value="Y",
command=self.processRadiobutton)
label.grid(row=1, column=1)
entry.grid(row=1, column=2)
btChangeText.grid(row=1, column=3)
rbRed.grid(row=1, column=4)
rbYellow.grid(row=1, column=5)
window.mainloop()
def processButton(self):
self.lb1["text"] = self.msg.get()
def processRadiobutton(self):
if self.v1.get() == "R":
self.lb1["fg"] = "red"
elif self.v1.get() == "Y":
self.lb1["fg"] = "yellow"
ChangeLabelDemo()
step1:安装pyinstaller包
pip install pyinstaller
step2:在cmd中进入当前文件路径下
cd E:\研究生\pytorch\untitled\encypt
step3:打包生成exe文件,使用如下命令,将其打包为单一exe(去掉-F则不是单一exe,-w是不生成window窗口)
pyinstaller -F -w Hello.py
生成了build,dist 和Hello.spec三个文件
exe文件在dist文件夹中,双击即可运行
python3将exe文件进行反编译为源码
exe反编译工具:pyinstxtractor.py下载:https://sourceforge.net/projects/pyinstallerextractor/
将pyinstxtractor.py放到exe文件相同目录
执行以下cmd命令,首先进入到dist文件夹下
cd dist
接下来,在cmd中执行下面命令
python pyinstxtractor.py Hello.exe
成功解压(反编译),多了个Hello.exe_extracted
文件夹
进入Hello.exe_extracted
文件夹,有个Hello文件,此为被解压出的pyc文件,需通过
接下来尝试对Hello
文件反编译
首先安装反编译uncompyle6
包
pip install uncompyle6
接下来进入exe所在文件夹
cd Hello.exe_extracted
需将Hello
文件添加.pyc
后缀,不然不能编译。运行下面代码反编译Hello.pyc
文件
uncompyle6 Hello.pyc
结果报错
Unknown magic number 227 in Hello.pyc
报错原因:
提示是Unknown magic number 227
,这个失败因为pyinstaller
工具打包的时候,会将代码文件的magic number
(python的版本及编译时间)给清除掉,所以反编译时候需要将magic numbe
r添加回去才能识别,magic number
我们可以通过解压主目录下的struct
文件中提取出来(一般是前16个字节,可以对比打包前的源文件),将struct
文件体中的前16个字节提取出来,然后在添加到文件中,然后再执行uncompyle6
反编译。
报错解决方法:Python3 如何反编译EXE
我们先看看struct
文件的内容,注意看第一行,这个就是缺的信息。第二行是以E3开始的。
接下来看Hello
文件的内容(不是pyc文件,所以得把pyc后缀去掉,然后,用vs打开,pycharm和记事本打开会乱码),可以看出是以E3
为起始,相对struct
文件少一第一行。
接下来要做的就是把缺失的magic number
添加到pyc文件中
在pycharm中运行以下代码
# import py_compile
# import Hello
# Compile.compile('Hello.py')
# import Hello
# Hello()
struct_path = r"E:\研究生\pytorch\untitled\encypt\dist\Hello.exe_extracted/struct"
fn = open(struct_path, 'rb')
print(fn.seek(0)) # 切换指针位置到开头
magic_number = fn.read(16) # 读取文件的前16个字节,从0开始
print(magic_number)
point_number = fn.tell() # 获取当前指针位置
print(point_number) # 获取当前指针位置
print(fn.read(1)) # 再读取第17个字节,输出值是e3
add_magic_num_file_name = r"E:\研究生\pytorch\untitled\encypt\dist\Hello.exe_extracted/Hello.pyc"
MAGIC_NUMBER = b'U\r\r\n\x00\x00\x00\x00\xac\x95\x15_\x01\x01\x00\x00'
f = open(add_magic_num_file_name, 'rb')
new_content = f.read()
new_add_magic_number_file_name = r"E:\研究生\pytorch\untitled\encypt\dist\Hello.exe_extracted/Hello.pyc"
n_f = open(new_add_magic_number_file_name, "wb")
n_f.write(MAGIC_NUMBER + new_content)
f.close()
n_f.close()
output>>>
0
b'U\r\r\n\x00\x00\x00\x00\xac\x95\x15_\x01\x01\x00\x00'
16
b'\xe3'
此时再用vs打开Hello
文件。完成,现在可以用uncompyle6
反编译了
在cmd中运行下面代码
uncompyle6 Hello.pyc
可见exe源代码被反编译出来,因此如果是公司开发的产品,其安全性有待考虑。
注:
1、如果注释存在中文,则会被解析成Unicode编码,可以用相应的工具转换即可
2、反编译的Hello
是主程序文件,其实还需要将PYZ-00.pyz_extracted
文件夹下的所有pyc文件都通过uncompyle6
进行反编译。
3、PYZ-00.pyz_extracted目录下的依赖库的pyc文件缺少的字节数与主程序不同,注意下PYZ-00.pyz_extracted目录下的依赖库的pyc文件缺少的字节数与主程序不同。
4、依赖库pyc文件批量反编译教程见这里
将py文件编译为pyd文件以防止反编译
将py文件编译为动态链接库,这样破解难度将大大增加。其中,在python里,pyd格式即动态链接库。使用cython即可编译为pyd文件。
首先安装cpython
pip install Cython
在Hello.py
文件创建新的.py文件,并命名为setup.py
,其内容为:
# -*- coding: utf-8 -*-
"""
Created on Friday July 1 16:40:20 2022
@author: Li Zhengping
link:
https://www.jb51.net/article/178209.html
https://blog.csdn.net/ZhaDeNianQu/article/details/87717293
"""
from distutils.core import setup
from Cython.Build import cythonize
setup(name = 'any words.....', ext_modules = cythonize(["Hello.py",]))
然后在cmd中执行以下命令
cd E:\研究生\pytorch\untitled\encypt
python setup.py build_ext --inplace
注意:编译需要相关的VC环境,我是安装 VS22版本的,不安装是无法编译的。
运行过程及生成结果如下图,其中红框的pyd文件即编译好了。因为我是64位的系统和python,所以会生成amd64后缀,我们把这个删掉重命名为Hello.pyd
即可。
注:当同时存在mylib.pyd和mylib.py时,引入优先级是pyd>py,所以不用移除py文件,默认引入时就是pyd。
此时,我们删除build文件夹
再次编译为exe即可。
cmd中运行下面代码
pyinstaller -F -w Hello.py
编译之后的文件
同样的在dist文件夹下存放exe文件
可以验证一下:再次反编译main.exe后
首先进入dist路径下
cd dist
注意要在exe同路径文件中放入pyinstxtractor.py文件
接下来运行
python pyinstxtractor.py Hello.exe
其结果是依然为Hello.pyc文件,而不是Hello.pyd文件,也就是打包的是Hello.py文件而不是Hello.pyd文件,这样操作依然能被反编译。
接下来搜了一下如何打包pyd文件,找到了如下博客:Python .py生成.pyd文件并打包.exe注意事项
首先需要创建一个main.py文件,其作为一个入口程序来导入pyd文件,pyd文件默认优先级高于py文件,因此Hello.py和Hello.pyd可以同时存在,也就是说Hello.py不需要删除。
main.py
内容如下
from tkinter import *
import Hello
if __name__ == '__main__':
Hello.ChangeLabelDemo()
注意:
1、程序的__main__
入口只能有一个,如果源py文件中有定义main入口,需要注释掉并调整代码缩进,否则通过main.py调用pyd文件遇到if name == ‘main’
:之后的代码都不会运行。
2、源文件Hello.py
文件头部import到的第三方库需全部复制到main.py
文件头部,不然运行会闪退
接下来在cmd输入
pyinstaller -F -w main.py
接下来我们用pyinstxtractor.py
解包main.exe
文件验证一下,命令行依次输入(记得先把pyinstxtractor.py复制到当前文件夹下):
cd dist
python pyinstxtractor.py main.exe
会得到一个main.exe_extracted
文件夹,在文件夹下发现文件Hello.pyd
,而不是Hello.py
文件,说明通过引用pyd文件打包成功。pyd文件是难以反编译的,因此源码可以得到很好的保护。
大工告成
参考链接
通过pyinstaller打包为exe:
1、pyinstxtractor.py 的改进 - 反编译pyinstaller生成exe的工具
2、pyinstaller打包pyqt5编写的项目为exe(脱离环境可运行)
3、对python生成的EXE文件 进行反编译
将py文件生成为pyd文件进行反编译:
4、PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译
5、PyInstaller封装程序反编译 PyInstxtractor + Uncompale
将pyd文件打包成exe文件:
6、Python .py生成.pyd文件并打包.exe注意事项
欢迎关注我的公众号《故障诊断与python学习》