You've already forked pyinstxtractor
mirror of
https://github.com/extremecoders-re/pyinstxtractor.git
synced 2025-08-03 00:25:51 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
957f830443 | ||
|
fb7c13d7db | ||
|
eba6b6681c | ||
|
4dc147a379 | ||
|
d68ef84279 | ||
|
816d50f1e6 | ||
|
624bde27e1 |
@@ -2,7 +2,7 @@
|
||||
|
||||
PyInstaller Extractor is a Python script to extract the contents of a PyInstaller generated Windows executable file. The contents of the pyz file (usually pyc files) present inside the executable are also extracted.
|
||||
|
||||
The header of the pyc files are automatically fixed so that a Python bytecode decompiler will recognize it. The script can run on both Python 2.x and 3.x. Pyinstaller versions 2.0, 2.1, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.5.1, 4.6, 4.7 are [tested](https://github.com/extremecoders-re/pyinstxtractor-test-binaries) & supported. Probably will work with other versions too.
|
||||
The header of the pyc files are automatically fixed so that a Python bytecode decompiler will recognize it. The script can run on both Python 2.x and 3.x. Pyinstaller versions 2.0, 2.1, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.5.1, 4.6, 4.7, 4.8, 4.9, 4.10, 5.0, 5.0.1, 5.1 are [tested](https://github.com/extremecoders-re/pyinstxtractor-test-binaries) & supported. Probably will work with other versions too.
|
||||
|
||||
This project was originally hosted on [SourceForge](https://sourceforge.net/projects/pyinstallerextractor/).
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
PyInstaller Extractor v2.0 (Supports pyinstaller 4.7, 4.6, 4.5.1, 4.5, 4.4, 4.3, 4.2, 4.1, 4.0, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
|
||||
PyInstaller Extractor v2.0 (Supports pyinstaller 5.1, 5.0.1, 5.0, 4.10, 4.9, 4.8, 4.7, 4.6, 4.5.1, 4.5, 4.4, 4.3, 4.2, 4.1, 4.0, 3.6, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
|
||||
Author : Extreme Coders
|
||||
E-mail : extremecoders(at)hotmail(dot)com
|
||||
Web : https://0xec.blogspot.com
|
||||
@@ -174,7 +174,7 @@ class PyInstArchive:
|
||||
|
||||
self.fPtr.seek(self.cookiePos + self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
|
||||
|
||||
if b'python' in self.fPtr.read(64):
|
||||
if b'python' in self.fPtr.read(64).lower():
|
||||
print('[+] Pyinstaller version: 2.1+')
|
||||
self.pyinstVer = 21 # pyinstaller 2.1+
|
||||
else:
|
||||
@@ -190,21 +190,22 @@ class PyInstArchive:
|
||||
self.fPtr.seek(self.cookiePos, os.SEEK_SET)
|
||||
|
||||
# Read CArchive cookie
|
||||
(magic, lengthofPackage, toc, tocLen, self.pyver) = \
|
||||
(magic, lengthofPackage, toc, tocLen, pyver) = \
|
||||
struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))
|
||||
|
||||
elif self.pyinstVer == 21:
|
||||
self.fPtr.seek(self.cookiePos, os.SEEK_SET)
|
||||
|
||||
# Read CArchive cookie
|
||||
(magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \
|
||||
(magic, lengthofPackage, toc, tocLen, pyver, pylibname) = \
|
||||
struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))
|
||||
|
||||
except:
|
||||
print('[!] Error : The file is not a pyinstaller archive')
|
||||
return False
|
||||
|
||||
print('[+] Python version: {0}'.format(self.pyver))
|
||||
self.pymaj, self.pymin = (pyver//100, pyver%100) if pyver >= 100 else (pyver//10, pyver%10)
|
||||
print('[+] Python version: {0}.{1}'.format(self.pymaj, self.pymin))
|
||||
|
||||
# Additional data after the cookie
|
||||
tailBytes = self.fileSize - self.cookiePos - (self.PYINST20_COOKIE_SIZE if self.pyinstVer == 20 else self.PYINST21_COOKIE_SIZE)
|
||||
@@ -275,12 +276,6 @@ class PyInstArchive:
|
||||
os.chdir(extractionDir)
|
||||
|
||||
for entry in self.tocList:
|
||||
basePath = os.path.dirname(entry.name)
|
||||
if basePath != '':
|
||||
# Check if path exists, create if not
|
||||
if not os.path.exists(basePath):
|
||||
os.makedirs(basePath)
|
||||
|
||||
self.fPtr.seek(entry.position, os.SEEK_SET)
|
||||
data = self.fPtr.read(entry.cmprsdDataSize)
|
||||
|
||||
@@ -290,6 +285,18 @@ class PyInstArchive:
|
||||
# Comment out the assertion in such a case
|
||||
assert len(data) == entry.uncmprsdDataSize # Sanity Check
|
||||
|
||||
if entry.typeCmprsData == b'd' or entry.typeCmprsData == b'o':
|
||||
# d -> ARCHIVE_ITEM_DEPENDENCY
|
||||
# o -> ARCHIVE_ITEM_RUNTIME_OPTION
|
||||
# These are runtime options, not files
|
||||
continue
|
||||
|
||||
basePath = os.path.dirname(entry.name)
|
||||
if basePath != '':
|
||||
# Check if path exists, create if not
|
||||
if not os.path.exists(basePath):
|
||||
os.makedirs(basePath)
|
||||
|
||||
if entry.typeCmprsData == b's':
|
||||
# s -> ARCHIVE_ITEM_PYSOURCE
|
||||
# Entry point are expected to be python scripts
|
||||
@@ -313,13 +320,13 @@ class PyInstArchive:
|
||||
with open(filename, 'wb') as pycFile:
|
||||
pycFile.write(pyc_magic) # pyc magic
|
||||
|
||||
if self.pyver >= 37: # PEP 552 -- Deterministic pycs
|
||||
if self.pymaj >= 3 and self.pymin >= 7: # PEP 552 -- Deterministic pycs
|
||||
pycFile.write(b'\0' * 4) # Bitfield
|
||||
pycFile.write(b'\0' * 8) # (Timestamp + size) || hash
|
||||
|
||||
else:
|
||||
pycFile.write(b'\0' * 4) # Timestamp
|
||||
if self.pyver >= 33:
|
||||
if self.pymaj >= 3 and self.pymin >= 3:
|
||||
pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3
|
||||
|
||||
pycFile.write(data)
|
||||
@@ -340,7 +347,7 @@ class PyInstArchive:
|
||||
# Skip PYZ extraction if not running under the same python version
|
||||
if pyc_magic != pycHeader:
|
||||
print('[!] Warning: This script is running in a different Python version than the one used to build the executable.')
|
||||
print('[!] Please run this script in Python{0} to prevent extraction errors during unmarshalling'.format(self.pyver))
|
||||
print('[!] Please run this script in Python {0}.{1} to prevent extraction errors during unmarshalling'.format(self.pymaj, self.pymin))
|
||||
print('[!] Skipping pyz extraction')
|
||||
return
|
||||
|
||||
|
Reference in New Issue
Block a user