80 lines
2.5 KiB
Python
80 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:
|
|
print("Failed to decompile %s" % path)
|