Patching a Video Game on Flareon CTF by Mandiant (Google Cloud)
Reverse Engineering · Published Jan 1, 2026 · 7 minute read
Learning assembly because i hate myself >:(

Blog 6 — 0.png
Flareon CTF was a reverse engineering CTF challenge developed by Mandiant, part of Google Cloud. There are 10 challenges developed by the author. At the time of writing, there are no finishers from Indonesia who have solved all of the challenges. All the challenge was hard for me, but at least i learn something during the analysis. And here is what i got from the challenge number \4 about Game Hacking
1. Challenge Overview
Difficulty
Medium
Category
Reverse Engineering
Topic
DWARF, Nim Language
1ti5wEdn3sd4ymYDud3s@flare-on.com
We are given folder package which contains a Windows executable named mydude.exe along with supporting asset for graphics, fonts, and sound files. The program was built using the Nim programming language. Upon execution, the game displays a bouncing frog character with two menu options: "DUDE" to start the game and "EXIT" to close the application. The controls are straightforward: up arrow to jump, down arrow to duck, and ESC to quit. In short, this game is similar to Chrome's offline dinosaur game or Flappy Bird where the player needs to avoid the obstacles.
Blog 6 — 1.png
Each obstacle is labeled with a single letter representing days of the week: (S)unday, (M)onday, (T)uesday, and (F)riday. The challenge title "Wednesday" means that the player acts as Wednesday. The game logic works as follows: when an obstacle approaches with a day that comes before Wednesday alphabetically (Monday or Sunday), the player must duck underneath it. When the obstacle represents a day after Wednesday alphabetically (Friday or Thursday), the player must jump over it.
Blog 6 — 2.png
The game tracks the player's score, incrementing by one for each correctly navigated obstacle. When the player performs the wrong action for an obstacle or collides with the solid part of an obstacle, a death sound plays and the game resets to the beginning. With 296 total obstacles to complete, manual gameplay becomes impossible since a single mistake or collision will trigger the game to restart from the beginning. So, Our goal is to patch the binary rather than attempting to beat the game manually.
Blog 6 — 3.png
2. Program Execution Flow
The main execution begins with updateScenerbzI, which serves as the orchestrator for the entire game state. This function is called every frame and handles position updates, animation updates, and triggers the collision detection system. During each frame, the collision detection activates through checkCollisionsP9bT which will checking each pair for potential collisions. When a collision is confirmed, both objects involved need to be notified so they can respond appropriately. The system calls onCollideBN6X twice, once for each object, passing the other object as a parameter. This wrapper function performs type checking and validation to ensure the objects can handle collision events, then dispatches to the appropriate object\-specific collision handler. For the player character, this leads to onCollide9byA, where the actual game logic resides.
Blog 6 — 4.png
The onCollide9byAjE9cSmbSbow3F9cTFQfLg function contains the core game logic that determines whether the player lives or dies. This function receives two parameters: the player object passed in the ECX register and the obstacle object passed in the EDX register.
Blog 6 — 5.png
Based on the image above, The first collision type handled is Type 3, which represents obstacles. When the function encounters a Type 3 collider at address 0x00432159, it compares a word value at offset +0x8 of the collider against reference data stored at address 0x0043e8fc. It also compares a byte at offset +0xa against data at 0x0043e8fe. These comparisons serve as zone markers that distinguish safe collision areas from fatal ones. If the comparison at 0x00432168 shows the values match, the code treats this as a safe zone and continues processing other colliders. However, if the values dont match (the player hit a solid dangerous part of the obstacle), the code executes a critical instruction at address 0x00432247: MOV byte ptr [EAX + 0xf9], 0x1. This instruction writes the value 1 to offset 0xf9 in the player object, which serves as the game over flag.
Blog 6 — 6.png
After processing all Type 3 colliders, the function begins a second loop starting at address 0x004321a2 to check for Type 5 colliders, view the image above. Type 5 represents the day label markers (S, M, F, etc.) that implement the core game mechanic. When the function finds a Type 5 collider at address 0x004321db, it performs a data comparison at 0x004321e9 to verify the label data matches expected values at addresses 0x0043e8e0 and 0x0043e8e4. If the label data does not match the expected pattern, the code skips this collider and continues the loop.
When a valid Type 5 day label collider is found, the code enters the validation logic at address 0x00432320. Then at address 0x00432348, the code loads the obstacle's day character from offset +0xf8 using MOVSX EDX, byte ptr [ESI + 0xf8], which sign\-extends the byte value into EDX. At 0x0043234f, it loads the player's expected day character from offset +0xf8 using MOVZX EAX, byte ptr [EDI + 0xf8], which zero\-extends the byte value into EAX. The comparison occurs at 0x00432356 with the instruction CMP EDX, EAX.
Blog 6 — 7.png
The result of this comparison determines the player's fate. If the values match, the jump at 0x00432358 is taken to address 0x00432261, where the code increments the global score. At 0x00432267, the instruction ADD EBX, 1 increments the score value, which is then stored back to the global variable at address 0x0044ddb0 via the instruction at 0x00432279. If the day characters do not match, the jump at 0x00432358 is not taken, and execution continues to address 0x0043235e. Here, the code executes another MOV byte ptr [EDI + 0xf9], 0x1 instruction, setting the same game over flag that Type 3 collisions set.
3. Vulnerability Analysis
The vulnerability lies on function onCollide9byA. The collision handler performs two checks that determine game state. The first check sets the game over flag when the player collides with solid obstacles. The second check sets the game over flag when the player performs the wrong action (jumping when they should duck, or vice versa).
Both death conditions funnel through the same mechanism: setting a single byte flag at offset 0xf9 in the player object. This creates an exploitable point because the game's entire death system depends on these two instructions executing. If these instructions can be prevented from executing or their effects can be bypassed, the player becomes invulnerable to both collision damage and wrong action penalties.
Blog 6 — 8.png
The day comparison logic presents another potential exploit point. This comparison uses a simple CMP instruction followed by a conditional JZ jump. The jump is taken when the day characters match, leading to the score increment path. When they do not match, execution falls through to the death flag instruction. This conditional jump represents a binary decision point that can be modified to always take the success path regardless of whether the day characters actually match. Once the game over flag is prevented from being set, or once the day comparison is forced to always succeed, the normal game rules no longer apply and the player's character can be immortal so we can reach 296 score easily.
4. Solution
The most reliable approach to solving this challenge is to neutralize the collision logic itself to remove the game’s ability to kill the player, rather than making automation bot to auto correct jump/duck behavior for each obstacle. The simplest way is to force the execution flow to always take the “success” path, regardless of player input.
From the execution flow, the win or lose condition are written in the function onCollide9byA, which makes it the ideal patch target. Inside this function, there are two different logical paths: one that handles solid obstacle collisions and another that validates whether the player performed the correct action (jump or duck) for a given day\-labeled obstacle.
Blog 6 — 9.png
At address 0x00432247, the game executes a MOV byte ptr [EAX + 0xf9], 0x1 instruction whenever the player hits a fatal part of an obstacle. A second, nearly identical instruction at 0x0043235e performs the same write when the player chooses the wrong action for a day prefix. By overwriting both of these instructions with NOPs, the game over flag is never set, regardless of what the collision logic decides internally. All comparisons, sound logic, and score updates still execute normally, but the final “kill switch” is effectively removed.
Blog 6 — cheat.gif
Once this patch is applied and the binary is re\-compiled, the player can collide with obstacles freely, ignore the jump/duck rules entirely, and continue moving forward without interruption. After passing 296 obstacles, the flag will revealed.
Blog 6 — flag.webp