Weblink 1.01 Serial Fishing and Oraculum
By: Gabri3l / ARTeam & Shub-Nigurrath /ARTeam
http://cracking.accessroot.com

Serial Fishing and Creating an Oraculum

The Target:
Weblink 1.01
http://www.inetpromoter.com/
The Tools:
Ollydbg 1.10
The Protection:
Serial

Other Information:
This tutorial will cover the discovery of a serial check routine. We will then fish a correct serial from the program memory. Finally we will create an oraculum that will return a correct serial. Written by both Shub-Nigurrath and Gabri3l of ARTeam.

Best viewed in Firefox at 1280x1024

Intro:

All the tools you will need can be found online:
http://home.t-online.de/home/Ollydbg/

First test out the program. Opening it up will present you with a nag telling you that there is "X amount of days left in the evaluation" You can try and register the program by selecting the Tools menu. Choose Weblink Options from the drop down menu. In the new window that opens up, choose Registration.
Enter any serial in this window and press Okay. You will be presented with a message box that says:

Write this message down. We will use it to find our serial.



Body:

Finding the Serial Routine and Fishing a Serial:

I am going to save us one step and tell you that the program is not packed or protected.

Open the program up in Olly. You will find yourself at the entrypoint here:

Now Right-Click and Search For -> All Referenced Text Strings. When the strings are found, Right-Click and choose Find Text. In the box that opens up type in "Your license key" and press Okay. You will find yourself on this line:

Double click that line and you will be here in the program:

I commented the code to help some of you understand it better. We can see there are a lot of compares followed by conditional Jumps (conditional jumps are jumps that are taken ONLY is a condition is met. Example: JNE is JUMP only if the condition is NOT EQUAL).
We want to trace into the calls before the conditional jumps. The calls before the jumps are the routines that check our serial. In those calls, AL is set. Now, select the first call:
00410BA4 . E8 813F0A00 CALL wlApp.004B4B2A
And Right-Click-> Follow. You will find yourself here:

This is the routine that calculates then checks out serial number. But it will only calculate our basic license key. How do we know this? Remember how there were two more checks before the jump to BAD LICENSE? This is because there are two more types of licenses we have for this program. The first check (The one we are in) is for a basic license key. The second check is for a full license key, and the third check is for company license key.

You can test this yourself. Press the - key to go to your previous location and choose each Call before the test AL,AL and Follow it. Pay attention to the line that says "dwl-basic1", "dwl-full1" or "dwl-company1". We of course want to register this program under the Company license key.
If you are not back to the image below press the - key until you are:

We want to register this program under a company license. So select line:

Now, Right-Click -> Follow. When you are in the company license routine set a breakpoint on the first line:

Go ahead and press RUN in Olly. Weblink will load and you will break on 004B4D2A. Press the Step-Over button and until you get to this line:

Press Step Over one more time and take a look at the EAX and the ECX registers. There are a bunch of numbers now in the registers. Go ahead and copy them down. They are our license key!
Try registering with our new license key... It works! Go ahead and move your clock forward a few months if you don't believe.

Analyzing the Serial Creation:

If you are interested in analyzing how your serial is calculated. In the Company license check, choose this line:

And Right-Click -> Follow. Scroll down until you are at the following place in code:

If you place a BP into 004CC442 you would see the seed string, from which the following serial is calculated. The real serial appears then at 004CC453 in EAX.

The seed string is calculated as follows:
1. the serials are calculated anyway, even when the program starts (We verified this when we started our program and found the company serial)
2. the serial's algo is very easy: fixed_string + Serial No. + username
so if the dialog shows you
User Name: bush
Serial No: 405B-133A
the serial number for Company's license would be: dwl-full1405b-133abush for full licence or dwl-company1405b-133abush for Company's one..
"bush" is found using GetCurrentUser()
The "Serial No." is the "c:\" hard disk serial number (low and hi word separated by a "-"), obtained calling GetVolumeInformation().
3. the CALL wlApp.004D08C9 at 004CC443 calculates the serial number easily, doing some math on the serial string. Easily reversible.

Creating an Oraculum:

It's particularly interesting to create an oraculum for this application because it generates internally for us while starting all the strings for all the possible licensing schemas foreseen by the company. Placing a BP in the point above underlined (4CC442) before running the program, that is while you are stopped at the program's OEP in Olly, the program calculates all the serials and checks against them the serial read from the registry. This is good for us because the Oraculum will be able to report the serials even before displaying the main application's window and without asking anything to the user.

