Government Security
Network Security Resources

Jump to content

Photo

A Pretty Cool Keygenme

- - - - - keygen c++ security windows bug hash md5 patch reverse engineering
  • Please log in to reply
No replies to this topic

#1 KOrUPt

KOrUPt

    Private First Class

  • Sergeant Major
  • 64 posts

Posted 08 November 2011 - 05:00 AM

Hey guys,

Not too often that I come by here but when I do I always take a look through my archives to see if I've anything interesting to contribute. I totally found something interesting stashed away today! Posted Image

I have to say I find it annoying that there is no dedicated source code section, meaning there is no way to distinguish between complete applications such as this one and users requesting help with a problem. Nevertheless...

This is an KeygenMe I wrote a while back for a number of reverse engineering community's I frequent. It's designed to be more educative than it is challenging, I wanted to demonstrate some common protection techniques used by executable protectors such as ASProtect and Enigma, alongside showing how a more complex license key system would work. For this reason, strings are not encrypted and the KeygenMe has been made intentionally verbose for ease of debugging and understanding.

So what protection does this thing employ?
1. Uses a Memory CRC check to verify internal routines have not been tampered with or ridden with breakpoints
2. Searches memory for software based breakpoints
3. Checks for existing hardware breakpoints
4. Uses multiple registration routines and calls into a fake one if the key is not formatted correctly
5. If HW BP's are found or memory is deemed corrupted, the registration routine is patched over and never called
6. Calls to the registration routine(s) are obfuscated with junk code

Okay, what about the license system?
1. Uses MD5, AES to hash and encrypt/decrypt
2. Uses RSA to sign and verify keys
3. All keys are tied to hardware ID's
4. Uses a keyfile

There's quite a bit more to the registration system, I suggest taking a look through the code to see how it works.

Here's what happens if you try and set a breakpoint within the registration routine, which can normally occur after doing a simple string search for anything matching "Registration successful", and then trying to step through the located code using a debugger. Which is often the first method of attack for newer reverse engineer's.

---------
Name: KOrUPt
Key: VT39-37NQ-ZW3J-4WKZ-24UF-X92K-BRNA-DHF6-2RRR-7VWU-G1NH-TBF8-GVP1
Signature: ...
---------

DetectHwBreakpoints()
ScanForSwBreakpoints()
CRC32_Generate_Table()
VerifyMemory()
CRC32_Generate_CRC()
>> Memory CRC = 0x46fe9266
>> Memory corrupt!
CheckKeyFormat()
>> Key format valid!
As you can see, the applications registration routine is no longer called, instead it just exits.

Running the application as intended with a valid key produces the following output:
Attempting to register application with given key file

P:\Development\KeygenMe>KeygenMe.exe

---------
Name: KOrUPt
Key: VT39-37NQ-ZW3J-4WKZ-24UF-X92K-BRNA-DHF6-2RRR-7VWU-G1NH-TBF8-GVP1
Signature: ...
---------

DetectHwBreakpoints()
ScanForSwBreakpoints()
CRC32_Generate_Table()
VerifyMemory()
CRC32_Generate_CRC()
>> Memory CRC = 0x2479fe52
CheckKeyFormat()
>> Key format valid!
RealRegistrationRoutine()
>> Key decoded successfully!
>> Pseudo Random Numbers within range!
-----
>> Key type: Pro key
>> An eerie myst shrouds your undead aurua o.0
-----
>> Key hardware id: C2480680-94E1FA5E
>> Local hardware id: C2480680-94E1FA5E
>> Hardware fingerprint valid
>> Checking Key signature
VerifyLicenseKey()
>> License key valid!!

P:\Development\KeygenMe>pause
Press any key to continue . . .

The attatched archive contains several files, compiled executable's alongside the source code files and others such as:
1. GenerateKey.bat - A simple batch file which runs the Keygen and generates a key file for the KeygenMe
2. RegisterApplication.bat - Another batch file that runs the KeygenMe and pauses so the output can be read
3. Licence.key, public.key, private.key - This is the key file itself and the public/private RSA key pair used to sign and verify license keys

I recommend readers begin by reading over the source of the Keygen itself, so as to gain an understanding of how license keys are constructed before trying to understand the KeygenMe.

