Python反编译教程(exe转py) 适用于目前最新的3.13

Python反编译教程(exe转py) 适用于目前最新的3.13

文章目录

一、将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 classCTOCEntry: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 classPyInstArchive: 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 pyinstallerdef__init__(self, path): self.filePath = path self.pycMagic =b'\0'*4 self.barePycList =[]# List of pyc's whose headers have to be fixeddefopen(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))returnFalsereturnTruedefclose(self):try: self.fPtr.close()except:passdefcheckFile(self):print('[+] Processing {0}'.format(self.filePath)) searchChunkSize =8192 endPos = self.fileSize self.cookiePos =-1if endPos <len(self.MAGIC):print('[!] Error : File is too short or truncated')returnFalsewhileTrue: startPos = endPos - searchChunkSize if endPos >= searchChunkSize else0 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)-1if startPos ==0:breakif self.cookiePos ==-1:print('[!] Error : Missing cookie, unsupported pyinstaller version or not a pyinstaller archive')returnFalse self.fPtr.seek(self.cookiePos + self.PYINST20_COOKIE_SIZE, os.SEEK_SET)ifb'python'in self.fPtr.read(64).lower():print('[+] Pyinstaller version: 2.1+') self.pyinstVer =21# pyinstaller 2.1+else: self.pyinstVer =20# pyinstaller 2.0print('[+] Pyinstaller version: 2.0')returnTruedefgetCArchiveInfo(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')returnFalse self.pymaj, self.pymin =(pyver//100, pyver%100)if pyver >=100else(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 ==20else 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))returnTruedefparseTOC(self):# Go to the table of contents self.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))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 directoryif name.startswith("/"): name = name.lstrip("/")iflen(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 !=''andnot os.path.exists(nmDir):# Check if path exists, create if not os.makedirs(nmDir)withopen(nm,'wb')as f: f.write(data)defextractFiles(self):print('[+] Beginning extraction...please standby') extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath)+'_extracted')ifnot 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 caseassertlen(data)== entry.uncmprsdDataSize # Sanity Checkif entry.typeCmprsData ==b'd'or entry.typeCmprsData ==b'o':# d -> ARCHIVE_ITEM_DEPENDENCY# o -> ARCHIVE_ITEM_RUNTIME_OPTION# These are runtime options, not filescontinue basePath = os.path.dirname(entry.name)if basePath !='':# Check if path exists, create if notifnot os.path.exists(basePath): os.makedirs(basePath)if entry.typeCmprsData ==b's':# s -> ARCHIVE_ITEM_PYSOURCE# Entry point are expected to be python scriptsprint('[+] 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/a97fdfif data[2:4]==b'\r\n':# < pyinstaller 5.3if self.pycMagic ==b'\0'*4: self.pycMagic = data[0:4] self._writeRawData(entry.name +'.pyc', data)else:# >= pyinstaller 5.3if 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:withopen(pycFile,'r+b')as pycFile:# Overwrite the first four bytes pycFile.write(self.pycMagic)def_writePyc(self, filename, data):withopen(filename,'wb')as pycFile: pycFile.write(self.pycMagic)# pyc magicif self.pymaj >=3and 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)# Timestampif self.pymaj >=3and 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 pyzifnot os.path.exists(dirName): os.mkdir(dirName)withopen(name,'rb')as f: pyzMagic = f.read(4)assert pyzMagic ==b'PYZ\0'# Sanity Check pyzPycMagic = f.read(4)# Python magic valueif 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 versionif 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))returnprint('[+] Found {0} files in PYZ archive'.format(len(toc)))# From pyinstaller 3.1+ toc is a list of tuplesiftype(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)ifnot 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)defmain():iflen(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反编译器进行反编译,如果打不开网址就使用魔法

在这里插入图片描述


经测试,PyLingual能够正确反编译3.13及以下所有版本的pyc🐂🍺

Read more

Flutter 三方库 flutter_image_test_utils 的鸿蒙化适配指南 - 实现端侧 UI 测试中的网络图片模拟、支持 HTTP 图片请求劫持与自动化渲染一致性验证实战

