FlareOn11: 01 - Frog
The Challenge
So this challenge was fun, easy and had lots of was to complete. First we are greeted with:

After downloading frog.7z
, we can list the file contents:
$7z l frog.7z
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,22 CPUs Intel(R) Core(TM) Ultra 9 185H (A06A4),ASM,AES-NI)
Scanning the drive for archives:
1 file, 10653489 bytes (11 MiB)
Listing archive: frog.7z
Enter password (will not be echoed):
--
Path = frog.7z
Type = 7z
Physical Size = 10653489
Headers Size = 529
Method = LZMA2:24 BCJ 7zAES
Solid = +
Blocks = 2
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2024-09-25 21:44:33 D.... 0 0 fonts
2024-09-25 21:44:33 D.... 0 0 img
2024-09-25 21:50:25 ....A 334 450752 README.txt
2024-09-25 18:39:48 ....A 4367 fonts/SIL Open Font License.txt
2024-09-25 18:39:48 ....A 153116 fonts/VT323-Regular.ttf
2024-09-25 21:38:02 ....A 7776 frog.py
2024-09-25 15:37:07 ....A 380 img/block.png
2024-09-25 16:03:26 ....A 61714 img/f11_statue.png
2024-09-25 16:36:39 ....A 266 img/floor.png
2024-09-25 16:04:49 ....A 3800 img/frog.png
2024-09-25 19:38:34 ....A 344691 img/win.png
2024-09-25 21:42:32 ....A 12966519 10202208 frog.exe
------------------- ----- ------------ ------------ ------------------------
2024-09-25 21:50:25 13542963 10652960 10 files, 2 folders
So we can solve this challenge either by using python, or by looking at a binary? Cool.
Frog.py
Let’s start by using python. When we start the game we see, just like described in the introduction, a frog on a mission to get to the statue.

