TUTORIAL #1
Published by +Tsehp


TOOLS NEEDED:
  1. A copy of the application (DONT EMAIL ME)
  2. Numega SoftIce (any version)
  3. Windasm/IDA Pro 4.04+
  4. Wisdec (Windows Installshield Dissambler)
  5. A Brain...

 

THINGS YOU NEED TO KNOW:
  1. A Basic knowledge of ASM
  2. A Basic knowledge of InstallShield scripts
  3. A Basic knowledge of Wisdec
  4. A Working knowledge of C/C++
  5. A Working knowledge of Win32 API
  6. Medium reversing/cracking experince

 

INTRODUCTION:

Today we are going to learn how to make a crappy bruteforcing keygen type thingy for a high end app. We begin by going to www.microstrategy.com to learn alittle about wtf this product is...

"MicroStrategy is world's leading provider of enterprise DSS software, which allows decision-makers to perform sophisticated analyses across large volume of data. MicroStrategy provides an integrated suit of decision support development and administrative tools, application servers and runtime interfaces that enables the construction of leading edge DSS applications.The Microstrategy API's, for developers to create software addons and companions for the Microstrategy 7.1 suite of products."

Ok, now that we know alittle about this product, lets run the Install Shield setup. After hitting the "NEXT" button a few times we come across a serial dialog, the serial dialogs "NEXT" button is grayed out. Sometimes programs that have Install Shield installations have a serial dialog that is grayed out until all the fields are filled in, so lets try filling in the fields:

Very interesting... Most likely the serial checking routines are in a DLL file that gets extracted to your temporary directory once you run the Install Shield setup. Most serial checks for Install Shield installers are done through external DLLs.

Lets close up the Installer and lets start viewing the Install Shield script.

ANALYSIS:

Now lets make a copy of the setup.ins (rename it to setup).

Open up "Windows Installshield Decompiler" and open up setup. Once the decompiling has finished goto the "Misc" menu and select "DLL Imports", we are going to see if the installer is using a external DLL to handle the serial checking. In the DLL Imports section we see the following are being called in this Install Shield script:

Prototype BOOL KERNEL32.GetVersionEx (LIST/POINTER )
Prototype LONG/NUMBER MAInTls.DeleteFilesFromMask (STRING ,STRING ,BOOL )
Prototype LONG/NUMBER MAInTls.WriteExpandStringToRegistry (LONG/NUMBER ,STRING ,STRING ,STRING ,LONG/NUMBER )
Prototype LONG/NUMBER MAInTls.UpdateSystemPath (STRING ,LONG/NUMBER )
Prototype LONG/NUMBER MAInTls.PurgeSystemPath (STRING )
Prototype LONG/NUMBER MAInTls.KillThisProcess (STRING )
Prototype LONG/NUMBER MAInTls.MakeRunningExeList ()
Prototype LONG/NUMBER MAInTls.GetFirstRunningExe (STRING ,LONG/NUMBER )
Prototype LONG/NUMBER MAInTls.GetNextRunningExe (STRING ,LONG/NUMBER )
Prototype LONG/NUMBER MAInTls.FindRunningExe (STRING )
Prototype LONG/NUMBER MAInTls.UpdateEnvironmentPath (STRING ,LONG/NUMBER )
Prototype LONG/NUMBER MAInTls.RegisterTlb (STRING )
Prototype LONG/NUMBER MAInTls.UnRegisterTlb (STRING )
Prototype LONG/NUMBER MAInTls.RegisterServer (STRING )
Prototype LONG/NUMBER MAInTls.UnregisterServer (STRING )
Prototype LONG/NUMBER MAInTls.RefreshShortcuts (STRING )
Prototype LONG/NUMBER MAInTls.RefreshEnvironment ()
Prototype LONG/NUMBER MAInTls.ShareFolder (STRING ,STRING )
Prototype LONG/NUMBER MAInTls.RemoveShareFolder (STRING )
Prototype LONG/NUMBER MAInTls.ExistShareFolder (STRING )
Prototype LONG/NUMBER MAInTls.ReplaceStringInFile (STRING ,STRING ,STRING )
Prototype LONG/NUMBER kernel32.OutputDebugString (STRING )
Prototype LONG/NUMBER MAInst.fnSerialkey (STRING )

