Thursday, August 13, 2009

A Basic KeyGenMe Tutorial

--------------------------------------------------------------------------Materials: OllyDbg -
Code:
http://www.ollydbg.de/
KeyGenMe -
Code:
http://crackmes.de/users/hexic/crack_me/download/
An actual desire to learn Alright. In this paper, I'm going to guide you in learning the serial algorithm for the crackmes.de challenge by HEXiC. Although a real-world application may not use such a simple process, this is excellent for beginners in the reverse engineering field. I will not code a key generator ("keygen") for you, but you should hopefully be able to with the information learned by the end of this tutorial. First, let's open the application up in OllyDbg. There are other programs available, but this is the most popular. Press F9 to run the executable until a window prompts us for a name and serial key. I will be using the name 'sToRm' for this example. Fill the serial field with some zeros just as a placeholder. Now, I have done some prior analysis of the assembly in this application and have determined that the validity of the serial is checked through the CALL KeyGenMe.00401262 at address 0040116F. Place a breakpoint here, and when you click 'Check,' it will pause on that line. Now, press F7 to step into the call. Just a side note, let's search the binary for all text strings and record a few select ones, as this will become important soon. Right-click in OllyDbg, and select 'All referenced text strings' from the 'Search for' submenu. You should see our inputted serial and name (twice, actually). Take note of the addresses: 004010C1 004010D7 004010F9 PUSH KeyGenMe.00403254 PUSH KeyGenMe.004032A4 PUSH KeyGenMe.00403254 ASCII "sToRm" ASCII "00000000000000" ASCII "sToRm"

Step down the code until you reach: 0040126F |. A0 54324000 MOV AL,BYTE PTR DS:[403254]

In this line, the binary is taking the value of the byte at address 00403254 (hey, look familiar?) and storing it to AL. If you look in your 'Hex dump' window and hop to that address, you can clearly see the inputted name. So, 00403254 is the first character of the name (73 or 's'). The next line divides our value by 10, leaving the 3 after the decimal point to be stored in AH. EDX also picks up 00000003 on the following line. The next line uses the EDX value of 3 to add to the address 00403235, essentially pulling a value from the charset of the serial number. 00403235 holds 34, which is converted to ASCII '4' and stored to AL. Therefore, the first character of our serial is 4. 00401278 0040127E 00401284 00401286 |. |. |. |. 8A82 35324000 8A15 A4324000 38D0 0F85 E8000000 MOV MOV CMP JNZ AL,BYTE PTR DS:[EDX+403235] DL,BYTE PTR DS:[4032A4] AL,DL KeyGenMe.00401374

The next line does the same as the previous, but it is hard-coded to pull

the first character of our inputted serial at 004032A4 and assign to DL. Comparing AL (4) to DL (0), we JMP to the end of the CALL to return to the badboy. Restart the execution using Ctrl+F2, and follow the same steps as before, only starting the serial with 4 this time. Now, we safely pass by the check of the first character of the serial and move onto the second. This is exactly like before, but notice how the commands now grab their values but one byte ahead: 0040128E 00401293 00401295 00401297 0040129D |. |. |. |. |. A0 55324000 F6F1 8AD4 8A82 35324000 8A15 A5324000 MOV DIV MOV MOV MOV AL,BYTE PTR DS:[403255] CL DL,AH AL,BYTE PTR DS:[EDX+403235] DL,BYTE PTR DS:[4032A5]

See the logic here? If you don't, I suggest you reread the explanation of the first part or just go outside and make friends (lol). Anyways, we see that the 2nd character of the valid serial for name 'sToRm' is 'T'. 004012AB 004012B0 |. |. A0 A6324000 2C 2D MOV AL,BYTE PTR DS:[4032A6] SUB AL,2D

These two lines are incredibly simple. 2D in hex is a hyphen ('-'), so we now have the third character of our serial and the completion of the first section of it. On to the next part! 004012B8 004012BA 004012BC 004012BE 004012C4 004012C6 004012C8 004012CA 004012CB |. 33D2 |. 33C0 |> /33C9 |. |8A8A 54324000 |. |0AC9 |. |74 05 |. |03C1 |. |42 |.^\EB EF XOR EDX,EDX XOR EAX,EAX /XOR ECX,ECX |MOV CL,BYTE PTR DS:[EDX+403254] |OR CL,CL |JE SHORT KeyGenMe.004012CD |ADD EAX,ECX |INC EDX \JMP SHORT KeyGenMe.004012BC

