文章目录

  • 一.背景
  • 二.工具准备
    • 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. Java怎么做三端_Java三端分离开发在线教育平台
  2. 特征工程之时间特征、自然语言特征、图像图像特征、数据分布不平衡
  3. fixing macOS Sierra fatal error: 'openssl/opensslv.h' or 'openssl/aes.h' file not found
  4. Python3 strip()去除字符串首尾指定信息
  5. Backend cache is always enabled
  6. html列表变成三个一行,HTML列表仅限第一行缩进
  7. java怎么将程序保存在桌面_在Java桌面应用程序中保留数据的最佳方法是什么?...
  8. Golang基本变量
  9. linux 驱动读写文件,Linux下读写寄存器
  10. Windows下MYSQL数据库BOOT密码的修改方法
  11. win10如何用管理员权限去运行服务器,让Visualstudio在win10下使用管理员方式运行...
  12. 尴尬又暖心!学生知乎上提问导师人品如何,没想到导师亲自回答了...
  13. idea 安装本地插件
  14. 安装Linux镜像文件,通过iso镜像文件硬盘安装Liunx (转载)
  15. MTK Android LCD模块驱动
  16. 原生汇率计算器系统源代码
  17. niosii spi 外部_转载:NIOS II spi详解
  18. MySql Order By 多个字段 排序规则
  19. Cocos2d-x 3.x 图形学渲染系列二十三
  20. 计算机信息安全攻防大赛,2018年度信息安全攻防大赛圆满收官

热门文章

  1. Render Feature 水下环境解决方案
  2. 简单的笔刷效果【OpenGL】
  3. 键盘没有fn键怎么办?
  4. Spark报错It appears that you are attempting to broadcast an RDD or reference an RDD from an action
  5. Redis数据类型简单操作---哈希
  6. Python使用阿里云开发者平台
  7. 基于随机遍历的羊菜狼问题(JAVA实现)
  8. 360上传应用|应用宝上传应用
  9. 见证花开!!码上有花
  10. 蜂巢科技发布首款创新产品“小清新”空气卫士