PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译
因为pyinstaller方便、兼容性相对较好,所以我们会经常见到一些用pyinstaller打包的Python程序,在这里我们了解一下如何对这些打包好的exe文件进行反编译(即反编译出.py文件),也可以通过对抗去改进我们程序的保护措施。
0.前提准备
确认程序
我们首先要确认一个exe程序他是不是由pyinstaller编译的,如果不是那就没必要再往下操作了。
一般我们可以看程序图标,比较标志性的两个
上方图标表示不带命令行(即pyinstaller -w)
下方图标表示带命令行
也可以对其以压缩包形式打开,有一些pyinstaller打包的软件内部呈现这个结构
当然了这些都是一般情况,如果不确定试试也无妨。有的软件还会加壳(比如常见的upx壳),我们需要先对其脱壳才能继续反编译,限于篇幅查壳、脱壳等这里不细说,默认大家需要反编译的软件是未加壳/已脱壳的。
用到的工具
pyinstxtractor.py ---- 将.exe解包
我转储了一份到我的对象储存里,给无法连接github的朋友下载
https://cos.lionugo.com/speedhx/python/pyinstxtractor.py
https://cos.lionugo.com/speedhx/python/pyinstxtractor.py
也可以在github下载
python-exe-unpacker/pyinstxtractor.py at master · countercept/python-exe-unpacker · GitHub
A helper script for unpacking and decompiling EXEs compiled from python code. - python-exe-unpacker/pyinstxtractor.py at master · countercept/python-exe-unpacker
https://github.com/countercept/python-exe-unpacker/blob/master/pyinstxtractor.py
uncompyle6 ---- .pyc转.py
pip install uncompyle6
Hex Editor Neo ---- 16进制编辑(用其他也可以)
大家可以在网上找找破解版的,出于版权保护我这里放上官网链接(155 CNY)
Hex Editor Neo: Download Fastest Hexadecimal Viewer/Editor
Download Hex Editor Neo: fast offline hexadecimal viewer and editor software application for Windows
https://www.hhdsoftware.com/hex-editor
1.exe解包
将上面准备的 pyinstxtractor.py 和exe文件(例如叫demo.exe)放到目录下
python pyinstxtractor.py demo.exe
这里我们建议使用,与打包这个文件一致的Python版本。也就是比如用python3.9打包的,我们最好也用python3.9来执行 pyinstxtractor.py (实际上不会有大问题,不过遇到问题可以尝试,命令行中会提示我们打包时所用的Python版本)
不出意外的话,我们可以在目录下见到一个,形如 demo.exe_extracted 的文件夹,里面的目录结构大概是
我们只关心两部分文件 PYZ-00.pyz_extracted 和该目录下没有后缀名的文件,我们下面细说
2.pyc文件转.py
打开 PYZ-00.pyz_extracted 文件夹,这里分两种情况:
如果目录下的文件名都是 .pyc.encrypted ,说明经过了加密(pyinstaller --key),当然了我们也是可以解密的,解密方法我放在下一篇文章里详细讲述
如果都是.pyc,那说明没有加密,我们可以使用 uncompyle6 将其反编译成.py文件(也就是源码)
以反编译 Client.pyc 文件为例,我们在该目录下启动命令行,然后键入
uncompyle6 -o Client.py Client.pyc
不出意外可以看到目录下已经有一个 Client.py 文件,里面就是源代码,变量和方法名都不会变,说它是源代码没有问题。
可是这个目录下有那么多的文件(绝大多数是调的库),我们怎么知道哪些是关键代码呢?这里有2种方法:
1、暴力破解,先按照经验剔除掉一些文件,然后每个都反编译一个个检查。
2、先找到入口方法,然后通过检查其导入的库一步步找齐所有我们需要的文件。值得注意的是,程序入口不在这个文件夹里面。
3.反编译程序入口
返回到上级目录,我们可以在里面看到这种,没有后缀名的文件:
其中就有我们寻找的入口方法,当然我们不用每个都反编译一次,按照经验找到你觉得最有可能的那个文件(如main、client),我们就来处理这个文件。
首先我们可以把这个文件直接重命名,给他后缀加上.pyc,然后用上面的方法(uncompyle6)对它进行反编译,小概率可以直接反编译成功。大概率会遇到错误,形如:
ImportError: Unknown magic number 227 in main_2016.pyc
这是因为它缺少了一段 magic head,我们需要手动将其补齐,就可以反编译了。
打开我们的 Hex Editor Neo,或者是其他十六进制编辑器也可以。用它打开我们目录下的2个文件,struct 和我们刚刚选的文件(下文以 main_2016 来举例,并且我已经提前将它的后缀名补上.pyc)。
对比这两个文件,我们可以发现 struct 文件中e3 00 00 00前面还有一段33 0d 0d 0a 70 79 69 30,这就是我们要补充上去的内容。有的教程说补8个字节,也有说补4个的,但最好根据实际情况来处理。
我们复制struct文件中多出来的这一串,粘贴到 main_2016.pyc 的开头,如果你在使用Hex Editor Neo,要先打开右上方的插入模式
粘贴过去之后,它是这个样子
保存,然后按照上面的方法用 uncompyle6 反编译
uncompyle6 -o main_2016.py main_2016.pyc
就可以看到源代码了
同时我们也看到了它import了 client2016 这个库,我们回到上一步的 PYZ-00.pyz_extracted 文件夹,不出意外你会找到 client2016.pyc ,它就是关键代码之一,将它反编译之后根据import的内容再慢慢凑齐所有关键代码。
至此,本文就结束了,关于破解被加密过的 .pyc.encrypted 文件的方法我放在下一篇中详细讲述。文章中如有不清晰、错误的地方发现后我会尽快更改,不足之处还望指正。
本文撰写时部分参考、引用了下列文章,深表感谢。
环境:
win7+python3.5(anaconda3)
理论上,win7及以上的系统和python任意版本均可。
一、基础脚本
首先我们构建一个简单的脚本,比如输出一串数字、文本之类,这里我们输出一串文字的同时计算一下3次方好了。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# -*- coding: utf-8 -*-"""Created on Wed Aug 29 09:18:13 2018@author: Li Zeng hai"""def test(num): print('num={n}, {n}^3={n2}'.format(n=num, n2=num**3)) if __name__ == '__main__': while 1: try: num = input('提示:输入"q"退出程序。\n请输入一个数字:') if num.lower() == 'q': break num = float(num) print(num) except: print('输入的数字不正确!') continue test(num) |
把这段脚本保存为mylib.py,运行一下这个脚本:

可以看到,脚本正常运行了。
为了演示,我们把下面的脚本新建到main.py脚本文件中,并且从mylib中引入测试函数。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# -*- coding: utf-8 -*-from mylib import * while 1: try: num = input('提示:输入"q"退出程序。\n请输入一个数字:') if num.lower() == 'q': break num = float(num) print(num) except: print('输入的数字不正确!') continue test(num) |
二、使用PyInstaller打包为exe
使用如下命令,将其打包为单一exe(去掉-F则不是单一exe)
|
1
|
pyinstaller -F main.py |
打包过程如下:
E:\t>pyinstaller -F main.py
505 INFO: PyInstaller: 3.3.1
505 INFO: Python: 3.5.5
505 INFO: Platform: Windows-7-6.1.7601-SP1
505 INFO: wrote E:\t\main.spec
505 INFO: UPX is not available.
505 INFO: Extending PYTHONPATH with paths
['E:\\t', 'E:\\t']
505 INFO: checking Analysis
505 INFO: Building Analysis because out00-Analysis.toc is non existent
505 INFO: Initializing module dependency graph...
521 INFO: Initializing module graph hooks...
521 INFO: Analyzing base_library.zip ...
6269 INFO: running Analysis out00-Analysis.toc
6269 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
required by d:\anaconda3\python.exe
6956 INFO: Caching module hooks...
6956 INFO: Analyzing E:\t\main.py
6956 INFO: Loading module hooks...
6956 INFO: Loading module hook "hook-pydoc.py"...
6956 INFO: Loading module hook "hook-xml.py"...
7283 INFO: Loading module hook "hook-encodings.py"...
7533 INFO: Looking for ctypes DLLs
7549 INFO: Analyzing run-time hooks ...
7549 INFO: Looking for dynamic libraries
7720 INFO: Looking for eggs
7720 INFO: Using Python library d:\anaconda3\python35.dll
7720 INFO: Found binding redirects:
[]
7720 INFO: Warnings written to E:\t\build\main\warnmain.txt
7751 INFO: Graph cross-reference written to E:\t\build\main\xref-main.html
7767 INFO: checking PYZ
7767 INFO: Building PYZ because out00-PYZ.toc is non existent
7767 INFO: Building PYZ (ZlibArchive) E:\t\build\main\out00-PYZ.pyz
8345 INFO: Building PYZ (ZlibArchive) E:\t\build\main\out00-PYZ.pyz completed successfully.
8345 INFO: checking PKG
8345 INFO: Building PKG because out00-PKG.toc is non existent
8345 INFO: Building PKG (CArchive) out00-PKG.pkg
9954 INFO: Building PKG (CArchive) out00-PKG.pkg completed successfully.
9954 INFO: Bootloader d:\anaconda3\lib\site-packages\PyInstaller\bootloader\Windows-64bit\run.exe
9954 INFO: checking EXE
9954 INFO: Building EXE because out00-EXE.toc is non existent
9954 INFO: Building EXE from out00-EXE.toc
9954 INFO: Appending archive to EXE E:\t\dist\main.exe
9954 INFO: Building EXE from out00-EXE.toc completed successfully.
E:\t>

