def prepend(self, main_pyc, out_dir): import tempfile is_prepend_magic = True edited_py_name = main_pyc + ".py" edited_pyc = tempfile.NamedTemporaryFile(mode='wb', suffix='.pyc', delete=False) try: if not os.path.exists(main_pyc): raise FileNotFoundException # Check if this pyc file is prepended with recognize magic bytes already. with open(main_pyc, 'rb') as tmp_pyc: from xdis import magics py_ver_num = magics.magic2int(tmp_pyc.read(4)) for key in magics.versions: if magics.magic2int(key) == py_ver_num: logger.info("Magic bytes is already appeneded.") is_prepend_magic = False copyfile(main_pyc, edited_pyc.name) if is_prepend_magic: magic = b'\x42\x0d\x0d\x0a' # Default magic for python 2.7 with edited_pyc as prepend_pyc: pyc_data = open(main_pyc, 'rb') prepend_pyc.write(magic) # Magic bytes prepend_pyc.write(b'\0' * 12) # Time stamp prepend_pyc.write(pyc_data.read()) pyc_data.close() (total, okay, failed, verify_failed) = PythonExectable.decompile_pyc( '', [edited_pyc.name], edited_py_name) if failed == 0 and verify_failed == 0: logger.info("Successfully decompiled.") else: logger.info( "Unable to decompile the pyc file. (Probably is already decompiled?)" ) if os.path.exists(edited_py_name): os.remove(edited_py_name) exit(1) except FileNotFoundException: logger.error("pyc file not found. Ignoring it now.") exit(1) finally: if os.path.exists(edited_pyc.name): os.remove(edited_pyc.name)
def test_basic(self): """Basic test of magic numbers""" if hasattr(sys, 'version_info'): version = version_tuple_to_str() if IS_PYPY: version += 'pypy' self.assertTrue( version in magics.magics.keys(), "version %s is not in magic.magics.keys: %s" % (version, magics.magics.keys())) self.assertEqual(magics.MAGIC, magics.int2magic(magics.magic2int(magics.MAGIC))) lookup = str(PYTHON_VERSION) if IS_PYPY: lookup += 'pypy' self.assertTrue( lookup in magics.magics.keys(), "PYTHON VERSION %s is not in magic.magics.keys: %s" % (lookup, magics.magics.keys())) if not (3, 5, 2) <= sys.version_info < (3, 6, 0): self.assertEqual( magics.sysinfo2magic(), magics.MAGIC, "magic from imp.get_magic() for %s " "should be sysinfo2magic()" % lookup)
def write_pycfile(fp, code_list, timestamp=None, version=xdis.PYTHON_VERSION): magic_bytes = magics[version] magic_int = magic2int(magic_bytes) fp.write(magic_bytes) if timestamp is None: timestamp = int(time.time()) write_source_size = version > 3.2 if version >= 3.7: if magic_int == 3393: fp.write(pack('I', timestamp)) fp.write(pack('I', 0)) else: # PEP 552. https://www.python.org/dev/peps/pep-0552/ # 0 in the lowest-order bit means used old-style timestamps fp.write(pack('<I', 0)) fp.write(pack('<I', timestamp)) else: fp.write(pack('<I', timestamp)) if write_source_size: fp.write(pack('<I', 0)) # size mod 2**32 for co in code_list: try: co_obj = dumps(co, python_version=version) if PYTHON3 and version < 3.0: co_obj = str.encode(co_obj) pass fp.write(co_obj) except: pass
def test_basic(self): """Basic test of magic numbers""" current = imp.get_magic() if hasattr(sys, 'version_info'): version = '.'.join([str(v) for v in sys.version_info[0:3]]) if IS_PYPY: version += 'pypy' self.assertTrue( version in magics.magics.keys(), "version %s is not in magic.magics.keys: %s" % (version, magics.magics.keys())) self.assertEqual(current, magics.int2magic(magics.magic2int(current))) lookup = str(PYTHON_VERSION) if IS_PYPY: lookup += 'pypy' self.assertTrue( lookup in magics.magics.keys(), "PYTHON VERSION %s is not in magic.magics.keys: %s" % (lookup, magics.magics.keys())) if not (3, 5, 2) <= sys.version_info < (3, 6, 0): self.assertEqual( magics.sysinfo2magic(), current, "magic from imp.get_magic() for %s " "should be sysinfo2magic()" % lookup)
def verify_file(real_source_filename, real_bytecode_filename): """Compile *real_source_filename* using the running Python interpreter. Then write bytecode out to a new place again using Python's routines. Next load it in using two of our routines. Compare that the code objects there are equal. Next write out the bytecode (using the same Python bytecode writin routine as in step 1. Finally compare the bytecode files. """ tempdir = tempfile.gettempdir() source_filename = os.path.join(tempdir, "testing.py") if not os.path.exists(real_source_filename): return if PYTHON_VERSION < 3.5: f = open(real_source_filename, 'U') else: f = open(real_source_filename, newline=None, errors='backslashreplace') codestring = f.read() f.close() codeobject1 = compile(codestring, source_filename, 'exec') (version, timestamp, magic_int, codeobject2, is_pypy, source_size) = load_module(real_bytecode_filename) # A hack for PyPy 3.2 if magic_int == 3180 + 7: magic_int = 48 assert MAGIC == magics.int2magic(magic_int), \ ("magic_int %d vs %d in %s/%s" % (magic_int, magics.magic2int(MAGIC), os.getcwd(), real_bytecode_filename)) bytecode_filename1 = os.path.join(tempdir, "testing1.pyc") dump_compile(codeobject1, bytecode_filename1, timestamp, MAGIC) (version, timestamp, magic_int, codeobject3, is_pypy, source_size) = load_module(real_bytecode_filename, fast_load=not is_pypy) # compare_code(codeobject1, codeobject2) # compare_code(codeobject2, codeobject3) bytecode_filename2 = os.path.join(tempdir, "testing2.pyc") dump_compile(codeobject1, bytecode_filename2, timestamp, magics.int2magic(magic_int)) compare_bytecode_files(bytecode_filename1, bytecode_filename2) return
def parse_toc(self) -> None: self.magic_int = magic2int(self.archive_contents[4:8]) (toc_position, ) = struct.unpack("!i", self.archive_contents[8:12]) self.toc = xdis.unmarshal.load_code( self.archive_contents[toc_position:], self.magic_int) # TODO wrap this in try block? logger.debug(f"[*] Found {len(self.toc)} entries in this PYZ archive") # From PyInstaller 3.1+ toc is a list of tuples if isinstance(self.toc, list): self.toc = dict(self.toc)
def copy_magic_into_pyc(input_pyc, output_pyc, src_version, dest_version): """Bytecodes are the same except the magic number, so just change that""" (version, timestamp, magic_int, co, is_pypy, source_size) = load_module(input_pyc) assert version == float(src_version), ( "Need Python %s bytecode; got bytecode for version %s" % (src_version, version)) magic_int = magic2int(magics[dest_version]) write_bytecode_file(output_pyc, co, magic_int) print("Wrote %s" % output_pyc) return
def test_basic(self): """Basic test of magic numbers""" current = imp.get_magic() if hasattr(sys, 'version_info'): version = '.'.join([str(v) for v in sys.version_info[0:3]]) self.assertTrue(version in magics.magics.keys(), "version %s is not in magic.magics.keys: %s" % (version, magics.magics.keys())) self.assertEqual(current, magics.int2magic(magics.magic2int(current))) self.assertTrue(str(PYTHON_VERSION) in magics.magics.keys(), "PYTHON VERSION %s is not in magic.magics.keys: %s" % (PYTHON_VERSION, magics.magics.keys()))
def verify_file(real_source_filename, real_bytecode_filename): """Compile *real_source_filename* using the running Python interpreter. Then write bytecode out to a new place again using Python's routines. Next load it in using two of our routines. Compare that the code objects there are equal. Next write out the bytecode (using the same Python bytecode writin routine as in step 1. Finally compare the bytecode files. """ tempdir = tempfile.gettempdir() source_filename = os.path.join(tempdir, "testing.py") if not os.path.exists(real_source_filename): return try: f = open(real_source_filename, 'U') except: return codestring = f.read() f.close() codeobject1 = compile(codestring, source_filename,'exec') (version, timestamp, magic_int, codeobject2, is_pypy, source_size) = load_module(real_bytecode_filename) # A hack for PyPy 3.2 if magic_int == 3180+7: magic_int = 48 assert MAGIC == magics.int2magic(magic_int), \ ("magic_int %d vs %d in %s/%s" % (magic_int, magics.magic2int(MAGIC), os.getcwd(), real_bytecode_filename)) bytecode_filename1 = os.path.join(tempdir, "testing1.pyc") dump_compile(codeobject1, bytecode_filename1, timestamp, MAGIC) (version, timestamp, magic_int, codeobject3, is_pypy, source_size) = load_module(real_bytecode_filename, fast_load=not is_pypy) # compare_code(codeobject1, codeobject2) # compare_code(codeobject2, codeobject3) bytecode_filename2 = os.path.join(tempdir, "testing2.pyc") dump_compile(codeobject1, bytecode_filename2, timestamp, magics.int2magic(magic_int)) compare_bytecode_files(bytecode_filename1, bytecode_filename2) return
def test_basic(self): """Basic test of magic numbers""" current = imp.get_magic() if hasattr(sys, 'version_info'): version = '.'.join([str(v) for v in sys.version_info[0:3]]) if IS_PYPY: version += 'pypy' self.assertTrue(version in magics.magics.keys(), "version %s is not in magic.magics.keys: %s" % (version, magics.magics.keys())) self.assertEqual(current, magics.int2magic(magics.magic2int(current))) lookup = str(PYTHON_VERSION) if IS_PYPY: lookup += 'pypy' self.assertTrue(lookup in magics.magics.keys(), "PYTHON VERSION %s is not in magic.magics.keys: %s" % (lookup, magics.magics.keys())) if not (3, 5, 2) <= sys.version_info < (3, 6, 0): self.assertEqual(magics.sysinfo2magic(), current, "magic from imp.get_magic() for %s " "should be sysinfo2magic()" % lookup)
def load_module_from_file_object(fp, filename="<unknown>", code_objects=None, fast_load=False, get_code=True): """load a module from a file object without importing it. See :func:load_module for a list of return values. """ if code_objects is None: code_objects = {} timestamp = 0 try: magic = fp.read(4) magic_int = magic2int(magic) # For reasons I don't understand, PyPy 3.2 stores a magic # of '0'... The two values below are for Python 2.x and 3.x respectively if magic[0:1] in ["0", b"0"]: magic = int2magic(3180 + 7) try: # FIXME: use the internal routine below float_version = magic_int2float(magic_int) except KeyError: if magic_int in (2657, 22138): raise ImportError( "This smells like Pyston which is not supported.") if len(magic) >= 2: raise ImportError( "Unknown magic number %s in %s" % (ord(magic[0:1]) + 256 * ord(magic[1:2]), filename)) else: raise ImportError("Bad magic number: '%s'" % magic) if magic_int in ( 3010, 3020, 3030, 3040, 3050, 3060, 3061, 3071, 3361, 3091, 3101, 3103, 3141, 3270, 3280, 3290, 3300, 3320, 3330, 3371, 62071, 62071, 62081, 62091, 62092, 62111, ): raise ImportError("%s is interim Python %s (%d) bytecode which is " "not supported.\nFinal released versions are " "supported." % (filename, versions[magic], magic2int(magic))) elif magic_int == 62135: fp.seek(0) return fix_dropbox_pyc(fp) elif magic_int == 62215: raise ImportError( "%s is a dropbox-hacked Python %s (bytecode %d).\n" "See https://github.com/kholia/dedrop for how to " "decrypt." % (filename, versions[magic], magic2int(magic))) try: # print version my_magic_int = PYTHON_MAGIC_INT magic_int = magic2int(magic) version = magic_int2float(magic_int) timestamp = None source_size = None sip_hash = None ts = fp.read(4) if version >= 3.7: # PEP 552. https://www.python.org/dev/peps/pep-0552/ pep_bits = ts[-1] if PYTHON_VERSION <= 2.7: pep_bits = ord(pep_bits) if (pep_bits & 1) or magic_int == 3393: # 3393 is 3.7.0beta3 # SipHash sip_hash = unpack("<Q", fp.read(8))[0] else: # Uses older-style timestamp and size timestamp = unpack("<I", fp.read(4))[0] # pep552_bits source_size = unpack("<I", fp.read(4))[0] # size mod 2**32 pass else: timestamp = unpack("<I", ts)[0] # Note: a higher magic number doesn't necessarily mean a later # release. At Python 3.0 the magic number decreased # significantly. Hence the range below. Also note inclusion of # the size info, occurred within a Python major/minor # release. Hence the test on the magic value rather than # PYTHON_VERSION, although PYTHON_VERSION would probably work. if ((3200 <= magic_int < 20121) and version >= 1.5 or magic_int in IS_PYPY3): source_size = unpack("<I", fp.read(4))[0] # size mod 2**32 if get_code: if my_magic_int == magic_int: bytecode = fp.read() co = marshal.loads(bytecode) elif fast_load: co = xdis.marsh.load(fp, magicint2version[magic_int]) else: co = xdis.unmarshal.load_code(fp, magic_int, code_objects) pass else: co = None except: kind, msg = sys.exc_info()[0:2] import traceback traceback.print_exc() raise ImportError("Ill-formed bytecode file %s\n%s; %s" % (filename, kind, msg)) finally: fp.close() return ( float_version, timestamp, magic_int, co, is_pypy(magic_int), source_size, sip_hash, )
def _determine_python_version(self): """Will attempt to determine what version of python was used when this py2exe PE was compiled. We need to know this because xdis requires knowledge of the python version to unmarshal the bytecode correctly""" potential_magic_nums = set() logger.debug("[*] Attempting to discover version for PYTHONSCRIPT resource") # Method 1: Looking for PythonXY.DLL resource in the same directory as the PYTHONSCRIPT resource. If there, # check to see if it has a VERSIONINFO resource with a FileVersion or ProductVersion field, # as these typically contain the python version. See https://github.com/erocarrera/pefile for more info on # the structures used below if hasattr(self, "archive_path"): parent_dir = self.archive_path.parents[0] else: parent_dir = pathlib.Path.cwd() for python_dll in os.listdir(parent_dir): if re.match(r"python[0-9]{0,2}\.dll", python_dll, re.I): logger.debug(f"[*] Found python DLL resource {str(python_dll)} in directory {parent_dir}") try: dll_class_inst = PortableExecutable(parent_dir.joinpath(python_dll)) except TypeError: logger.debug(f"[!] PyDecipher could not create a PE/DLL class instance for {str(python_dll)}") else: dll_class_inst.load_version_info(quiet=True) if dll_class_inst.python_version: potential_magic_nums.add(version_str_to_magic_num_int(dll_class_inst.python_version)) finally: break # Method 2: Check to see if there are pyc files in the same directory with magic numbers for pyc_file in parent_dir.rglob("*.pyc"): with pyc_file.open("rb") as pyc_file_ptr: try: magic_bytes = pyc_file_ptr.read(4) magic_num = magic2int(magic_bytes) except: # TODO make more specific error catching pass else: potential_magic_nums.add(magic_num) break # Searching the PYTHONSCRIPT resource for strings like c:\python24\lib\site-packages\py2exe\boot_common.py b_python_regex = re.compile(b"(python)([0-9]{2})", re.I) script_re_obj = b_python_regex.search(self.resource_contents) if script_re_obj: version_str = script_re_obj.group(2).decode("utf-8") logger.info( "[*] Detected potential version string in PYTHONSCRIPT resource: {}".format( script_re_obj.group().decode("utf-8") ) ) potential_magic_nums.add(version_str_to_magic_num_int(version_str[0] + "." + version_str[1])) if potential_magic_nums: logger.info(f"[*] Will attempt to unmarshal using these python magic numbers: {potential_magic_nums}") return potential_magic_nums else: logger.info( "[!] Couldn't find any python magic numbers to hint at the python version of this resource. " "Will attempt to brute-force determine the correct magic number." ) return
def load_module_from_file_object(fp, filename='<unknown>', code_objects=None, fast_load=False, get_code=True): """load a module from a file object without importing it. See :func:load_module for a list of return values. """ if code_objects is None: code_objects = {} timestamp = 0 try: magic = fp.read(4) magic_int = magics.magic2int(magic) # For reasons I don't understand, PyPy 3.2 stores a magic # of '0'... The two values below are for Python 2.x and 3.x respectively if magic[0:1] in ['0', b'0']: magic = magics.int2magic(3180 + 7) try: # FIXME: use the internal routine below float_version = float(magics.versions[magic][:3]) # float_version = magics.magic_int2float(magic_int) except KeyError: if magic_int in (2657, 22138): raise ImportError( "This smells like Pyston which is not supported.") if len(magic) >= 2: raise ImportError( "Unknown magic number %s in %s" % (ord(magic[0:1]) + 256 * ord(magic[1:2]), filename)) else: raise ImportError("Bad magic number: '%s'" % magic) if magic_int in (3010, 3020, 3030, 3040, 3050, 3060, 3061, 3361, 3371): raise ImportError( "%s is interim Python %s (%d) bytecode which is " "not supported.\nFinal released versions are " "supported." % (filename, magics.versions[magic], magics.magic2int(magic))) elif magic_int == 62135: fp.seek(0) return fix_dropbox_pyc(fp) elif magic_int == 62215: raise ImportError( "%s is a dropbox-hacked Python %s (bytecode %d).\n" "See https://github.com/kholia/dedrop for how to " "decrypt." % (filename, magics.versions[magic], magics.magic2int(magic))) try: # print version ts = fp.read(4) my_magic_int = magics.magic2int(imp.get_magic()) magic_int = magics.magic2int(magic) if magic_int == 3393: timestamp = 0 _ = unpack("<I", ts)[0] # hash word 1 _ = unpack("<I", fp.read(4))[0] # hash word 2 elif magic_int in (3394, 3401): timestamp = 0 _ = unpack("<I", fp.read(4))[0] # pep552_bits else: timestamp = unpack("<I", ts)[0] # Note: a higher magic number doesn't necessarily mean a later # release. At Python 3.0 the magic number decreased # significantly. Hence the range below. Also note inclusion of # the size info, occurred within a Python major/minor # release. Hence the test on the magic value rather than # PYTHON_VERSION, although PYTHON_VERSION would probably work. if (((3200 <= magic_int < 20121) and (magic_int not in (5892, 11913, 39170, 39171))) or (magic_int in magics.IS_PYPY3)): source_size = unpack("<I", fp.read(4))[0] # size mod 2**32 else: source_size = None if get_code: if my_magic_int == magic_int: bytecode = fp.read() co = marshal.loads(bytecode) elif fast_load: co = xdis.marsh.load(fp, magics.magicint2version[magic_int]) else: co = xdis.unmarshal.load_code(fp, magic_int, code_objects) pass else: co = None except: kind, msg = sys.exc_info()[0:2] import traceback traceback.print_exc() raise ImportError("Ill-formed bytecode file %s\n%s; %s" % (filename, kind, msg)) finally: fp.close() return float_version, timestamp, magic_int, co, is_pypy( magic_int), source_size
def load_module(filename, code_objects=None, fast_load=False, get_code=True): """load a module without importing it. load_module(filename: string): version, magic_int, code_object filename: name of file containing Python byte-code object (normally a .pyc) code_object: code_object from this file version: Python major/minor value e.g. 2.7. or 3.4 magic_int: more specific than version. The actual byte code version of the code object Parsing the code object takes a bit of parsing time, but sometimes all you want is the module info, time string, code size, python version, etc. For that, set get_code=False. """ if code_objects is None: code_objects = {} timestamp = 0 fp = open(filename, 'rb') try: magic = fp.read(4) magic_int = magics.magic2int(magic) # For reasons I don't understand PyPy 3.2 stores a magic # of '0'... The two values below are for Python 2.x and 3.x respectively if magic[0:1] in ['0', b'0']: magic = magics.int2magic(3180+7) try: version = float(magics.versions[magic][:3]) except KeyError: if len(magic) >= 2: raise ImportError("Unknown magic number %s in %s" % (ord(magic[0])+256*ord(magic[1]), filename)) else: raise ImportError("Bad magic number: '%s'" % magic) if magic_int in (3361,): raise ImportError("%s is interim Python %s (%d) bytecode which is " "not supported.\nFinal released versions are " "supported." % ( filename, magics.versions[magic], magics.magic2int(magic))) elif magic_int == 62135: fp.seek(0) return fix_dropbox_pyc(fp) elif magic_int == 62215: raise ImportError("%s is a dropbox-hacked Python %s (bytecode %d).\n" "See https://github.com/kholia/dedrop for how to " "decrypt." % ( filename, version, magics.magic2int(magic))) try: # print version ts = fp.read(4) timestamp = unpack("I", ts)[0] my_magic_int = magics.magic2int(imp.get_magic()) magic_int = magics.magic2int(magic) # Note: a higher magic number doesn't necessarily mean a later # release. At Python 3.0 the magic number decreased # significantly. Hence the range below. Also note inclusion of # the size info, occurred within a Python major/minor # release. Hence the test on the magic value rather than # PYTHON_VERSION, although PYTHON_VERSION would probably work. if 3200 <= magic_int < 20121: source_size = unpack("I", fp.read(4))[0] # size mod 2**32 else: source_size = None if get_code: if my_magic_int == magic_int: bytecode = fp.read() co = marshal.loads(bytecode) elif fast_load: co = xdis.marsh.load(fp, magic_int, code_objects) else: co = xdis.unmarshal.load_code(fp, magic_int, code_objects) pass else: co = None except: import traceback traceback.print_exc() raise ImportError("Ill-formed bytecode file %s" % filename) finally: fp.close() return version, timestamp, magic_int, co, is_pypy(magic_int), source_size
def extract_files(self): magic_nums: set = set() decompression_errors = 0 successfully_extracted = 0 entry: CTOCEntry for entry in self.toc: data = self.archive_contents[entry. entry_offset:entry.entry_offset + entry.compressed_data_size] if entry.compression_flag: try: data = zlib.decompress(data) except zlib.error as e: decompression_errors += 1 logger.debug( f"[!] PyInstaller CArchive decompression failed with error: {e}" ) continue else: if len(data) != entry.uncompressed_data_size: logger.warning( f"[!] {entry.name} entry in CArchive listed its uncompressed data size as" f" {entry.uncompressed_data_size}, however in actuality, uncompressed to be {len(data)}" " bytes. This may be a sign that the CArchive was manually altered." ) if "\\" in entry.name: tmp: PureWindowsPath = pathlib.PureWindowsPath(entry.name) else: tmp: Path = Path(entry.name) file_path = pathlib.Path(self.output_dir).joinpath(tmp) if len(file_path.parents) > 1: # every path has '.' as a parent file_path.parent.mkdir(parents=True, exist_ok=True) if entry.type_code == self.ArchiveItem.PYSOURCE: if ord(data[:1]) == ord(xdis.marsh.TYPE_CODE) or ord( data[:1]) == (ord(xdis.marsh.TYPE_CODE) | xdis.unmarshal.FLAG_REF): file_path = file_path.parent / (file_path.name + ".pyc") if len(magic_nums) > 1: magic_num = next(iter(magic_nums)) logger.warning( "[!] More than one magic number found within this CArchive. Using magic number" f" {magic_num}, but also found numbers: {magic_nums}" ) elif len(magic_nums) == 0: logger.warning( f"[!] No magic numbers have been found yet, queueing this file for later." ) # TODO: add this file to a do-later list, when you know the magic num #TODO does this actually happen? dig deeper... pass data = pydecipher.bytecode.create_pyc_header( next(iter(magic_nums))) + data else: file_path = file_path.parent / (file_path.name + ".py") if "pyi" not in entry.name: logger.info( f"[!] Potential entrypoint found at script {entry.name}.py" ) elif entry.type_code == self.ArchiveItem.PYMODULE: magic_bytes = data[:4] # Python magic value magic_nums.add(magic2int(magic_bytes)) file_path = file_path.parent / (file_path.name + ".pyc") if entry.type_code != self.ArchiveItem.RUNTIME_OPTION: self.output_dir.mkdir(parents=True, exist_ok=True) with file_path.open(mode="wb") as f: f.write(data) successfully_extracted += 1 if entry.type_code in (self.ArchiveItem.PYZ, self.ArchiveItem.ZIPFILE): output_dir_name = (str( file_path.parent.joinpath( utils.slugify(file_path.name.split(".")[0]))) + "_output") pydecipher.unpack(file_path, output_dir=output_dir_name) if decompression_errors: logger.debug( f"[!] Failed to write {decompression_errors} files due to decompression errors." ) if successfully_extracted: logger.info( f"[+] Successfully extracted {successfully_extracted} files from this CArchive." )
def load_module_from_file_object(fp, filename='<unknown>', code_objects=None, fast_load=False, get_code=True): """load a module from a file object without importing it. See :func:load_module for a list of return values. """ if code_objects is None: code_objects = {} timestamp = 0 try: magic = fp.read(4) magic_int = magics.magic2int(magic) # For reasons I don't understand, PyPy 3.2 stores a magic # of '0'... The two values below are for Python 2.x and 3.x respectively if magic[0:1] in ['0', b'0']: magic = magics.int2magic(3180+7) try: # FIXME: use the internal routine below float_version = float(magics.versions[magic][:3]) # float_version = magics.magic_int2float(magic_int) except KeyError: if magic_int in (2657, 22138): raise ImportError("This smells like Pyston which is not supported.") if len(magic) >= 2: raise ImportError("Unknown magic number %s in %s" % (ord(magic[0:1])+256*ord(magic[1:2]), filename)) else: raise ImportError("Bad magic number: '%s'" % magic) if magic_int in (3010, 3020, 3030, 3040, 3050, 3060, 3061, 3361, 3371): raise ImportError("%s is interim Python %s (%d) bytecode which is " "not supported.\nFinal released versions are " "supported." % ( filename, magics.versions[magic], magics.magic2int(magic))) elif magic_int == 62135: fp.seek(0) return fix_dropbox_pyc(fp) elif magic_int == 62215: raise ImportError("%s is a dropbox-hacked Python %s (bytecode %d).\n" "See https://github.com/kholia/dedrop for how to " "decrypt." % ( filename, magics.versions[magic], magics.magic2int(magic))) try: # print version ts = fp.read(4) my_magic_int = magics.magic2int(imp.get_magic()) magic_int = magics.magic2int(magic) if magic_int == 3393: timestamp = 0 _ = unpack("<I", ts)[0] # hash word 1 _ = unpack("<I", fp.read(4))[0] # hash word 2 elif magic_int in (3394, 3401): timestamp = 0 _ = unpack("<I", fp.read(4))[0] # pep552_bits else: timestamp = unpack("<I", ts)[0] # Note: a higher magic number doesn't necessarily mean a later # release. At Python 3.0 the magic number decreased # significantly. Hence the range below. Also note inclusion of # the size info, occurred within a Python major/minor # release. Hence the test on the magic value rather than # PYTHON_VERSION, although PYTHON_VERSION would probably work. if 3200 <= magic_int < 20121 and magic_int not in (5892, 11913, 39170, 39171): source_size = unpack("<I", fp.read(4))[0] # size mod 2**32 else: source_size = None if get_code: if my_magic_int == magic_int: bytecode = fp.read() co = marshal.loads(bytecode) elif fast_load: co = xdis.marsh.load(fp, magics.magicint2version[magic_int]) else: co = xdis.unmarshal.load_code(fp, magic_int, code_objects) pass else: co = None except: kind, msg = sys.exc_info()[0:2] import traceback traceback.print_exc() raise ImportError("Ill-formed bytecode file %s\n%s; %s" % (filename, kind, msg)) finally: fp.close() return float_version, timestamp, magic_int, co, is_pypy(magic_int), source_size