Challenge
The challenge presents as a big directory with many assets, seems like a game. And it is! From Readme.txt we need to reverse only dude.exe and not other files. The exe is a Super Mario-styled game in which you have to pass all the obstacles to win and get the flag:
As the challenge title states, you are the Wednesday. The obstacles are 1 or 2 stacked blocks each one representing the initial letter of the day, and you have to jump or duck depending on how the order of the block is, being in order with the day of the week. In the image above, I’ll have to duck since the order is Sunday ⇒ Monday and Wednesday will be subsequent in the correct order.
Btw I’m a noob on platform games, so I won’t do it the correct way :)
Solution
If we inspect the dude.exe
with Detect It Easy we get this:
Compiler: Nim
. Nim? What is this?
Turns up that Nim is actually a language, and there are many references to a SDL2 library (a lib for game dev) inside the disassembled executable. The final result of an application written with Nim is a compiled C/C++ executable.
From IDA we see a lot of functions, most of which we aren’t obviously interested. Some lines contain the already mentioned SDL references, some others are compiler added code. Most of exports are resolved at runtime, so we don’t see them inside the Imports menu in IDA. So we search through strings, trying to catch some interesting one. Scrolling down we find some of them related to the scoring logic, like setting up the score text:
From IDA, we identified the most important parts of the code flow:
_NimMainInner
jumps to NimMainModule
so we need to investigate further inside this function. We download all the subs that this function invokes by saving graph in txt:
obtaining a more-readable file:
There are many ways which we might exploit to beat the game:
- score: maybe the game is set to be beaten with a score number that equals
N
. If we force that value toN
, we might win - force the win scene:
Nim
sets up the scenes somewhere in the code, we might find a way to redirect control flow into the win scene and get the flag - collisions: we can try to disable collisions and just open the game and wait the end
Anyway, I decided to go with the 3rd way. Searching context information reveals an interesting result:
From IDA we find that that this is the outer sub that eventually jumps into this sub:
that is the internal sub that handles the collision logic. Inside, @ loc_432261
there is a block of code that does the score increment depending on the collision. The hypothesis is if the player didn’t collide, it sets the score to ebx
register and increments it:
The other case must be the error case in which the scene is reset and the game is restarted.
Ok easy, so we just patch the jz
to a jmp
forcing the code not to restart the scene.
But… it’s not enough. If you restart and you don’t touch the keyboard, it fails? Why?
… That block of code controls only the being in order part, not really the impact with other blocks. So you can either
- just duck down for minutes and wait
- search further and disable the block collision
I didn’t have a quite heavy object nearby to put on the 🔽 of the keyboard, so I continued :)
By inspecting the code flow in the same function there’s an additional case above that block, with similar logic:
I think the cmp [edx+2], al
in some way checks the altitude of the player, trying to understand if the space next to the player is free (player has jumped or ducked down so no obstacle ahead), but the important thing here is to patch the jnz
instruction with a jmp
to the location. Test the game and see collision are not my problem now:
Now, I run the game, I leave my keyboard, I make coffee and wait for a few minutes. Eventually, I get the flag with coffee in my hand :)
See you in the next research!