Lets take a look at which DLL Libraries are being called

Kernel32
Standard windows bullshit
MAInTls
Unknown
MAInst
Unknown

Ok, now lets look at the function names for the unknown DLLs. The function names in MAInTls don't suggest anything suspicous so we can forget about it. MAInst only has 1 function that gets used in this script and its called "fnSerialkey", the name of this function sounds very suspicous. Lets browse through the script until we find a reference to that function:

<LABEL_0074> REF: 00002A70 000062C5 00006916 
  |
00004180: 00B6   START OF FUNCTION (6*StrLocals + 5*NumLocals)
000041A5: 0128   IF (StrLength (StrLocal[0001]) != 0000000E) THEN
000041C5: 012F        Return (00000000)
000041C6: 0000   ENDIF
000041E2: 0128   IF (MAInst.fnSerialkey (StrLocal[0001]) <= 00000000) THEN
00004202: 012F        Return (00000000)
00004203: 0000   ENDIF
0000420F: 012F   Return
00004211: 011D   NumLocal[0005] = NumLocal[0002] & 01000000
0000421E: 0128   NumLocal[0005] = NumLocal[0005] > 00000000
00004230: 00B7   Return (NumLocal[0005])
00004235: 00B8   END OF FUNCTION ()

Lets analyze the above snippet. "StrLocal[0001]" is most likely the serial key that you type in. First it does a "StrLength" function on the serial to see if the length is equal to 0x0E. If its not equal to that, it'll return with 0. If it is equal to 0x0E, it will begin to call up the "fnSerialkey" function with the "StrLocal[0001]" variable. If the "fnSerialkey" function returns 0 or less, then that most likely means the function has failed just like the "StrLength" compare, it's returning with 0.

Go back into wisdec and test out this theory. Change the <= to >= and in script.ins. Once your back in the serial dialog, type in 14 (0x0E) random keys and hit the "NEXT" button. It should take you to the next set up of the installation. This proves what we have suspected all along.

Here is what the dialog should look like when you have passed the serial dialog:

The text inside "Current Settings:" might be different if you're installing this app for the first time.

GETTING DLLS:

Ok, now that we know whats going on, we have to actually get the MAInst DLL file. When Install Shield installers run, they create a little folder in your temp folder (C:\Windows\Temp for me) and they dump out all there support files in there.

Run the setup program. Once it starts up, get into Windows Explorer or whatever you use and go into your temp folder. Once inside your temp folder, look for folders that look like this "ISTMP???.DIR". If you have more then 1 folder that looks like that then look through all of them until you find MAInst.DLL file. For me this file was located in C:\WINDOWS\TEMP\_ISTMP1.DIR\_ISTMP0.DIR.

Once you find this file, copy it to a safe place and finally exit the installation. When your done this process copy your setup file over the setup.ins that you modified.

THE ALGO:

I spent about an hour converting the algo to C only to see that reversing it to make a legit keygen is gay and a waiste of my time. So what we're going to do is exploit the developers stupidity.

Load the DLL exports of MAInst.dll into Soft-Ice. After you've done that set a breakpoint on "fnSerialkey". Run the installer and enter in a fake 14 char serial to just get a feel for what the algos like. It looks very long and gay doesn't it? No problem because we're going to make a bruteforcer for this crap.

I'm not going to copy/paste the entire algo here but i'll paste significant parts that need to be arranged for the bruteforcer. Here is what the fnSerialkey looks like when you first get into it:

10001310                 mov     eax, [esp+arg_0]  <--- Move char pointer into EAX
10001314                 push    eax  <--- Push the char pointer into stack
10001315                 call    sub_1000146C  <--- Calls the algo here
1000131A                 add     esp, 4
1000131D                 retn    4

Moving into the algo call we see serial length checks:

10001482                 mov     eax, [ebp+arg_0]  <--- Move serial char pointer into EAX
10001485                 push    eax  <--- Push the serial char pointer into stack
10001486                 call    _strlen  <--- Calls the strlen to get serial string length
1000148B                 add     esp, 4
1000148E                 mov     [ebp+var_10], eax  <--- Save it
10001491                 cmp     [ebp+var_10], 9  <--- Is the serial length 9?
10001495                 jz      short loc_100014A5  <--- Jump if it is (GOOD)
10001497                 cmp     [ebp+var_10], 0Eh  <--- Is the serial length 14?
1000149B                 jz      short loc_100014A5  <--- Jump if it is (GOOD)
1000149D                 or      eax, 0FFFFFFFFh  <--- Serial length is invalid
100014A0                 jmp     loc_10001772  <--- Jumps to a ret at the end of the function