There's a complication anyway because the serials are calculated for different licenses (basic up to company) and the oraculum should also be able to contextualize the serial he reports, so as one can choose which serial to use.

As usual I'll assume you have read the Oraculum tutorial by Shub-Nigurrath.

This time not all the code we have to write is inside the calbacks of the Oraculum framework, but also inside a different way to use the classes. We'll have to modify even the main() function of the oraculum. We'll report here only those functions and anything else. The whole code will not either be attached here, do your homework to rebuild all. ;-)

We will consider the registration string (used as a seed for the serial number) as the license identifier so what we expect to have from the Oraculum is something like:

The correct serials for licence: dwl-company1c43-1204Admin is
16202456789096436001
The correct serials for licence: dwl-full1c43-1204Admin is:
123456789221313
The correct serials for licence: dwl-basic1c43-1204Admin is:
187306112531237
The correct serials for licence: cwDataBasec43-1204Admin is:
112345678901352

To obtain this we have to fish two type of information, the seed string and the serial number, in two points in the program. As specified in the previous section..

004CC442 |. 50 PUSH EAX      ; here there will be the seed string into EAX

004CC453 |. 50 PUSH EAX      ; here the corresponding serial string appears into EAX.
 

This is the whole code, we placed some comments directly into it so as to clarify the approach. It should be clear enough...

#include "Oraculum.h"

 

DECLARE_CALLBACK_PROTOTYPES(DoPatch_callback, DoActionPatch_callbackStop, DoActionPatch_callbackResume);

 

//Places the original bytes away to let the program continue running.

//Used into the callbacks

BYTE OriginalyBytes[HALT_SIZE];

DWORD dwOriginalyBytesAddr=0;

 

int main(int argc, char** argv)

