获得PyInstaller打包exe的py源码
参考链接:https://laucyun.com/33359ed9f725529ac9b606d054c8459d.html
way1:pyi-archive_viewer 提取pyc,uncompyle6反编译pyc得到py
way2:python-exe-unpacker https://github.com/countercept/python-exe-unpacker
way3:PyInstaller Extractor https://sourceforge.net/projects/pyinstallerextractor/
PyInstaller Extractor (修改添加3.7支持)
1 """
2 PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
3 Author : Extreme Coders
4 E-mail : extremecoders(at)hotmail(dot)com
5 Web : https://0xec.blogspot.com
6 Date : 29-November-2017
7 Url : https://sourceforge.net/projects/pyinstallerextractor/
8
9 For any suggestions, leave a comment on
10 https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/
11
12 This script extracts a pyinstaller generated executable file.
13 Pyinstaller installation is not needed. The script has it all.
14
15 For best results, it is recommended to run this script in the
16 same version of python as was used to create the executable.
17 This is just to prevent unmarshalling errors(if any) while
18 extracting the PYZ archive.
19
20 Usage : Just copy this script to the directory where your exe resides
21 and run the script with the exe file name as a parameter
22
23 C:\path\to\exe\>python pyinstxtractor.py <filename>
24 $ /path/to/exe/python pyinstxtractor.py <filename>
25
26 Licensed under GNU General Public License (GPL) v3.
27 You are free to modify this source.
28
29 CHANGELOG
30 ================================================
31
32 Version 1.1 (Jan 28, 2014)
33 -------------------------------------------------
34 - First Release
35 - Supports only pyinstaller 2.0
36
37 Version 1.2 (Sept 12, 2015)
38 -------------------------------------------------
39 - Added support for pyinstaller 2.1 and 3.0 dev
40 - Cleaned up code
41 - Script is now more verbose
42 - Executable extracted within a dedicated sub-directory
43
44 (Support for pyinstaller 3.0 dev is experimental)
45
46 Version 1.3 (Dec 12, 2015)
47 -------------------------------------------------
48 - Added support for pyinstaller 3.0 final
49 - Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
50
51 Version 1.4 (Jan 19, 2016)
52 -------------------------------------------------
53 - Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana)
54
55 Version 1.5 (March 1, 2016)
56 -------------------------------------------------
57 - Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)
58
59 Version 1.6 (Sept 5, 2016)
60 -------------------------------------------------
61 - Added support for pyinstaller 3.2
62 - Extractor will use a random name while extracting unnamed files.
63 - For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.
64
65 Version 1.7 (March 13, 2017)
66 -------------------------------------------------
67 - Made the script compatible with python 2.6 (Thanks to Ross for reporting)
68
69 Version 1.8 (April 28, 2017)
70 -------------------------------------------------
71 - Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
72
73 Version 1.9 (November 29, 2017)
74 -------------------------------------------------
75 - Added support for pyinstaller 3.3
76 - Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)
77
78 """
79
80 from __future__ import print_function
81 import os
82 import struct
83 import marshal
84 import zlib
85 import sys
86 import imp
87 import types
88 from uuid import uuid4 as uniquename
89
90
91 class CTOCEntry:
92 def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):
93 self.position = position
94 self.cmprsdDataSize = cmprsdDataSize
95 self.uncmprsdDataSize = uncmprsdDataSize
96 self.cmprsFlag = cmprsFlag
97 self.typeCmprsData = typeCmprsData
98 self.name = name
99
100
101 class PyInstArchive:
102 PYINST20_COOKIE_SIZE = 24 # For pyinstaller 2.0
103 PYINST21_COOKIE_SIZE = 24 + 64 # For pyinstaller 2.1+
104 MAGIC = b'MEI\014\013\012\013\016' # Magic number which identifies pyinstaller
105
106 def __init__(self, path):
107 self.filePath = path
108
109
110 def open(self):
111 try:
112 self.fPtr = open(self.filePath, 'rb')
113 self.fileSize = os.stat(self.filePath).st_size
114 except:
115 print('[*] Error: Could not open {0}'.format(self.filePath))
116 return False
117 return True
118
119
120 def close(self):
121 try:
122 self.fPtr.close()
123 except:
124 pass
125
126
127 def checkFile(self):
128 print('[*] Processing {0}'.format(self.filePath))
129 # Check if it is a 2.0 archive
130 self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
131 magicFromFile = self.fPtr.read(len(self.MAGIC))
132
133 if magicFromFile == self.MAGIC:
134 self.pyinstVer = 20 # pyinstaller 2.0
135 print('[*] Pyinstaller version: 2.0')
136 return True
137
138 # Check for pyinstaller 2.1+ before bailing out
139 self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
140 magicFromFile = self.fPtr.read(len(self.MAGIC))
141
142 if magicFromFile == self.MAGIC:
143 print('[*] Pyinstaller version: 2.1+')
144 self.pyinstVer = 21 # pyinstaller 2.1+
145 return True
146
147 print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')
148 return False
149
150
151 def getCArchiveInfo(self):
152 try:
153 if self.pyinstVer == 20:
154 self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
155
156 # Read CArchive cookie
157 (magic, lengthofPackage, toc, tocLen, self.pyver) = \
158 struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))
159
160 elif self.pyinstVer == 21:
161 self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
162
163 # Read CArchive cookie
164 (magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \
165 struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))
166
167 except:
168 print('[*] Error : The file is not a pyinstaller archive')
169 return False
170
171 print('[*] Python version: {0}'.format(self.pyver))
172
173 # Overlay is the data appended at the end of the PE
174 self.overlaySize = lengthofPackage
175 self.overlayPos = self.fileSize - self.overlaySize
176 self.tableOfContentsPos = self.overlayPos + toc
177 self.tableOfContentsSize = tocLen
178
179 print('[*] Length of package: {0} bytes'.format(self.overlaySize))
180 return True
181
182
183 def parseTOC(self):
184 # Go to the table of contents
185 self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)
186
187 self.tocList = []
188 parsedLen = 0
189
190 # Parse table of contents
191 while parsedLen < self.tableOfContentsSize:
192 (entrySize, ) = struct.unpack('!i', self.fPtr.read(4))
193 nameLen = struct.calcsize('!iiiiBc')
194
195 (entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = \
196 struct.unpack( \
197 '!iiiBc{0}s'.format(entrySize - nameLen), \
198 self.fPtr.read(entrySize - 4))
199
200 name = name.decode('utf-8').rstrip('\0')
201 if len(name) == 0:
202 name = str(uniquename())
203 print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name))
204
205 self.tocList.append( \
206 CTOCEntry( \
207 self.overlayPos + entryPos, \
208 cmprsdDataSize, \
209 uncmprsdDataSize, \
210 cmprsFlag, \
211 typeCmprsData, \
212 name \
213 ))
214
215 parsedLen += entrySize
216 print('[*] Found {0} files in CArchive'.format(len(self.tocList)))
217
218
219
220 def extractFiles(self):
221 print('[*] Beginning extraction...please standby')
222 extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')
223
224 if not os.path.exists(extractionDir):
225 os.mkdir(extractionDir)
226
227 os.chdir(extractionDir)
228
229 for entry in self.tocList:
230 basePath = os.path.dirname(entry.name)
231 if basePath != '':
232 # Check if path exists, create if not
233 if not os.path.exists(basePath):
234 os.makedirs(basePath)
235
236 self.fPtr.seek(entry.position, os.SEEK_SET)
237 data = self.fPtr.read(entry.cmprsdDataSize)
238
239 if entry.cmprsFlag == 1:
240 data = zlib.decompress(data)
241 # Malware may tamper with the uncompressed size
242 # Comment out the assertion in such a case
243 assert len(data) == entry.uncmprsdDataSize # Sanity Check
244
245 with open(entry.name, 'wb') as f:
246 f.write(data)
247
248 if entry.typeCmprsData == b's':
249 print('[+] Possible entry point: {0}'.format(entry.name))
250
251 elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':
252 self._extractPyz(entry.name)
253
254
255 def _extractPyz(self, name):
256 dirName = name + '_extracted'
257 # Create a directory for the contents of the pyz
258 if not os.path.exists(dirName):
259 os.mkdir(dirName)
260
261 with open(name, 'rb') as f:
262 pyzMagic = f.read(4)
263 assert pyzMagic == b'PYZ\0' # Sanity Check
264
265 pycHeader = f.read(4) # Python magic value
266
267 if imp.get_magic() != pycHeader:
268 print('[!] Warning: The script is running in a different python version than the one used to build the executable')
269 print(' Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver))
270
271 (tocPosition, ) = struct.unpack('!i', f.read(4))
272 f.seek(tocPosition, os.SEEK_SET)
273
274 try:
275 toc = marshal.load(f)
276 except:
277 print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name))
278 return
279
280 print('[*] Found {0} files in PYZ archive'.format(len(toc)))
281
282 # From pyinstaller 3.1+ toc is a list of tuples
283 if type(toc) == list:
284 toc = dict(toc)
285
286 for key in toc.keys():
287 (ispkg, pos, length) = toc[key]
288 f.seek(pos, os.SEEK_SET)
289
290 fileName = key
291 try:
292 # for Python > 3.3 some keys are bytes object some are str object
293 fileName = key.decode('utf-8')
294 except:
295 pass
296
297 # Make sure destination directory exists, ensuring we keep inside dirName
298 destName = os.path.join(dirName, fileName.replace("..", "__"))
299 destDirName = os.path.dirname(destName)
300 if not os.path.exists(destDirName):
301 os.makedirs(destDirName)
302
303 try:
304 data = f.read(length)
305 data = zlib.decompress(data)
306 except:
307 print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName))
308 open(destName + '.pyc.encrypted', 'wb').write(data)
309 continue
310
311 with open(destName + '.pyc', 'wb') as pycFile:
312 pycFile.write(pycHeader) # Write pyc magic
313 pycFile.write(b'\0' * 4) # Write timestamp
314 if self.pyver >= 33:
315 pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3
316 if self.pyver >= 37:
317 pycFile.write(b'\0' * 4) # Size parameter added in Python 3.7
318 pycFile.write(data)
319
320
321 def main():
322 if len(sys.argv) < 2:
323 print('[*] Usage: pyinstxtractor.py <filename>')
324
325 else:
326 arch = PyInstArchive(sys.argv[1])
327 if arch.open():
328 if arch.checkFile():
329 if arch.getCArchiveInfo():
330 arch.parseTOC()
331 arch.extractFiles()
332 arch.close()
333 print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
334 print('')
335 print('You can now use a python decompiler on the pyc files within the extracted directory')
336 return
337
338 arch.close()
339
340
341 if __name__ == '__main__':
342 main()
推荐使用pyi-archive_viewer。而python-exe-unpacker 有时会报错,(两年前的工具了)
使用pyi-archive_viewer需要先安装PyInstaller(https://github.com/pyinstaller/pyinstaller)
- 安装PyInstaller模块,注意是:pip install PyInstaller
pyi-archive_viewer
相关命令:
U: go Up one level
O <name>: open embedded archive name
X <name>: extract name
Q: quit
- pyi-archive_viewer中使用x命令提取文件
pyinstaller打包会去掉pyc的magic和时间戳,需要手动添加数据
(可以先提取第一个文件,后面提取的文件对比添加数据)
对照添加数据
主模块的依赖包在PYZ-00.pyz,使用o命令打开
之后再使用x命令提取文件。
- 反编译pyc
pip install uncompyle6
uncompyle6 (https://github.com/rocky/python-uncompyle6)
Run
$ uncompyle6 *compiled-python-file-pyc-or-pyo*
For usage help:
$ uncompyle6 -h
当然也有很多pyc反编译在线工具可用
- PyInstaller Extractor
python pyinstxtractor.py xxx.exe
生成目录
获得PyInstaller打包exe的py源码的更多相关文章
- Python: pyinstaller打包exe(含file version信息)
最近项目上一直都是用Spyder直接运行.py文件的方式来执行每日的自动化程序,每天都要手动去点击Run来执行一次,所以考虑把.py文件直接打包成exe,然后用windows的task schedul ...
- pyinstaller 打包exe程序读不到配置文件No such file
挺久没更新博客的,一来之前是觉得才疏学浅,记录下来的太简单没人看.二来时间上不是很充裕(不是借口,有时间打游戏,没时间总结) 偶然有一次发现同事在搜索解决问题的时候正在看我博客的解决思路,很奇妙的感觉 ...
- python pyinstaller 打包exe报错
今天用python 使用pyinstaller打包exe出现错误 环境pyqt5 + python3.6 在导入pyqt5包之前加上如下代码 import sysimport osif hasattr ...
- 利用PyInstaller打包exe文件
前言 平常我们通过Python写完一些小脚本之后,如果使用不频繁的话,一般会选择在DOS界面直接跑脚本,或者在IDE中运行.但当我们需要频繁使用某些脚本,或者在没有Python环境的机器上也能顺利运行 ...
- Pyinstaller 打包exe 报错 "failed to execute script XXX"的一种解决方案
最近用PyQt5写了一个界面小程序,需要打包成exe给到其他windows上使用,一开始使用python 3.7 64位,用pyinstaller打包exe,在64位机上运行正常. 但是目标电脑是32 ...
- Pyinstaller打包exe,丢失图标等问题
Pyinstaller打包exe,丢失图标等问题 一.原因 exe运行时会解压一个名为'_MEI*'的资源文件夹到电脑的临时目录,程序结束时删除. 程序里使用'\图标.png'这样的路径,exe运行时 ...
- 3.9 run_main.py源码(兼容python2和3)
3.9 run_main.py源码(兼容python2和3) 以下代码在python2和python3上都跑通过,python3只需注释掉上面红色框框区域代码就行(最后一步发送邮箱代码,我注释掉了). ...
- Django框架base.py源码
url.py文件 from django.conf.urls import url from django.contrib import admin from app_student import v ...
- pyinstaller打包exe文件,运行时一闪而过
pyinstaller打包exe文件出现命令窗口一闪而过 原因:exe运行过程中出错了,解决这些错误就可以了 解决方法: 通过 cd path >> xxx.exe 在命令行中运行exe文 ...
随机推荐
- 使用 js 实现一个简易版的 async 库
使用 js 实现一个简易版的 async 库 具有挑战性的前端面试题 series & parallel 串行,并行 refs https://www.infoq.cn/article/0NU ...
- Swift in Action
Swift in Action Swift Playgrounds https://apps.apple.com/us/app/swift-playgrounds/id1496833156?mt=12 ...
- local JSON file loader in js
local JSON file loader in js "use strict"; /** * * @author xgqfrms * @license MIT * @copyr ...
- Intersection Observer
Intersection Observer Intersection Observer API https://developer.mozilla.org/en-US/docs/Web/API/Int ...
- 关于MacBook Air/Pro 外接显示器时,显示器黑屏无反应的解决方法,顺便求助M1芯片的mac 外接显示器如何开启Hidpi
显示器黑屏,无反应,频繁闪烁的原因 先说结论,直接换type-c转DP的显示器连接线吧,如果显示器不支持dp接口,那自求多福吧. 事情是这样的,m1版本的macbook air 刚发布就马上入手了一台 ...
- PriorityQueue使用介绍
这玩意儿叫优先级队列,是一个类,继承了AbstractQueue类,实现了Serializable接口. jdk文档里是这么描述这玩意的: 基于优先级堆的无限优先级queue . 优先级队列的元素根据 ...
- 最实用JS 留着学习
1.A标签删除 function input(){ var b = window.confirm("确认要删除本条信息!"); if(b==true){ ret ...
- HarmonyOS三方件开发指南(13)-SwipeLayout侧滑删除
鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录:1. SwipeLayout组件功能介绍2. SwipeLayout使用方法3. SwipeLa ...
- Docker Elasticsearch 集群配置
一:选用ES原因 公司项目有些mysql的表数据已经超过5百万了,各种业务的查询入库压力已经凸显出来,初步打算将一个月前的数据迁移到ES中,mysql的老数据就物理删除掉. 首先是ES使用起来比较方便 ...
- c语言:分治算法之大数相乘
我们把整数A由规模n分为n1和n2,把整数B由规模m分为m1和m2,如下图: 则A分为n1位的A1和n2位的A1,B分为m1位的B1和m2位的B2,如下式所示: 以此类推,我们可以把A1.A2.B1. ...