参考链接: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源码的更多相关文章

  1. Python: pyinstaller打包exe(含file version信息)

    最近项目上一直都是用Spyder直接运行.py文件的方式来执行每日的自动化程序,每天都要手动去点击Run来执行一次,所以考虑把.py文件直接打包成exe,然后用windows的task schedul ...

  2. pyinstaller 打包exe程序读不到配置文件No such file

    挺久没更新博客的,一来之前是觉得才疏学浅,记录下来的太简单没人看.二来时间上不是很充裕(不是借口,有时间打游戏,没时间总结) 偶然有一次发现同事在搜索解决问题的时候正在看我博客的解决思路,很奇妙的感觉 ...

  3. python pyinstaller 打包exe报错

    今天用python 使用pyinstaller打包exe出现错误 环境pyqt5 + python3.6 在导入pyqt5包之前加上如下代码 import sysimport osif hasattr ...

  4. 利用PyInstaller打包exe文件

    前言 平常我们通过Python写完一些小脚本之后,如果使用不频繁的话,一般会选择在DOS界面直接跑脚本,或者在IDE中运行.但当我们需要频繁使用某些脚本,或者在没有Python环境的机器上也能顺利运行 ...

  5. Pyinstaller 打包exe 报错 "failed to execute script XXX"的一种解决方案

    最近用PyQt5写了一个界面小程序,需要打包成exe给到其他windows上使用,一开始使用python 3.7 64位,用pyinstaller打包exe,在64位机上运行正常. 但是目标电脑是32 ...

  6. Pyinstaller打包exe,丢失图标等问题

    Pyinstaller打包exe,丢失图标等问题 一.原因 exe运行时会解压一个名为'_MEI*'的资源文件夹到电脑的临时目录,程序结束时删除. 程序里使用'\图标.png'这样的路径,exe运行时 ...

  7. 3.9 run_main.py源码(兼容python2和3)

    3.9 run_main.py源码(兼容python2和3) 以下代码在python2和python3上都跑通过,python3只需注释掉上面红色框框区域代码就行(最后一步发送邮箱代码,我注释掉了). ...

  8. Django框架base.py源码

    url.py文件 from django.conf.urls import url from django.contrib import admin from app_student import v ...

  9. pyinstaller打包exe文件,运行时一闪而过

    pyinstaller打包exe文件出现命令窗口一闪而过 原因:exe运行过程中出错了,解决这些错误就可以了 解决方法: 通过 cd path >> xxx.exe 在命令行中运行exe文 ...

随机推荐

  1. DuckDuckGo Privacy Browse‪r‬

    DuckDuckGo Privacy Browser https://apps.apple.com/app/duckduckgo-privacy-browser/id663592361 https:/ ...

  2. git hooks All In One

    git hooks All In One $ xgqfrms git:(main) cd .git/ $ .git git:(main) ls COMMIT_EDITMSG HEAD branches ...

  3. WebAR in Action

    WebAR in Action WebAR (Web + AR) 增强现实 https://developer.mozilla.org/en-US/docs/Web/API/WebAR_API Web ...

  4. Web Components & HTML5 & template & slot

    Web Components & HTML5 & template & slot https://developer.mozilla.org/en-US/docs/Web/HT ...

  5. js currying & js 科里化

    js currying & js 科里化 var test = ( function (a){ console.log(`a2 =`, a);// 1 // console.log(`b2 = ...

  6. outlook & email & animation

    outlook & email & animation tada position div

  7. git stash & git stash pop

    git stash & git stash pop $ git checkout feature/select-seat-system $ git checkout feature/app-d ...

  8. NGK乘势而上打造生态所,建立全方位的区块链生态系统

    当金融理财变成了生活的一部分,购买金融衍生品的眼光成为了影响生活质量重要组成部分.这是一个不缺少黄金的年代,一夜间实现财务自由的故事每天都在上演,但是由于太多人缺少发现黄金的眼睛,只能被财富和机遇拒之 ...

  9. HQYJ嵌入式学习笔记——C语言复习day2

    1.计算机的数值表示 数值类型和非数值类型 二进制 0,1 (0b1001) 八进制 0~7   (0146) 十进制 0~9 十六进制 0~f (0x3f) 八进制转二进制-->一位八进制数换 ...

  10. 020_CSS3

    目录 如何学习CSS 什么是CSS 发展史 快速入门 css的优势 三种CSS导入方式 拓展:外部样式两种写法 选择器 基本选择器 层次选择器 结构伪类选择器 属性选择器 美化网页元素 为什么要美化网 ...