当前位置: 代码网 > it编程>前端脚本>Python > 将PyInstaller打包的exe文件转换为pyc文件的方法

将PyInstaller打包的exe文件转换为pyc文件的方法

2026年03月01日 Python 我要评论
一、将exe文件转换成pyc文件新建一个unpack.py文件,将以下代码复制粘贴进去from __future__ import print_functionimport osimport stru

一、将exe文件转换成pyc文件

  1. 新建一个unpack.py文件,将以下代码复制粘贴进去
from __future__ import print_function
import os
import struct
import marshal
import zlib
import sys
from uuid import uuid4 as uniquename


class ctocentry:
    def __init__(self, position, cmprsddatasize, uncmprsddatasize, cmprsflag, typecmprsdata, name):
        self.position = position
        self.cmprsddatasize = cmprsddatasize
        self.uncmprsddatasize = uncmprsddatasize
        self.cmprsflag = cmprsflag
        self.typecmprsdata = typecmprsdata
        self.name = name


class pyinstarchive:
    pyinst20_cookie_size = 24           # for pyinstaller 2.0
    pyinst21_cookie_size = 24 + 64      # for pyinstaller 2.1+
    magic = b'mei\014\013\012\013\016'  # magic number which identifies pyinstaller

    def __init__(self, path):
        self.filepath = path
        self.pycmagic = b'\0' * 4
        self.barepyclist = [] # list of pyc's whose headers have to be fixed


    def open(self):
        try:
            self.fptr = open(self.filepath, 'rb')
            self.filesize = os.stat(self.filepath).st_size
        except:
            print('[!] error: could not open {0}'.format(self.filepath))
            return false
        return true


    def close(self):
        try:
            self.fptr.close()
        except:
            pass


    def checkfile(self):
        print('[+] processing {0}'.format(self.filepath))

        searchchunksize = 8192
        endpos = self.filesize
        self.cookiepos = -1

        if endpos < len(self.magic):
            print('[!] error : file is too short or truncated')
            return false

        while true:
            startpos = endpos - searchchunksize if endpos >= searchchunksize else 0
            chunksize = endpos - startpos

            if chunksize < len(self.magic):
                break

            self.fptr.seek(startpos, os.seek_set)
            data = self.fptr.read(chunksize)

            offs = data.rfind(self.magic)

            if offs != -1:
                self.cookiepos = startpos + offs
                break

            endpos = startpos + len(self.magic) - 1

            if startpos == 0:
                break

        if self.cookiepos == -1:
            print('[!] error : missing cookie, unsupported pyinstaller version or not a pyinstaller archive')
            return false

        self.fptr.seek(self.cookiepos + self.pyinst20_cookie_size, os.seek_set)

        if b'python' in self.fptr.read(64).lower():
            print('[+] pyinstaller version: 2.1+')
            self.pyinstver = 21     # pyinstaller 2.1+
        else:
            self.pyinstver = 20     # pyinstaller 2.0
            print('[+] pyinstaller version: 2.0')

        return true


    def getcarchiveinfo(self):
        try:
            if self.pyinstver == 20:
                self.fptr.seek(self.cookiepos, os.seek_set)

                # read carchive cookie
                (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, pyver, pylibname) = \
                struct.unpack('!8siiii64s', self.fptr.read(self.pyinst21_cookie_size))

        except:
            print('[!] error : the file is not a pyinstaller archive')
            return false

        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)

        # overlay is the data appended at the end of the pe
        self.overlaysize = lengthofpackage + tailbytes
        self.overlaypos = self.filesize - self.overlaysize
        self.tableofcontentspos = self.overlaypos + toc
        self.tableofcontentssize = toclen

        print('[+] length of package: {0} bytes'.format(lengthofpackage))
        return true


    def parsetoc(self):
        # go to the table of contents
        self.fptr.seek(self.tableofcontentspos, os.seek_set)

        self.toclist = []
        parsedlen = 0

        # parse table of contents
        while parsedlen < self.tableofcontentssize:
            (entrysize, ) = struct.unpack('!i', self.fptr.read(4))
            namelen = struct.calcsize('!iiiibc')

            (entrypos, cmprsddatasize, uncmprsddatasize, cmprsflag, typecmprsdata, name) = \
            struct.unpack( \
                '!iiibc{0}s'.format(entrysize - namelen), \
                self.fptr.read(entrysize - 4))

            try:
                name = name.decode("utf-8").rstrip("\0")
            except unicodedecodeerror:
                newname = str(uniquename())
                print('[!] warning: file name {0} contains invalid bytes. using random name {1}'.format(name, newname))
                name = newname
            
            # prevent writing outside the extraction directory
            if name.startswith("/"):
                name = name.lstrip("/")

            if len(name) == 0:
                name = str(uniquename())
                print('[!] warning: found an unamed file in carchive. using random name {0}'.format(name))

            self.toclist.append( \
                                ctocentry(                      \
                                    self.overlaypos + entrypos, \
                                    cmprsddatasize,             \
                                    uncmprsddatasize,           \
                                    cmprsflag,                  \
                                    typecmprsdata,              \
                                    name                        \
                                ))

            parsedlen += entrysize
        print('[+] found {0} files in carchive'.format(len(self.toclist)))


    def _writerawdata(self, filepath, data):
        nm = filepath.replace('\\', os.path.sep).replace('/', os.path.sep).replace('..', '__')
        nmdir = os.path.dirname(nm)
        if nmdir != '' and not os.path.exists(nmdir): # check if path exists, create if not
            os.makedirs(nmdir)

        with open(nm, 'wb') as f:
            f.write(data)


    def extractfiles(self):
        print('[+] beginning extraction...please standby')
        extractiondir = os.path.join(os.getcwd(), os.path.basename(self.filepath) + '_extracted')

        if not os.path.exists(extractiondir):
            os.mkdir(extractiondir)

        os.chdir(extractiondir)

        for entry in self.toclist:
            self.fptr.seek(entry.position, os.seek_set)
            data = self.fptr.read(entry.cmprsddatasize)

            if entry.cmprsflag == 1:
                try:
                    data = zlib.decompress(data)
                except zlib.error:
                    print('[!] error : failed to decompress {0}'.format(entry.name))
                    continue
                # malware may tamper with the uncompressed size
                # 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
                print('[+] possible entry point: {0}.pyc'.format(entry.name))

                if self.pycmagic == b'\0' * 4:
                    # if we don't have the pyc header yet, fix them in a later pass
                    self.barepyclist.append(entry.name + '.pyc')
                self._writepyc(entry.name + '.pyc', data)

            elif entry.typecmprsdata == b'm' or entry.typecmprsdata == b'm':
                # m -> archive_item_pypackage
                # m -> archive_item_pymodule
                # packages and modules are pyc files with their header intact

                # from pyinstaller 5.3 and above pyc headers are no longer stored
                # https://github.com/pyinstaller/pyinstaller/commit/a97fdf
                if data[2:4] == b'\r\n':
                    # < pyinstaller 5.3
                    if self.pycmagic == b'\0' * 4: 
                        self.pycmagic = data[0:4]
                    self._writerawdata(entry.name + '.pyc', data)

                else:
                    # >= pyinstaller 5.3
                    if self.pycmagic == b'\0' * 4:
                        # if we don't have the pyc header yet, fix them in a later pass
                        self.barepyclist.append(entry.name + '.pyc')

                    self._writepyc(entry.name + '.pyc', data)

            else:
                self._writerawdata(entry.name, data)

                if entry.typecmprsdata == b'z' or entry.typecmprsdata == b'z':
                    self._extractpyz(entry.name)

        # fix bare pyc's if any
        self._fixbarepycs()


    def _fixbarepycs(self):
        for pycfile in self.barepyclist:
            with open(pycfile, 'r+b') as pycfile:
                # overwrite the first four bytes
                pycfile.write(self.pycmagic)


    def _writepyc(self, filename, data):
        with open(filename, 'wb') as pycfile:
            pycfile.write(self.pycmagic)            # pyc magic

            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.pymaj >= 3 and self.pymin >= 3:
                    pycfile.write(b'\0' * 4)  # size parameter added in python 3.3

            pycfile.write(data)


    def _extractpyz(self, name):
        dirname =  name + '_extracted'
        # create a directory for the contents of the pyz
        if not os.path.exists(dirname):
            os.mkdir(dirname)

        with open(name, 'rb') as f:
            pyzmagic = f.read(4)
            assert pyzmagic == b'pyz\0' # sanity check

            pyzpycmagic = f.read(4) # python magic value

            if self.pycmagic == b'\0' * 4:
                self.pycmagic = pyzpycmagic

            elif self.pycmagic != pyzpycmagic:
                self.pycmagic = pyzpycmagic
                print('[!] warning: pyc magic of files inside pyz archive are different from those in carchive')

            # skip pyz extraction if not running under the same python version
            if self.pymaj != sys.version_info.major or self.pymin != sys.version_info.minor:
                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}.{1} to prevent extraction errors during unmarshalling'.format(self.pymaj, self.pymin))
                print('[!] skipping pyz extraction')
                return

            (tocposition, ) = struct.unpack('!i', f.read(4))
            f.seek(tocposition, os.seek_set)

            try:
                toc = marshal.load(f)
            except:
                print('[!] unmarshalling failed. cannot extract {0}. extracting remaining files.'.format(name))
                return

            print('[+] found {0} files in pyz archive'.format(len(toc)))

            # from pyinstaller 3.1+ toc is a list of tuples
            if type(toc) == list:
                toc = dict(toc)

            for key in toc.keys():
                (ispkg, pos, length) = toc[key]
                f.seek(pos, os.seek_set)
                filename = key

                try:
                    # for python > 3.3 some keys are bytes object some are str object
                    filename = filename.decode('utf-8')
                except:
                    pass

                # prevent writing outside dirname
                filename = filename.replace('..', '__').replace('.', os.path.sep)

                if ispkg == 1:
                    filepath = os.path.join(dirname, filename, '__init__.pyc')

                else:
                    filepath = os.path.join(dirname, filename + '.pyc')

                filedir = os.path.dirname(filepath)
                if not os.path.exists(filedir):
                    os.makedirs(filedir)

                try:
                    data = f.read(length)
                    data = zlib.decompress(data)
                except:
                    print('[!] error: failed to decompress {0}, probably encrypted. extracting as is.'.format(filepath))
                    open(filepath + '.encrypted', 'wb').write(data)
                else:
                    self._writepyc(filepath, data)


def main():
    if len(sys.argv) < 2:
        print('[+] usage: pyinstxtractor.py <filename>')

    else:
        arch = pyinstarchive(sys.argv[1])
        if arch.open():
            if arch.checkfile():
                if arch.getcarchiveinfo():
                    arch.parsetoc()
                    arch.extractfiles()
                    arch.close()
                    print('[+] successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
                    print('')
                    print('you can now use a python decompiler on the pyc files within the extracted directory')
                    return

            arch.close()


if __name__ == '__main__':
    main()

  1. 暴富.exeunpack.py放在同一个目录中,cmd执行如下命令:等待出现successfully
python unpack.py 暴富.exe

在同目录下生成的暴富.exe_extracted文件夹里找到暴富.pyc

二、将pyc文件反编译成py代码

暴富.pyc拖入pylingual反编译器进行反编译,如果打不开网址就使用魔法

以上就是将pyinstaller打包的exe文件转换为pyc文件的方法的详细内容,更多关于pyinstaller exe文件转为pyc文件的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com