Keygenme 0x01 - "simpledatas_keygenme_1" by simpledata
Prev: 0x00 - OldSoft's Keygenme #2
"simpledatas_keygenme_1" by simpledata can be found here. Like the prior level, this is classified on the site as a "Very Easy" C/C++ program for the Windows platform. Note that the .zip password this time is "crackmes.de".
When we run the program we find that it is (surprise) very straightforward: a name and serial number are requested, and when we select "Check" we receive feedback on our input.
Let's load it into IDA and look for feedback prompts in the Strings window (Shift+F12) so that we can find the proper function. "Great job cracker!" appears to be the success message, so let's double-click it to jump to its address in the IDA view. If we then right-click to view its cross-references, we find just one. Hit "OK" to jump to it.
We land in a subroutine which takes two character pointers as arguments and ultimately lands in one of two boxes/sets of instructions - one with a success message, one with a failure message - before exiting. We can safely assume that the two character pointer args likely point to our name and serial buffers, so this subroutine should contain all we need in order to craft a keygen.
However, looking at the length of this subroutine and the total number of args and vars, trying to interpret this based on the instructions themselves like we did in the previous level would be unwise. Instead, let's break at the first "push ebp" instruction and run the program so that we can see exactly what is being moved between registers and memory addresses. I will enter "alex" as the name and "test123" as the serial.
After hitting the breakpoint, let's step past the first 3 instructions of function prologue/stack frame maintenance. Here is what happens next in order:
"simpledatas_keygenme_1" by simpledata can be found here. Like the prior level, this is classified on the site as a "Very Easy" C/C++ program for the Windows platform. Note that the .zip password this time is "crackmes.de".
When we run the program we find that it is (surprise) very straightforward: a name and serial number are requested, and when we select "Check" we receive feedback on our input.
Let's load it into IDA and look for feedback prompts in the Strings window (Shift+F12) so that we can find the proper function. "Great job cracker!" appears to be the success message, so let's double-click it to jump to its address in the IDA view. If we then right-click to view its cross-references, we find just one. Hit "OK" to jump to it.
We land in a subroutine which takes two character pointers as arguments and ultimately lands in one of two boxes/sets of instructions - one with a success message, one with a failure message - before exiting. We can safely assume that the two character pointer args likely point to our name and serial buffers, so this subroutine should contain all we need in order to craft a keygen.
However, looking at the length of this subroutine and the total number of args and vars, trying to interpret this based on the instructions themselves like we did in the previous level would be unwise. Instead, let's break at the first "push ebp" instruction and run the program so that we can see exactly what is being moved between registers and memory addresses. I will enter "alex" as the name and "test123" as the serial.
After hitting the breakpoint, let's step past the first 3 instructions of function prologue/stack frame maintenance. Here is what happens next in order:
- The address of our name buffer (ebp+8h) is loaded into eax, then placed into this subroutine's stack frame (ebp-Ch). We can confirm this by checking the value of eax during these two instructions.
- The address of our serial buffer (ebp+Ch) is loaded into eax, then placed into this subroutine's stack frame (ebp-10h). We can confirm this as well by checking the value of eax during these two instructions.
- The pointer to our name buffer is placed into the top of the stack ([esp]) and strlen is called on it. The result is placed into ebp-30h.
- Zero is loaded into ebp-13Ch. We aren't sure what this is for just yet, so let's call it "STARTS_AT_ZERO." Possibly an iterator? A count? A sum?
- The pointer to our name buffer is placed into eax, and the first item of this array is loaded into eax (I entered "alex," so my value is 61h for "a"). Then, this is loaded into ebp-140h.
- Finally, zero is loaded into ebp-29Ch. However, if we peek ahead we can see that this is used in a compare, so this is likely an iterator initialized to zero.
Let's rename these args and vars so that we have a better understanding of what's going on later. Here's how it looks:
The next box is straightforward: the iterator is compared against the length of the name we entered. If it is less than the name length, we don't take the jump, and move to the left box.
The left box is pretty easy to understand now that our variables are renamed:
- The pointer to our name buffer is loaded into eax.
- The iterator is added to the pointer so that we iterate over each character.
- The character is loaded into edx.
- Our "STARTS_AT_ZERO" address is loaded into eax, and edx is added to it.
- Finally, our iterator is incremented.
It looks like our "STARTS_AT_ZERO" variable will be the sum of the name characters, so let's rename it. The left box now looks like this:
Once we have our char total for our name, our iterator increments to match the name length (four in my case) and we take the jump and move onto the right box. If we scan through it quickly, we find three sprintf calls which place three different formatted strings of "%d%d%d%d" in three different locations. We appear to either be generating three different serials, or possibly using sprintf multiple times for a single serial.
In either case, if we work backwards from each sprintf, we know that the value in esp will be the destination ptr, the value in esp+4 will be the format string ("%d%d%d%d" for all three), and the four values in esp+8h through esp+14h will be the four arguments provided. Let's add some comments to make this easier to understand, and let's also break after each sprintf to examine the stack and note the result of each output, adding them as a comment.
With the comments, this becomes very easy to understand. It's clear the three sprintfs are independent, each outputting some combination of: name length, the sum of the name chars, the value of the first name char, and a constant.
Finally, this box ends with a strcmp. We can see that its two arguments are our serial buffer and the first sprintf buffer. If these aren't equal, we take a jump to a box which has the same check, but with the second sprintf buffer. If these aren't equal, we do one final check and compare our serial to the third sprintf buffer. Failing all three checks means that a ebp-2Ch check value, initialized to zero, is never set to one and we fail the "__Z11isValidatedb" boolean check. However, if any of those three strcmps pass, the check value is set to one and we pass the boolean check and succeed.
So, for any given name there are typically three correct serial numbers. Let's craft a keygen in Python (2.7) which will output all three for us:
print "enter name: "
name = raw_input()
name_len = str(len(name))
first_char = str(ord(name[0]))
name_sum = str(sum([ord(i) for i in name]))
print "acceptable serials are: "
print name_len + "110" + first_char + name_sum
print first_char + "95" + name_sum + name_len
print name_sum + "85" + name_len + first_char
If we run it and use "alexander" as a test, we get three serials:
enter name:
alexander
acceptable serials are:
911097948
97959489
94885997