Jump to content
Tuts 4 You

An educative KeygenMe(source code included)


KOrUPt

Recommended Posts

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! tongue.gif

This is an KeygenMe I wrote a while back. 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 fileP:\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

KeygenMe.zip

  • Like 2
Link to comment

nice tut!

I was playing with the though of writing my own keygenme, which would use a mixture of all basic crypto ideas/algos and write a tut on that. However, i found that i lack the necessary background knowledge on many subjects... :S

:)

Link to comment

Hey Metr0, yeah, things are going well. You working on anything interesting these days?

And thank you for the kind comments guys. If anyone can suggest any improvements or ideas for a future version. Anything cool you'd like to see etc... Let me know! :D

Link to comment

Nice work.

Your EXE protector sounds interesting. I am coding a tool like that currently, but focusing on the compression only, figured that DRM is a waste of time since people will hack it no matter how hard I try to stop that.

This keygenme source looks also quite interesting. Would be interesting though if you added some product activation features if your website was still up ;). Or a testbed to use in XAMPP at least.

Link to comment

Thank you Mudlord.

I agree people will eventually break a protection but hey kudo's to slowing them down and offering them a challenge.

I don't quite understand what you mean, are you talking about implementing server checks?

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...