[CRACK] JeFF0Falltrades' master0Fnone 1_x86_Demystified

In this write up I will tackle the first crackme 1_x86_Demystified from the jeFF0Falltrades awesome (and very recommended) tutorial serie “master0Fnone”. This write up is long due, I solved the challenge almost a year ago and I always told me “yeah I will write the write up”. Now it’s here, better late than never. Hope you can learn something from it!

Tools I will use:

  • Windows 11, to run the crackme
  • x86dbg, to do some dynamic analysis
  • Ghidra, to do some renaming…I mean static analysis

The objective is to find a key somewhere in the binary. We don’t know anything else other than this. I will try to run through my thought process while analyzing the binary. As I said, I already solved the challenge some time ago so I have some flashes in my mind about what to expect but still I don’t remember much.

Step 1: Triage

First, let’s get the binary and run it. After playing with it for a bit I can come to the following conclusions

  1. It doesn’t matter who wins
  2. It doesn’t matter if you draw
  3. Some strange characters are printed after each move

img1 Strange runes

So that ←[1;1H←[2J looks interesting. Mi first thought would be that this is the flag, encrypted in some way…but the situation looks funky. It’s a bit too in our face, not really in the binary as it was said. The thing is, currently we cannot do much with this information. Let’s move to more in depth analysis.

Step 2: The Main Function and a Detour

I’ve open Ghidra and analyzed the binary. We can easily find the main function by filtering the functions. By looking at the disassembly we can already see that the binary has not been stripped or at least has not been completely stripped. We can identify the names of some interesting functions: _init_game, _print_board and _check_game_state.

img2 The complete main with some variables already renamed

Just from those functions we can guess a general outline of the process flow: first we initialize the game and then we jump into the main game loop. Inside the loop we check for game completition, we run a mysterious function that has no name (FUN_00401c61), we print the strange characters, we print the board, we check game state and go ahead with the following turn. In case of draw or victory, we jump out of the loop and print whoever wins or the draw message. Pretty standard stuff.

So, the strange character sequence. It seems pretty hardcoded so at this point I realize something is strange and I might have misinterpreted. What do when nothing make sense?

img3 The first step of every search and the last Hail Mary before giving up

I’ll spare you the search and take a brief detour to explain what these characters do. We are talking about ANSI escape sequences. In our case they are used to modify the terminal each time the board is redrawn. We have 2 different commands prefixed by \x1b [, which is just the introduction to indicate that it’s an ASCII escape sequence

  1. 1;1H: Positions the cursor at row 1 column 1. It is needed because you want to influence with the next one also what you already wrote
  2. 2J: clear everything

So it just does a clean to the terminal. But wait, in our case nothing happens and the characters are just printed. Yes, the problem is in the configs of the Windows cmd. In order to make it work you add the following flag in the registry (to disable it change the last 1 to 0).

REG ADD HKCU\CONSOLE /f /v VirtualTerminalLevel /t REG_DWORD /d 1

Step 3: Going Deep

Now, you should not assume that just because a function is called “useless function” it contains useless info. If you are doing malware analysis or trying to crack a protector you must understand that your adversaries will try to make you lose your time, it doesn’t matter how…there is social engineering also in the deep assembly of executable files, as in everything else.

Trying to not lose ourselves in phylosophical ramblings and by uphelding the previously cited principle of not wasting time, let’s go directly into _check_game_state , which in this case, is where you want to be. If you investigate the other functions you will find that nothing really interesting for our purpose is happening. You should do that by the way, you could learn that FUN_00401c61 is the function that, as we could have guessed from the position, ask the player for the input.

The check game function checks if the game is ended and who won by changing the game status. If you really are sharp, you could even guess (correctly) that the variable local_15e is our board. Think about it and cross check with other clues. It’s a 333 array (18x18 matrix), it has been passed to the initialization (as it should) and in the function that prints it (maybe it was too obvious…). So now we have to understand the logic of how the algorithm checks the board to understand if someone has won.

img4 Our guy, _check_game_state (I cannot fit everything)

Step 4: The Algorithm

Look at the code we can see that two functions are called, but we don’t know what they do, also the loops are not really clear at first sight, but we can guess that they are looping in some way to check if there are any tris in the board. We can also see that our game_status variable can be one of 3 values: 0, 1 or 2. If you go back to main you can easily learn that:

  • 0 means someone won
  • 1 means it’s a draw
  • 2 means the game is still going on

You can see how two loops set the game state to 0, one sets it to 1 and if the return is not called, game_status 2 is returned. The general pattern for each loop is the following: call FUN_00401778, check if it returns 0 and if it doesn’t, change game_status and return. You could reverse the variables used in the loops and FUN_00401778 to learn that the function is checking for row, columns and diagonal matches, but I want you to focus on the loop that checks for a winner.

img5 You should see something interesting

What can you see? Some variables are set, ok, but the very strange part is that print. A print inside the check function? I don’t know how much you played before jumping into reversing, but nothing is printed besides what we already discussed (next move, win, draw). And the variable printed, a string as you can see from the %s, is that variable local_30, which is set to 0 and then modified by FUN_00401778. This function also doesn’t take any other parameters so it seem pretty independent from the state of the game. I guess we have a really interesting candidate for finding the flag.

Step 5: Getting the flag

Now, I could tell you that I studied in depth FUN_00401778 and understood all those shifts and and encrypted values to then write a small program to decrypt and print the flag. The reality is that as soon as I’ve seen that print function that we talked about before I spun up x32dbg and tried to make it print through some “jmp manipulation”. With this particular crackme it’s easy to find where what we see in Ghidra is when loading in memory the program, just search for the pattern representing the call to FUN_00401778 (e8 aa fd ff ff).

img6 Here is the call. Keep an eye on the jump

My guess at this point is that that jump is always taken and we never print for that reason. In reality, this is a wrong assumption because it is not even that jump, but the previous one, at 00701FAE, that is always taken. Trial and error when cracking it’s always the best way to learn.

We have some jmp to manipulate! Nop the first, play with the flag with the others in a way that are not taken ever, and you’ll get to 00702008. Run over and voilà!

img7 I’ll take it