您当前的位置:首页 > IT编程 > python
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:python反编译教程之2048小游戏实例

51自学网 2021-10-30 22:53:40
  python
这篇教程python反编译教程之2048小游戏实例写得很实用,希望能帮到您。

一.背景

一道ctf题,通过破解2048游戏获得flag

游戏的规则很简单,需要控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。

二.工具准备

1.pyinstxtractor.py脚本用于反编译python

脚本内容如下

from __future__ import print_functionimport osimport structimport marshalimport zlibimport sysimport impimport typesfrom uuid import uuid4 as uniquenameclass CTOCEntry: 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 = nameclass PyInstArchive: 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 pyinstaller def __init__(self, path): self.filePath = path def open(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))  return False return True def close(self): try:  self.fPtr.close() except:  pass def checkFile(self): print('[*] Processing {0}'.format(self.filePath)) # Check if it is a 2.0 archive self.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.0  print('[*] Pyinstaller version: 2.0')  return True # Check for pyinstaller 2.1+ before bailing out self.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 True print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive') return False def 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 False print('[*] Python version: {0}'.format(self.pyver)) # Overlay is the data appended at the end of the PE self.overlaySize = lengthofPackage self.overlayPos = self.fileSize - self.overlaySize self.tableOfContentsPos = self.overlayPos + toc self.tableOfContentsSize = tocLen print('[*] Length of package: {0} bytes'.format(self.overlaySize)) return True def parseTOC(self): # Go to the table of contents self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET) self.tocList = [] parsedLen = 0 # Parse table of contents while 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 += entrySize print('[*] 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 not  if 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 case  assert len(data) == entry.uncmprsdDataSize # Sanity Check  with 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 pyz if not os.path.exists(dirName):  os.mkdir(dirName) with open(name, 'rb') as f:  pyzMagic = f.read(4)  assert pyzMagic == b'PYZ/0' # Sanity Check  pycHeader = f.read(4) # Python magic value  if 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))  return  print('[*] Found {0} files in PYZ archive'.format(len(toc)))  # From pyinstaller 3.1+ toc is a list of tuples  if type(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 = key.decode('utf-8')  except:   pass  # Make sure destination directory exists, ensuring we keep inside dirName  destName = 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)   continue  with open(destName + '.pyc', 'wb') as pycFile:   pycFile.write(pycHeader) # Write pyc magic   pycFile.write(b'/0' * 4) # Write timestamp   if self.pyver >= 33:   pycFile.write(b'/0' * 4) # Size parameter added in Python 3.3   pycFile.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')   return  arch.close()if __name__ == '__main__': main()

2.winhex用于编辑16进制的软件

压缩包已上传至博主资源,下载地址:https://blog.csdn.net/qq_50216270?type=download

三.反编译

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 informationimport randomfrom tkinter import Frame, Label, CENTERimport logicimport 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'))   continue  self.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 = False  if 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小游戏实例的文章就介绍到这了,更多相关python反编译2048小游戏内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net!


python 如何读、写、解析CSV文件
解决pytorch 数据类型报错的问题
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。