MKS toolkit revisited
continuing drlan's work
student
Not Assigned
25 July 1998
by bb
Courtesy of Fravia's page of reverse engineering
slightly edited
by fravia+
fra_00xx
980715
bb
0100
NA
PC
Well, another essay by bb, that shows you how to use your windows' knowledge to fool windows' targets into doing anything you want. I would advice you to read first bb's other (very good) essay about nag screen reversing techniques.
There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner ( )Intermediate ( )Advanced ( )Expert

When cracking a plentitude of targets all obstructed in the same fashion, it's useful to find a common link. This essay finds the common link in 150-or-so tools within the MKS Toolkit, and offers a easy solution to the problem without having to modify all the individual tools.
MKS Toolkit revisited
continuing drlan's work
Written by bb


Introduction
Well, drlan seems to have already done this, but I didn't like the method. He went about cracking each of over a hundred executables to ignore the date check and the nag. I'm a lazy sonofabitch, that sounds like too much work for me. He had requested at the end of his essay for someone to look into making a generic patcher for all the EXEs. I'll go about doing this a different way, I think (I hope) a somewhat more "elegant" and "just" crack. The goal here is: find a way to crack all 150 executables without having to change them all.

Tools required
LCC
HIEW


Target's URL/FTP
MKS Toolkit 6.1 (for Intel platforms)

Program History
For more information, feel free to read drlan's essay.

Essay
So let's start with what we already know from Drlan's essay. There's a 30-day time limit and a nag screen. Each program individually checks for the 30-day time limit, and each calls a CreateProcess to run MKSDEMO.EXE, which contains the nag. Well, the nag will be pretty simple to get rid of, then. Just re-write MKSDEMO.EXE to exit silently, right?