There is an issue with keygenning when asymmetric key systems are in use(namely RSA)... In most cases when working with applications that use asymmetric key systems(such as this KeygenMe), even if you generate a valid key, it's still invalid because the key cannot be signed! In a real world situation, you definitely wouldn't have the private key used to sign license keys just sitting under your nose(It'd be locked in a safe. And I was hesitant to include it, but I figured the project would be incomplete without it). So some of you may be wondering why this KeygenMe is using an asymmetric key system, meaning it would normally require a patch before keys are deemed valid(which defeats the purpose of keygenning really). Well, here's an answer for all the curious people out there...

I used an asymmetric key system as I was going to write an article detailing how to keygen this application... And in the article I wanted an opportunity to point out a common error I see made by developers, the error of storing the public key on disk and not verifying its integrity before it's loaded! This silly mistake completely destroys the security gained from using an asymmetric key system! Just think about what can happen... I can now generate my own pair of private/public RSA keys, sign the given license key using my generated private key, and overwrite the existing public key with the public key matching my private key, which the application will load with no problems whatsoever! Now my key is valid, no patch required.

Is it hard to sign a key? Nope...
#define WIN_32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x501
#pragma comment(linker, "/filealign:0x200 /ignore:4078 /merge:.text=.data /merge:.rdata=.data")
#pragma comment(lib, "ssleay32.lib")
#pragma comment(lib, "libeay32.lib")
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/engine.h>
#include <openssl/aes.h>
#define nr_bits 2048 // rsa...


RSA* RSA_read_from_file(char *filename)
{
	char *buffer;
	int include_private_data = 0, max_hex_size = (nr_bits / 4) + 1;
	FILE* keyfile;
	RSA* rsa;
	
	rsa = RSA_new();
	if(!rsa) return NULL;
	
	buffer = (char *)malloc(max_hex_size);
	if(buffer){
		keyfile = fopen(filename, "r");
		if(keyfile){
			fscanf(keyfile, "%d", &include_private_data);
			fscanf(keyfile, "%s", buffer);
			BN_hex2bn(&rsa->n, buffer);
			fscanf(keyfile, "%s", buffer);
			BN_hex2bn(&rsa->e, buffer);
			if(include_private_data){
				fscanf(keyfile, "%s", buffer);
				BN_hex2bn(&rsa->d, buffer);
				fscanf(keyfile, "%s",buffer);
				BN_hex2bn(&rsa->p, buffer);
				fscanf(keyfile, "%s",buffer);
				BN_hex2bn(&rsa->q, buffer);
				fscanf(keyfile, "%s",buffer);
				BN_hex2bn(&rsa->dmp1, buffer);
				fscanf(keyfile, "%s",buffer);
				BN_hex2bn(&rsa->dmq1, buffer);
				fscanf(keyfile, "%s",buffer);
				BN_hex2bn(&rsa->iqmp, buffer);
			}
			fclose(keyfile);
		}
		free(buffer);
	}
	return rsa;
}

void SignLicenseKey(char *szKey, char **signature_hex)
{
	unsigned char* signature;
	unsigned int slen, verified;

	RSA* private_key = RSA_read_from_file("private.key");
	if(private_key){
		signature = (unsigned char *) malloc(RSA_size(private_key));
		if(signature){
			RSA_sign(NID_md5, (unsigned char *)szKey, strlen(szKey), signature, &slen, private_key);
			*signature_hex = (char *)malloc(slen * 2 + 1);
			if(*signature_hex){
				for(int i = 0; i < slen; i++) sprintf(*signature_hex + i * 2, "%02x", signature[i]);
			}
			
			RSA_free(private_key);
			free(signature);
		}
	}
	
	return;
}

int main(int argc, char **argv)
{
	char *signature = 0; // rsa signature
	
	if(argc != 2){
		printf("\tUsage: %s <Key to sign>\n", argv[0]);
		return -1;
	}
	

	// sign key
	SignLicenseKey((char *)argv[1], &signature);
	
	// write key details to file
	FILE* signatureFile;
	signatureFile = fopen("signature.txt", "w");
	if(signatureFile){
		printf("Generated signature for key %s\n", argv[1]);
		fprintf(signatureFile, "%s", signature);
		fclose(signatureFile);
	}
	
	return 0;
}

I've included this example within this post as I know a few people may base their own license system off of this code, and as such would have fallen into the intentionally placed security holes. So here's to this post hoping they don't, as they've been warned

I hope this application is helpful for some, it certainly should serve as great reference material and I hope it can teach a few people here a few cool things they didn't know before.

I was going to include an external executable protector I wrote a while back to accompany this post but I figured there's enough for you guys to get your head around as it is without having to worry about virtualized code that encrypts/decrypts as its executed(KMemCrypt but with virtualization, for those that remember that project).


Any comments, criticism, whatever, fire away.


KOrUPt

Attached Files


Edited by KOrUPt, 08 November 2011 - 06:33 AM.

Coder and Reverse Engineer. My blog.





Also tagged with one or more of these keywords: keygen, c++, security, windows, bug, hash, md5, patch, reverse engineering