Ok, so now we have discovered that the algo accepts both 9 char serials and 14 char serials but Install Shield makes it so that it only accepts 14 char serials. Maybe because they wanted to make it backwards compatible for older versions or someshit? I really don't know.

After we follow the path of execution some more we come up to this little nugget:

100014A5                 cmp     [ebp+var_10], 0Eh  <--- Compares lenght to 14 once again
100014A9                 jnz     short loc_100014C1  <--- This check is for 9 char serials
100014AB                 mov     ecx, [ebp+arg_0]  <--- Gets serial char pointer again
100014AE                 movsx   edx, byte ptr [ecx+9]  <--- Gets the 9th byte
100014B2                 cmp     edx, 2Dh  <--- Compare 9th byte to 0x2D (-)
100014B5                 jz      short loc_100014C1  <--- If it is 0x2D (-) then jump (GOOD)
100014B7                 mov     eax, 0FFFFFFFEh  <--- Set as bad serial
100014BC                 jmp     loc_10001772  <--- Jumps to a ret at the end of the function

Now we have alittle more information about the format of our serial. It has to be 14 chars long and the 9th char has to be a "-". So basically we have a serial looking like this "????????-????". After going through the algo some more we notice that the first 8 chars of the serial must be all numbers and the last 4 chars can be numbers or letters. So to clarify even more the serial looks like this "NNNNNNNN-LLLL" (N = Numbers, L = Letters or numbers).

After tracing down a little more we see something very special:

10001623                 mov     ecx, [ebp+var_18]  <--- Gets DWORD that was calc'd by the algo
10001626                 xor     ecx, 0B73BBh  <--- Xor it by 0xB73BB
1000162C                 mov     [ebp+var_18], ecx  <--- Put it back
1000162F
1000162F loc_1000162F:                           
1000162F                 cmp     [ebp+var_18], 80000h  <--- Compare DWORD xored above to 0x8000
10001636                 jbe     short loc_10001642  <--- Jump if below or equal (GOOD)
10001638                 mov     eax, 0FFFFFFFDh  <--- Set as failed serial
1000163D                 jmp     loc_10001772  <--- Jumps to a ret at the end of the function

The crap in [ebp+var_18] is calculated at 1000158C. Its pretty straight forward but too long to list. It loops through each of the 4 chars at the end of the serial doing the follwoing:

  1. Makes sure the character is a letter or a number
  2. If it's a letter the char gets subtracted by 0x37. If it's a number it gets subtracted by 0x30
  3. Shift the holding variable by 5
  4. Add the subtracted char to the holding var
  5. Once all 4 chars are done. Take shifting var and xor it by 0xB73BB
  6. Shifting var must be below 0x8000

Here is the C source for calculating 4 valid chars for the end of the serial:

void topSerialize()
{
    char topSerial[] = "0000";
    long shiftVar = 0;
    long newVar = 0;
    long temp = 0;

    while(true)
    {
        shiftVar = 0;
        newVar = 0;
        temp = 0;

        topSerial[0] = rand()%(0x5A-0x30)+0x30;
        topSerial[1] = rand()%(0x5A-0x30)+0x30;
        topSerial[2] = rand()%(0x5A-0x30)+0x30;
        topSerial[3] = rand()%(0x5A-0x30)+0x30;

        for( int i = 0; i < 4; i++ )
        {
            if ( topSerial[i] > 0x29 && topSerial[i] < 0x3A || topSerial[i] > 0x40 && topSerial[i] < 0x5B)
            {
                temp = topSerial[i];

                if ( temp >= 0x30 && temp <= 0x39 )
                    temp -= 0x30;

                if ( temp >= 0x41 )
                    temp -= 0x37;

                shiftVar <<= 5;
                shiftVar += temp;

                newVar++;
            }
        }

        if ( ((shiftVar ^ 0xB73BB) <= 0x80000) && newVar == 4 )
            break;
    }

}
              

