

Overview
- Completion Time: Around 4 days
- Language: C/C++
- Difficulty: 2.4
- Quality: 5.2
- Platform & Arch: Windows, x86-64
- Link: CrackMe
Hello! Today we’ll be walking through my solution to an interesting CrackMe that has a series of interesting steps we need to follow to properly solve – let’s begin!
We start with the binary we downloaded from crackmes.one – Random.exe – and run it in our terminal with the following results – before and after we provide some input:


So it looks like our original guess of “test” isn’t correct. While we didn’t get incredibly lucky and successfully guess the password on the first go we did learn a few bits of valuable information,
- When we first run the binary we get a prompt asking for medicine and two images – for now, we’ll call these images “Image A” and “Image B”
- When we try to provide some input it looks like the program takes our input and uses it to produce another image as evident of the “C” symbol that gets printed
Making note of this, we throw our binary into Ghidra to see what we can find,
It looks like our binary can be broken down into roughly five sections, lets see what we found:
- Figure 1-1: We see some function call patterns – several calls of FUN_140001ae0, followed by one call of FUN_140001020, followed by several more calls of FUN_140001ae0. We notice that we call FUN_140001ae0 exactly eight times – the exact number of rows in “Image A” and “Image B” – so for now we’ll rename the function to LineCheckAndPrint since when we jump into it looks like its doing a bit more than just printing. We can also rename FUN_140001020 to SpecialPrint since it looks like its being used to print a newline character between images A and B and also handles printing the prompt asking the user for the medicine. Finally, at the bottom of Figure 1-1 we have one last function call – FUN_140001080 – which, when we examine it in Ghidra, internally makes a call to vfscanf – we rename the function and identify the first two notable parameters as: “%s” and pUserInput – we can confirm that this is the user input by running the program in a debugger, setting a breakpoint on Main, single-stepping instructions until we reach this point in the program and check memory to confirm.
- Figure 1-2: While our image says “local_58” we know from debugging that this value is actually the provided user input prompt. We can now put together a working theory that each time we called LineCheckAndPrint the argument we passed to be printed was an array of values, then the function does some magic behind the scenes to convert each element in the array to a printable character. Here, the first do … while loop seems to simply be subtracting 0x30 from each element. Our second do … while loop seems to be be subtracting 0x30 again, but after double checking the assembly behind Ghidras decompilation we notice that the program is doing a do … while loop to add 0xD ( ADD byte ptr [RBP + i * 0x1 + -0x50], 0xD ) instead of subtracting.
- Figure 1-3: There are several interesting things going on here! First, we notice that this function is that Ghidra is referencing several DAT_XX hard-coded data locations in memory and that each of these locations are exactly 1 byte away from each another. Additionally, we notice that these are individually addressed from index 0 to index 55 (56 total elements) – this is the exact number of characters in Images A and B (7 characters * 8 row = 56 characters total). Next, we notice that we add each element in this image by (UserInput[i] >> 0x1f). When we check with a debugger, we confirm that elements that are being bitshifted here are from Image A. Finally, we see one call to SpecialPrint that prints two new lines (“\n\n“) followed by one call to LineCheckAndPrint for each row in Image B.
- Figure 1-4: Nothing special appears to be happening here, but the program does seem to be checking values from Image A and Image B then updating uVar6 and uVar2 – an educated guess might be that its checking each element individually to confirm if users input was correct.
- Figure 1-5: At the end of our program, we derive some value uVar5 from uVar6 (which was incremented each time the users input had a matching correct character) and – depending on the value of uVar5 – the program sets either a “Success” or “Failure” message to be printed.
At this point we can also make the assumption that the program author was trying to give us a hint, and that the two images printed at the beginning of the program – Image A and Image B – were actually Bobby Healthy and Bobby Sick – and the goal of this CrackMe is to provide some input such that the elements in Bobby Sick are modified to match the elements in Bobby Healthy.
Lets take a quick look at our LineCheckAndPrint function in more detail,

It looks like it’s individually checking each of the 7 characters in our row array representing the character to be printed.
For each element in our row array, three checks are made…
- Is the element greater than 1? Cast the value to a char (“%c”) and print.
- Is the element equal to 0? Print a space.
- Is the element equal to 1? Print a zero.
And from our theory earlier we now know that the goal of our program is to provide some input such that, after modification, our LineCheckPrint function will modify the Bobby Sick image elements to print a combination of “0”s and “Spaces” such that it matches the Bobby Healthy image. We also know from Figure 1-3 that each element in of our Bobby Sick image is hard-coded into the binary (as DAT_XX references in Ghidra); lets lay out our Bobby Sick and Bobby Healthy image arrays side by side.

The last parts of this puzzle involve recognizing two additional points,
- Our answer is likely a string of numbers, since we know that subtracting an ASCII character by 0x30 gives its value as an integer.
- The bitwise AND operation we saw earlier can largely be ignored since it seems likes its only used to mask the bits we want to keep.
Therefore the solution is as simple as figuring out how many spaces to “right shift” so LineCheckPrint prints a space (right shift MSB – most significant bit – to “0”) or prints a 0 (right shift the MSB to “1”). This means our answer to this CrackMe is simply how many spaces we need to right-shift each Bobby Sick element to print the correct character!
For example,
- In our Bobby Sick image, row 0 column 0 has a value of “0” – we know LineCheckPrint will print a space when it receives zero so we don’t need to bitshift right any spaces to print the space character we need, therefore our input will be 0.
- In our Bobby Sick image, row 0 column 1 has a value of “80” (binary: 1000 0000); to match our Bobby Healthy image row 0 column 1 character of “0” we need to right-shift 0x80 by seven spaces (binary: 1000 0000 -> 0000 0001) so LineCheckPrint correctly prints the “0” character we need, therefore our input will be 7.
- In our Bobby Sick image, row 0 column 2 has a value of “01” (binary: 0001); to match our Bobby Healthy image row 0 column 2 character is “0” so we don’t need to right-shift “0x1” at all since LineCheckPrint correctly prints the “0” character we need when it receives 1, therefore our input will be 0.
We repeat this for every element in our Bobby Sick image – our answer in array format looks like:

Or when provided as input: 07014620353040506000012238300008007020113308080020070008
Lets confirm our answer and wrap up this CrackMe,

Nice! It looks like our theory was correct and that the answer was just a long string of numbers. In summary, after examining the binary in Ghidra we determined that the answer would be a long string of ASCII characters; when the ASCII character was a number, it represented how many times we needed to right-shift the corresponding Bobby Sick element so LineCheckPrint could correctly print a matching “space” or “0” character. Alternatively, we could’ve solved this CrackMe by patching the instructions we found in Figure 1-4 and Figure 1-5 so that the program always prints the success message regardless of our input. I hope you enjoyed this walkthrough, if you have any questions feel free to reach out, you can find my contact information in the About Me section of my website. Have a good one!