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

【C++】 —— 笔试刷题day_28

【C++】 —— 笔试刷题day_28

一、游游的重组偶数 题目解析 这道题,有q组数据,每一次输入一个正整数x,让我们将这个数进行重排,变成一个偶数,然后返回(如果x本身就是一个偶数那可以直接返回x); 如果不存在合法解,就是x通过重排后,无法变成一个偶数,就输出-1; 算法思路 这道题,总体来说还是比较简单的; 对于正整数x,我们可以把它当作一个字符串进行输入;(如果按照整数输入,我们还要将这个数x的每一位变换成对应数组) 我们知道,如果一个数是偶数,那最低位一定是一个偶数,这样我们只需判断字符串的最后一位即可知道这个数是否是偶数;如果这个数是偶数,那就直接输出即可;如果最后一位不是偶数,那就从第一位开始向后找,找到一位是偶数,然后把它交换到最后一位;然后输出即可;如果遍历完这个字符串,还没找到一位是偶数的,那就表示这个数x通过重拍无法变成偶数,输出-1即可。 题目解析 #include<iostream>usingnamespace std; string func(){ string str; cin >>

By Ne0inhk
C++ 多线程同步之互斥锁(mutex)实战

C++ 多线程同步之互斥锁(mutex)实战

C++ 多线程同步之互斥锁(mutex)实战 💡 学习目标:掌握 C++ 标准库中互斥锁的基本用法,理解多线程同步的核心原理,能够解决多线程环境下的资源竞争问题。 💡 学习重点:std::mutex 与 std::lock_guard 的使用、死锁的产生原因及规避方法、实际场景中的同步案例实现。 48.1 多线程同步的必要性 在多线程编程中,当多个线程同时访问共享资源时,会出现资源竞争问题。 例如两个线程同时对同一个变量进行读写操作,会导致最终结果与预期不符。 这种问题被称为线程安全问题,而解决该问题的核心就是线程同步。 ⚠️ 注意事项:线程不同步会引发数据竞争,造成程序运行结果不可预测,甚至导致程序崩溃。 举个简单的反例,两个线程同时对全局变量 count 进行自增操作: #include<iostream>#include<thread>usingnamespace std;int count

By Ne0inhk
(最新原创毕设)Java上门帮厨管理系统/03.01白嫖源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案

(最新原创毕设)Java上门帮厨管理系统/03.01白嫖源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案

摘  要 随着现代生活节奏的加快和人们对便捷、高质量餐饮服务需求的增加,上门帮厨作为一种新兴的服务模式逐渐受到欢迎。然而,传统的上门帮厨管理方式依赖于电话预约和手工记录,不仅效率低下,而且难以满足用户对服务质量透明度和个性化的需求。为此,本文提出了一个基于Spring Boot框架的临沂上门帮厨管理系统。该系统旨在通过信息化手段优化厨师与用户之间的互动流程,提高服务效率,增强用户体验,并为管理者提供有效的运营支持。 基于Spring Boot的临沂上门帮厨管理系统集成了多种功能模块,以满足不同用户群体的需求。普通用户可以通过注册登录进入系统,浏览首页展示的轮播图、菜品资讯、菜品信息推荐等信息,并进行相关操作。系统提供了菜品资讯的查看、点赞、收藏和评论功能,以及菜品信息的详情查看、评分、预约等功能。用户还可以在线提交问题反馈,查看个人账户信息并进行修改。 厨师用户可以查看订单详情,进行订单审核和回复,提交佣金提现申请,并查看提现记录。这些功能模块的设计充分考虑了厨师的实际需求,旨在帮助他们更好地管理和提升自己的服务水平。 管理员负责整个系统的运维工作,包括新注册用户的审核、菜品信

By Ne0inhk
C++ 多态:面向对象的动态行为核心机制

C++ 多态:面向对象的动态行为核心机制

C++ 多态:面向对象的动态行为核心机制 💡 学习目标:掌握多态的概念与分类,理解虚函数的作用原理,能够熟练使用多态实现程序的动态行为扩展。 💡 学习重点:静态多态与动态多态的区别、虚函数的定义与使用、纯虚函数与抽象类、多态的实战应用场景。 一、多态的概念与分类 ✅ 结论:多态是 C++ 面向对象三大特性之一,指同一行为在不同对象上表现出不同的形态,核心是“一个接口,多种实现”。 多态主要分为两大类,二者的实现原理和触发时机截然不同: 1. 静态多态:编译阶段确定调用关系,也叫编译时多态,实现方式包括函数重载和运算符重载 2. 动态多态:运行阶段确定调用关系,也叫运行时多态,实现方式是虚函数 + 基类指针/引用 生活中的多态示例:同样是“动物叫”这个行为,猫的叫声是“喵喵喵”,狗的叫声是“汪汪汪”,不同动物对象表现出不同的行为形态。 二、静态多态:编译时确定的多态性 💡 静态多态的调用关系在编译阶段就已确定,编译器会根据参数列表的差异匹配对应的函数。

By Ne0inhk