
 odin's_________    _______        ______    _____________
   ____\       /____\     /________\    /____|     /_____/_______ _________
  /     _____/      _____/    __    ___/     '____/ /   |        \   _____/___
 /     |      \    ||    |    |    |    \    .     \    |    |    |  \__     /
 \_____        \___| \____    |____      \___|      \___|____|    |_____     |
       |_______/ <hook>  `----'    |_____/   |______/        |____|    |_____|
                                                            tutorial v1.51

           TOPIC: CRACKING/PATCHING SOFTART'S DESKEY v1.02.010
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


X. TABLE OF CONTENTS
~~~~~~~~~~~~~~~~~~~~
   1. FOREWARDS
   2. INTRODUCTION
   3. TOOLS USED FOR THIS
   4. THE CRACKING STARTS
   5. MAKING A PATCH
   6. THE URL'S

1. FOREWARDS
~~~~~~~~~~~~
   Welcome. So how come you're interested in reading this tutorial?  
Perhaps you think cracking is cool and seems easy, and that cracking 
is a way to be famous on the internet today. Well, if you think 
like this please stop reading now. Why? Mainly because most crackers 
don't crack because they think it's "cool." and want to be famous. 
Cracking is something they actually do because they think its a great 
thing to spend their precious time on, believe it or not. Most crackers,
not to say all, also code in one or several computer languages. 
   On the other hand if you're very interested in how computers work 
internally, and you like to program in languages like Pascal, C++ and 
Assembler, then I believe this tutorial is worth reading for people 
such as you, and you might even learn something from it.
   If you're the third category, you've been learning cracking for
some time now and read every little article about cracking you
can get, then this tutorial also is very good to read. You can always
learn something that you didn't know before. Even I can learn things
I didn't know by listening to others and reading various text files.

   To become a cracker will take several years. And to become a good 
cracker will take even longer. The key to success is practice and, in 
my point of view, learning and listening to other crackers.

I'll skip the most things about how SoftICE works because there
are several good tutorials out about that. One is Exact's SoftICE
tutorial, very good and recommended reading.


2. INTRODUCTION
~~~~~~~~~~~~~~~
  This text files purpose is to show and hopefully learn
you how to patch away a time limit of a program. The program we will
use is SoftArt's Deskey. It isn't necessary to patch this program
because you can also enter a registration code in the registry to
get the program to work fully, but then you have to write a keymaker
because the code depends on the Windows name and company (the one
you enter when you installed Windows 95). So we're going to do it
the lazy way and patch it. There are of course other ways to
do this crack.

3. TOOLS YOU WILL NEED FOR THIS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   (1) SoftICE 2.0/3.0, of course, our beloved debugger.
   (2) Ultra Edit or another good hex editor.
   (3) A patch generator, or if you write the patch yourself
       as I do.

4. THE CRACKING STARTS
~~~~~~~~~~~~~~~~~~~~~~
   Ok, let's begin with removing the semicolon from the user32.dll
and kernel32.dll lines in SOFTICE.DAT if you haven't done so already. 

   Now let's do some detective work, by checking the Deskey help
file. You'll notice that this program will stop working after
60 minutes. This limit is what we're going to remove. There
are a few possible solutions for the programmer to obtain this
limit. To check which functions the program use, let's test the 
approach Qapla used in his tutorial:
  Start the explorer and press the right mouse button on the
Deskey exe file. Now choose Quick-View. You'll notice all the 
calls the program uses and which dll's store the code for 
the calls. When you look at them, you'll notice some interesting 
functions.

  Import Table
  ------------

      .
      .
      .
      .
      KERNEL32.dll
      Ordinal Function Name
      ------- -------------
      .
      .
      .
      008f    FreeLibrary
      0188    MultiByteToWideChar
      011d    GetSystemTime        <----- Interesting
      00e4    GetLocalTime         <----- Interesting
      025d    lstrcmpiA
      00ca    GetDateFormatA
      .
      .
      .
      USER32.dll
      Ordinal Function Name
      ------- -------------
      .
      .
      .
      00ec    GetDlgItemTextA
      007d    DefWindowProcA
      01fe    SetTimer             <----- Interesting
      01a2    PostMessageA
      0224    TrackPopupMenu
      01b8    RemoveMenu
      .
      .
      .
      
Ok, as you see I'm interested in 3 different calls. We can try
to see if the program uses KERNEL32!GetSystemTime(); and 
KERNEL32!GetLocalTime();. Enter SoftICE and put a breakpoint 
on these two. Enable them just when you're about to start
the program. If you do it before, you risc to break on these
calls used by another program. So just before you start
Deskey, enable these. Ok, now you started it and program pops
up in the traybar. Nothing happens. Oh well, then we can
exclude these ones. Actually a cracker might have excluded them 
from the beginning and tried some other calls first. Why? 
Probably because a programmer usually uses these calls if the
program has a 30-day limit, not a 60-minutes one. Ok, now
we have one left to test, USER32!SetTimer();. Exit Deskey and
put a breakpoint on SetTimer(); just before you start Deskey.
Finally, SoftICE detected the use of this function by Deskey.
Press F11. It should look something like this now:

                 .
                 .
                 .
  0137:0040397F  833D1890400000  CMP   DWORD PTR [00409018], 00
  0137:00403986  7409            JZ    00403991
  0137:00403988  833D1C90400000  CMP   DWORD PTR [0040901C], 00
  0137:0040398F  7421            JZ    004039B2
  0137:00403991  6A00            PUSH  00
  0137:00403993  A128904000      MOV   EAX,[00409028]
  0137:00403998  6880EE3600      PUSH  0036EE80
  0137:0040399D  6834120000      PUSH  00001234
  0137:004039A2  50              PUSH  EAX
  0137:004039A3  FF158CB44000    CALL  [USER32!SetTimerA] <-- you're here
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB12            JMP   004039C4
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB09            JMP   004039C4
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB07            JMP   004039CB
                 .
                 .
                 .

Let's check the SetTimerA function in our we-cant-be-without-it 
API guide:

   The SetTimer function creates a timer with the specified time-out value. 

   UINT SetTimer(
      HWND  hwnd,       // handle of window for timer messages
      UINT  idTimer,    // timer identifier
      UINT  uTimeout,   // time-out value
      TIMERPROC  tmprc  // address of timer procedure
   );
   .
   .
   .
   uTimeout
      Specifies the time-out value, in milliseconds. 
   .
   .
   .

Aha, let's check on the code again:

  0137:00403991  6A00            PUSH  00                 <-- tmprc
  0137:00403993  A128904000      MOV   EAX,[00409028]     <-- pushed later
  0137:00403998  6880EE3600      PUSH  0036EE80           <-- uTimeout
  0137:0040399D  6834120000      PUSH  00001234           <-- idTimer
  0137:004039A2  50              PUSH  EAX                <-- HWND
  0137:004039A3  FF158CB44000    CALL  [USER32!SetTimerA] <-- you're here

Hmm, very interesting indeed, let's check the value 0036EE80 in
the SoftICE debugger:

  :? 36ee80
  0036EE80  0003600000  "6¯Ç"

An even and nice value. And if you read further in the API help
file you'll notice that the uTimeout should be in milliseconds.
1 second is 1000 milliseconds. Let's do some calculating:

  3600000/1000=3600 seconds.
  
  60 seconds*60 minutes=3600 seconds, which is 1 hour

We've found the right one! This call creates a timer which will be
checked when the program process the WM_TIMER message from Windows.
The WM_TIMER message is sent when 1 hour has past. Let's check
the API reference once more:

   WM_TIMER 

   wTimerID = wParam;            // timer identifier 
   tmprc = (TIMERPROC *) lParam; // address of timer callback 
 
   The WM_TIMER message is posted to the installing thread's message 
   queue or sent to the appropriate TimerProc callback function after 
   each interval specified in the SetTimer function used to install a 
   timer. 
   .
   .
   .

So now we know where he creates the timer. If you remove this 
SetTimer(); call the WM_TIMER message will never be sent, 
resulting in that the 60 minute limit will be REMOVED!! Let's 
take a look at this example code below:

  SetTimer(hwnd, idTimer, 0x36EE80, tmprc);
                          ^-- hex value

     WM_TIMER: {                 <-- this structure is reached when
       PostQuitMessage(0);           the hex value above reach 0. If
        ^-- will exit the program    the timer never is set this 
     }                               structure wont be reached.

This is very simple to understand, I hope :-). 
So perhaps you think, "hey, let's NOP away the whole structure"
 
   (for those of you not familiar with NOP: it means NO OPERATION 
    and are very commonly used when patching. The computer will 
    do nothing when it executes this instruction.)

Nono stop! Don't NOP away the whole call. Well first of all
a good rule when patching is that, never alter the code more
then you actually need. It looks nice (who'll notice anyway), and
it decreases the chance of program crash due to doing something
stupid. So how should we do instead? Well let's check that code
once more:

                 .
                 .
                 .
  0137:0040397F  833D1890400000  CMP   DWORD PTR [00409018], 00
  0137:00403986  7409            JZ    00403991
  0137:00403988  833D1C90400000  CMP   DWORD PTR [0040901C], 00
  0137:0040398F  7421            JZ    004039B2
  0137:00403991  6A00            PUSH  00
  0137:00403993  A128904000      MOV   EAX,[00409028]
  0137:00403998  6880EE3600      PUSH  0036EE80
  0137:0040399D  6834120000      PUSH  00001234
  0137:004039A2  50              PUSH  EAX
  0137:004039A3  FF158CB44000    CALL  [USER32!SetTimerA] <-- you're here
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB12            JMP   004039C4
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB09            JMP   004039C4
  0137:004039A9  8BBC24AC060000  MOV   EDI,[ESP+000006AC]
  0137:004039B0  EB07            JMP   004039CB
                 .
                 .
                 .

The CMP (compare) instructions above seems very interesting.
As you might notice, the one at 00403986 will jump to 00403991
and start the timer. If you check the JZ at 0040398F it will go
to 004039B2 and therefor jump over the timer, resulting in
that there will be no time limit. So to solve this simple 
problem just change the JZ 00403991 at 00403986 to JMP 004039B2 
instead. Like this:

  0137:00403986  7409            JZ    00403991
  ---> to --->
  0137:00403986  EB2A            JMP   004039B2

Now let's apply the patch to the exe file. Load up your favorite 
hex editor. In this case I'll use Ultra Edit. Now load the exe file.
Choose search and enter the following bytes: 68 80 EE 36 00.
So why do we search after these? Well that's very easy. Check the
code once again:

  0137:00403993  A128904000      MOV   EAX,[00409028]
  0137:00403998  6880EE3600      PUSH  0036EE80       <--- this one
  0137:0040399D  6834120000      PUSH  00001234

As you see these bytes stands for the instruction PUSH 0036EE80.
"Uhu, I don't have those cryptic numbers to the left of my
instructions!!". Well that's easy to fix. Just write 'code on'
and you'll see these cryptic numbers, also known as OPerand codes.
"Why didn't you search 83 3D 18 90 40 00 00 for example?". Well
that's because I know the ones we searched for only exists one
time in the exe file. The one mentioned above (83 3D...) exists
several times, so you cannot actually know which of those to use,
if you don't check the surrounding bytes that is. Always do
"search next" so you are sure that that byte combination doesn't
exist somewhere else in the file.

Now let's change the bytes needed. Some bytes above '68 80 EE 36 00' 
you will find '74 09' which is the JZ 00403991 instruction. This 
is the two bytes we want to change. So how do you know which numbers
to actually change to? That's also easy. In the debugger when you're
looking at the code just use the 'a' command. Like This:

  0137:0040397F  833D1890400000  CMP   DWORD PTR [00409018], 00
  0137:00403986  7409            JZ    00403991
  0137:00403988  833D1C90400000  CMP   DWORD PTR [0040901C], 00
  ----------------------------------------^ code window ^ -----
  :a 00403986 JMP 004039B2
  ----------------------------------------^ prompt ^-----------

This will change the instruction at 00403986 and a new code will
pop up, EB2A. So this is the code you want to change for the 7409
one. Remember, that if you use the 'a' command it will not change
the code permanently, only temporary. That's why we have to use
a hex editor. So go to the '74 09' bytes and change it to 'EB 2A'.
Now save the exe file, voila! That's it. Now start the program
up and test if it works. If SoftICE doesn't break on SetTimer(); 
it probably worked. If it does, read this all again :-).

One thing has to be said also. If you for example want to change 
a instruction with the opcode 'C1 E1 10' (3 bytes) to a instruction 
that only has a 2 byte opcode, for example '0B D1', you have to NOP 
away the last byte. NOP has the hex value 90. 
Like this:

   C1 E1 10      becomes --->  0B D1 90

   SHL   ECX, 10 becomes --->  OR   EDX, ECX  <- 0B D1
                               NOP            <- 90

As you see I change the F3 to 90 and therefore put the instruction
NOP there. If you didn't do this the chance of a program crash
would be 98%.

   That's all. To patch is very simple, but to find the bytes to 
change is harder. Remember that the byte combination can exist 
somewhere else so check the surrounding bytes.

5. MAKING THE PATCH
~~~~~~~~~~~~~~~~~~~
Now it's time to make this patch available to the public. To
write something like "uhu change the bytes at blabla to blabla"
doesn't look that good, does it? So now it's time to make
a exe file that changes those bytes asap so the user don't have
to use Ultra Edit every time. I've included a program in Pascal
done for this task. There are also several good patch generators.
One for Windows 95, that I strongly recommend, is Qapla's
PatchIt '97. It's fast and nice interface (happy now Qapla :-)
To convert this program to C++ should be easy. You have to know
one more thing to make a patch, where the bytes are located in
the file. This is called "offset.", to check the offset just
go to the bytes you changed, and look at the status bar in Ultra
Edit, it should say the offset (pos) in hex.

I did the patch in Turbo Pascal 7, most would probably do it in 
Asm :). Anyway, here is the patch:

----------------------------------------------------------------------
----------------------------------------------------------------------

Program Patcher;

Uses Crt;

Const offset   : Longint             = $2D86; { $ means a hex value }
      bytes    : Byte                = 2;
      len      : Longint             = 51200; { file length }
      orgbytes : Array[1..2] Of Byte = ($74, $09);
      newbytes : Array[1..2] Of Byte = ($EB, $2A);
      {---------------------------------------------------------}
      filename : String[12]          = 'DESKEY.EXE';
      errnof   : String[43]          = '    ERROR: File DESKEY.EXE was not found.';
      errver   : String[36]          = '    ERROR: File size is not correct.';
      errpch   : String[36]          = '    ERROR: File seems to be patched.';
      msgdon   : String[21]          = '    Patch successful!';
      ask      : String[26]          = '    Continue anyway? (Y/N)';

Procedure message(message : String);
Begin

  WriteLn;
  WriteLn(message);
  Halt;

End;

Procedure patchfile;
Var fil  : File Of Byte;
    teck : Byte;
    n    : Byte;
    ch   : Char;
Begin

  Assign(fil, filename);

  {$I-}
  Reset(fil);
  {$I+}

  If (Not(IOResult=0)) Then message(errnof);
  If (Not(FileSize(fil)=len)) Then
  Begin

    WriteLn;
    WriteLn(errver);
    WriteLn(ask);
    Repeat Until Keypressed;

    ch:=ReadKey;

    If Upcase(ch)='N' Then Halt;

  End;

  Seek(fil, offset);

  For n:=1 To bytes Do
  Begin

    Read(fil, teck);
    If (Not(teck=orgbytes[n])) Then message(errpch);

  End;

  Seek(fil, offset);
  For n:=1 To bytes Do Write(fil, newbytes[n]);
  Close(fil);

  WriteLn;
  WriteLn(msgdon);

  Halt;

End;

Begin

  WriteLn;
  WriteLn('    SoftArts Deskey v1.02.010 Patch');
  WriteLn('         By ODIN / RBS^TFT^PIE in 1997');
  patchfile;

End.

----------------------------------------------------------------------
----------------------------------------------------------------------


Some final words. Take some programs and play with patching them
in various ways. This gives you experience, and hopefully you'll
become a better cracker.

Thanks to Qapla, kOUGER, Hook and Tgunner for help while making this
tutorial.

A special greeting goes to ED!SON.

6. THE URL'S
~~~~~~~~~~~~
  --
  My E-Mail
       cracking@usa.net
  --
  SoftArts Deskey v1.02.010
       http://www.spiresoft.com
  --
  Ultra Edit vX
       http://www.windows95.com/apps/
  --