

Overview
- Completion Time: Around 1 hour
- Language: C/C++
- Difficulty: 1.5
- Quality: 3.0
- Platform & Arch: Windows, x86-64
- Link: CrackMe
Hello! Today we’ll be walking through my solution to a very simple CrackMe that may serve as a good introductory puzzle for someone new to software RE.
We start with the binary we downloaded from crackmes.one – GoCrackMe.exe – and run it in our terminal with the following results – before and after we provide some input:


Unsurprisingly, we didn’t get far by just guessing the password. Let’s try throwing it into Ghidra.
While it’s normally pretty straight forward to find our Main function, since this is a beginner CrackMe it may be helpful to show the steps the reader may take to find it. When Ghidra first loads our binary, after it’s done running all of its analyzers, it offers to take us to the Entry point – Ghidra finds this entry point function by referring to the corresponding field in the PE header that saves the binaries entry point address. At the entry point for this binary, we see the following:
Let’s analyze this a bit more,
- Figure 1-1: Here, we see the entry point that Ghidra identified. We can see from our decompilation window (and assembly listing) that we have two functions at the entry point – one for the security cookie and the other for the rest of the code (FUN_140001d94). Lets enter the latter function and go from there.
- Figure 1-2: In this function (FUN_140001d94) we can see see several function calls. From experience, at this point we can easily identify Main and continue our analysis but for the sake of this walkthrough lets assume it’s very unclear to us which of these functions Main really is. One easy way of determining which function is Main is by examining the Function Call Graph ( Window -> Function Call Graph ).
- Figure 1-3: In this image we see the Function Call Graph for FUN_1400014d94. After we clean up some of the reference nodes, we can see two “big functions” that make several function calls each – I’ve highlighted them in yellow to make them more visible. In the bottom left we have FUN_140001290 and in the bottom center we have FUN_140002450 – lets examine both to see which is most likely to be Main.
- Figure 1-4: Now that we’re inside FUN_140002450, it’s obvious to us that this function cannot be Main due to the large number of compiler-specific functions and extensive use of “magic numbers”. Instead, we recognize this function as a complicated, boiler plate function added by the compiler that’s similar to many of the other compiler added functions we saw in FUN_1400014d94. We can now confidently rename FUN_140001290 to Main and continue our analysis.
Now, let’s examine Main:
While there’s some initial confusion, Main looks pretty straight forward:
- Figure 2-1: Once we get past Main’s local variable definitions, we get into the bulk of the function and see several “cout” and “cin” C++ symbol uses. We know this to be the case due to cout/cin_exref being passed as the first function parameter, even though Ghidra has incorrectly identified these operator symbols as function calls. We also use some basic deduction skills to identify the “Correct Answer” and other message strings; additionally we see some value being read in from the user input via cin and compared to our “Correct Answer” value with a memcmp call. Lets rename everything and see if we can clear up our decompilation.
- Figure 2-2: After renaming our variables, our decompilation is now much easier to read. However, from this point in Main we still can’t really tell what our sCorrectPassword value is yet – let’s scroll up a bit and see what we find.
- Figure 2-3: Finally we see what our sCorrectPasword value might be – from the decompilation, it looks like Ghidra is reading a hex value (0x4d6b734072433047) into sCorrectPassword indexes 0-7 and our 8th string index is set to 0x33. The program then transforms our hex value using several operations, the most notable transform being a bitshift left operation by 8 (auVar1 << 8). When we replicate this in Python we get something that vaguely resembles a password, but when we try it as our password it doesn’t work.
Armed with this information, lets see what we can find from our debugger:
- Figure 3-1: One line in particular from our decompilation – the memcmp function call – stands out. We know that at this point in the program we’re comparing 9 characters from the correct password to our user input, and given this we know that if we can find sCorrectPassword in memory at this location we’ll find the correct password as well! Lets examine our [RSP + 0x40] instruction (and memory location) in our debugger to see what we find.
- Figure 3-2: Sure enough, we find the correct password – G0Cr@ckM3 – stored as plaintext ASCII in memory. The only check we need to bypass to get to this point is a check to see if the user input is 9 characters long (cmp qword ptr ss:[rsp+30], 9) so we provide a basic password of “123456789” to get to our [RSP + 0x40] instruction; in our debugger, we could have also chosen to replace the CMP instruction with a NOP to bypass the check entirely.
- Figure 3-3: When we test out our newly found password, it seems to be correct!
In summary, this was a very simple CrackMe. All we needed to do was find Main, identify our memcmp function call, and view that memory location in our debugger to find our password. Alternatively, we could have patched the program with several NOP‘s until our “Congratulations!” message was loaded into our variable, and then patched in an unconditional jump (JMP) to a cout function call. Overall, despite being very easy, this CrackMe is a great primer for someone new to SRE – I hope you found it useful, and have a great rest of your day.