一、将exe文件转换成pyc文件
- 新建一个
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()
- 将
暴富.exe跟unpack.py放在同一个目录中,cmd执行如下命令:等待出现successfully
python unpack.py 暴富.exe

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

二、将pyc文件反编译成py代码
将暴富.pyc拖入pylingual反编译器进行反编译,如果打不开网址就使用魔法

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