700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > pyinstall 打包python3.7.1的exe反编译

pyinstall 打包python3.7.1的exe反编译

时间:2019-03-17 01:21:15

相关推荐

pyinstall 打包python3.7.1的exe反编译

pyinstall 打包python3.7.1的exe反编译

由于 pyinstall打包的EXE文件,被360认为可能有木马病毒,首先怀疑计算数机系统有毒。用360杀毒,病毒倒是查处了十几个,对于一个对计算机不是十分精通的人来说,只能按360的提示去做,结果造成许多应用程序的快捷键丢失,三搞两搞,系统崩溃,重做系统,一不小心,把python的源文件丢失,半个多月的心血全费(我是刚刚学习python)。不幸中的万幸,单位电脑中有EXE文件,便产生了反编译的念头。我使用的python是3.7.1 ,与网上提供人资料有点不同,反反复复的查月资料,不断实践、改进、探索,基本恢复python的源文件。实践过程如下:第一步:把EXE反编译为pyc文件。根据网上提供的资料,我选了两个文件 archive_viewer.py 和 pyinstxtractor.py反编译exe(由于 archive_viewer.py反编译的文件,最终没能成共把exe反编译为PY),其操作过程就不写了。pyinstxtractor.py

"""PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)Author : Extreme CodersE-mail : extremecoders(at)hotmail(dot)comWeb : Date : 29-November-Url : /projects/pyinstallerextractor/For any suggestions, leave a comment on/topic/34455-pyinstaller-extractor/This script extracts a pyinstaller generated executable file.Pyinstaller installation is not needed. The script has it all.For best results, it is recommended to run this script in thesame version of python as was used to create the executable.This is just to prevent unmarshalling errors(if any) whileextracting the PYZ archive.Usage : Just copy this script to the directory where your exe residesand run the script with the exe file name as a parameterC:\path\to\exe\>python pyinstxtractor.py <filename>$ /path/to/exe/python pyinstxtractor.py <filename>Licensed under GNU General Public License (GPL) v3.You are free to modify this source.CHANGELOG================================================Version 1.1 (Jan 28, )-------------------------------------------------- First Release- Supports only pyinstaller 2.0Version 1.2 (Sept 12, )-------------------------------------------------- Added support for pyinstaller 2.1 and 3.0 dev- Cleaned up code- Script is now more verbose- Executable extracted within a dedicated sub-directory(Support for pyinstaller 3.0 dev is experimental)Version 1.3 (Dec 12, )-------------------------------------------------- Added support for pyinstaller 3.0 final- Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)Version 1.4 (Jan 19, )-------------------------------------------------- Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: /Djamana)Version 1.5 (March 1, )-------------------------------------------------- Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)Version 1.6 (Sept 5, )-------------------------------------------------- Added support for pyinstaller 3.2- Extractor will use a random name while extracting unnamed files.- For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.Version 1.7 (March 13, )-------------------------------------------------- Made the script compatible with python 2.6 (Thanks to Ross for reporting)Version 1.8 (April 28, )-------------------------------------------------- Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)Version 1.9 (November 29, )-------------------------------------------------- Added support for pyinstaller 3.3- Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)"""from __future__ import print_functionimport osimport structimport marshalimport zlibimport sysimport impimport typesfrom uuid import uuid4 as uniquenameclass CTOCEntry:def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):self.position = positionself.cmprsdDataSize = cmprsdDataSizeself.uncmprsdDataSize = uncmprsdDataSizeself.cmprsFlag = cmprsFlagself.typeCmprsData = typeCmprsDataself.name = nameclass PyInstArchive:PYINST20_COOKIE_SIZE = 24 # For pyinstaller 2.0PYINST21_COOKIE_SIZE = 24 + 64# For pyinstaller 2.1+MAGIC = b'MEI\014\013\012\013\016' # Magic number which identifies pyinstallerdef __init__(self, path):self.filePath = pathdef open(self):try:self.fPtr = open(self.filePath, 'rb')self.fileSize = os.stat(self.filePath).st_sizeexcept:print('[*] Error: Could not open {0}'.format(self.filePath))return Falsereturn Truedef close(self):try:self.fPtr.close()except:passdef checkFile(self):print('[*] Processing {0}'.format(self.filePath))# Check if it is a 2.0 archiveself.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)magicFromFile = self.fPtr.read(len(self.MAGIC))if magicFromFile == self.MAGIC:self.pyinstVer = 20# pyinstaller 2.0print('[*] Pyinstaller version: 2.0')return True# Check for pyinstaller 2.1+ before bailing outself.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)magicFromFile = self.fPtr.read(len(self.MAGIC))if magicFromFile == self.MAGIC:print('[*] Pyinstaller version: 2.1+')self.pyinstVer = 21# pyinstaller 2.1+return Trueprint('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')return Falsedef getCArchiveInfo(self):try:if self.pyinstVer == 20:self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)# Read CArchive cookie(magic, lengthofPackage, toc, tocLen, self.pyver) = \struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))elif self.pyinstVer == 21:self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)# Read CArchive cookie(magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))except:print('[*] Error : The file is not a pyinstaller archive')return Falseprint('[*] Python version: {0}'.format(self.pyver))# Overlay is the data appended at the end of the PEself.overlaySize = lengthofPackageself.overlayPos = self.fileSize - self.overlaySizeself.tableOfContentsPos = self.overlayPos + tocself.tableOfContentsSize = tocLenprint('[*] Length of package: {0} bytes'.format(self.overlaySize))return Truedef parseTOC(self):# Go to the table of contentsself.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)self.tocList = []parsedLen = 0# Parse table of contentswhile 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))name = name.decode('utf-8').rstrip('\0')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 += entrySizeprint('[*] Found {0} files in CArchive'.format(len(self.tocList)))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:basePath = os.path.dirname(entry.name)if basePath != '':# Check if path exists, create if notif not os.path.exists(basePath):os.makedirs(basePath)self.fPtr.seek(entry.position, os.SEEK_SET)data = self.fPtr.read(entry.cmprsdDataSize)if entry.cmprsFlag == 1:data = zlib.decompress(data)# Malware may tamper with the uncompressed size# Comment out the assertion in such a caseassert len(data) == entry.uncmprsdDataSize # Sanity Checkwith open(entry.name, 'wb') as f:f.write(data)if entry.typeCmprsData == b's':print('[+] Possible entry point: {0}'.format(entry.name))elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':self._extractPyz(entry.name)def _extractPyz(self, name):dirName = name + '_extracted'# Create a directory for the contents of the pyzif not os.path.exists(dirName):os.mkdir(dirName)with open(name, 'rb') as f:pyzMagic = f.read(4)assert pyzMagic == b'PYZ\0' # Sanity CheckpycHeader = f.read(4) # Python magic valueif imp.get_magic() != pycHeader:print('[!] Warning: The script is running in a different python version than the one used to build the executable')print(' Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver))(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))returnprint('[*] Found {0} files in PYZ archive'.format(len(toc)))# From pyinstaller 3.1+ toc is a list of tuplesif type(toc) == list:toc = dict(toc)for key in toc.keys():(ispkg, pos, length) = toc[key]f.seek(pos, os.SEEK_SET)fileName = keytry:# for Python > 3.3 some keys are bytes object some are str objectfileName = key.decode('utf-8')except:pass# Make sure destination directory exists, ensuring we keep inside dirNamedestName = os.path.join(dirName, fileName.replace("..", "__"))destDirName = os.path.dirname(destName)if not os.path.exists(destDirName):os.makedirs(destDirName)try:data = f.read(length)data = zlib.decompress(data)except:print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName))open(destName + '.pyc.encrypted', 'wb').write(data)continuewith open(destName + '.pyc', 'wb') as pycFile:pycFile.write(pycHeader)# Write pyc magicpycFile.write(b'\0' * 4)# Write timestampif self.pyver >= 33:pycFile.write(b'\0' * 4) # Size parameter added in Python 3.3pycFile.write(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')returnarch.close()if __name__ == '__main__':main()

