-1

I reverse engineered a program protection algorithm and got the result in this source file I wrote, but I would know this type of protection. Please tell me what is the type of this protection ? And is there any ability to create a keygen of this algorithm ?

#include <stdio.h>
#include <string.h>

char* transformStrip = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ";

char* myusername = "MYUSERNAME";
char* myserial   = "MYSERIAL33222222222222222";

//OK
int TransformChar(char c)
{
    for(int i=0;i<0x22;i++)
    {
        if(transformStrip[i] == c)
            return i;
    }
    return 0;
}

//OK
bool MagicCalculator1(char* magicBuffer, int number)
{
    int a = 0;
    //int rez = 0;
    for (int i=0;i<0x20;i++)
    {
        int rez = (unsigned char)magicBuffer[i];
        rez *= number;
        rez += a;
        magicBuffer[i] = (unsigned char)rez;
        if(rez >= 0x100)
        {
            a = rez;
            if(a < 0)
                a += 0xFF;
            a >>= 8;
        }else
            a = 0;
    }
    return (bool)a;
}

bool MagicCalculator2(char* magicBuffer, int position)
{
    int a = 0;
    for (int i=0;i<0x20;i++)
    {
        int rez = magicBuffer[i];
        a += rez;
        rez = position + a;
        magicBuffer[i] = (char) rez;
        position = 0;
        if(rez >= 0x100)
        {
            a = rez;
        }else
            a = 0;

    }
    return (bool)a;
}

// sizeof(magicBuffer) = 0x20
// OK
int SerialMagicGenerator(char* serial, char* magicBuffer, int number)
{
    int serialLength = strlen(serial);
    if (serialLength <= 0)
        return 0;

    for(int i=0;i<serialLength;i++)
    {
        char c = serial[i];
        if (c != '\x20' && c != '\x2D')
        {
            int pos = TransformChar(c);
            if (MagicCalculator1(magicBuffer, number))
                return -1;
            if (MagicCalculator2(magicBuffer, pos))
                return -1;
        }
    }
    return 0;
}

unsigned short MagicValidator(char* combinedBuffer, int number)
{
    int c = 0;
    if(number)
    {
        for(int i=0;i<number;i++)
        {
            int rez = ((unsigned char) combinedBuffer[i]) << 8;
            c ^= rez;
            for(int j=0;j<8;j++)
            {
                if(c & 0x8000)
                {
                    c += c;
                    c ^= 0x1021;
                }else
                    c <<= 1;
            }
        }
    }
    unsigned short s = (unsigned short) ( ((c & 0xFF) << 8) | ((c & 0xFF00) >> 8) );
    return s;
}

bool UsernameMagicVerifier(char* randomizedUsername, char* magicBuffer)
{
    char combinedBuffer[0x20];
    memset(combinedBuffer, 0, 0x20);
    memcpy(combinedBuffer, randomizedUsername, 0x10);
    memcpy(combinedBuffer + 0x10, magicBuffer, 0x10);
    unsigned short mustHaveValue = (unsigned short) (MagicValidator(combinedBuffer, 0x1E) & 0x7FFF);
    unsigned short foundValue =    (unsigned short) ((*(unsigned short*) (magicBuffer + 0xE)) & 0x7FFF);
    return (mustHaveValue == foundValue);
}

bool FinalizerVerifier(void*a, void*b)
{
    //assuming this always returns true
    return true;
}

char RandomizedUsernameBuffer[0x10];

char* RandomizeUsername(char* username)
{
    memset(RandomizedUsernameBuffer, 0, 0x10);
    int usernameLength = strlen(username);
    if(usernameLength >= 1)
    {
        if(usernameLength < 0x10)
        {
            int size = (usernameLength >> 2) * 4;
            memcpy(RandomizedUsernameBuffer, username, size);
            memcpy(RandomizedUsernameBuffer + size, username + size, usernameLength & 3);
            if( usernameLength + 1 < 0x10 )
            {
                char* ptr;
                ptr = RandomizedUsernameBuffer + size + (usernameLength & 3) + 1;
                for(int i=0;ptr<RandomizedUsernameBuffer+0x10;ptr++)
                {
                    if( username[i] != '\0')
                    {
                        *ptr = username[i];
                        i++;
                    }else
                        i = 0;
                }
            }
        }
    }
    return RandomizedUsernameBuffer;
}

bool SerialChecker(char* username, char* serial)
{
    char magicBuffer[0x20];
    memset(magicBuffer, 0, 0x20);
    char* randomizedUsername = RandomizeUsername(username);
    SerialMagicGenerator(serial, magicBuffer, 0x22);
    if ( UsernameMagicVerifier(randomizedUsername, magicBuffer) )
    {
        return FinalizerVerifier(/*?*/NULL, /*?*/NULL);
    }
    return false;
}

int main()
{
    SerialChecker(myusername, myserial);
    return 0;
}

1 Answer 1

4

Short, slightly snarky answer: yes, there must be a way to make a keygen. If there wasn't, the creators of the software themselves wouldn't be able to create keys to sell.

Longer answer: If the software vendor wants the encryption to be non-identifiable for people who have reversed the decryption part, they need to use some assymetric key algorithm, where the public key is embedded into the software, and the private key stays with the vendor. There are well-known algorithms, and corresponding libraries, to do that, like RSA. However, those are not used in your program, once because they are too big, and also because they contain lookup tables which make them easily identifiable.

From a first glance at your algorithm, there's nothing much happening that's too difficult to reverse. Your RandomizeUserName just seems to shuffle the bits of the user name in the same way that the keygen would; your MagicCalculator functions seem to check if the serial number is well-formed, for some definition of well-formed. Then, MagicValidator calculates what looks like a 16-bit CRC on the 16 bytes username + 14 first bytes of the serial, then compares that to bytes 15/16 of the serial.

So, a keygen would have to: - perform the same calculation on a username that your RandomizeUserName does - invent 14 bytes of serial - generate the CRC from the randomized user name + serial, and put it into bytes 15/16 - append 16 more bytes to the serial in a way that MagicCalculator1 and 2 are satisfied (return 0), choose any bytes you want to achieve this - undo SerialMagicGenerator to get a serial that includes blanks and dashes just like SerialMagicGenerator does.

So, with a bit of experimenting and trial-and-error, it should be very well possible to make a key generator for this.

Side note:

I pondered for a while if it is morally ok to help someone with what is clearly a precursor to software theft. Assuming, in the first place, that the OP hasn't tried to invent a protection algorithm himself, and opened this question to check how "strong" his protection is. Unfortunately, even now in 2016, there seem to be lots of programmers - and college spits out more of them every year - who think that "shuffle a few bits around, pepper with some self-invented maths, and compile to machine language" makes whatever they want to achieve hard to crack. Of course, we of RE.SE all know it doesn't.

In this case, the OP obviously isn't too experienced in reversing, or he wouldn't have asked the question; still, he arrived at a point where he's obviously 1-2 hours away from a working keygen. In answering the question the way I did, i hope to make it obvious that all the bit-shuffling in the "protection" algorithm won't help much. If you want to prevent people from creating key generators for your software, use asymmetric encryption, and make sure your private key doesn't get leaked, and if only one future "software protection coder" reads this example and learns from it, then my answer will have served its purpose.

1
  • Should I create an algorithm that bruteforce a serial that meet the conditions ? Or is there a way reverse the whole algorithm ?
    – Etor Madiv
    Apr 9, 2016 at 15:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.