482 lines
18 KiB
Plaintext
482 lines
18 KiB
Plaintext
|
||
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<><36>"
|
||
|
||
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/
|
||
-- |