jockcranley's T0AD K3YG3N Tutorial

July 14, 2015 - Reading time: 6 minutes

Target: jockcranley's T0AD K3YG3N

URL: http://www.crackmes.de/users/jockcranley/t0ad_k3yg3n/

Protection: Serial

Description: Crackme with a serial protection.

Tools: Immunity Debugger

First load the crackme into Immunity Debugger and press ALT+F9 to get to the crackme-code.
If we scroll down in the debugger window we find some I/O-calls.

0040142E |. C70424 64A0400> MOV DWORD PTR SS:[ESP],toadkey3.0040A064 ; |||ASCII "< T0AD K3YG3N >"
00401435 |. E8 966C0000     CALL <JMP.&msvcrt.puts>;                  ; ||\puts
0040143A |. C70424 74A0400> MOV DWORD PTR SS:[ESP],toadkey3.0040A074 ; ||ASCII "Username: "
00401441 |. E8 926C0000     CALL <JMP.&msvcrt.printf>                ; |\printf
00401446 |. A1 D4E14000     MOV EAX,DWORD PTR DS:[<&msvcrt._iob>]    ; |
0040144B |. 894424 08       MOV DWORD PTR SS:[ESP+8],EAX             ; |
0040144F |. C74424 04 0900> MOV DWORD PTR SS:[ESP+4],9               ; |
00401457 |. 8D4424 23       LEA EAX,DWORD PTR SS:[ESP+23]            ; |
0040145B |. 890424          MOV DWORD PTR SS:[ESP],EAX               ; |
0040145E |. E8 7D6C0000     CALL <JMP.&msvcrt.fgets>                 ; \fgets

So lets set a breakpoint at the fgets call and see what the program will do to our entered username.

Right after we've entered a username some checks are made to ensure the lenght of the name.

00401463 |. C74424 38 0000> MOV DWORD PTR SS:[ESP+38],0   ; Initialize the index-pointer
0040146B |. EB 1B           JMP SHORT toadkey3.00401488   ; Jump to the end
0040146D |> 8D5424 23      /LEA EDX,DWORD PTR SS:[ESP+23] ; Get the name variable
00401471 |. 8B4424 38      |MOV EAX,DWORD PTR SS:[ESP+38] ; Get the index-pointer
00401475 |. 01D0           |ADD EAX,EDX                   ; Set correct position in the name variable
00401477 |. 0FB600         |MOVZX EAX,BYTE PTR DS:[EAX]   ; Get current char
0040147A |. 3C 20          |CMP AL,20                     ; Compare current char to space
0040147C |. 75 05          |JNZ SHORT toadkey3.00401483   ; If current char != space jump
0040147E |. E9 0B010000    |JMP toadkey3.0040158E         ; Jump to access denied message
00401483 |> 834424 38 01   |ADD DWORD PTR SS:[ESP+38],1   ; Increment the index-pointer
00401488 |> 837C24 38 08    CMP DWORD PTR SS:[ESP+38],8   ; Compare the index-pointer to 8
0040148D |.^7E DE          \JLE SHORT toadkey3.0040146D   ; If the index-pointer is less or equal to 8, loop

So now we know the rules for a valid username, it can't contain any spaces and has to be eight characters long.
But since the input method is fgets a valid username can be seven characters long containing a new line character as the eight character.
We can also see that the ninth character in our entered username is 0x00.

If we trace on in the code we find the serial calculation routine right after the fgets call for the password.

