pyinstaller-decompiler/get_pyc.py

84 lines
2.5 KiB
Python

#!/usr/bin/env python2.7
from __future__ import print_function
import marshal
import struct
import sys
import time
import uncompyle6
from pathlib2 import Path
def _pack_uint32(val):
""" Convert integer to 32-bit little-endian bytes """
return struct.pack("<I", val)
def code_to_bytecode(code, mtime=0, source_size=0):
"""
Serialise the passed code object (PyCodeObject*) to bytecode as a .pyc file
The args mtime and source_size are inconsequential metadata in the .pyc file.
"""
# Get the magic number for the running Python version
if sys.version_info >= (3,4):
from importlib.util import MAGIC_NUMBER
else:
import imp
MAGIC_NUMBER = imp.get_magic()
# Add the magic number that indicates the version of Python the bytecode is for
#
# The .pyc may not decompile if this four-byte value is wrong. Either hardcode the
# value for the target version (eg. b'\x33\x0D\x0D\x0A' instead of MAGIC_NUMBER)
# or see trymagicnum.py to step through different values to find a valid one.
data = bytearray(MAGIC_NUMBER)
# Handle extra 32-bit field in header from Python 3.7 onwards
# See: https://www.python.org/dev/peps/pep-0552
if sys.version_info >= (3,7):
# Blank bit field value to indicate traditional pyc header
data.extend(_pack_uint32(0))
data.extend(_pack_uint32(int(mtime)))
# Handle extra 32-bit field for source size from Python 3.2 onwards
# See: https://www.python.org/dev/peps/pep-3147/
if sys.version_info >= (3,2):
data.extend(_pack_uint32(source_size))
data.extend(marshal.dumps(code))
return data
if len(sys.argv) < 3:
print("Usage %s <in_path> <pyc_path> <py_path>" % sys.argv[0])
sys.exit(1)
in_path = Path(sys.argv[1])
pyc_path = Path(sys.argv[2])
py_path = Path(sys.argv[3])
for path in in_path.rglob("*.co"):
try:
co = marshal.loads(path.read_bytes())
pyc = code_to_bytecode(co, time.time())
pyc_file = pyc_path / path.relative_to(in_path).with_suffix(".pyc")
pyc_file.parent.mkdir(parents=True, exist_ok=True)
with open(str(pyc_file), "wb") as file:
file.write(pyc)
py_file = py_path / path.relative_to(in_path).with_suffix(".py")
py_file.parent.mkdir(parents=True, exist_ok=True)
with open(str(py_file), 'w') as decompiled:
uncompyle6.main.decompile_file(str(pyc_file), decompiled)
except KeyboardInterrupt:
break
except:
print("Failed to decompile %s" % path)
else:
print("Decompiled %s" % path)