Start of Game
However, there is no apparant way to get there because of the walls around the trophy.
The python code
Alright, now let’s look at frog.py
:
import pygame
pygame.init()
pygame.font.init()
screen_width = 800
screen_height = 600
tile_size = 40
tiles_width = screen_width // tile_size
tiles_height = screen_height // tile_size
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
victory_tile = pygame.Vector2(10, 10)
pygame.key.set_repeat(500, 100)
pygame.display.set_caption('Non-Trademarked Yellow Frog Adventure Game: Chapter 0: Prelude')
dt = 0
floorimage = pygame.image.load("img/floor.png")
blockimage = pygame.image.load("img/block.png")
frogimage = pygame.image.load("img/frog.png")
statueimage = pygame.image.load("img/f11_statue.png")
winimage = pygame.image.load("img/win.png")
gamefont = pygame.font.Font("fonts/VT323-Regular.ttf", 24)
text_surface = gamefont.render("instruct: Use arrow keys or wasd to move frog. Get to statue. Win game.",
False, pygame.Color('gray'))
flagfont = pygame.font.Font("fonts/VT323-Regular.ttf", 32)
flag_text_surface = flagfont.render("nope@nope.nope", False, pygame.Color('black'))
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, passable):
super().__init__()
self.image = blockimage
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.passable = passable
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
def draw(self, surface):
surface.blit(self.image, self.rect)
class Frog(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = frogimage
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
def draw(self, surface):
surface.blit(self.image, self.rect)
def move(self, dx, dy):
self.x += dx
self.y += dy
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
blocks = []
player = Frog(0, 1)
def AttemptPlayerMove(dx, dy):
newx = player.x + dx
newy = player.y + dy
# Can only move within screen bounds
if newx < 0 or newx >= tiles_width or newy < 0 or newy >= tiles_height:
return False
# See if it is moving in to a NON-PASSABLE block. hint hint.
for block in blocks:
if newx == block.x and newy == block.y and not block.passable:
return False
player.move(dx, dy)
return True
def GenerateFlagText(x, y):
key = x + y*20
encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf"
return ''.join([chr(ord(c) ^ key) for c in encoded])
def main():
global blocks
blocks = BuildBlocks()
victory_mode = False
running = True
while running:
# poll for events
# pygame.QUIT event means the user clicked X to close your window
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w or event.key == pygame.K_UP:
AttemptPlayerMove(0, -1)
elif event.key == pygame.K_s or event.key == pygame.K_DOWN:
AttemptPlayerMove(0, 1)
elif event.key == pygame.K_a or event.key == pygame.K_LEFT:
AttemptPlayerMove(-1, 0)
elif event.key == pygame.K_d or event.key == pygame.K_RIGHT:
AttemptPlayerMove(1, 0)
# draw the ground
for i in range(tiles_width):
for j in range(tiles_height):
screen.blit(floorimage, (i*tile_size, j*tile_size))
# display the instructions
screen.blit(text_surface, (0, 0))
# draw the blocks
for block in blocks:
block.draw(screen)
# draw the statue
screen.blit(statueimage, (240, 240))
# draw the frog
player.draw(screen)
if not victory_mode:
# are they on the victory tile? if so do victory
if player.x == victory_tile.x and player.y == victory_tile.y:
victory_mode = True
flag_text = GenerateFlagText(player.x, player.y)
flag_text_surface = flagfont.render(flag_text, False, pygame.Color('black'))
print("%s" % flag_text)
else:
screen.blit(winimage, (150, 50))
screen.blit(flag_text_surface, (239, 320))
# flip() the display to put your work on screen
pygame.display.flip()
# limits FPS to 60
# dt is delta time in seconds since last frame, used for framerate-
# independent physics.
dt = clock.tick(60) / 1000
pygame.quit()
return
def BuildBlocks():
blockset = [
Block(3, 2, False),
Block(4, 2, False),
Block(5, 2, False),
Block(6, 2, False),
Block(7, 2, False),
Block(8, 2, False),
Block(9, 2, False),
Block(10, 2, False),
Block(11, 2, False),
Block(12, 2, False),
Block(13, 2, False),
Block(14, 2, False),
Block(15, 2, False),
Block(16, 2, False),
Block(17, 2, False),
Block(3, 3, False),
Block(17, 3, False),
Block(3, 4, False),
Block(5, 4, False),
Block(6, 4, False),
Block(7, 4, False),
Block(8, 4, False),
Block(9, 4, False),
Block(10, 4, False),
Block(11, 4, False),
Block(14, 4, False),
Block(15, 4, True),
Block(16, 4, False),
Block(17, 4, False),
Block(3, 5, False),
Block(5, 5, False),
Block(11, 5, False),
Block(14, 5, False),
Block(3, 6, False),
Block(5, 6, False),
Block(11, 6, False),
Block(14, 6, False),
Block(15, 6, False),
Block(16, 6, False),
Block(17, 6, False),
Block(3, 7, False),
Block(5, 7, False),
Block(11, 7, False),
Block(17, 7, False),
Block(3, 8, False),
Block(5, 8, False),
Block(11, 8, False),
Block(15, 8, False),
Block(16, 8, False),
Block(17, 8, False),
Block(3, 9, False),
Block(5, 9, False),
Block(11, 9, False),
Block(12, 9, False),
Block(13, 9, False),
Block(15, 9, False),
Block(3, 10, False),
Block(5, 10, False),
Block(13, 10, True),
Block(15, 10, False),
Block(16, 10, False),
Block(17, 10, False),
Block(3, 11, False),
Block(5, 11, False),
Block(6, 11, False),
Block(7, 11, False),
Block(8, 11, False),
Block(9, 11, False),
Block(10, 11, False),
Block(11, 11, False),
Block(12, 11, False),
Block(13, 11, False),
Block(17, 11, False),
Block(3, 12, False),
Block(17, 12, False),
Block(3, 13, False),
Block(4, 13, False),
Block(5, 13, False),
Block(6, 13, False),
Block(7, 13, False),
Block(8, 13, False),
Block(9, 13, False),
Block(10, 13, False),
Block(11, 13, False),
Block(12, 13, False),
Block(13, 13, False),
Block(14, 13, False),
Block(15, 13, False),
Block(16, 13, False),
Block(17, 13, False)
]
return blockset
if __name__ == '__main__':
main()
So we see that this is indeed a Python script creating a simple game using the Pygame library. The game features a player-controlled frog that must navigate to a statue to win the game. Here’s a breakdown of the code:
Imports and Initialization
The script imports the pygame
module and initializes it along with the font module.
import pygame
pygame.init()
pygame.font.init()
Constants and Setup
Screen dimensions and tile size are defined.
The game window is created with the specified width and height.
A clock is created to manage the game’s frame rate.
The victory tile’s position is set using a pygame.Vector2
object.
Keyboard repeat settings are configured to allow holding down keys for movement.
The window’s caption is set.
screen_width = 800
screen_height = 600
tile_size = 40
tiles_width = screen_width // tile_size
tiles_height = screen_height // tile_size
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
victory_tile = pygame.Vector2(10, 10)
pygame.key.set_repeat(500, 100)
pygame.display.set_caption('Non-Trademarked Yellow Frog Adventure Game: Chapter 0: Prelude')
dt = 0
Images for the floor, blocks, frog, statue, and win screen are loaded.
floorimage = pygame.image.load("img/floor.png")
blockimage = pygame.image.load("img/block.png")
frogimage = pygame.image.load("img/frog.png")
statueimage = pygame.image.load("img/f11_statue.png")
winimage = pygame.image.load("img/win.png")
Two fonts are initialized, and text surfaces for instructions and an email address are created.
gamefont = pygame.font.Font("fonts/VT323-Regular.ttf", 24)
text_surface = gamefont.render("instruct: Use arrow keys or wasd to move frog. Get to statue. Win game.",
False, pygame.Color('gray'))
flagfont = pygame.font.Font("fonts/VT323-Regular.ttf", 32)
flag_text_surface = flagfont.render("nope@nope.nope", False, pygame.Color('black'))
Classes
Block
: Represents an obstacle in the game. It has an image, a position, and a flag indicating whether it is passable.
Frog
: Represents the player’s character. It has an image, a position, and methods for drawing and moving.
class Block(pygame.sprite.Sprite):
# ... class definition ...
class Frog(pygame.sprite.Sprite):
# ... class definition ...
Game Variables
An empty list blocks
is initialized to store block objects.
A Frog
object player
is created, representing the player’s character.
blocks = []
player = Frog(0, 1)
Functions
AttemptPlayerMove(dx, dy)
: Attempts to move the player in the specified direction, checking for screen bounds and collisions with non-passable blocks.
GenerateFlagText(x, y)
: Generates a string by XORing a hardcoded encoded string with a key derived from the player’s position. This is likely used for some sort of puzzle or secret within the game.
main()
: The main game loop that handles events, updates the game state, and renders the game to the screen. It also checks for victory conditions and displays the win screen if the player reaches the victory tile.
BuildBlocks()
: Creates and returns a list of Block
objects that represent the level’s layout.
def AttemptPlayerMove(dx, dy):
# ... function definition ...
def GenerateFlagText(x, y):
# ... function definition ...
def main():
# ... function definition ...
def BuildBlocks():
# ... function definition ...
Main Game Loop
The game loop handles user input, updates the game state, and renders the game.
It checks for QUIT
events to close the game and key presses to move the player.
This part of the loop checks for events such as the user clicking the window’s close button (QUIT event) or pressing a key (KEYDOWN event). If the close button is clicked, the game will stop running. If a key is pressed, the game checks which key it is and calls AttemptPlayerMove with the appropriate direction to move the player.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key in (pygame.K_w, pygame.K_UP):
AttemptPlayerMove(0, -1)
elif event.key in (pygame.K_s, pygame.K_DOWN):
AttemptPlayerMove(0, 1)
elif event.key in (pygame.K_a, pygame.K_LEFT):
AttemptPlayerMove(-1, 0)
elif event.key in (pygame.K_d, pygame.K_RIGHT):
AttemptPlayerMove(1, 0)
The game world is drawn, including the floor, blocks, statue, and frog. The screen is first cleared with a black color. Then, the game world is drawn tile by tile, starting with the floor. After that, obstacles (blocks) and the victory tile (statue) are drawn. Finally, the player’s character (frog) is drawn on top.
screen.fill((0, 0, 0)) # Clear the screen with a black color
for y in range(tiles_height):
for x in range(tiles_width):
screen.blit(floorimage, (x * tile_size, y * tile_size))
for block in blocks:
screen.blit(block.image, block.rect)
screen.blit(statueimage, (victory_tile.x * tile_size, victory_tile.y * tile_size))
screen.blit(player.image, player.rect)
If the player reaches the victory tile, the game enters victory mode, and a flag text is generated and printed to the console. The game checks if the player’s position matches the victory tile’s position. If it does, the victory screen is displayed, a flag text is generated using the GenerateFlagText function, and the flag text is printed to the console. The game loop is then stopped.
if player.rect.topleft == (victory_tile.x * tile_size, victory_tile.y * tile_size):
screen.blit(winimage, (0, 0))
flag_text = GenerateFlagText(player.rect.x, player.rect.y)
print(flag_text)
running = False
The display is updated with pygame.display.flip()
, and the frame rate is capped at 60 FPS. pygame.display.flip() updates the entire screen with everything that’s been drawn during this iteration of the loop. The clock.tick(60) call ensures that the game runs at no more than 60 frames per second (FPS), which keeps the game running at a consistent speed across different hardware.
pygame.display.flip()
dt = clock.tick(60)
This code provides a basic framework for a tile-based puzzle game. The player can move the frog around the screen, avoiding blocks and trying to reach the victory tile. The game includes a simple victory condition and a secret message. Cool, now that we have spent way too much looking at the code, let’ s try to solve it.
Obtaining the flag
So there are many ways to solve this one, when solving puzzles I like to think of different approaches and solutions. I will provide a couple of possible solutions, some more creative than others.
Solution 1: No game needed
For example, we see the function GenerateFlagText
:
def GenerateFlagText(x, y):
key = x + y*20
encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf"
return ''.join([chr(ord(c) ^ key) for c in encoded])
This expects two arguments, when we look where this function is called:
if player.x == victory_tile.x and player.y == victory_tile.y:
victory_mode = True
flag_text = GenerateFlagText(player.x, player.y)
flag_text_surface = flagfont.render(flag_text, False, pygame.Color('black'))
print("%s" % flag_text)
So we need to figure out what the values of victory_tile
are. We can see that they are set by:
victory_tile = pygame.Vector2(10, 10)
So you can create your stand alone program to get the flag.
def GenerateFlagText(x, y):
key = x + y*20
encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf"
return ''.join([chr(ord(c) ^ key) for c in encoded])
print(GenerateFlagText(10,10))
Solution 2: Blocks-IT
Another way to solve this is to remove the walls, we see a big block of walls:
blockset = [
Block(3, 2, False),
Block(4, 2, False),
Block(5, 2, False),
...
Block(15, 13, False),
Block(16, 13, False),
Block(17, 13, False)
]
They represent the gray blocks where the frog can’t go to. We can just remove most of the walls to make sure the frog is able to walk to the trophy. We can slightly modify the original code of the main function:
Getting the flag was never this easy:

def main():
custom_blocks = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [5, 12], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [6, 12], [6, 13], [7, 4], [7, 5], [7, 6], [7, 7], [7, 8], [7, 9], [7, 10], [7, 11], [7, 12], [7, 13], [7,14], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8], [8, 9], [8, 10], [8, 11], [8, 12], [8, 13], [8,14], [9, 4], [9, 5], [9, 6], [9, 7], [9, 9], [9, 10], [9, 11], [9, 12], [9,13], [9,14], [10, 4], [10, 5], [10, 6], [10, 11], [10,12], [10,13], [11, 4], [11, 5], [11, 6], [11,11], [11,12], [12, 4], [12, 5], [12, 6], [12, 7], [13, 4], [13, 5], [13, 6], [13, 7], [13, 8], [13, 9], [14, 3], [14, 4], [14, 5], [14, 6], [14, 7], [14, 8], [14, 9], [15,2], [15, 3], [15, 4], [15, 5], [15, 6], [15, 7], [16,1], [16, 2], [16, 3]]
global blocks
blocks = BuildBlocks(custom_blocks)
...
def BuildBlocks(coordinates):
new_blocks = [] # Start with an empty list for the new blocks
for x, y in coordinates: # Unpack each pair of coordinates
new_block = Block(x, y, False) # Create a new Block
new_blocks.append(new_block) # Append the new Block to the list
return new_blocks # Return the list of new Block objects
if __name__ == '__main__':
main()
Making it easy to just walk towards the flag.
Solution 3: Play to win
In the first solution we read the position the player needs to be in to trigger the flag decryption. We can also set our starting point to that location:
victory_tile = Frog(10,10)
Solution 4: No blocks? No problem
We can also remove all the blocks, making it easier for the frog to get to the statue:
def BuildBlocks():
blockset = [
]
return blockset
if __name__ == '__main__':
main()
And there we go:

Solution 5: Not all blocks are False
When we take another look at the original code, especially the blockset
, we see that there are two blocks (13,10) and (15,4) which are set to True
instead of False
, i.e. you can just walk through them!
And so without the game code we can just walk towards the statue:

Solution 6: Cheatcodes enabled
We can use some GTA2 love to unlock all levels win the game. So when you type ’tumyfrog’ while playing the game, you will see the flag:
def main():
global blocks
blocks = BuildBlocks()
victory_mode = False
running = True
entered_text = ""
while running:
# poll for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w or event.key == pygame.K_UP:
AttemptPlayerMove(0, -1)
elif event.key == pygame.K_s or event.key == pygame.K_DOWN:
AttemptPlayerMove(0, 1)
elif event.key == pygame.K_a or event.key == pygame.K_LEFT:
AttemptPlayerMove(-1, 0)
elif event.key == pygame.K_d or event.key == pygame.K_RIGHT:
AttemptPlayerMove(1, 0)
elif event.unicode.isalpha(): # Check if the key pressed is a letter
entered_text += event.unicode # Add the letter to the entered text
if entered_text.lower().endswith("tumyfrog"):
print(entered_text)
player.x = int(victory_tile.x) # Teleport player to victory tile x position
player.y = int(victory_tile.y) # Teleport player to victory tile y position
player.rect.topleft = (player.x * tile_size, player.y * tile_size) # Update player rect position
And so, if we use the classic frog themed cheatcode:

Solution 7: Press t to teleport
We can modify the game, such that when a key is pressed, the player is teleports to a random location:
def TeleportPlayer():
while True:
# Generate a random position within the bounds of the screen
random_x = random.randint(0, tiles_width - 1)
random_y = random.randint(0, tiles_height - 1)
# Check if the random position is not occupied by a block
position_occupied = any(block.x == random_x and block.y == random_y and not block.passable for block in blocks)
# If the position is free, move the player to that position
if not position_occupied:
player.x = random_x
player.y = random_y
player.rect.top = player.y * tile_size
player.rect.left = player.x * tile_size
break # Exit the loop once a valid position is found
def main():
global blocks
blocks = BuildBlocks()
victory_mode = False
running = True
print(victory_tile.x)
while running:
# poll for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w or event.key == pygame.K_UP:
AttemptPlayerMove(0, -1)
elif event.key == pygame.K_s or event.key == pygame.K_DOWN:
AttemptPlayerMove(0, 1)
elif event.key == pygame.K_a or event.key == pygame.K_LEFT:
AttemptPlayerMove(-1, 0)
elif event.key == pygame.K_d or event.key == pygame.K_RIGHT:
AttemptPlayerMove(1, 0)
elif event.key == pygame.K_t: # Check if 'T' key is pressed
TeleportPlayer()
It only takes 380 key presses:

