python反编译教程之2048小游戏实例
这篇文章主要给大家介绍了关于python反编译教程之2048小游戏的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一.背景
一道ctf题,通过破解2048游戏获得flag
游戏的规则很简单,需要控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。
二.工具准备
1.pyinstxtractor.py脚本用于反编译python
脚本内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | 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)
10.最后可以得到puzzle.py文件
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | #!/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小游戏实例的文章就介绍到这了