python反编译-以2048小游戏为例
文章目录
- 一.背景
- 二.工具准备
- 1.pyinstxtractor.py脚本用于反编译python
- 2.winhex用于编辑16进制的软件
- 三.反编译
- 1.放置脚本
- 2.运行脚本
- 3.找到软件名文件和struct文件
- 4.托入winhex进行对比
- 5.将struct多出的那一行复制到puzzle前面
- 6.更改其后缀为.pyc
- 7.安装第三方库uncompyle
- 8.python版本为3.8以下可以调用uncompyle
- 9.python版本为3.8以上可以选择在线工具(.pyc>.py)
- 10.最后可以得到puzzle.py文件
- 11.找到flag大公告成
一.背景
一道ctf题,通过破解2048游戏获得flag
游戏的规则很简单,需要控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。
二.工具准备
1.pyinstxtractor.py脚本用于反编译python
脚本内容如下
from __future__ import print_function
import os
import struct
import marshal
import zlib
import sys
import imp
import types
from 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()
2.winhex用于编辑16进制的软件
压缩包已上传至博主资源
三.反编译
1.放置脚本
将脚本和待编译的exe文件放在同一路径下后,在路径框中输入cmd打开终端
2.运行脚本
在终端中输入python后输入脚本名和待反编译exe文件名
编译成功后会在原路径生成如下文件夹
3.找到软件名文件和struct文件
4.托入winhex进行对比
5.将struct多出的那一行复制到puzzle前面
6.更改其后缀为.pyc
7.安装第三方库uncompyle
8.python版本为3.8以下可以调用uncompyle
对应路径终端输入uncompyle6 puzzle.pyc > puzzle.py
9.python版本为3.8以上可以选择在线工具(.pyc>.py)
https://tool.lu/pyc/
10.最后可以得到puzzle.py文件
代码如下
#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
import random
from tkinter import Frame, Label, CENTER
import logic
import constants as cclass GameGrid(Frame):def __init__(self):Frame.__init__(self)self.grid()self.master.title('C1CTF2019')self.master.bind('<Key>', self.key_down)self.commands = {c.KEY_J: logic.down,c.KEY_K: logic.up,c.KEY_L: logic.right,c.KEY_H: logic.left,c.KEY_RIGHT_ALT: logic.right,c.KEY_LEFT_ALT: logic.left,c.KEY_DOWN_ALT: logic.down,c.KEY_UP_ALT: logic.up,c.KEY_RIGHT: logic.right,c.KEY_LEFT: logic.left,c.KEY_DOWN: logic.down,c.KEY_UP: logic.up }self.grid_cells = []self.init_grid()self.init_matrix()self.update_grid_cells()self.mainloop()def init_grid(self):background = Frame(self, c.BACKGROUND_COLOR_GAME, c.SIZE, c.SIZE, **('bg', 'width', 'height'))background.grid()for i in range(c.GRID_LEN):grid_row = []for j in range(c.GRID_LEN):cell = Frame(background, c.BACKGROUND_COLOR_CELL_EMPTY, c.SIZE / c.GRID_LEN, c.SIZE / c.GRID_LEN, **('bg', 'width', 'height'))cell.grid(i, j, c.GRID_PADDING, c.GRID_PADDING, **('row', 'column', 'padx', 'pady'))t = Label(cell, '', c.BACKGROUND_COLOR_CELL_EMPTY, CENTER, c.FONT, 5, 2, **('master', 'text', 'bg', 'justify', 'font', 'width', 'height'))t.grid()grid_row.append(t)self.grid_cells.append(grid_row)def gen(self):return random.randint(0, c.GRID_LEN - 1)def init_matrix(self):self.matrix = logic.new_game(4)self.history_matrixs = list()self.matrix = logic.add_two(self.matrix)self.matrix = logic.add_two(self.matrix)def update_grid_cells(self):for i in range(c.GRID_LEN):for j in range(c.GRID_LEN):new_number = self.matrix[i][j]if new_number == 0:self.grid_cells[i][j].configure('', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))continueself.grid_cells[i][j].configure(str(new_number), c.BACKGROUND_COLOR_DICT[new_number], c.CELL_COLOR_DICT[new_number], **('text', 'bg', 'fg'))self.update_idletasks()def key_down(self, event):key = repr(event.char)if key == c.KEY_BACK and len(self.history_matrixs) > 1:self.matrix = self.history_matrixs.pop()self.update_grid_cells()print('back on step total step:', len(self.history_matrixs))elif key in self.commands:(self.matrix, done) = self.commands[repr(event.char)](self.matrix)if done:self.matrix = logic.add_two(self.matrix)self.history_matrixs.append(self.matrix)self.update_grid_cells()done = Falseif logic.game_state(self.matrix) == 'win':self.grid_cells[1][0].configure('C1CTF', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))self.grid_cells[1][1].configure('{2048', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))self.grid_cells[1][2].configure('_1s_', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))self.grid_cells[1][3].configure('fun}', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))if logic.game_state(self.matrix) == 'lose':self.grid_cells[1][1].configure('You', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))self.grid_cells[1][2].configure('Lost!', c.BACKGROUND_COLOR_CELL_EMPTY, **('text', 'bg'))def generate_next(self):index = (self.gen(), self.gen())while self.matrix[index[0]][index[1]] != 0:index = (self.gen(), self.gen())self.matrix[index[0]][index[1]] = 2gamegrid = GameGrid()
11.找到flag大公告成
python反编译-以2048小游戏为例相关推荐
- 20行python代码的入门级小游戏-200行Python代码实现的2048小游戏
2048这个小游戏大家都不陌生,应该都玩过,之前已经在网上见过各个版本的2048实现了,有JAVA.HTML5等,今天我就给大家来一个我自己在 实验楼 学到的python版2048.所有代码加起来才2 ...
- 微信小程序 wxapkg 反编译 获得微信小游戏跳一跳源码
前言 昨天 V2EX 上的一篇通过抓包来获取微信跳一跳源码的文章走红,文章连接点击这里 我也在通过文章中的方式进行了抓包,但是并未探测到小游戏的下载连接,可能微信对此已经进行了修复.而且上文中提供的下 ...
- 我的名片能运行Linux和Python,还能玩2048小游戏,成本只要20元
晓查 发自 凹非寺 量子位 报道 | 公众号 QbitAI 猜猜它是什么?印着姓名.职位和邮箱,看起来是个名片.可是右下角有芯片,看起来又像是个PCB电路板. 其实它是一台超迷你的ARM计算机,不仅 ...
- python游戏代码五子棋_用20行Python代码实现2048小游戏,你会吗?
前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...
- python秒表游戏代码_用20行Python代码实现2048小游戏,你会吗?
前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...
- python里graphics的使用_使用graphics.py实现2048小游戏
1.过年的时候在手机上下载了2048玩了几天,心血来潮决定用py写一个,刚开始的时候想用QT实现,发现依赖有点大.正好看到graphics.py是基于tkinter做的封装就拿来练手,并借用了CSDN ...
- Python学习—2048小游戏等4个小练习
Python学习-2048小游戏等4个小练习 转载于:https://www.cnblogs.com/ChangAn223/p/10627777.html
- python小项目——2048小游戏(详解)
2048游戏 原版游戏地址 第一部分 导入所需要的库 第二部分 确认游戏键位设置,并和对应的操作关联 第三部分 获取用户输入的值,并直到有效键位 第四部分 对矩阵的应用,减少代码量 第五部分 创建棋盘 ...
- Python实现2048小游戏
2048小游戏也算是一款好玩的益智休闲小游戏,下面本博主用 python 语言将该游戏复现,感兴趣的小伙伴点击 关注 哦! 同时博主还用 java 语言复现了该游戏,可点击以下链接浏览博主的另一篇文章 ...
最新文章
- Java怎么做三端_Java三端分离开发在线教育平台
- 特征工程之时间特征、自然语言特征、图像图像特征、数据分布不平衡
- fixing macOS Sierra fatal error: 'openssl/opensslv.h' or 'openssl/aes.h' file not found
- Python3 strip()去除字符串首尾指定信息
- Backend cache is always enabled
- html列表变成三个一行,HTML列表仅限第一行缩进
- java怎么将程序保存在桌面_在Java桌面应用程序中保留数据的最佳方法是什么?...
- Golang基本变量
- linux 驱动读写文件,Linux下读写寄存器
- Windows下MYSQL数据库BOOT密码的修改方法
- win10如何用管理员权限去运行服务器,让Visualstudio在win10下使用管理员方式运行...
- 尴尬又暖心!学生知乎上提问导师人品如何,没想到导师亲自回答了...
- idea 安装本地插件
- 安装Linux镜像文件,通过iso镜像文件硬盘安装Liunx (转载)
- MTK Android LCD模块驱动
- 原生汇率计算器系统源代码
- niosii spi 外部_转载:NIOS II spi详解
- MySql Order By 多个字段 排序规则
- Cocos2d-x 3.x 图形学渲染系列二十三
- 计算机信息安全攻防大赛,2018年度信息安全攻防大赛圆满收官