Well, not quite. We also need to fool the caller a bit. Let's take a look at that EnumWindows call that Drlan mentions. For our purposes, it's important. (I'm using the VIW.EXE as our little test proggie.)

Inside the callback function of the EnumWindows, we have:


00017747: 8B7508                       mov    esi,[ebp][00008]

0001774A: 6AEB                         push   0EB

0001774C: 56                           push   esi

0001774D: FF15F8B64300                 call   GetWindowLongA ;USER32.dll

00017753: 3DEBED0900                   cmp    eax,00009EDEB

00017758: 7533                         jne   .00001778D   ---------- (1)

0001775A: 8D459C                       lea    eax,[ebp][-0064]

0001775D: 6A64                         push   064

0001775F: 50                           push   eax

00017760: 56                           push   esi

00017761: FF1538B84300                 call   GetWindowTextA ;USER32.dll

00017767: 8D4D9C                       lea    ecx,[ebp][-0064]

0001776A: 68BCEB4200                   push   00042EBBC ; "MKS Toolkit Demo"

0001776F: 51                           push   ecx

00017770: E8BB540000                   call  .00001CC30   ---------- (2)

00017775: 83C408                       add    esp,008

00017778: 85C0                         test   eax,eax

0001777A: 7511                         jne   .00001778D   ---------- (3)

Ok, we start with a GetWindowLong(arg_8, 0xeb). The 0eb is most probably for the USERDATA. (check your header files if you're unsure..) That would mean the CMP right before jmp#1 is a magic number that our window needs to have in it's userdata. Once it passes that, we have a GetWindowText, which will pull our the window title into the buffer at ebp-64. That buffer, along with an argument @ 42eBBc which just so happens to be the text "MKS Toolkit Demo", gets passed to call#2, so we can guess that's the strcmp.

So we've got to fool this guy with two things: our userdata section has to have the magic number 0x09edeb in it, and our window needs to be called "MKS Toolkit Demo". Right on, let's write some code.

I'm using LCC, so some particulars apply differently to the compiler that you may choose. I've got code that looks like:


#include <win.h>

main()

{

HWND hwnd;



hwnd=CreateWindow(

"STATIC" /* lpClassName */,

"MKS Toolkit Demo" /* lpWindowName */,

WS_DISABLED|WS_POPUP /* dwStyle */,

0 /* x */,

0 /* y */,

0 /* nWidth */,

0 /* nHeight */,

NULL /* hWndParent */,

NULL /* hMenu */,

NULL /* hInstance */,

0 /* lParam */ );



if (hwnd==NULL) {

  printf("no hwnd\n");

  exit(-1);

  }

SetWindowLong(hwnd,GWL_USERDATA,0x0009edeb);

SetWindowText(hwnd,"MKS Toolkit Demo");

Sleep(1000);

exit(0);

}

We create a window of no size, set the appropriate magic number and window text, then sleep for a short time, allowing our MKS executable to find us and bypass the evil message about being unable to start MKSDEMO.EXE... Compile this and link it with "-subsystem windows", replace the old MKSDEMO.EXE, and you've got no more nag. Now let's move on to the time limit.

What do we have here? Well, we've got a registry value in HKEY_LOCAL_MACHINE called SOFTWARE\Mortice Kern Systems\Toolkit\DemoVersion\DemoNumber which contains some number that represents the software install date, and then we've got another routine that comes along with a GetSystemTime and produces a similar number. A comparison between these two numbers is done, and if it just so happens to differ beyond 0x278d00, or thirty days, the executable refuses to work.

My first thought was to hook the RegQueryValueEx API function to check if we're looking for that registry key, and if we are, do a GetSystemTime and return the appropriate juju. That would probably work, though it would mean we'd need some sort of setup program for MKS to initialize the hook, and that just seems sloppy.

But then it occurred to me, since every executable is calling MKSDEMO, why can't we just have MKSDEMO update the key to the current time everytime a MKS tool is run? After I stopped giggling (for some reason, I found this intensely funny), I realized that this was justice preserved. MKS went through all this trouble to make it hard on us, and here we are using the mechanism of their own obstructionist tactics against them.

So our object now is to figure out how the number in the registry key is developed from GetSystemTime, and then to put that code into our own MKSDEMO.C. Our install time in DemoNumber will get updated to the current time and our 30-day trial will consistently renew itself whenever we run an MKS tool.

At 4175e0 we have our call to RegQueryValueEx, and there's a call to 423420 shortly thereafter, which has our GetSystemTime in it. The function at 423420 returns the number we want in eax, so we've got to reverse this routine. We need to remember the SYSTEMTIME structure, so follow along. (In this code, the WORDs of the structure get shoved into the DWORDs of the registers. Many times the WORD in the high part of the DWORD gets masked out, sometimes it doesn't; the high WORD never matters, though, so I'm ignoring it in the comments.)


typedef struct _SYSTEMTIME {

    WORD wYear; 	// 0x10

    WORD wMonth;	// 0x0e

    WORD wDayOfWeek; 	// 0x0c

    WORD wDay; 		// 0x0a

    WORD wHour; 	// 0x08

    WORD wMinute; 	// 0x06

    WORD wSecond; 	// 0x04

    WORD wMilliseconds; // 0x02

} SYSTEMTIME;

The fairly long code snippet looks like:


00023420: 55                           push   ebp

00023421: 8BEC                         mov    ebp,esp

00023423: 83EC10                       sub    esp,010

00023426: 53                           push   ebx

00023427: 56                           push   esi ; save regs

00023428: 8D45F0                       lea    eax,[ebp][-0010]

0002342B: 57                           push   edi ; save edi

0002342C: 50                           push   eax

0002342D: FF1590B64300                 call   GetSystemTime ; ebp-10=Systemtime

00023433: 8B5DF2                       mov    ebx,[ebp][-000E] ; ebx=wMonth

00023436: 8B45F0                       mov    eax,[ebp][-0010] ; eax=wYear

00023439: 8BCB                         mov    ecx,ebx

0002343B: 8B55F6                       mov    edx,[ebp][-000A] ; edx=wDay

0002343E: 81E1FFFF0000                 and    ecx,00000FFFF

00023444: 4A                           dec    edx ; wDay--

; the table in 42f73e is

;   int mon2day[14]={ 0x0, 0x0, 0x1f, 0x3b, 0x5a, 0x78, 0x97, 0xb5,

;                     0xd4, 0xf3, 0x111, 0x130, 0x14e, 0x16d };

; which, given a month, gives us the number of days for each month since Jan 01

00023445: 668B344D3EF74200             mov    si,[00042F73E][ecx]*2 ; si=mon2day[ecx]

0002344D: 8D8844F8FFFF                 lea    ecx,[eax][0FFFFF844] ;ecx=edi-1980

00023453: 8BF9                         mov    edi,ecx ; edi=wYear-1980

00023455: 6603F2                       add    si,dx ; si += wDay

; si now = the number of days into the year from Jan 01, discounting the leap year

; so we should expect a leap calculation now

00023458: 81E7FFFF0000                 and    edi,00000FFFF

0002345E: 8BC7                         mov    eax,edi ; eax=wYear-1980

00023460: 99                           cdq ; edx=0, (usually a precursor to a modulo?)

00023461: 33C2                         xor    eax,edx

00023463: 2BC2                         sub    eax,edx

00023465: 83E003                       and    eax,003 ; eax = wYear % 3

00023468: 33C2                         xor    eax,edx

0002346A: 2BC2                         sub    eax,edx

0002346C: 7507                         jne   .000023475 ; if we're not in a leap year, jump

0002346E: 6683FB02                     cmp    bx,002 ; if we're not past February yet,

00023472: 7601                         jbe   .000023475 ; jump

00023474: 46                           inc    esi ; add a day

; our leap year calculation is done

00023475: 8D14C9                       lea    edx,[ecx][ecx]*8 ; edx=9*(wYear-1980)

00023478: 8D4703                       lea    eax,[edi][00003] ; eax=(wYear-1980)+3 (+3?)

0002347B: 8D0CD1                       lea    ecx,[ecx][edx]*8 ; ecx=73*(wYear-1980)

; 73 is an important number, 73 * 5 = 365, we should see that nearby

0002347E: 99                           cdq ; edx =0

0002347F: 8BD9                         mov    ebx,ecx

00023481: 83E203                       and    edx,003

00023484: 03DE                         add    ebx,esi ; ebx=(73(wYear-1980)+#days)

00023486: 03C2                         add    eax,edx 

00023488: C1F802                       sar    eax,002 ; eax=(wYear+3)/4

; this eax will be used to calculate how many leap days have past

0002348B: 8D0C8B                       lea    ecx,[ebx][ecx]*4 ; ecx=73*5*wYear+si=365wYear+si

; Here's our 365 days a year we're calculating since 1980

0002348E: 8D8408440E0000               lea    eax,[eax][ecx][000000E44]

; and here we have an adjustment for 3652 days, about 10 years-ish, 1970-ish instead of

; 1980, plus an adjustment for leap days past

; our #days calculation is now complete.

; we need #days * 24 hrs a day * 60 mins an hour * 60 seconds a min

; to compute it in seconds.. We'll see it here.

00023495: 25FFFF0000                   and    eax,00000FFFF

0002349A: 8D1440                       lea    edx,[eax][eax]*2 ; edx=3*#days

0002349D: 8B45F8                       mov    eax,[ebp][-0008] ; eax=whour

000234A0: 25FFFF0000                   and    eax,00000FFFF

000234A5: 8D04D0                       lea    eax,[eax][edx]*8 ; edx=8*3*#days+wHour

; 24 hrs in a day + wHour here, gives us eax = total # of hours so far since begin date.

000234A8: 8B55FA                       mov    edx,[ebp][-0006] ; edx=wMinute

000234AB: 8BC8                         mov    ecx,eax

000234AD: 81E2FFFF0000                 and    edx,00000FFFF

000234B3: C1E104                       shl    ecx,004 ; ecx=totalhrs*16

000234B6: 2BC8                         sub    ecx,eax ; make that totalhrs*15

000234B8: 8D048A                       lea    eax,[edx][ecx]*4 ; eax=15*totalhrs*4+wMinute

; 60 mins per hour + minutes, one last * 60 and we should be done.

000234BB: 8B55FC                       mov    edx,[ebp][-0004] ; edx=wSecond

000234BE: 8BC8                         mov    ecx,eax

000234C0: 81E2FFFF0000                 and    edx,00000FFFF

000234C6: C1E104                       shl    ecx,004 ; * 16

000234C9: 2BC8                         sub    ecx,eax ; * 15

000234CB: 8D048A                       lea    eax,[edx][ecx]*4 ; 15*4*totalsecs+wSecond

; eax = the # of seconds past since some target date.

000234CE: 8B4D08                       mov    ecx,[ebp][00008]

; and finally, if we've got arg to put this in, put it there, otherwise we'll drop it in eax

000234D1: 85C9                         test   ecx,ecx

000234D3: 7402                         je    .0000234D7

000234D5: 8901                         mov    [ecx],eax

000234D7: 5F                           pop    edi

000234D8: 5E                           pop    esi

000234D9: 5B                           pop    ebx

000234DA: 8BE5                         mov    esp,ebp

000234DC: 5D                           pop    ebp

000234DD: C3                           retn

Not bad, certainly easier than some serial # routines. We can simplify this a bit in C:


GetSystemTime(&st);

adddays=mon2day[st.wMonth]+st.wDay-1;

if ( ! (st.wYear-1980)%4 )

  if (st.wMonth>2)

    adddays++;



yeardays=st.wYear-1980;

yeardays*=73;

yeardays=(yeardays*4)+(yeardays+adddays);



leapdays=(st.wYear-1980+3)/4;

yeardays+=leapdays;

yeardays+=3652;



mytime = st.wSecond + (st.wMinute*60) + (st.wHour*60*60) + (yeardays*60*60*24);

What's curious here is the adjustments of 3,652 days. This calculation starts with Jan 01, 1980, but then instead gives us 10 years extra. The routine actually calculates the number of UTC seconds since Jan 01, 1970, but the programmer chose to calculate the date from 1980 and then add in the 10 extra years. Doubtless we'll never figure out why, but who cares? It's beat, and that's all that matters.

Add this into our MKSDEMO.C program, along with an appropriate registry change, and we're using MKS toolkit, nagless and forever.


#include <win.h>

main()

{

HWND hwnd;

SYSTEMTIME st;

PHKEY keyhand;

unsigned long mytime;

int mon2day[14]={ 0x0, 0x0, 0x1f, 0x3b, 0x5a, 0x78, 0x97, 0xb5,

                  0xd4, 0xf3, 0x111, 0x130, 0x14e, 0x16d };



hwnd=CreateWindow("STATIC", "MKS Toolkit Demo", WS_DISABLED|WS_POPUP, 0, 0, 0, 0,

                  NULL, NULL, NULL, NULL, 0);

SetWindowLong(hwnd,GWL_USERDATA,0x0009edeb);

SetWindowText(hwnd,"MKS Toolkit Demo");



GetSystemTime(&st);

adddays=mon2day[st.wMonth]+st.wDay-1;

if ( ! (st.wYear-1980)%4 )

  if (st.wMonth>2)

    adddays++;



yeardays=st.wYear-1980;

yeardays*=73;

yeardays=(yeardays*4)+(yeardays+adddays);



leapdays=(st.wYear-1980+3)/4;

yeardays+=leapdays;

yeardays+=3652;



mytime = st.wSecond + (st.wMinute*60) + (st.wHour*60*60) + (yeardays*60*60*24);



RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Mortice Kern Systems\\Toolkit\\DemoVersion", 

             0, KEY_WRITE, &keyhand);

RegSetValueEx(keyhand,"DemoNumber",0,REG_DWORD,&mytime,sizeof(DWORD));



Sleep(1000);

exit(0);

}



Ob Duh
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one. Should you want to STEAL this software instead, you don't need to crack its protection scheme at all: you'll find it on most Warez sites, complete and already regged, farewell.

You are deep inside fravia's page of reverse engineering, choose your way out:

redhomepage redlinks redsearch_forms red+ORC redstudents' essays redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_fravia+
redIs reverse engineering legal?