• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    python反编译教程之2048小游戏实例

    一.背景

    一道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 uniquename
    
    
    class 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 = name
    
    
    class 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 information
    import random
    from tkinter import Frame, Label, CENTER
    import logic
    import constants as c
    
    class 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]] = 2
    
    
    gamegrid = GameGrid()
    
    

    11.找到flag大公告成

    总结

    到此这篇关于python反编译教程之2048小游戏实例的文章就介绍到这了,更多相关python反编译2048小游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • Python实现简单2048小游戏
    • Python实现简单的2048小游戏
    • 一步步教你用Python实现2048小游戏
    • 用Python写一个无界面的2048小游戏
    • Python新手实现2048小游戏
    • python实现2048小游戏
    • 用Python手把手教你实现2048小游戏
    上一篇:python 如何读、写、解析CSV文件
    下一篇:解决pytorch 数据类型报错的问题
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    python反编译教程之2048小游戏实例 python,反,编译,教程,之,2048,