Flutter 三方库 flutter_image_test_utils 的鸿蒙化适配指南 - 实现端侧 UI 测试中的网络图片模拟、支持 HTTP 图片请求劫持与自动化渲染一致性验证实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 flutter_image_test_utils 的鸿蒙化适配指南 - 实现端侧 UI 测试中的网络图片模拟、支持 HTTP 图片请求劫持与自动化渲染一致性验证实战 前言 在进行 Flutter for OpenHarmony 的自动化 UI 测试(Widget Test / Integration Test)时,网络图片的加载往往是最大的“变数”。由于测试环境可能处于隔离内网或不稳定的网络中,真实的图片下载会导致测试用例因超时而断断续续。flutter_image_test_utils 是一款强大的测试辅助库,它能完美模拟(Mock)网络图片请求。本文将指导大家如何在鸿蒙端构建极致稳定的视觉回归测试。 一、原原理性解析 / 概念介绍 1.1

By Ne0inhk
鸿蒙APP开发从入门到精通:鸿蒙电商购物车全栈项目——用户管理、商品列表、购物车

鸿蒙APP开发从入门到精通:鸿蒙电商购物车全栈项目——用户管理、商品列表、购物车

《鸿蒙APP开发从入门到精通》第13篇:鸿蒙电商购物车全栈项目——用户管理、商品列表、购物车 🛒📱 内容承接与核心价值 这是《鸿蒙APP开发从入门到精通》的第13篇——用户管理、商品列表、购物车篇,100%承接第12篇的「运维监控、生态运营与专属变现」项目架构,完成鸿蒙电商购物车全栈项目的基础功能实现。 学习目标: * 掌握用户管理的设计与实现; * 实现用户注册、登录、用户信息管理; * 理解商品列表的设计与实现; * 实现商品列表、商品详情、商品搜索; * 掌握购物车管理的设计与实现; * 实现添加商品到购物车、修改购物车商品数量、删除购物车商品; * 优化用户管理、商品列表、购物车的用户体验(响应速度、数据安全、用户反馈)。 学习重点: * 鸿蒙APP用户管理的开发流程; * 用户管理的分类与使用场景; * 商品列表的设计与实现; * 购物车管理的设计与实现。 一、 用户管理基础 🎯 1.1 用户管理定义 用户管理是指对应用的用户进行管理,主要包括以下方面:

By Ne0inhk
HarmonyOS6半年磨一剑 - RcImage组件填充模式与形状系统设计(一)

HarmonyOS6半年磨一剑 - RcImage组件填充模式与形状系统设计(一)

目录 * 前言 * 项目简介 * 核心特性 * 开源计划 * rchoui官网 * 文档概述 * 第一章: 填充模式系统 * 1.1 填充模式类型定义 * 1.2 填充模式对比分析 * 1.3 填充模式实现机制 * 第二章: contain 模式深度解析 * 2.1 contain 模式工作原理 * 2.2 contain 模式适用场景 * 第三章: cover 模式深度解析 * 3.1 cover 模式工作原理 * 3.2 cover 模式适用场景 * 第四章: fill 模式深度解析 * 4.1 fill 模式工作原理 * 4.2 fill

By Ne0inhk
大力学习台灯T6/T6Pro 救砖实战:macOS/Windows 用 mtkclient 从 Fastboot 无限重启完整恢复(含固件下载地址)

大力学习台灯T6/T6Pro 救砖实战:macOS/Windows 用 mtkclient 从 Fastboot 无限重启完整恢复(含固件下载地址)

大力学习台灯T6/T6Pro(MTK)救砖实战(小白可用):macOS/Windows 用 mtkclient 从 Fastboot/Logo 无限重启完整恢复(含恢复原机 SN/proinfo) 本文记录一次 Dali T6 学习机(联发科 MTK 平台,示例识别为 MT6771/0x788 系列)从“卡 Fastboot / Logo 无限重启”到 成功进入系统,并最终 恢复原机 SN/设备身份(proinfo) 的完整过程。 如果你是小白:你只需要按本文顺序复制粘贴命令即可。每一步我都写了: TL;DR(傻瓜式总流程:照抄就能修) 下面这套是“最短路径”修复流程:

By Ne0inhk