• pyinstaller 打包exe 防反编译(加密)


    如果对安全性要求不高,可以选择直接将py文件,打包成exe即可,解压出来的是pyc文件,pyc文件还原成源码,也是有一定复杂度的,相对而言也是有一定的安全性的

    在源文件同级目录下创建build文件

    buidl_pyd.py

    1. # -*- ecoding: utf-8 -*-
    2. # @ModuleName: build_pyd
    3. # @Function:
    4. # @Author: darling
    5. # @Time: 2022-11-12 14:58
    6. # -*- coding: utf-8 -*-
    7. from distutils.core import setup
    8. from Cython.Build import cythonize
    9. setup(
    10. name='any words.....',
    11. ext_modules=cythonize(["you_source_file.py", ]
    12. ),
    13. )

    打开cmd执行 python .\build_pyd.py build_ext --inplace
    同级目录下会生成一个.pyd的文件you_source_file.cp39-win_amd64.pyd
    修改成和源文件名一致

    同级目录下创建一个main.py的入口文件

    注意:

    1、程序的__main__入口只能有一个,如果源py文件中有定义main入口,需要注释掉并调整代码缩进,否则通过main.py调用pyd文件遇到if name == ‘main’:之后的代码都不会运行。
    2、源文件you_source_file.py文件头部import到的第三方库需全部复制到main.py文件头部,不然运行会闪退,相当于是,引用包没打进去

    main.py

    1. # -*- ecoding: utf-8 -*-
    2. # @ModuleName: main
    3. # @Function:
    4. # @Author: darling
    5. # @Time: 2022-11-12 15:21
    6. import hashlib
    7. import json
    8. import math
    9. import os
    10. import sys
    11. import time
    12. import urllib.parse
    13. from concurrent.futures import ThreadPoolExecutor
    14. import execjs
    15. import requests
    16. from loguru import logger
    17. sys.path.append('cron/message_dic.py')
    18. from cron.message_dic import message as msg
    19. global client
    20. client = ''
    21. global token
    22. token = ''
    23. global user
    24. user = ''
    25. global pro, cus, username
    26. global result
    27. result = {}
    28. import you_source_file
    29. if __name__ == '__main__':
    30. you_source_file()

    pyinstaller打包时,执行main方法进行打包image

    扩展

    包解压命令

    在exe同路径文件中放入pyinstxtractor.py文件
    cmd运行python pyinstxtractor.py .\main.exe
    将exe解包,会得到相应的文件,如果是pyd文件,则是安全的,如果直接打包,则解压出来的是pyc文件,不安全pyinstxtractor.py

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

    传送门

  • 相关阅读:
    设计模式之代理模式
    2022年9月8号Java的23设计模式学习(课时一)单例模式
    4. 条件查询
    水声信号调制方式智能识别技术
    西门子V20变频器调试基本步骤示例
    【Ribbon】SpringCloud的Ribbon负载均衡使用
    部署搭建decentraland流程讲解
    SwiftUI——如何使用新的NavigationStack和NavigationSplitView(如何页面跳转2.0以及如何制作侧栏)
    张量的基本概念+张量的聚合、拼接、比较、随机化采样、序列化等操作+升维、降维
    高可用集群HA、LVS+Keepalived、健康检测
  • 原文地址:https://blog.csdn.net/brightgreat/article/details/127823005