{

 

       COraculum Oraculum;

 

       if(argc==1) {

             printf("Instruction:\n\n”);

             printf("programName <path_of_WLink.exe>\n\n”);

             printf("The program launches WLink normally.\n”);

             printf("You have to press in WLink the \"Register\" menu and enter any long enough serial.\n”);

             printf("The Oraculum will tell you the real serial.\n”);

             printf("Launch this program from a DOS window to see more infos.\n\n”);

             printf("Tested on version 1.x\n”);

             printf("-----------------------------\n”);

             printf("&8~) Shub-Nigurrath of ARTeam\n");

             return 0;

       }

      

       if(argc!=1)

             Oraculum.SetPath(argv[1]);

 

       //Setup the exe scanning library and control structure

       Oraculum.Setup();

 

       // 004CC3F4 . 59            POP ECX        ;  USER32.77D4919B

       // 004CC3F5 . 56            PUSH ESI       ; /pFileSystemNameSize

       // 004CC3F6 . 56            PUSH ESI       ; |pFileSystemNameBuffer

       // 004CC3F7 . 56            PUSH ESI       ; |pFileSystemFlags

       // 004CC3F8 . 56            PUSH ESI       ; |pMaxFilenameLength

       // 004CC3F9 . 50            PUSH EAX       ; |pVolumeSerialNumber

       // 004CC3FA . 56            PUSH ESI       ; |MaxVolumeNameSize

       // 004CC3FB . 56            PUSH ESI       ; |VolumeNameBuffer

       // 004CC3FC . 68 24CB4F00   PUSH wlApp.004FCB24     ; |RootPathName = "c:\\"

       // 004CC401 . FF15 4C524E00 CALL DWORD PTR DS:[<&KERNEL32.GetVolumeInformationA>];

       //    

       // Final Destination: 004CC442

       // 59565656565056566824CB4F00FF154C524E00

       // Pattern

       // :x59:x56:x56:x56:x56:x50:x56:x56:x68:x24:xCB:x4F:x00:xFF:x15:x4C:x52:x4E:x00

       // Offset: 0x4E

       //

       // From the final destination I would go forward to the next important point:

       // 004CC453  |.  50             PUSH EAX

       // which is at an Offset 0x5F from the initial pattern and an offset of 0x11

       // from the previous patch location! Into this final location I will fish

       // the real serial.

 

 

       Oraculum.AddPattern

(":x59:x56:x56:x56:x56:x50:x56:x56:x68:x24:xCB:x4F:x00:xFF:x15:x4C:x52:x4E:x00", 0x4E);

      

       // Set the final callbacks for the COraculum class.

       Oraculum.SetCallBacks( DoPatch_callback,

                          DoActionPatch_callbackStop,

                          DoActionPatch_callbackResume);

 

       //Add some specific messages to the final output, just to beautify it

       cout << endl

            << "---------------------------------oOOo-----------------------------------------" << endl

            << "This oraculum reports all valid serials for this application" << endl

            << "You can understand which to use reading the serial value. Company's one is the most complete."

            << endl << endl;

 

       // Do the patch at the desired location, and create the suspended process,

       // using CreateProcess API. All these things are managed by COraculum class.

       // The loop code is a little different than usual because we have to fish 4 serial

       // types, to cover all the registration possibility offered by the application.

       // So the whole cycle is repeated:

       // 1. place and EBFE and wait till the program reach the trap

       // 2. store away the seed serial string

       // 3. place another EBFE a little forward to trap the real serial

       // 4. store away the serial number.

       // 5. repeat if idx<5.

       int idx=0;

       if(Oraculum.FindPatterns()>0)

             while(idx<5) {

                    if(Oraculum.DoPatch()>=0)

                          idx++;

             }

      

       //Everything has been done so we can close the victim process

       Oraculum.KillProcess();

                         

       //Return accumulated messages!

       cout << "Messages Stack dump, read information in reverse order.." << endl

            << "--------------------------------------------------------" << endl;

 

       //Flushes the messages stack.

       //Note that because the oraculum uses a messages stack the messages appears

       //in reverse order

       while(!Oraculum.MessageStack().empty()) {

             int idx=Oraculum.MessageStack().size();

             TextString str=Oraculum.MessageStack().top();

             Oraculum.MessageStack().pop();

             printf("%d> %s\n", idx--, str.c_str());

            

       }

 

       cout << "---------------------------------oOOo-----------------------------------------" << endl;

 

       return 0;   

}

 

void DoPatch_callback(COraculum *oraculum, DWORD dwMemoryPatchAddr) {

      

       //Places in a safe place the real bytes of the application..

       //later will be restored to let it continue..

       oraculum->ReadProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)dwMemoryPatchAddr,

             (void*)&OriginalyBytes, HALT_SIZE, NULL);

 

       //Also stores the patch location memory address..

       dwOriginalyBytesAddr=dwMemoryPatchAddr;

      

       oraculum->WriteProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)dwMemoryPatchAddr,

             (void*)&COraculum::HALT_CODE, HALT_SIZE, NULL);     // places the EBFE loop

 

}

 

void DoActionPatch_callbackStop(COraculum *oraculum) {

 

       //If we are here means that the EBFE location specified in the DoPatch_callback

       //has been reached: the program is endless cycling at the same EIP.

 

       //it's time to restore the applications' original bytes.

       oraculum->WriteProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)dwOriginalyBytesAddr,

             (void*)&OriginalyBytes, HALT_SIZE, NULL);    

      

       //EAX contains the pointer to the seed string..

       oraculum->ReadProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)oraculum->GetProcessContext()->Eax,

             oraculum->GetProcessBuffer(), 100, NULL);

 

       //pushes the string in the messages stack for later retrieval..

       char str[256];

       sprintf(str, "The correct serials for licence: %s is:",

               oraculum->GetProcessBuffer());

 

       oraculum->pushMessage(str);

 

       //////////////////////////////////////////////////////////////////////////

       //Prepare things for the second stop a little forward, where to get the real

       //serial..

       //COraculum class is not able to stop on its stops (it’s not recursive) so we

       //must code on our own the essential things:

       //place an EBFE, loop till it’s reached, restore the original code and read EAX..

      

       //The new location where to stop is 0x11 bytes forward the actual location where

       //we stopped..

       dwOriginalyBytesAddr=dwOriginalyBytesAddr +0x11;

 

       //Places in a safe place the real bytes of the application..later will be restored.

       oraculum->ReadProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)dwOriginalyBytesAddr,  //the second stop is 0x11 bytes forward

             (void*)&OriginalyBytes, HALT_SIZE, NULL);

 

       //writes the EBFE bytes to stop at the right location

       oraculum->WriteProcessMemory(oraculum->GetPI()->hProcess,

             (LPVOID)dwOriginalyBytesAddr,

             (void*)&COraculum::HALT_CODE, HALT_SIZE, NULL);   // places the EBFE loop

 

       //resume the thread from it's suspended mode to be able to get to the new EBFE loop

       //(that is continue the execution)

       ResumeThread(oraculum->GetPI()->hThread);

 

       //Check continuously while we reach the point

       while(GetThreadContext(oraculum->GetPI()->hThread, oraculum->GetProcessContext()))

       {

             //If EIP is equal to the address where we placed the EBFE loop we reached

             //the right point thus we can collect the remaining information

             if(oraculum->GetProcessContext()->Eip==dwOriginalyBytesAddr) {

                   

                    //suspend again the thread to let our oraculum take required data.

                    SuspendThread(oraculum->GetPI()->hThread);

                   

                    //Eax contains the pointer to real serial code string..

                    oraculum->ReadProcessMemory(oraculum->GetPI()->hProcess,

                          (LPVOID)oraculum->GetProcessContext()->Eax,

                          oraculum->GetProcessBuffer(), 100, NULL);    

 

                    //The buffer used by COraculum class is unique thus with this call

                    //we have just overwritten it. It was still containing the the serial

                    //seed string.

                    //For this cycle we do not anymore need to modify this buffer so we

                    //can leave the push of its

                    //value into the messages stack to the DoActionPatch_callbackResume

                    //callback.

 

                    //restore the application's original bytes so as to let it continue

                    //once resumed

                    oraculum->WriteProcessMemory(oraculum->GetPI()->hProcess,

                          (LPVOID)dwOriginalyBytesAddr,

                          (void*)&OriginalyBytes, HALT_SIZE, NULL); // removes EBFE loop

 

                    //stop the while loop

                    break;

 

             }

             Sleep(10);

       }

 

       //Once returned from this callback the oraculum framework will resume the

       //thread and will continue to the next stop set in the DoPatch_callback callback.

}

 

