文章目录

  • 一.背景
  • 二.工具准备
    • 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小游戏为例相关推荐

  1. 20行python代码的入门级小游戏-200行Python代码实现的2048小游戏

    2048这个小游戏大家都不陌生,应该都玩过,之前已经在网上见过各个版本的2048实现了,有JAVA.HTML5等,今天我就给大家来一个我自己在 实验楼 学到的python版2048.所有代码加起来才2 ...

  2. 微信小程序 wxapkg 反编译 获得微信小游戏跳一跳源码

    前言 昨天 V2EX 上的一篇通过抓包来获取微信跳一跳源码的文章走红,文章连接点击这里 我也在通过文章中的方式进行了抓包,但是并未探测到小游戏的下载连接,可能微信对此已经进行了修复.而且上文中提供的下 ...

  3. 我的名片能运行Linux和Python,还能玩2048小游戏,成本只要20元

    晓查 发自 凹非寺  量子位 报道 | 公众号 QbitAI 猜猜它是什么?印着姓名.职位和邮箱,看起来是个名片.可是右下角有芯片,看起来又像是个PCB电路板. 其实它是一台超迷你的ARM计算机,不仅 ...

  4. python游戏代码五子棋_用20行Python代码实现2048小游戏,你会吗?

    前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...

  5. python秒表游戏代码_用20行Python代码实现2048小游戏,你会吗?

    前些天在b站上看到有个大佬用c写了一个2048小游戏,我便一下来了兴趣.心想着,我貌似也能用Python来整一波,话不多说,直接开搞. 2048的游戏规则: 2048游戏总共有16个格子,初始时会有两 ...

  6. python里graphics的使用_使用graphics.py实现2048小游戏

    1.过年的时候在手机上下载了2048玩了几天,心血来潮决定用py写一个,刚开始的时候想用QT实现,发现依赖有点大.正好看到graphics.py是基于tkinter做的封装就拿来练手,并借用了CSDN ...

  7. Python学习—2048小游戏等4个小练习

    Python学习-2048小游戏等4个小练习 转载于:https://www.cnblogs.com/ChangAn223/p/10627777.html

  8. python小项目——2048小游戏(详解)

    2048游戏 原版游戏地址 第一部分 导入所需要的库 第二部分 确认游戏键位设置,并和对应的操作关联 第三部分 获取用户输入的值,并直到有效键位 第四部分 对矩阵的应用,减少代码量 第五部分 创建棋盘 ...

  9. Python实现2048小游戏

    2048小游戏也算是一款好玩的益智休闲小游戏,下面本博主用 python 语言将该游戏复现,感兴趣的小伙伴点击 关注 哦! 同时博主还用 java 语言复现了该游戏,可点击以下链接浏览博主的另一篇文章 ...

最新文章

  1. Taro+react开发(21)--注意参数格式
  2. DeepLesion 更新下载
  3. 2020全球开源基础设施技术峰会分享 | 自研软硬一体化加速方案
  4. SAP License:SAP软件实施要点
  5. getCurrentSession与openSession的区别
  6. MyBatis多参数传递之默认命名方式示例——MyBatis学习笔记之十二
  7. 关于ARMA模型的R语言实现
  8. ecplise插入图片太大_PPT文件太大?100M的PPT一秒变18M,这个压缩方法实在绝了
  9. 如何使用plink进行二分类性状的GWAS分析并计算PRS得分
  10. java 使用Apache PDFBox 对 PDF 文件进行剪裁
  11. 东南大学张浩计算机,“智慧物联网与智能计算”高端论坛在东南大学举行
  12. linux 查询系统io,Linux系统IO实时监控iostat命令详解
  13. 华为P10手机有计算机功能吗,手机教程知识:华为P10支持NFC功能吗 华为P10新功能介绍...
  14. 自定义控件---继承ViewGroup类方式(循序渐进之第2步效果----图片左右拖动+自动回弹效果)
  15. rust超低配置补丁_Rust新增存在类型支持
  16. 谷歌 smarthome_为什么Apple的HomeKit需要所有新的Smarthome硬件?
  17. cruise软件模型,混动仿真模型,cruise与simulink联合仿真模型,Cruise混动仿真模型
  18. 【选型指南】数字源表/源测量单元/SMU选型的7个重要指标
  19. linux shell 基本命令大全,常用的Linux shell命令详解收集整理
  20. 安卓源码,安卓开发,跑步打卡项目app源码

热门文章

  1. 自媒体运营入门基础知识,掌握这4步,你也可以拥有百万粉丝
  2. Vova and Trophies _Codeforces 1082B
  3. HTML——5(图片)
  4. 如何摆放饰物让您的玄关更能招财
  5. 如何刷新微信服务器小程序版本,微信小程序版本自动更新
  6. 年末阿里百度等大厂技术面试题汇总,灵魂拷问
  7. 用WPS图片编辑出透明
  8. 在Win8下无法打开 hlp 帮助文件的问题
  9. 【在线教育】- 讲师模块功能实现
  10. 使用最大逆向匹配进行提槽