最终在目录下生成build、disk文件夹。其中exe文件在disk中。运行正常。如下图:

三、反编译Pyinstaller打包的exe
这样的exe已经可以被迁移到别的电脑上使用了,如果是为了方便其他电脑使用,到这一步就已经结束了。但有时候,我们可能是把这个功能给很多人用,但如果不想开源或者被别人破解,到这一步是还不够的。
因为到这一步的exe是可以被别人反编译出源码的。
此处用到的工具:
exe反编译工具:pyinstxtractor.py:点击此处去下载
pyc反编译工具:Easy Python Decompiler 或者在线反编译pyc。
将pyinstxtractor.py放到exe文件相同目录,执行以下cmd命令:
|
1
|
python pyinstxtractor.py main.exe |
如果成功执行,将在同目录下生成新的反编译文件夹——main.exe_extracted,如下图:


同时,我们可以在下图圈起来的路径下,找到当时我们exe引入的mylib模块,其为pyc格式。

pyc格式的文件,反编译起来就非常简单了。用前文提供的工具或者网上找个在线的分分钟反编译出来,我们看下反编译的结果:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#!/usr/bin/env python# visit http://tool.lu/pyc/ for more information'''Created on Wed Aug 29 09:18:13 2018@author: Li Zeng hai''' def test(num): print('num={n}, {n}^3={n2}'.format(n=num, n2=num ** 3)) if __name__ == '__main__': while None: try: num = input( '\xe6\x8f\x90\xe7\xa4\xba\xef\xbc\x9a\xe8\xbe\x93\xe5\x85\xa5"q"\xe9\x80\x80\xe5\x87\xba\xe7\xa8\x8b\xe5\xba\x8f\xe3\x80\x82\n\xe8\xaf\xb7\xe8\xbe\x93\xe5\x85\xa5\xe4\xb8\x80\xe4\xb8\xaa\xe6\x95\xb0\xe5\xad\x97\xef\xbc\x9a') if num.lower() == 'q': break num = float(num) print(num) except: None None None print( '\xe8\xbe\x93\xe5\x85\xa5\xe7\x9a\x84\xe6\x95\xb0\xe5\xad\x97\xe4\xb8\x8d\xe6\xad\xa3\xe7\xa1\xae\xef\xbc\x81') continue |
可以看到,基本上完美的把源码反编译出来了。其中涉及到中文的地方,会因为编码问题有所改变。但是非中文部分,几乎一模一样。
就问你怕不怕!!!
四、将脚本编译为pyd以防止反编译
好怕怕 ,哈哈。
如何解决呢,可以考虑将模块py文件编译为动态链接库,这样破解难度将大大增加。其中,在python里,pyd格式即动态链接库。使用cython即可编译,如果是anaconda是自带,python有的可能不带cython,安装即可:
|
1
|
pip install Cython |
我们在main.py所在的文件夹新建py文件,在此处我命名为build_pyd.py,其内容如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# -*- coding: utf-8 -*-"""Created on Wed Aug 29 13:33:20 2018@author: Li Zeng hai""" from distutils.core import setupfrom Cython.Build import cythonize setup( name = 'any words.....', ext_modules = cythonize(["mylib.py",] ),) |
然后执行以下cmd命令:
|
1
|
python build_pyd.py build_ext --inplace |
运行过程及生成结果如下图,其中红框的pyd文件即编译好了。因为我是64位的系统和python,所以会生成amd64后缀,我们把这个删掉重命名为mylib.pyd即可。
注:当同时存在mylib.pyd和mylib.py时,引入优先级是pyd>py,所以不用移除py文件,默认引入时就是pyd。