void DoActionPatch_callbackResume(COraculum *oraculum) {

 

       //writes the collected code into the messages stack

       char str[256];

       sprintf(str,"%s", oraculum->GetProcessBuffer());

       oraculum->pushMessage(str);

}

 

the most important function where you should spend some time reading it is the DoActionPatch_callbackStop which contains a little of work to place a second EBFE stop just after the first one. This is due to a current limitation of the COraculum which is not recursive..nothing impossible to avoid as you might see.

The result of the whole oraculum is somethings as following (remember to read from 1 to 11), so serial for dwl-company1c43-1204Admin is 16202456789096436001.

-------------------------------oOOo--------------------------------------
This oraculum reports all valid serials for this application
You can understand which to use reading the serial value. Company's one is the most complete.

Messages Stack dump, read information in reverse order..
--------------------------------------------------------
11> The target process has been terminated! Restart it directly and enter the gathered informations..
10> 187306112531237
9> The correct serials for licence: dwl-basic1c43-1204Admin is:
8> 16202456789096436001
7> The correct serials for licence: dwl-company1c43-1204Admin is:
6> 123456789221313
5> The correct serials for licence: dwl-full1c43-1204Admin is:
4> 187306112531237
3> The correct serials for licence: dwl-basic1c43-1204Admin is:
2> 112345678901352
1> The correct serials for licence: cwDataBasec43-1204Admin is:
-------------------------------oOOo--------------------------------------


 

Conclusion:

We used this particular program purely as a demonstration for serial fishing and creating an oraculum. Install if in evaluation mode and use it just for following this tutorial then you should remove it. If you like the program and are going to use it please purchase it, developers deserve your support to continue their work!

Thanks to the whole ARTeam:
[Nilrem] [JDog45] [Shub - Nigurrath] [MaDMAn_H3rCuL3s] [Ferrari] [Kruger] [Teerayoot] [R@dier] [ThunderPwr] [Eggi] [EJ12N] [Stickman 373] [Bone Enterprise] [KaGra]

Thanks to all the people who take time to write tutorials.
Thanks to all the people who continue to develop better tools.
Thanks to Exetools, Woodmann, SND, TSRH, MP2K, TEAMICU and all the others for being a great place of learning.
Thanks also to The Codebreakers Journal, and the Anticrack forum.

If you have any suggestions, comments or corrections email me: Gabri3l2003[at]yahoo.com  ..