参考链接: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. 利用设置新数据存储结构解决vue中折叠面板双向绑定index引起的问题

    问题背景是,在进行机器性能可视化的前端开发时,使用折叠面板将不同机器的性能图表画到不同的折叠面板上去.而机器的选择利用select下拉选项来筛选. 由于在折叠面板中,通过 如下v-model双向绑定了 ...

  2. js sort tricks All In One

    js sort tricks All In One js 排序技巧 const arr = [ { label: 'False 1 ', disabled: false, }, { label: 'F ...

  3. 三维码 & 二维码 & 一维码

    三维码 & 二维码 & 一维码 3D, 2D, 1D 防伪国家标准 -<结构三维码防伪技术条件> http://www.xinhuanet.com/tech/2019-12 ...

  4. Redis all in one

    Redis all in one Redis: REmote DIctionary Server https://redis.io/topics/quickstart Download, extrac ...

  5. NGK DeFi Baccarat怎么玩能赚钱?

    市面上大多数DeFi项目都是基于以太坊来开发的,除了吞吐量低.存储量小以及交易速度慢等问题以外,高额的Gas手续费将不少终端用户拒之门外. 基于此NGK.IO推出了低门槛的DeFi项目-- Bacca ...

  6. 07_MySQL修改数据库的表结构

    修改数据库的表结构

  7. Python算法_整数反转(02)

    给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1: 输入: 123输出: 321 示例 2: 输入: -123输出: -321 示例 3: 输入: 120 输出: 2 ...

  8. 使用 Tye 辅助开发 k8s 应用竟如此简单(四)

    续上篇,这篇我们来进一步探索 Tye 更多的使用方法.本篇我们来了解一下如何在 Tye 中如何进行日志的统一管理. Newbe.Claptrap 是一个用于轻松应对并发问题的分布式开发框架.如果您是首 ...

  9. Java自学第6期——Collection、Map、迭代器、泛型、可变参数、集合工具类、集合数据结构、Debug

    集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型值. ...

  10. H5跳转app代码

    不罗嗦直接上代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...