python反编译教程之2048小游戏实例
一.背景
一道ctf题,通过破解2048游戏获得flag
游戏的规则很简单,需要控制所有方块向同一个方向运动,两个相同数字方块撞在一起之后合并成为他们的和,每次操作之后会随机生成一个2或者4,最终得到一个“2048”的方块就算胜利了。
二.工具准备
1.pyinstxtractor.py脚本用于反编译python
脚本内容如下
from__future__importprint_function
importos
importstruct
importmarshal
importzlib
importsys
importimp
importtypes
fromuuidimportuuid4asuniquename
classCTOCEntry:
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
classPyInstArchive:
PYINST20_COOKIE_SIZE=24#Forpyinstaller2.0
PYINST21_COOKIE_SIZE=24+64#Forpyinstaller2.1+
MAGIC=b'MEI\014\013\012\013\016'#Magicnumberwhichidentifiespyinstaller
def__init__(self,path):
self.filePath=path
defopen(self):
try:
self.fPtr=open(self.filePath,'rb')
self.fileSize=os.stat(self.filePath).st_size
except:
print('[*]Error:Couldnotopen{0}'.format(self.filePath))
returnFalse
returnTrue
defclose(self):
try:
self.fPtr.close()
except:
pass
defcheckFile(self):
print('[*]Processing{0}'.format(self.filePath))
#Checkifitisa2.0archive
self.fPtr.seek(self.fileSize-self.PYINST20_COOKIE_SIZE,os.SEEK_SET)
magicFromFile=self.fPtr.read(len(self.MAGIC))
ifmagicFromFile==self.MAGIC:
self.pyinstVer=20#pyinstaller2.0
print('[*]Pyinstallerversion:2.0')
returnTrue
#Checkforpyinstaller2.1+beforebailingout
self.fPtr.seek(self.fileSize-self.PYINST21_COOKIE_SIZE,os.SEEK_SET)
magicFromFile=self.fPtr.read(len(self.MAGIC))
ifmagicFromFile==self.MAGIC:
print('[*]Pyinstallerversion:2.1+')
self.pyinstVer=21#pyinstaller2.1+
returnTrue
print('[*]Error:Unsupportedpyinstallerversionornotapyinstallerarchive')
returnFalse
defgetCArchiveInfo(self):
try:
ifself.pyinstVer==20:
self.fPtr.seek(self.fileSize-self.PYINST20_COOKIE_SIZE,os.SEEK_SET)
#ReadCArchivecookie
(magic,lengthofPackage,toc,tocLen,self.pyver)=\
struct.unpack('!8siiii',self.fPtr.read(self.PYINST20_COOKIE_SIZE))
elifself.pyinstVer==21:
self.fPtr.seek(self.fileSize-self.PYINST21_COOKIE_SIZE,os.SEEK_SET)
#ReadCArchivecookie
(magic,lengthofPackage,toc,tocLen,self.pyver,pylibname)=\
struct.unpack('!8siiii64s',self.fPtr.read(self.PYINST21_COOKIE_SIZE))
except:
print('[*]Error:Thefileisnotapyinstallerarchive')
returnFalse
print('[*]Pythonversion:{0}'.format(self.pyver))
#OverlayisthedataappendedattheendofthePE
self.overlaySize=lengthofPackage
self.overlayPos=self.fileSize-self.overlaySize
self.tableOfContentsPos=self.overlayPos+toc
self.tableOfContentsSize=tocLen
print('[*]Lengthofpackage:{0}bytes'.format(self.overlaySize))
returnTrue
defparseTOC(self):
#Gotothetableofcontents
self.fPtr.seek(self.tableOfContentsPos,os.SEEK_SET)
self.tocList=[]
parsedLen=0
#Parsetableofcontents
whileparsedLen3.3somekeysarebytesobjectsomearestrobject
fileName=key.decode('utf-8')
except:
pass
#Makesuredestinationdirectoryexists,ensuringwekeepinsidedirName
destName=os.path.join(dirName,fileName.replace("..","__"))
destDirName=os.path.dirname(destName)
ifnotos.path.exists(destDirName):
os.makedirs(destDirName)
try:
data=f.read(length)
data=zlib.decompress(data)
except:
print('[!]Error:Failedtodecompress{0},probablyencrypted.Extractingasis.'.format(fileName))
open(destName+'.pyc.encrypted','wb').write(data)
continue
withopen(destName+'.pyc','wb')aspycFile:
pycFile.write(pycHeader)#Writepycmagic
pycFile.write(b'\0'*4)#Writetimestamp
ifself.pyver>=33:
pycFile.write(b'\0'*4)#SizeparameteraddedinPython3.3
pycFile.write(data)
defmain():
iflen(sys.argv)<2:
print('[*]Usage:pyinstxtractor.py')
else:
arch=PyInstArchive(sys.argv[1])
ifarch.open():
ifarch.checkFile():
ifarch.getCArchiveInfo():
arch.parseTOC()
arch.extractFiles()
arch.close()
print('[*]Successfullyextractedpyinstallerarchive:{0}'.format(sys.argv[1]))
print('')
print('Youcannowuseapythondecompileronthepycfileswithintheextracteddirectory')
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
对应路径终端输入uncompyle6puzzle.pyc>puzzle.py
9.python版本为3.8以上可以选择在线工具(.pyc>.py)
https://tool.lu/pyc/
10.最后可以得到puzzle.py文件
代码如下
#!/usr/bin/envpython
#visithttp://tool.lu/pyc/formoreinformation
importrandom
fromtkinterimportFrame,Label,CENTER
importlogic
importconstantsasc
classGameGrid(Frame):
def__init__(self):
Frame.__init__(self)
self.grid()
self.master.title('C1CTF2019')
self.master.bind('',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()
definit_grid(self):
background=Frame(self,c.BACKGROUND_COLOR_GAME,c.SIZE,c.SIZE,**('bg','width','height'))
background.grid()
foriinrange(c.GRID_LEN):
grid_row=[]
forjinrange(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)
defgen(self):
returnrandom.randint(0,c.GRID_LEN-1)
definit_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)
defupdate_grid_cells(self):
foriinrange(c.GRID_LEN):
forjinrange(c.GRID_LEN):
new_number=self.matrix[i][j]
ifnew_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()
defkey_down(self,event):
key=repr(event.char)
ifkey==c.KEY_BACKandlen(self.history_matrixs)>1:
self.matrix=self.history_matrixs.pop()
self.update_grid_cells()
print('backonsteptotalstep:',len(self.history_matrixs))
elifkeyinself.commands:
(self.matrix,done)=self.commands[repr(event.char)](self.matrix)
ifdone:
self.matrix=logic.add_two(self.matrix)
self.history_matrixs.append(self.matrix)
self.update_grid_cells()
done=False
iflogic.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'))
iflogic.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'))
defgenerate_next(self):
index=(self.gen(),self.gen())
whileself.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小游戏内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。