Tuesday, June 3, 2014

HardCrypt CrackMe analysis

Hello people,

I was solving today random crackmes' as everyday ,and I passed by this easy but interesting crackme.
What really drew my attention is the key generation part, it wasn't a usual bruteforcing or some calculus equations you paste in your key generator application using your best programming/scripting language, interpret/compile and execute it to get the right key, instead it used a smart method to avoid anyone writting a simple key generator, we are going in this article to walkthrough this crackme and the way I solved it, we are going as well to discuss some other solutions people submitted.

So take a seat, bring your coffee and enjoy the ride.

First of all, you can get a copy of that crackme on the famous repository, it's named (HardCrypt), written by The Mentor.

Now, let's load our binary into PEiD to check if it was packed, luckily we found some cool info that points out no packer has been used on this binary.
Of course, that might be a false positive, experienced reverse engineers should know about "custom packers", a kind of packer that is able to fool an executable scanner tool.

We are ready to go now, let's load it into our lovely immunity disassembler (you can use your best debugger/disassembler), let's make the very usual steps anyone would begin reversing a crackme with, finding references to known strings.

As we can see here, we were lucky (until now), we found some reference strings that are well familiar to us. Let's double click the "wrong pass" one, that will take us into the text section that contains a pointer to this buffer.

Here, we can see that this application uses some glibc functions, like gets(), puts(), system() and such, that's too good until now, specially after seeing the strcmp() line. As we all know, strcmp() is a function that takes 2 parameters and returns zero if they are the same, otherwise if not. Let's take a further look in that strcmp() call, we are going to make a breakpoint on strcmp() line, and restart our application within the debugger, and as soon as you input a string, your debugger will pause on the breakpoint, a look on the stack memory, we will notice both parameters push'd as parameters to execute the strcmp() function.

Very interesting, isn't it? an unknown string passed to strcmp() with our string. Of course, you can really patch the crackme simply in this level and you are done, but I bet you enjoyed nothing by going the easy way (sometimes, in real application cases, you might not be able to even patch it).

Now let's take a deeper dive into this other unknown string, it's situated at the double word EBP-137 on the stack segment ,which is the address 0x0022FE11, also we can notice that this string is generated even before we provide our input into the crackme, if we scrolled up until we reach the beginning of our current procedure, place a break point into that address (on my machine, it is : 0x00401340), and restart the application within the debugger, we can notice that the breakpoint is hit before facing the gets() function (before inputting our  string), all that means simply that the key is generated in the prologue of the application, it doesn't have any mathematical relation with our input, which means the key is generated using one (or many) of our system's resources (date, ram usage, CPU ticks, etc...).

Let's examine that by stepping over the instructions starting from 0x00401340, the place we put a breakpoint at last time, until hitting the address 0x0040135A. Here we can notice a call to time() call, it's a windows API function that returns system time, here it's used with NULL as parameter, and the returned value is stored at address 0x0022FF34. After that we can also notice another win32 API call localtime(), which takes a time value and corrects it to the local zone timing. localtime() function takes one input, a pointer to the location where we stored the system time (the returned value of time(), in assembly any return value is saved into the accumulator register EAX).
On MSDN, localtime() returns a struct that contains an exact values about current time, this struct is named tm structure, Microsoft defined it on MSDN as follows :

struct tm {
  int tm_sec;
  int tm_min;
  int tm_hour;
  int tm_mday;
  int tm_mon;
  int tm_year;
  int tm_wday;
  int tm_yday;
  int tm_isdst;

In our crackme, after finishing localtime() function call, a pointer to tm structure is saved into EAX, then the struct members are copied into memory location 0x0022FF10 .

So far so good, after that a busy wait loop is executed at address 0x004013B2, looping while doing nothing for 190 million times.
After that loop, we can find a very interesting iteration starting at address 0x004013CF, this loop iterates 3 times, each iteration calls the win32 API function GetCursorPos(), this function (as MSDN defines) takes one parameter, a pointer to a POINT structure. Again, looking at POINT structure on MSDN, it's a structure that contains 2 members holding our cursor coordinates. In our crackme, this struct is located at address 0x0022FD08. After finishing the function GetCursorPos() call, it multiplies the y cursor coordinates by the x coordinates, adding the result into a variable initialized with the hexadecimal value 0x0022FFF0, and saved at memory address 0x0022FF38.

After the three iterations, the double word located at address 0x0022FF38 is multiplied by the sixth member of our time structure, which is the (tm_mon, month), then subtracted by the third member of the time structure, which is (tm_hour, obviously the hour). It then makes a function call to windows API function _itoa() that takes an integer variable, a pointer to a string, and a numbering system, converting that integer variable, in the number system provided, to a string, and saving it into the location we gave in the parameter list.

Finally, it appends the string "H!J" with our resulting string, and we now have the key that's going to be compared with our input string.

Now, as I mentioned earlier, we can just patch our strcmp function in an earlier stage of our analysis, and that's the easy (boring) way, but that won't always be the case, sometimes, some binaries may contain some CRC or checksums, which protect the binary image of being altered. After our analysis we now know how to write a very simple algorithm that generates a valid key, to be used in that crackme, and when checked, it outputs the happy string "Right Password".

Of course, even that solution isn't that easy, the generation function gets input from time() and our mouse cursor position, which most probably won't work by just coding a keygen, because simply the time and the cursor position will be different while executing each of them, our keygen and the crackme. One way made by Kirjava ( ) ,he injected a process that holds the execution of the crackme right at the beginning of its execution ,it then injects a dll, which reads the memory location containing the valid generated serial, printing it and restores the execution flow of the crackme, this is a smart solution in my opinion. Another solution is code caving, it's a kind of patching but still makes the job done.

Here is a sample C-like pseudocode for the key generation procedure :

Finally, I enjoyed the way the author of this crackme used in order to generate the key, so all greetz goes to him.

A thanks must go to Kirjava for his solutions, and crackmes.

-time() msdn :
-localtime() msdn :
-GetCursorPos() msdn :
-tm structure msdn :
-POINT structure msdn :

No comments:

Post a Comment