把pyinstxtractor.py 与exe放在同目录下,执行

python pyinstxtractor.py ZkDuibi1.exe(我的应用程序ZkDuibi1.exe)

生成一个Zkduibi1.exe_extracted文件夹。

可以看红线标出的部分,第一个文件夹是库文件,有系统的,也有自己编写的,好在我编写都是以My_xxx_xxx开,虽然是初次学习反编译,但还是很容易找出了自己编写的文件。第二个文件记录着pyc文件的头信息。第三个文件是自己的pyc文件,只是没有扩展名,可自己家上,到此,反编译第一步(exe-------pyc)完成。

由于pyinstall把py编译为exe时,把pyc文件的头部信息去掉,所以,我们反编译时要动手加入头部信息。

打开第二、第三个文件(我用HXD)

比较这俩个文件的头部,发现zkduibi比struct少了十六个字节(据说是版本信息等等,不要搞明白),添加上这十六个字节。

下一步就是把pyc反编译为py,先用网上的在线工具均没成功。

后用uncompyle6 反编译成功!uncompyle6的安装,

pip install uncompyle6

执行uncompyle6 xxxx.pyc > xxx.py即可。

下一步反编译PYZ-00.pyz_extracted中的文件,用HXD打开文件,与struct比较,发现至少4个字节(以E3为标),增加4个字节(00 00 00 00)(如过不增加这4 个字节,错误如下)

本想到此,就能顺利完成反编译,结果,有的文件能顺利反编译,有的不能。错误如下

从图中“GBK”可知,应与编码有关,查阅“\ufffd”是python中不可显示的中文,到底是什么,忽然想起,在编写 py文件时有过怪字符现象。故编写了只有几个怪字符的py文件,用HXD打开,发现怪字符编码为EF BF BD (�),紧接着在用HDX打开反编译出错的文件,查找EF BF BD (�)替换为可显示字符(任意如 aaa)。存盘,再次反编译成功。

反编译工作完成!!!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。