Now we have the most important parts of the algo that we need to make a keygen bruteforcer thingy!

THE C CODE:

Here is the C Code. What we do here is we take MAInst.DLL and we load it up, then we use our "topSerialize" function to generate a good last 4 chars for the serial. Once that has been generated it will start incrementing numbers 1 by 1 until "fnSerialkey" function returns something higher then 0. Once the keygen bruteforcer thingy recieves a value higher then 0, it will take the serial code and output it to the screen for the cheapass warez addict to use for his lameass company.

Here it is! (REMEBER TO HAVE MAInst.DLL IN THE KEYGEN FOLDER OR YOUR SYSTEM FOLDER!!!):

#include <windows.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>

char topSerial[] = "0000";
int serial = 0;

void topSerialize()
{
    long shiftVar = 0;
    long newVar = 0;
    long temp = 0;

    while(true)
    {
        shiftVar = 0;
        newVar = 0;
        temp = 0;

        topSerial[0] = rand()%(0x5A-0x30)+0x30;
        topSerial[1] = rand()%(0x5A-0x30)+0x30;
        topSerial[2] = rand()%(0x5A-0x30)+0x30;
        topSerial[3] = rand()%(0x5A-0x30)+0x30;

        for( int i = 0; i < 4; i++ )
        {
            if ( topSerial[i] > 0x29 && topSerial[i] < 0x3A || topSerial[i] > 0x40 && topSerial[i] < 0x5B)
            {
                temp = topSerial[i];

                if ( temp >= 0x30 && temp <= 0x39 )
                    temp -= 0x30;

                if ( temp >= 0x41 )
                    temp -= 0x37;

                shiftVar <<= 5;
                shiftVar += temp;

                newVar++;
            }
        }

        if ( ((shiftVar ^ 0xB73BB) <= 0x80000) && newVar == 4 )
            break;
    }

}

void main()
{
    printf("MICROSTRATEGY RUNTIME ENVIROMENT v7.1.309.113   *KEYGEN*\n\n\n");
    printf("\n*KEYGEN BY: Tha Great ALI_G!!!\n\n");
    srand(GetTickCount());

    int  result = 0;

    printf("Loading DLL... ");
    HMODULE loadedDLL = LoadLibrary("MAInst.dll");
    int serialFunc = (int)GetProcAddress(loadedDLL, "fnSerialkey");

    if ( loadedDLL == NULL || serialFunc == NULL)
    {
        printf("FAILED!\n\n\n");
        printf("MAKE SURE YOU HAVE THE MAInst.dll FILE IN THE SAME DIRECTORY AS THIS PATCH");
        exit(0);
    }

    printf("Done!\nScanning for random valid top serial keys... ");
    topSerialize();
    printf("Done!\nScanning for valid key... ");

    char mySerial[] = "                                  ";
    char tempSerial[] = "                                  ";
    LPSTR strserial = mySerial;
    int serialLen = 0;
    unsigned long blah = 00000000;

    while(true)
    {

        memset(&mySerial, 0x00, 0x0D);
        memset(&tempSerial, 0x00, 0x0D);

        sprintf(tempSerial, "%i-%s", blah, topSerial);
        serialLen = strlen(tempSerial);

        for(int i = 0; i < 0x0E-serialLen; i++)
            strcat(mySerial, "0");

        strcat(mySerial, tempSerial);


        __asm
        {
            push strserial;
            call serialFunc;
            mov result, EAX;
        }

        if ( result > 0 )
        {
            printf("Done!\nSerial found: %s\n\n", mySerial);
            _getch();

            printf("B00YAKASHA!!\n\n");
            break;
        }

        serial++;
        blah++;
        serialLen = 0;
    }


    FreeLibrary(loadedDLL);
	
}

Yes, I know its very messy and looks very confusing and unoptimized but this will generate a serial for you in a fraction of a second!!!

CONCLUSION:

Welp, we're done. Run this program and it'll give you a random valid key. It's pretty sad to see software developers making it so easy for people like me to do this shit. This is only one of may ways to make a keygen for this program so keep investigating!

If you have any questions/comments/ect.. you can contact me at shogo2001@yahoo.com or catch me on IRC, my nickname is vman_ on EFNet.