You may pass over this loop at first, thinking "oh, it's just doing something with the name" and skip over it, but this loop is also important. It's actually going to give us the next eight characters of our serial! After resetting EAX and EDX (which will be the counter for this loop), we enter it. And now we see this damn 00403254 address coming up again! EDX equals 0 at this moment, so it will store the value of 00403254 to CL (and soon ECX; watch the registers on the right). We skip the JMP out of the loop until we run out of characters in our name to load. The hex of the 's' in 'sToRm' is added to the empty EAX, EDX is incremented by 1, ECX is reset, and we start the loop over again, this time pulling the second value of our name. If you're lazy, you'll see that the value of EAX (our full name) at the end of the loop is 1F5. This could have also been easily calculated with a calculator set to hex (hey, calc.exe!). 000001F5 is pushed onto the stack, some formatting is done, and then we are quickly thrusted into another loop. 004012E2 004012E4 004012EA 004012F0 004012F2 004012F4 004012F5 004012F8 |. /EB 11 |> |8A81 C0324000 |. |8A91 A7324000 |. |38D0 |. |75 06 |. |41 |> \83F9 08 |.^ 75 EA JMP SHORT KeyGenMe.004012F5 /MOV AL,BYTE PTR DS:[ECX+4032C0] |MOV DL,BYTE PTR DS:[ECX+4032A7] |CMP AL,DL |JNZ SHORT KeyGenMe.004012FA |INC ECX CMP ECX,8 \JNZ SHORT KeyGenMe.004012E4

ECX is now the counter, and the inputted serial is being checked against the hex value of our name we just determined. Don't see it? You may need to squint a little. The first five digits of our name value are zeros, because 1F5 in hex is really 000001F5 (eight characters). The ECX counter will increment until it equals 8, where it will then JMP out of the loop to avoid pulling and comparing values that aren't part of the hexidecimal portion of the serial. Therefore, our serial is now '4T-000001F5'. We're getting closer. :) Reset the execution, and return past this loop with our newly-found piece. Once again, the next lines call for a hyphen, or else we will be JMP'd to the badboy. Add it to the serial, and proceed once again. 0040130A 0040130C 00401311 00401317 0040131D 0040131F 00401321 00401327 0040132D 0040132F |. |. |. |. |. |. |. |. |. |. 33C0 B9 10000000 8B1D B8324000 8A83 52324000 F6F1 8AD4 8A82 35324000 8A15 B0324000 38D0 75 43 XOR MOV MOV MOV DIV MOV MOV MOV CMP JNZ EAX,EAX ECX,10 EBX,DWORD PTR DS:[4032B8] AL,BYTE PTR DS:[EBX+403252] CL DL,AH AL,BYTE PTR DS:[EDX+403235] DL,BYTE PTR DS:[4032B0] AL,DL SHORT KeyGenMe.00401374

Look somewhat familiar? These lines are near-identical to the ones that determined the first character of our serial. Follow in the 'Hex dump' to the addresses that are requested, and you will find that it is now sending the 1st-tolast character of our name through the division algorithm. For instance, EBX equals 5, so the value of 00403257 is ASCII 'R' (or 52 in hex). Can you figure out why EBX equals 5 in the first place? Anyways, divide the value by 10, and we are found with the fact that our next character in the serial must be a 'G', because it produces a hex value of 47. Follow this procedure again for the next (and last character) in the inputted name, and a 9 is produced. Now, we have a serial of '4T-000001F5-G9' for name 'sToRm'. Reset the execution (as if you haven't done it enough), and then run it with our credentials. Voila! You've just reverse engineered this algorithm. If you really want to make sure you understand the concepts behind this crackme, then try the entire process over again with a different name. If you wish to master it, then code a keygen with the information you've learned about it. ;) Cheers! sToRm

No comments:

Post a Comment