Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
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
Пример #4
0
    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)
Пример #5
0
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
Пример #6
0
    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)
Пример #7
0
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
Пример #8
0
    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()))
Пример #9
0
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
Пример #10
0
    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)
Пример #11
0
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,
    )
Пример #12
0
    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
Пример #13
0
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
Пример #14
0
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
Пример #15
0
    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."
            )
Пример #16
0
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