此时,我们删除build、disk文件夹,重复步骤二,再次编译为exe即可。
注意:编译需要相关的VC环境,因为python3.5是基于 VS14版本的,所以我这里安装的也是。不安装是无法编译的。
可以验证一下:
再次反编译main.exe后,原来的路径E:\t\dist\main.exe_extracted\out00-PYZ.pyz_extracted之下,已经找不到mylib.pyc了。
因为他已经不是可以直接反编译出来的文件了。
那么他在哪呢,他是作为pyd存在于上层目录中了。如下图:

pyd的反编译难度是相当高的,至此,就大功告成了!
到此这篇关于PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译的文章就介绍到这了,更多相关PyInstaller Python文件打包为exe内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
微信公众号搜索 “ 脚本之家 ” ,选择关注
原文链接:https://blog.csdn.net/ZhaDeNianQu/article/details/87717293
https://sourceforge.net/projects/easypythondecompiler/
http://tools.bugscaner.com/decompyle/
https://baijiahao.baidu.com/s?id=1730523797478910448&wfr=spider&for=pc
如何将Python打包后的exe还原成.py文件?
用到的工具
-
pyinstxtractor.py 拆包(解压)工具,将exe文件解压成一个文件夹
-
uncompyle6 pyc反编译工具
-
010EditorEditor 或者其他二进制查看与修改工具,我这里用的010Editor
安装方法
pip install uncompyle6
第一步:解包
python3 pyinstxtractor.py ***.exe # 这里替换成你要反编译的exe文件
# 会生成一个以 exe文件名+_extracted 的文件夹,这个就是解包后的数据
第二步:添加头信息
PyInstaller打包后,pyc文件的前8个字节会被抹掉,所以最后要自己添加回去。前四个字节为python编译的版本,后四个字节为时间戳。想要获得编译版本可以查看打包文件里struct的信息
1). 进入文件夹,找到以exe文件名命名的文件(没有后缀),这个就是目的文件
2). 用 010Editor 打开 struct,前八位就是我们想要的信息,将其复制
3). 用 010Editor 打开目的文件我这里是 abc_text,将上一步复制的信息插入到开头
修改前:
修改后:
4). 将目的文件我这里是 abc_text,添加pyc的后缀
第三步:逆向目的文件.pyc
1). 其实这里已经可以使用了。了解python的都知道pyc是py文件编译后的二进制文件,因此如果想要分析源码还得继续逆向成.py文件
2). uncompyle6逆向pyc文件
uncompyle6 abc_text.pyc > abc_text.py



浙公网安备 33010602011771号