NothingToSeeHere (reverse)
Are you ready to play a game? beware of snakes!.
We are given a game written in python that looks quite short, but that’s because the main game logic is loaded from bytecode.
logic = "eJzU+7muxNye5Yl9997MrKrsLqm9<truncated>"
logic = base64.b64decode(logic)
logic = zlib.decompress(logic)
logic = marshal.loads(logic[16:])
mod = types.ModuleType("gamelogic")
exec(logic, mod.__dict__)
logic = mod.Logic(player_cpos)
The bytecode can be decompiled by importing the decompile
function from uncompyle6
.
import os, sys, time
import base64 ,zlib, marshal, importlib, types, dis
from uncompyle6.main import decompile
logic = "eJzU+7muxNye5Yl9997MrKrsLqm9<truncated>"
logic = base64.b64decode(logic)
logic = zlib.decompress(logic)
logic = marshal.loads(logic[16:])
mod = types.ModuleType("gamelogic")
exec(logic, mod.__dict__)
# print(dir(logic))
decompile(3.7, logic, sys.stdout)
This gives
# uncompyle6 version 3.6.0
# Python bytecode 3.7
# Decompiled from: Python 3.7.2 (v3.7.2:9a3ffc0492, Dec 24 2018, 02:44:43)
# [Clang 6.0 (clang-600.0.57)]
# Embedded file name: gamelogic.py
import random, zlib, pickle, base64
class Logic:
DEBUG = False
def __init__(self, player_pos):
if self.DEBUG:
self.game_map = []
gd = open('clean.txt', 'r').read().split('\n')
for _ in range(4):
self.game_map.append(' ')
for gl in gd:
self.game_map.append(' ' + gl + ' ')
for _ in range(2):
self.game_map.append(' ')
else:
self.game_map = 'eJxN2+eWolzYIGwRRDCgYkAEzDnnhKE655xD<truncated>'
self.game_map = base64.b64decode(self.game_map)
self.game_map = zlib.decompress(self.game_map)
self.game_map = pickle.loads(self.game_map)
self.viewport = 'What you see =>\n╔═══════════╗\n║ ║ \n║ ║ \n║ ☻ ║ \n║ ║ \n║ ║ \n╚═══════════╝'
self._Logic__gen_decode_key()
self.player_move(player_pos)
def __update_viewport(self, data):
new_viewport = []
if self.DEBUG:
new_viewport.append('[DEBUG] What you see =>')
else:
new_viewport.append('What you see =>')
if self.DEBUG:
new_viewport.append('╔════╣DEBUG╠═════╗')
else:
new_viewport.append('╔════════════════╗')
for dl in data:
new_viewport.append('║' + dl + '║')
if self.DEBUG:
new_viewport.append('╚════╣DEBUG╠═════╝')
else:
new_viewport.append('╚════════════════╝')
# smile_edit = list(new_viewport[4])
# smile_edit[8] = '☻'
# new_viewport[4] = ''.join(smile_edit)
self.viewport = '\n'.join(new_viewport)
def __gen_decode_key(self):
random.seed(949127234)
self.d_keys = []
for r in range(93):
kr = []
for k in range(155):
kr.append(random.randint(33, 126))
self.d_keys.append(kr)
def __decode_view(self, data, key):
if self.DEBUG:
return data
new_data = []
for d, k in zip(data, key):
l = []
for i, sd in enumerate(d):
l.append(chr(ord(sd) ^ k[i]))
new_data.append(''.join(l))
return new_data
def player_move(self, player_pos):
pos_x, pos_y = player_pos
if pos_x > 15:
pos_x = 15
if pos_y > 10:
pos_y = 10
data = []
key = []
for i in range(5):
vp = self.game_map[(pos_y + i)]
vp = vp[pos_x:pos_x]
vk = self.d_keys[(pos_y + i)]
vk = vk[pos_x:pos_x]
data.append(vp)
key.append(vk)
data = self._Logic__decode_view(data, key)
self._Logic__update_viewport(data)
There is a game map in this class, but after decoding it, it turns out that it is encrypted.
import base64, zlib, pickle
game_map = 'eJxN2+eWolzYIGwRRDCgYkAEzDnnhKE655xD<truncated>'
game_map = base64.b64decode(game_map)
game_map = zlib.decompress(game_map)
game_map = pickle.loads(game_map)
print('\n'.join(game_map))
▶ python3 load_map.py
]Olv@Dn[SZmQCQT\zM
DSJ~i|ALI
...
player_move
is responsible for decrypting it using the key generated in __gen_decode_key
. The reason we cannot play the game to find the flag is because of the following checks.
if pos_x > 15:
pos_x = 15
if pos_y > 10:
pos_y = 10
Comment them out and we can navigate to even further parts of the map. But the map is still showing too little, because we only set to show a 5x5 grid.
for i in range(5):
vp = self.game_map[(pos_y + i)]
vp = vp[pos_x:pos_x + 5]
vk = self.d_keys[(pos_y + i)]
vk = vk[pos_x:pos_x + 5]
data.append(vp)
key.append(vk)
So change the numbers to show a larger part of the map, then modify ntsh.py to import from the modified gamelogic instead of the original bytecode.
for i in range(85):
vp = self.game_map[(pos_y + i)]
vp = vp[pos_x:pos_x + 145]
vk = self.d_keys[(pos_y + i)]
vk = vk[pos_x:pos_x + 145]
data.append(vp)
key.append(vk)
Now play the game and we can find the flag at the right end of the map.