And there are many, many more solutions to think of. We can also see if we can play around with the binary.
Frog.exe
Now, let’ s look at the executable.
Determining the used language
$ file frog.exe
frog.exe: PE32 executable (console) Intel 80386, for MS Windows
That does not tell us that much, did the creator by any chance use py2exe or a similar tool to create an executable? Let’s hunt for references to python:
$ strings frog.exe | grep -i python
pyi-python-flag
Reported length (%d) of Python shared library name (%s) exceeds buffer size (%d)
Path of Python shared library (%s) and its name (%s) exceed buffer size (%d)
Failed to pre-initialize embedded python interpreter!
Failed to allocate PyConfig structure! Unsupported python version?
Failed to set python home path!
Failed to start embedded python interpreter!
bpython38.dll
4python38.dll
Cool. We can now use pyinstxtractor.py to verify this claim.
Still python?
$ python3 pyinstxtractor.py frog.exe
[+] Processing frog.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.8
[+] Length of package: 12677239 bytes
[+] Found 220 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_pkgres.pyc
[+] Possible entry point: pyi_rth_setuptools.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: game.pyc
[+] Found 493 files in PYZ archive
[+] Successfully extracted pyinstaller archive: frog.exe
When we look at the produced files:
$ ls
_asyncio.pyd libcrypto-1_1.dll libtiff-5.dll pyimod01_archive.pyc python38.dll setuptools
base_library.zip libffi-7.dll libwebp-7.dll pyimod02_importers.pyc PYZ-00.pyz _socket.pyd
_bz2.pyd libjpeg-9.dll _lzma.pyd pyimod03_ctypes.pyc PYZ-00.pyz_extracted _ssl.pyd
_ctypes.pyd libmodplug-1.dll _multiprocessing.pyd pyimod04_pywin32.pyc _queue.pyd struct.pyc
_decimal.pyd libogg-0.dll _overlapped.pyd pyi_rth_inspect.pyc SDL2.dll typeguard-4.3.0.dist-info
freetype.dll libopus-0.dll portmidi.dll pyi_rth_multiprocessing.pyc SDL2_image.dll unicodedata.pyd
game.pyc libopusfile-0.dll pyexpat.pyd pyi_rth_pkgres.pyc SDL2_mixer.dll VCRUNTIME140.dll
_hashlib.pyd libpng16-16.dll pygame pyi_rth_pkgutil.pyc SDL2_ttf.dll wheel-0.43.0.dist-info
importlib_metadata-8.5.0.dist-info libssl-1_1.dll pyiboot01_bootstrap.pyc pyi_rth_setuptools.pyc select.pyd zlib1.dll
Nice, we can now use a python decompiler on the pyc files within the extracted directory. For example by using uncompyle6:
$ uncompyle6 -o ../ game.pyc
-- Stacks of completed symbols:
START ::= |- stmts .
_come_froms ::= \e__come_froms . COME_FROM
_come_froms ::= \e__come_froms . COME_FROM_LOOP
_come_froms ::= \e__come_froms COME_FROM .
...
# file game.pyc
# Deparsing stopped due to parse error
game.pyc --
# decompile failed
Even though it states it failed, we are presented with:
# uncompyle6 version 3.9.2
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.8.13 (default, Sep 29 2024, 13:55:46)
# [GCC 11.4.0]
# Embedded file name: game.py
import pygame
pygame.init()
pygame.font.init()
screen_width = 800
screen_height = 600
tile_size = 40
tiles_width = screen_width // tile_size
tiles_height = screen_height // tile_size
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
victory_tile = pygame.Vector2(10, 10)
pygame.key.set_repeat(500, 100)
pygame.display.set_caption("Non-Trademarked Yellow Frog Adventure Game: Chapter 0: Prelude")
dt = 0
floorimage = pygame.image.load("img/floor.png")
blockimage = pygame.image.load("img/block.png")
frogimage = pygame.image.load("img/frog.png")
statueimage = pygame.image.load("img/f11_statue.png")
winimage = pygame.image.load("img/win.png")
gamefont = pygame.font.Font("fonts/VT323-Regular.ttf", 24)
text_surface = gamefont.render("instruct: Use arrow keys or wasd to move frog. Get to statue. Win game.", False, pygame.Color("gray"))
flagfont = pygame.font.Font("fonts/VT323-Regular.ttf", 32)
flag_text_surface = flagfont.render("nope@nope.nope", False, pygame.Color("black"))
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, passable):
super().__init__()
self.image = blockimage
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.passable = passable
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
def draw(self, surface):
surface.blit(self.image, self.rect)
class Frog(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = frogimage
self.rect = self.image.get_rect()
self.x = x
self.y = y
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
def draw(self, surface):
surface.blit(self.image, self.rect)
def move(self, dx, dy):
self.x += dx
self.y += dy
self.rect.top = self.y * tile_size
self.rect.left = self.x * tile_size
blocks = []
player = Frog(0, 1)
def AttemptPlayerMoveParse error at or near `RETURN_VALUE' instruction at offset 112
def GenerateFlagText(x, y):
key = x + y * 20
encoded = "¥·¾±½¿·\x8d¦½\x8dãã\x92´¾³\xa0·ÿ½¼ü±½¿"
return "".join([chr(ord(c) ^ key) for c in encoded])
def main():
global blocks
blocks = BuildBlocks()
victory_mode = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w or event.key == pygame.K_UP:
AttemptPlayerMove(0, -1)
elif event.key == pygame.K_s or event.key == pygame.K_DOWN:
AttemptPlayerMove(0, 1)
elif event.key == pygame.K_a or event.key == pygame.K_LEFT:
AttemptPlayerMove(-1, 0)
elif event.key == pygame.K_d or event.key == pygame.K_RIGHT:
AttemptPlayerMove(1, 0)
for i in range(tiles_width):
for j in range(tiles_height):
screen.blit(floorimage, (i * tile_size, j * tile_size))
else:
screen.blit(text_surface, (0, 0))
for block in blocks:
block.draw(screen)
else:
screen.blit(statueimage, (240, 240))
player.draw(screen)
if not victory_mode:
if player.x == victory_tile.x:
if player.y == victory_tile.y:
victory_mode = True
flag_text = GenerateFlagText(player.x, player.y)
flag_text_surface = flagfont.render(flag_text, False, pygame.Color("black"))
print("%s" % flag_text)
else:
screen.blit(winimage, (150, 50))
screen.blit(flag_text_surface, (239, 320))
pygame.display.flip()
dt = clock.tick(60) / 1000
pygame.quit()
def BuildBlocks():
blockset = [
Block(3, 2, False),
Block(4, 2, False),
Block(5, 2, False),
Block(6, 2, False),
Block(7, 2, False),
Block(8, 2, False),
Block(9, 2, False),
Block(10, 2, False),
Block(11, 2, False),
Block(12, 2, False),
Block(13, 2, False),
Block(14, 2, False),
Block(15, 2, False),
Block(16, 2, False),
Block(17, 2, False),
Block(3, 3, False),
Block(17, 3, False),
Block(3, 4, False),
Block(5, 4, False),
Block(6, 4, False),
Block(7, 4, False),
Block(8, 4, False),
Block(9, 4, False),
Block(10, 4, False),
Block(11, 4, False),
Block(14, 4, False),
Block(15, 4, True),
Block(16, 4, False),
Block(17, 4, False),
Block(3, 5, False),
Block(5, 5, False),
Block(11, 5, False),
Block(14, 5, False),
Block(3, 6, False),
Block(5, 6, False),
Block(11, 6, False),
Block(14, 6, False),
Block(15, 6, False),
Block(16, 6, False),
Block(17, 6, False),
Block(3, 7, False),
Block(5, 7, False),
Block(11, 7, False),
Block(17, 7, False),
Block(3, 8, False),
Block(5, 8, False),
Block(11, 8, False),
Block(15, 8, False),
Block(16, 8, False),
Block(17, 8, False),
Block(3, 9, False),
Block(5, 9, False),
Block(11, 9, False),
Block(12, 9, False),
Block(13, 9, False),
Block(15, 9, False),
Block(3, 10, False),
Block(5, 10, False),
Block(13, 10, True),
Block(15, 10, False),
Block(16, 10, False),
Block(17, 10, False),
Block(3, 11, False),
Block(5, 11, False),
Block(6, 11, False),
Block(7, 11, False),
Block(8, 11, False),
Block(9, 11, False),
Block(10, 11, False),
Block(11, 11, False),
Block(12, 11, False),
Block(13, 11, False),
Block(17, 11, False),
Block(3, 12, False),
Block(17, 12, False),
Block(3, 13, False),
Block(4, 13, False),
Block(5, 13, False),
Block(6, 13, False),
Block(7, 13, False),
Block(8, 13, False),
Block(9, 13, False),
Block(10, 13, False),
Block(11, 13, False),
Block(12, 13, False),
Block(13, 13, False),
Block(14, 13, False),
Block(15, 13, False),
Block(16, 13, False),
Block(17, 13, False)]
return blockset
if __name__ == "__main__":
main()
This looks very much like the version we have been playing before. So we can insert the same hacks here as we did with the python version. Note though, this retrieved version is not playable, since it contains (fixable) errors, for example:
def AttemptPlayerMoveParse error at or near `RETURN_VALUE' instruction at offset 112
def GenerateFlagText(x, y):
key = x + y * 20
encoded = "¥·¾±½¿·\x8d¦½\x8dãã\x92´¾³\xa0·ÿ½¼ü±½¿"
return "".join([chr(ord(c) ^ key) for c in encoded])
That’s not a real problem, we can work that out if we wanted to. More important is that we confirmed that the python version and the executable version are probably based on the same source. So, all in all, many ways to obtain the flag.