• 获得PyInstaller打包exe的py源码


     参考链接:https://laucyun.com/33359ed9f725529ac9b606d054c8459d.html

    way1:pyi-archive_viewer 提取pyc,uncompyle6反编译pyc得到py

    way2:python-exe-unpacker   https://github.com/countercept/python-exe-unpacker

    way3:PyInstaller Extractor  https://sourceforge.net/projects/pyinstallerextractor/

    PyInstaller Extractor (修改添加3.7支持)

      1 """
      2 PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
      3 Author : Extreme Coders
      4 E-mail : extremecoders(at)hotmail(dot)com
      5 Web    : https://0xec.blogspot.com
      6 Date   : 29-November-2017
      7 Url    : https://sourceforge.net/projects/pyinstallerextractor/
      8 
      9 For any suggestions, leave a comment on
     10 https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/
     11 
     12 This script extracts a pyinstaller generated executable file.
     13 Pyinstaller installation is not needed. The script has it all.
     14 
     15 For best results, it is recommended to run this script in the
     16 same version of python as was used to create the executable.
     17 This is just to prevent unmarshalling errors(if any) while
     18 extracting the PYZ archive.
     19 
     20 Usage : Just copy this script to the directory where your exe resides
     21         and run the script with the exe file name as a parameter
     22 
     23 C:path	oexe>python pyinstxtractor.py <filename>
     24 $ /path/to/exe/python pyinstxtractor.py <filename>
     25 
     26 Licensed under GNU General Public License (GPL) v3.
     27 You are free to modify this source.
     28 
     29 CHANGELOG
     30 ================================================
     31 
     32 Version 1.1 (Jan 28, 2014)
     33 -------------------------------------------------
     34 - First Release
     35 - Supports only pyinstaller 2.0
     36 
     37 Version 1.2 (Sept 12, 2015)
     38 -------------------------------------------------
     39 - Added support for pyinstaller 2.1 and 3.0 dev
     40 - Cleaned up code
     41 - Script is now more verbose
     42 - Executable extracted within a dedicated sub-directory
     43 
     44 (Support for pyinstaller 3.0 dev is experimental)
     45 
     46 Version 1.3 (Dec 12, 2015)
     47 -------------------------------------------------
     48 - Added support for pyinstaller 3.0 final
     49 - Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
     50 
     51 Version 1.4 (Jan 19, 2016)
     52 -------------------------------------------------
     53 - Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana)
     54 
     55 Version 1.5 (March 1, 2016)
     56 -------------------------------------------------
     57 - Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)
     58 
     59 Version 1.6 (Sept 5, 2016)
     60 -------------------------------------------------
     61 - Added support for pyinstaller 3.2
     62 - Extractor will use a random name while extracting unnamed files.
     63 - For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.
     64 
     65 Version 1.7 (March 13, 2017)
     66 -------------------------------------------------
     67 - Made the script compatible with python 2.6 (Thanks to Ross for reporting)
     68 
     69 Version 1.8 (April 28, 2017)
     70 -------------------------------------------------
     71 - Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)
     72 
     73 Version 1.9 (November 29, 2017)
     74 -------------------------------------------------
     75 - Added support for pyinstaller 3.3
     76 - Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)
     77 
     78 """
     79 
     80 from __future__ import print_function
     81 import os
     82 import struct
     83 import marshal
     84 import zlib
     85 import sys
     86 import imp
     87 import types
     88 from uuid import uuid4 as uniquename
     89 
     90 
     91 class CTOCEntry:
     92     def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):
     93         self.position = position
     94         self.cmprsdDataSize = cmprsdDataSize
     95         self.uncmprsdDataSize = uncmprsdDataSize
     96         self.cmprsFlag = cmprsFlag
     97         self.typeCmprsData = typeCmprsData
     98         self.name = name
     99 
    100 
    101 class PyInstArchive:
    102     PYINST20_COOKIE_SIZE = 24           # For pyinstaller 2.0
    103     PYINST21_COOKIE_SIZE = 24 + 64      # For pyinstaller 2.1+
    104     MAGIC = b'MEI1413121316'  # Magic number which identifies pyinstaller
    105 
    106     def __init__(self, path):
    107         self.filePath = path
    108 
    109 
    110     def open(self):
    111         try:
    112             self.fPtr = open(self.filePath, 'rb')
    113             self.fileSize = os.stat(self.filePath).st_size
    114         except:
    115             print('[*] Error: Could not open {0}'.format(self.filePath))
    116             return False
    117         return True
    118 
    119 
    120     def close(self):
    121         try:
    122             self.fPtr.close()
    123         except:
    124             pass
    125 
    126 
    127     def checkFile(self):
    128         print('[*] Processing {0}'.format(self.filePath))
    129         # Check if it is a 2.0 archive
    130         self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
    131         magicFromFile = self.fPtr.read(len(self.MAGIC))
    132 
    133         if magicFromFile == self.MAGIC:
    134             self.pyinstVer = 20     # pyinstaller 2.0
    135             print('[*] Pyinstaller version: 2.0')
    136             return True
    137 
    138         # Check for pyinstaller 2.1+ before bailing out
    139         self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
    140         magicFromFile = self.fPtr.read(len(self.MAGIC))
    141 
    142         if magicFromFile == self.MAGIC:
    143             print('[*] Pyinstaller version: 2.1+')
    144             self.pyinstVer = 21     # pyinstaller 2.1+
    145             return True
    146 
    147         print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')
    148         return False
    149 
    150 
    151     def getCArchiveInfo(self):
    152         try:
    153             if self.pyinstVer == 20:
    154                 self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
    155 
    156                 # Read CArchive cookie
    157                 (magic, lengthofPackage, toc, tocLen, self.pyver) = 
    158                 struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))
    159 
    160             elif self.pyinstVer == 21:
    161                 self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
    162 
    163                 # Read CArchive cookie
    164                 (magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = 
    165                 struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))
    166 
    167         except:
    168             print('[*] Error : The file is not a pyinstaller archive')
    169             return False
    170 
    171         print('[*] Python version: {0}'.format(self.pyver))
    172 
    173         # Overlay is the data appended at the end of the PE
    174         self.overlaySize = lengthofPackage
    175         self.overlayPos = self.fileSize - self.overlaySize
    176         self.tableOfContentsPos = self.overlayPos + toc
    177         self.tableOfContentsSize = tocLen
    178 
    179         print('[*] Length of package: {0} bytes'.format(self.overlaySize))
    180         return True
    181 
    182 
    183     def parseTOC(self):
    184         # Go to the table of contents
    185         self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)
    186 
    187         self.tocList = []
    188         parsedLen = 0
    189 
    190         # Parse table of contents
    191         while parsedLen < self.tableOfContentsSize:
    192             (entrySize, ) = struct.unpack('!i', self.fPtr.read(4))
    193             nameLen = struct.calcsize('!iiiiBc')
    194 
    195             (entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = 
    196             struct.unpack( 
    197                 '!iiiBc{0}s'.format(entrySize - nameLen), 
    198                 self.fPtr.read(entrySize - 4))
    199 
    200             name = name.decode('utf-8').rstrip('')
    201             if len(name) == 0:
    202                 name = str(uniquename())
    203                 print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name))
    204 
    205             self.tocList.append( 
    206                                 CTOCEntry(                      
    207                                     self.overlayPos + entryPos, 
    208                                     cmprsdDataSize,             
    209                                     uncmprsdDataSize,           
    210                                     cmprsFlag,                  
    211                                     typeCmprsData,              
    212                                     name                        
    213                                 ))
    214 
    215             parsedLen += entrySize
    216         print('[*] Found {0} files in CArchive'.format(len(self.tocList)))
    217 
    218 
    219 
    220     def extractFiles(self):
    221         print('[*] Beginning extraction...please standby')
    222         extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')
    223 
    224         if not os.path.exists(extractionDir):
    225             os.mkdir(extractionDir)
    226 
    227         os.chdir(extractionDir)
    228 
    229         for entry in self.tocList:
    230             basePath = os.path.dirname(entry.name)
    231             if basePath != '':
    232                 # Check if path exists, create if not
    233                 if not os.path.exists(basePath):
    234                     os.makedirs(basePath)
    235 
    236             self.fPtr.seek(entry.position, os.SEEK_SET)
    237             data = self.fPtr.read(entry.cmprsdDataSize)
    238 
    239             if entry.cmprsFlag == 1:
    240                 data = zlib.decompress(data)
    241                 # Malware may tamper with the uncompressed size
    242                 # Comment out the assertion in such a case
    243                 assert len(data) == entry.uncmprsdDataSize # Sanity Check
    244 
    245             with open(entry.name, 'wb') as f:
    246                 f.write(data)
    247 
    248             if entry.typeCmprsData == b's':
    249                 print('[+] Possible entry point: {0}'.format(entry.name))
    250 
    251             elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':
    252                 self._extractPyz(entry.name)
    253 
    254 
    255     def _extractPyz(self, name):
    256         dirName =  name + '_extracted'
    257         # Create a directory for the contents of the pyz
    258         if not os.path.exists(dirName):
    259             os.mkdir(dirName)
    260 
    261         with open(name, 'rb') as f:
    262             pyzMagic = f.read(4)
    263             assert pyzMagic == b'PYZ' # Sanity Check
    264 
    265             pycHeader = f.read(4) # Python magic value
    266 
    267             if imp.get_magic() != pycHeader:
    268                 print('[!] Warning: The script is running in a different python version than the one used to build the executable')
    269                 print('    Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver))
    270 
    271             (tocPosition, ) = struct.unpack('!i', f.read(4))
    272             f.seek(tocPosition, os.SEEK_SET)
    273 
    274             try:
    275                 toc = marshal.load(f)
    276             except:
    277                 print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name))
    278                 return
    279 
    280             print('[*] Found {0} files in PYZ archive'.format(len(toc)))
    281 
    282             # From pyinstaller 3.1+ toc is a list of tuples
    283             if type(toc) == list:
    284                 toc = dict(toc)
    285 
    286             for key in toc.keys():
    287                 (ispkg, pos, length) = toc[key]
    288                 f.seek(pos, os.SEEK_SET)
    289 
    290                 fileName = key
    291                 try:
    292                     # for Python > 3.3 some keys are bytes object some are str object
    293                     fileName = key.decode('utf-8')
    294                 except:
    295                     pass
    296 
    297                 # Make sure destination directory exists, ensuring we keep inside dirName
    298                 destName = os.path.join(dirName, fileName.replace("..", "__"))
    299                 destDirName = os.path.dirname(destName)
    300                 if not os.path.exists(destDirName):
    301                     os.makedirs(destDirName)
    302 
    303                 try:
    304                     data = f.read(length)
    305                     data = zlib.decompress(data)
    306                 except:
    307                     print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName))
    308                     open(destName + '.pyc.encrypted', 'wb').write(data)
    309                     continue
    310 
    311                 with open(destName + '.pyc', 'wb') as pycFile:
    312                     pycFile.write(pycHeader)      # Write pyc magic
    313                     pycFile.write(b'' * 4)      # Write timestamp
    314                     if self.pyver >= 33:
    315                         pycFile.write(b'' * 4)  # Size parameter added in Python 3.3
    316                     if self.pyver >= 37:
    317                         pycFile.write(b'' * 4)  # Size parameter added in Python 3.7
    318                     pycFile.write(data)
    319 
    320 
    321 def main():
    322     if len(sys.argv) < 2:
    323         print('[*] Usage: pyinstxtractor.py <filename>')
    324 
    325     else:
    326         arch = PyInstArchive(sys.argv[1])
    327         if arch.open():
    328             if arch.checkFile():
    329                 if arch.getCArchiveInfo():
    330                     arch.parseTOC()
    331                     arch.extractFiles()
    332                     arch.close()
    333                     print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
    334                     print('')
    335                     print('You can now use a python decompiler on the pyc files within the extracted directory')
    336                     return
    337 
    338             arch.close()
    339 
    340 
    341 if __name__ == '__main__':
    342     main()
    View Code

    推荐使用pyi-archive_viewer。而python-exe-unpacker 有时会报错,(两年前的工具了)


    使用pyi-archive_viewer需要先安装PyInstaller(https://github.com/pyinstaller/pyinstaller

    • 安装PyInstaller模块,注意是:pip install PyInstaller

    pyi-archive_viewer

     相关命令:

    U: go Up one level
    O <name>: open embedded archive name
    X <name>: extract name
    Q: quit

    • pyi-archive_viewer中使用x命令提取文件

    pyinstaller打包会去掉pyc的magic和时间戳,需要手动添加数据

    (可以先提取第一个文件,后面提取的文件对比添加数据)

     对照添加数据

    主模块的依赖包在PYZ-00.pyz,使用o命令打开

     之后再使用x命令提取文件。

    • 反编译pyc

    pip install uncompyle6

    uncompyle6 (https://github.com/rocky/python-uncompyle6)

    Run

    $ uncompyle6 *compiled-python-file-pyc-or-pyo*
    

    For usage help:

    $ uncompyle6 -h

     当然也有很多pyc反编译在线工具可用


    • PyInstaller Extractor

     python pyinstxtractor.py xxx.exe

     

     生成目录

  • 相关阅读:
    生成括号问题(22)
    Starting Jetty: Exception in thread "main" java.lang.UnsupportedClassVersionError: org/eclipse/jetty/start/Main : Unsupported major.minor version 52.0
    Oracle 使用Nid 修改数据库的DBID 和 Database Name
    Oracle SCN与时间的相互转换
    Oracle 启动 停止JOB
    Apache 负载均衡 端口转发 配置
    Oracle 将 A 用户下所有表的增删改查 赋予 B 用户
    更改表空间及数据文件的名称
    Oracle 缓存命中率问题一则(里面有个问题咨询大佬们)
    更改python版本
  • 原文地址:https://www.cnblogs.com/DirWang/p/12018949.html
Copyright © 2020-2023  润新知