7 Commits

Author SHA1 Message Date
extremecoders-re
957f830443 Correctly calculate the python version, fixes #37
The python version is stored as major*100 + minor

Details: https://github.com/pyinstaller/pyinstaller/commit/9f7c92
2022-06-13 16:49:39 +05:30
extremecoders-re
fb7c13d7db Mention support for pyinstaller 5.1 2022-06-05 12:08:31 +03:00
extremecoders-re
eba6b6681c Do a case insensitive string search for "python" in cookie buffer, Fixes #47 2022-06-05 11:45:51 +03:00
extremecoders-re
4dc147a379 Correct handling order of runtime options, followup #44 2022-05-13 11:01:54 +03:00
extremecoders-re
d68ef84279 Ignore pyinstaller runtime options, Fixes #44 2022-05-11 12:36:50 +03:00
extremecoders-re
816d50f1e6 Mention support for pyinstaller 4.10, 5.0, 5.0.1 2022-04-29 00:05:18 +03:00
extremecoders-re
624bde27e1 Mention support for pyinstaller 4.9, 4.8 2022-02-25 16:52:11 +05:30
2 changed files with 22 additions and 15 deletions

View File

@@ -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/).

View File

@@ -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