004014C5 |. 0FB64424 24     MOVZX EAX,BYTE PTR SS:[ESP+24] ; charValue = Second char of the username
004014CA |. 66:0FBED0       MOVSX DX,AL                    ; DX = Second char of the username
004014CE |. 6BD2 56         IMUL EDX,EDX,56                ; xorValue = charValue * 0x56
004014D1 |. 66:C1EA 08      SHR DX,8                       ; xorValue = xorValue >> 0x08
004014D5 |. C0F8 07         SAR AL,7                       ; charValue = charValue >> 0x07
004014D8 |. 89D3            MOV EBX,EDX                    ; EBX = xorValue
004014DA |. 29C3            SUB EBX,EAX                    ; EBX = EBX - charValue
004014DC |. 89D8            MOV EAX,EBX                    ; charValue = EBX
004014DE |. 0FBEC0          MOVSX EAX,AL
004014E1 |. 894424 3C       MOV DWORD PTR SS:[ESP+3C],EAX  ; xorValue = charValue
004014E5 |. C74424 34 0000 >MOV DWORD PTR SS:[ESP+34],0    ; Initialize the index-pointer
004014ED |. EB 41           JMP SHORT toadkey3.00401530    ; Jump to the end of the loop
004014EF |> 8D5424 23      /LEA EDX,DWORD PTR SS:[ESP+23]  ; Get the username variable
004014F3 |. 8B4424 34      |MOV EAX,DWORD PTR SS:[ESP+34]  ; Get the index-pointer
004014F7 |. 01D0           |ADD EAX,EDX                    ; Set correct position in the name variable
004014F9 |. 0FB600         |MOVZX EAX,BYTE PTR DS:[EAX]    ; Get current char
004014FC |. 0FBEC0         |MOVSX EAX,AL
004014FF |. 334424 3C      |XOR EAX,DWORD PTR SS:[ESP+3C]  ; value = char xor xorValue
00401503 |. 83E0 3C        |AND EAX,3C                     ; value = value and 0x3c
00401506 |. 894424 2C      |MOV DWORD PTR SS:[ESP+2C],EAX  ; Save value to value variable
0040150A |. 8B4424 2C      |MOV EAX,DWORD PTR SS:[ESP+2C]  ; value2 = value
0040150E |. 83C0 30        |ADD EAX,30                     ; value2 = value2 + 0x30
00401511 |. 8D4C24 11      |LEA ECX,DWORD PTR SS:[ESP+11]  ; Get the password variable
00401515 |. 8B5424 34      |MOV EDX,DWORD PTR SS:[ESP+34]  ; Get the index-pointer
00401519 |. 01CA           |ADD EDX,ECX                    ; Set correct position in the password variable
0040151B |. 8802           |MOV BYTE PTR DS:[EDX],AL       ; Password[i] = value2
0040151D |. 8B5424 2C      |MOV EDX,DWORD PTR SS:[ESP+2C]  ; Get variable value
00401521 |. 89D0           |MOV EAX,EDX                    ; value2 = value
00401523 |. 01C0           |ADD EAX,EAX                    ; value = value + value
00401525 |. 01D0           |ADD EAX,EDX                    ; value = value + value2
00401527 |. 894424 3C      |MOV DWORD PTR SS:[ESP+3C],EAX  ; xorValue = value
0040152B |. 834424 34 01   |ADD DWORD PTR SS:[ESP+34],1    ; Increment the index-pointer
00401530 |> 837C24 34 08    CMP DWORD PTR SS:[ESP+34],8    ; Compare the index-pointer to 8
00401535 |.^7E B8          \JLE SHORT toadkey3.004014EF    ; If the index-pointer is less or equal to 8, loop

That's the whole password check routine, so now it's time to make a keygen.

Here is a keygen written in C#.

using System;
namespace jockcranleys_T0AD_K3YG3N
{
    class Program
    {
        readonly static char[] Password = new char[9];
        readonly static char[] Name = new char[9];

        static void Main(string[] args)
        {
            Console.Write("Enter username: ");
            var name = Console.ReadLine();
            if (name == null) return;
            if (name.Length < 7 || name.Contains(" "))
            {
                Console.WriteLine("Name must be at least 7 characters long and cant't contain spaces.");
                Console.ReadKey();
                return;   
            }
            FixName(name);
            GeneratePassword();
            Console.WriteLine("Password: " + new string(Password));
            Console.ReadKey();
        }

        private static void FixName(string name)
        {
            if (name.Length == 7)
            {
                name += Convert.ToChar(0x0a);
            }
            for (var i = 0; i < 8; i++)
            {
                Name[i] = name[i];
            }
            Name[8] = Convert.ToChar(0x00);
        }

        private static void GeneratePassword()
        {
            var charValue = Convert.ToInt64(Name[1]);
            var xorVal = charValue * 0x56;
            xorVal = xorVal >> 0x08;
            xorVal = xorVal - (charValue >> 0x07);
            for (var i = 0; i <= 8; i++)
            {
                var chr = Convert.ToInt64(Name[i]);
                chr ^= xorVal;
                chr &= 0x3C;
                var val1 = chr;
                chr += 0x30;
                Password[i] = Convert.ToChar(chr);
                chr = val1 * 3;
                xorVal = chr;
            }
        }
    }
}