Keygenning diablo2oo2's crackme 7

Overview

The following tutorial will document how to program a keygenerator for diablo2oo2’s seventh crackme.

Functions are labeled to make the tutorial easier to follow.

The crackme can be found here.

Reverse engineering the crackme.

Load up the executable in x32dbg. The debugger should load the executable at the following code:

d2k2_1.png

Run the crackme in x32dbg.

Immediately, there is some points of notice.

  1. There is a thread being created.
  2. There is a string setting a control called “Checking Serial…”
  3. The third argument of the “CreateThread” function points to a array of code at 0x403288

Place a breakpoint at 0x403288, enter any name into the name box (I used “mudlord”) and run…

2.png

The thread shows all the code used for the serial verification. The main points for the following code are as follows:

  • If entered serial doesn’t equal 0x20 in length, go to error message
  • If a name is entered, check if its greater than 0x28 chars or less than 4 chars
  • Check the format of the serial to see if it contains 0-9 and A-F characters only

From there it goes to the main core:

3.png

  1. Do a initial hash of the username using a CRC32 algorithm using the zlib/DEFLATE polynomial
  2. The result of this hash is then modified to a more optimal value which is used as the counter for the remainder of the name loop.
  3. Loop through the value through a horrendously large number of CRC32 buffer iterations, changing the name buffer’s first 4 bytes by the value returned by the CRC32 function.
  4. Once this is complete, use the final value as a key.
  5. Create a lookup table using the original username, hashed with a CRC32.
  6. Using a combination of the lookup table and the encryption key, verify the serial.

Steps 2-4 are illustrated by the following code:

	uint8_t* nameptr = namebuf1;
	uint32_t enc_key;
	{
		uint32_t crc = crc32(namebuf1, 1);
		*(uint32_t*)nameptr += crc;
		uint32_t cnt = crc;
		cnt &= 0xFFFFFF;
		cnt -= 0xF0000;
		while (cnt != 0)
		{
			crc = crc32(namebuf1, 1);
			*(uint32_t*)nameptr += crc;
			cnt--;
		}
		enc_key = crc;
}

The lookup table is generated by the following code in the crackme.

4.png

The resulting algorithm in C for the keygen.

uint8_t lut[4] = { 0 };
DWORD* lutptr = lut;
*(DWORD*)(lutptr) = _rotl(crc32(name, namelen) ^ namelen, 3);

The final verification step (Step 6) does several things.

5.png

  1. Each 4 byte segment from the entered serial key is gathered.
  2. It is then byteswapped.
  3. A 1 byte segment from the computed LUT is retrieved. Each 4 byte segment from the entered serial is checked in the main verify codesegment using 1 byte segment from the computed LUT.

6.png

For each 4 byte serial segment the output from the verification function is checked to match exactly the key set from earlier.

To satisfy the check the resulting C code can be used.

for (int i = 0; i < 4; i++)
	{
		int gen = (uint8_t)lut[i];
		gen ^= enc_key;
		gen = _byteswap_ulong(gen);
		gen = _rotl(gen, 4);
		gen += namelen;
		gen ^= enc_key;
		DWORD* bufptr = serialbuffer;
		*(DWORD*)(bufptr+i) = _byteswap_ulong(gen);
}

The code is done in this way to form a exact mirror image of the verification code. That is, the order of operations is the exact opposite and the mathematics used is the exact opposite of the verification function and the checking function. So really, all that was needed to solve was a little logic and backwards thinking, instead of just reverse engineering the code and reimplementing in C.

Keygen source code.

Source code to the keygen is here. MSVC2019 is used to compile. It should compile out of the box. Feel free to use the template for your own crackme keygens.

Written on April 18, 2019