May 1998
"Xara3D2 V2.10"
(A tough nut to crack)
Win '95 PROGRAM
Win Code Reversing
 
 
by The Sandman 
 
 
Code Reversing For Beginners 
 
 
 
Program Details
Program Name: xara3d2n.exe
Program Type: 3D Logo Utility
Program Location: Here or  Here
Program Size: 665K 
 
   
Tools Used:
Softice
HexWork Shop32 
 
Rating
Easy ( X )  Medium ( X  )  Hard (    )  Pro (    ) 
There is a crack, a crack in everything. That's how the light gets in.
 
  
 
Zara3D2 V2.10
( A tough nut to crack  )
Written by The Sandman
 
 
 
Introduction
 
The author(s) of this utility can be found at: http://www.club.xara.com
 
This is a great utility to create instant 3D text in a variety of styles using almost any type of background you care to use. This software is well worth registering, it's fast and easy to use.
 
 
About this protection system
 
Registration is via two ways.  The first is when the program first runs, you have the option of purchasing the software, where you will then have access to the 'Unlock' screen and the second way is via selecting the 'Help' menu then choosing the 'Unlock' option.
    
While in the Unlock screen your asked to enter the 'Unlock' code, which the program expects a10 digit code.  From what I've learned about this program it seems to generate a different registration key each time it is installed.  This key is used to generate the actual 'Unlock' code which you must enter before you can register the program.
 
Therefore, it would be a waste of time 'sniffing' out this code, it probably won't work on other installations, besides, since the program doesn't ask for the User's details to register itself with we can work on patching the program so it registers itself first time once installed on the hard drive.
 
This program is not compacted or encrypted in anyway.
 
 
The Essay 
     
After many hours and dozens of attempts I finally managed to crack this program but it wasn't easy and in the end it took what seemed like an eternity to figure out just how I did it.  I never did find out where the 'Ascii' version of the code was stored.
 
By now if you've been following my essay's you will know that the first thing we do is to use the File Manager's 'Quick View' option to check on the actual program's .EXE file to see what imported functions this program uses. It uses all the standard functions associated with this kind of utility and because most of it's functions end in 'A' this tells us that it's a 32-bit utility..

OK, now run up Xara3D2 and go into it's 'Unlock' Screen via the Help menu.

For the Unlock code type in any numbers you wish, I used 7777777
Our task is to patch rather than sniff out the 'Unlock' code, we have 30 days to complete this task before the program expires *grin*
 
Once done, press Ctrl-D and type into Softice:

BPX messageboxa

 
Now type X to leave Softice and press the 'OK' button so that Xara3D2 can get on with verifying our Unlock code.

As expected, Softice breaks at the start of the MessageBoxA routine, from here press F11 once then answer 'OK' to the 'Invalid Unlock code', message that now pops up and again we're back in to Softice.

We should see the following snippet of code in Softice.
 
 

:004753D5 push [ebp+0C]
:004753D8 push [edi+78]
:004753DB push [ebp+08]
:004753DE push eax
:004753DF Call dword ptr [User32!MessageBoxA]
:004753E5 mov dword ptr [esi], ebx                             ;We land here
:004753E7 cmp dword ptr [ebp-04], 00000000
:004753EB mov dword ptr [ebp+0C], eax
:004753EE je 004753FB
:004753F0 push 00000001
:004753F2 push [ebp-04]
:004753F5 Call dword ptr [004F6CF0]
:004753FB push 00000001
:004753FD mov ecx, edi
:004753FF call 00475326
:00475404 mov eax, dword ptr [ebp+0C]
:00475407 pop edi
:00475408 pop esi
:00475409 pop ebx
:0047540A leave
:0047540B ret 000C    ;When we 'ret'urn from here it will take us to memory 
                   ;location: 014F::00475476
 
Here's where the fun really begins... While in softice if you scroll up from where we've just landed you'll see dozens of jb's and ja's that are all worth investigating a little further.  If you now place a Softice breakpoint at:
 
:00475354                      push ebp

You will be able to trace the program's flow as it heads towards the call [User32messageboxa] instruction, which we've just returned from.

Incidentally, before the program can display a message box on our screen it must first place a number of values on the stack so that when the messagebox routine is called it can access these values in order to create the final messagebox in the way the programmer had wanted.

int MessageBox(

       HWND  hWnd,    // handle of owner window
       LPCTSTR  lpText,    // address of text in message box
       LPCTSTR  lpCaption, // address of title of message box
       UINT  uType     // style of message box
      );

-  If we don't have a window, that call's our MessageBox we set  "hWnd" to zero.
-  "lpText" will be the offset of our text (where in memory it is stored)
-  "lpCaption" ---> offset of the messagebox Caption
-  "uType" if set to "0" then it corresponds to a "normal"  MessageBox with a nice OK-button (Type mb_ok)

Anyway, once we've explored the section of code between 014F::00475354 to 014F:0047540B and found that there isn't anywhere we can patch the program we leave via the ret instruction to another section of code at: 014F:475476

* Referenced by a CALL at Addresses:
|:00402405   , :004037A5   , :00404015   , :0040B865   , :0040BBC6
|:0040C59E   , :0040D286   , :0040D2B6   , :0040D538   , :0041134C
|:00411E85   , :00417B92   , :00418F1C   , :00418F7C   , :00419F2E
|:0041C32B   , :0046A84D   , :0046EB47   , :00471B74   , :00471CCF
|:00471D01   , :00474E92   , :0047A7A0   , :00481A56
|
:0047542F B8C04A4800   mov eax, 00484AC0
:00475434 E83FE5FDFF   call 00453978
:00475439 51           push ecx
:0047543A 56           push esi
:0047543B 8D4DF0       lea ecx, dword ptr [ebp-10]
:0047543E E8CA83FFFF   call 0046D80D
:00475443 FF7508       push [ebp+08]
:00475446 8365FC00     and dword ptr [ebp-04], 00000000
:0047544A 8D4DF0       lea ecx, dword ptr [ebp-10]
:0047544D E83195FFFF   call 0046E983
:00475452 8B7510       mov esi, dword ptr [ebp+10]
:00475455 83FEFF       cmp esi, FFFFFFFF
:00475458 7503         jne 0047545D
:0047545A 8B7508       mov esi, dword ptr [ebp+08]
:0047545D E831A60000   call 0047FA93
:00475462 8B4004       mov eax, dword ptr [eax+04]
:00475465 56           push esi
:00475466 FF750C       push [ebp+0C]
:00475469 8BC8         mov ecx, eax
:0047546B 8B10         mov edx, dword ptr [eax]
:0047546D FF75F0       push [ebp-10]
 
:00475470 FF9294000000        call dword ptr [edx+00000094] ;Calls the routine that deals with
                                                                                          ;setting up the 'Invalid UnLock code'
                                                                                          ;message when we get it wrong.

:00475476 834DFCFF              or dword ptr [ebp-04], FFFFFFFF ;We land here after the above call.
:0047547A 8D4DF0                  lea ecx, dword ptr [ebp-10]  ;Register EAX points to our beggar off
                                                                                       ;message at loc: 0157:00A92F80
:0047547D 8BF0                      mov esi, eax
:0047547F E8D484FFFF          call 0046D958
:00475484 8B4DF4                  mov ecx, dword ptr [ebp-0C]
:00475487 8BC6                      mov eax, esi
:00475489 64890D00000000     mov dword ptr fs:[00000000], ecx
:00475490 5E                          pop esi
:00475491 C9                          leave
:00475492 C20C00                  ret 000C
 
 
At this point I cleared all of Softice's breakpoints with bc * and placed a new breakpoint at location: 014F:0047542F by simply scrolling up Softice's Code Window and then double-clicking on this line.
 
:0047542F B8C04A4800               mov eax, 00484AC0
 
Why this line?.  Well as you can see from the above snippet of code it is called from many places within the program's code, and since this line is the start of this code block we want to try and work out what this block does.

OK, now re-run Xara3D2 from the Unlock screen, enter a dummy Unlock code and press the 'OK' button.

We now land at our new breakpoint and again, we trace through the code by using the F10 key and still we find no jnz or jpz instruction that allows us to bypass the calling of the 'You have entered an invalid Unlock code' message.  We at least know we're still on the right track because we find the location of this beggar off message at memory location: 0157:0040c5A3

OK, keep pressing the F10 key until we hit the 'ret'urn instruction at: :00475492 so that we emerge at the following section of code:
 

* Referenced by  (C)onditional Jumps at Addresses:
|:0040C3A9(C), :0040C3BD(C), :0040C3D9(C), :0040C3F5(C), :0040C411(C)
|:0040C42D(C), :0040C449(C), :0040C465(C), :0040C4DC(C)
 
:0040C595 6AFF          push FFFFFFFF
:0040C597 6A10          push 00000010
:0040C599 68BD0B0000    push 00000BBD ;Reference to our beggar off msg.
:0040C59E E88C8E0600    call 0047542F ;Call's the routine we just left
:0040C5A3 0F842A010000  mov al,[esp+13]

Here's where the program push's onto the stack our beggar off message, we're getting closer now to finding where to patch this program, but wait a minute, if we scroll up Softice's Code Window we see that there are many conditional jumps in close proximity to this beggar off function, nine in fact.  Does this mean we have to patch all nine locations to get the program to accept our dummy unlock code?..  Let's first look at the code that comes before this beggar off routine and see if we can get a 'feel' for the code's logic.. Remember, we're looking for a good place to patch the code, not to sniff out the unlock code, which as we know is different each time it is installed on the User's machine.

:0040C395 83F801                      cmp eax, 00000001
:0040C398 0F8505020000            jnz 0040C5A3
:0040C39E 8B842440010000        mov eax, dword ptr [esp+00000140]
:0040C3A5 8378F807                   cmp dword ptr [eax-08], 00000007
:0040C3A9 0F85E6010000           jz 0040C595   ;1st jump to the 'Beggar off routine'
:0040C3AF 0FBE10                    movsx edx, byte ptr [eax]
:0040C3B2 52                            push edx
:0040C3B3 E808530400             call 004516C0
:0040C3B8 83C404                    add esp, 00000004
:0040C3BB 85C0                       test eax, eax
:0040C3BD 0F84D2010000         jz 0040C595   ;2nd jump to the 'Beggar off routine'
:0040C3C3 8B842440010000      mov eax, dword ptr [esp+00000140]
:0040C3CA 0FBE4801               movsx ecx, byte ptr [eax+01]
:0040C3CE 51                           push ecx
:0040C3CF E8EC520400           call 004516C0
:0040C3D4 83C404                   add esp, 00000004
:0040C3D7 85C0                       test eax, eax
:0040C3D9 0F84B6010000         jz 0040C595   ;3rd jump to the 'Beggar off routine'
:0040C3DF 8B942440010000     mov edx, dword ptr [esp+00000140]
:0040C3E6 0FBE4202               movsx eax, byte ptr [edx+02]
:0040C3EA 50                          push eax
:0040C3EB E8D0520400           call 004516C0
:0040C3F0 83C404                   add esp, 00000004
:0040C3F3 85C0                       test eax, eax
:0040C3F5 0F849A010000         jz 0040C595   ;4th jump to the 'Beggar off routine'
:0040C3FB 8B8C2440010000    mov ecx, dword ptr [esp+00000140]
:0040C402 0FBE5103               movsx edx, byte ptr [ecx+03]
:0040C406 52                           push edx
:0040C407 E8B4520400            call 004516C0
:0040C40C 83C404                   add esp, 00000004
:0040C40F 85C0                       test eax, eax
:0040C411 0F847E010000         jz 0040C595 ;5th jump to the 'Beggar off routine'
:0040C417 8B842440010000     mov eax, dword ptr [esp+00000140]
:0040C41E 0FBE4804               movsx ecx, byte ptr [eax+04]
:0040C422 51                           push ecx
:0040C423 E898520400             call 004516C0
:0040C428 83C404                    add esp, 00000004
:0040C42B 85C0                       test eax, eax
:0040C42D 0F8462010000         jz 0040C595  ;6th jump to the 'Beggar off routine'
:0040C433 8B942440010000      mov edx, dword ptr [esp+00000140]
:0040C43A 0FBE4205                movsx eax, byte ptr [edx+05]
:0040C43E 50                           push eax
:0040C43F E87C520400            call 004516C0
:0040C444 83C404                    add esp, 00000004
:0040C447 85C0                        test eax, eax
:0040C449 0F8446010000          jz 0040C595  ;7th jump to the 'Beggar off routine'
:0040C44F 8B8C2440010000      mov ecx, dword ptr [esp+00000140]
:0040C456 0FBE5106                movsx edx, byte ptr [ecx+06]
:0040C45A 52                           push edx
:0040C45B E860520400             call 004516C0
:0040C460 83C404                    add esp, 00000004
:0040C463 85C0                       test eax, eax
:0040C465 0F8446010000          jz 0040C595  ;8th jump to the 'Beggar off routine'
:0040C46B 8B842440010000     mov eax, dword ptr [esp+00000140]
:0040C472 0FBE4804                movsx ecx, byte ptr [eax+04]
:0040C476 0FBE5006                movsx edx, byte ptr [eax+06]
:0040C47A 8D0C49                   lea ecx, dword ptr [ecx+2*ecx]
:0040C47D 8D0CCA                  lea ecx, dword ptr [edx+8*ecx]
:0040C480 0FBE5002                movsx edx, byte ptr [eax+02]
:0040C484 8D0C49                   lea ecx, dword ptr [ecx+2*ecx]
:0040C487 8D0CCA                  lea ecx, dword ptr [edx+8*ecx]
:0040C48A 0FBE5005              movsx edx, byte ptr [eax+05]
:0040C48E 8D0C49                  lea ecx, dword ptr [ecx+2*ecx]
:0040C491 8D0CCA                 lea ecx, dword ptr [edx+8*ecx]
:0040C494 0FBE10                  movsx edx, byte ptr [eax]
:0040C497 8D0C49                  lea ecx, dword ptr [ecx+2*ecx]
:0040C49A 8D0CCA                 lea ecx, dword ptr [edx+8*ecx]
:0040C49D 0FBE5001              movsx edx, byte ptr [eax+01]
:0040C4A1 8D0C49                  lea ecx, dword ptr [ecx+2*ecx]
:0040C4A4 8D0CCA                 lea ecx, dword ptr [edx+8*ecx]
:0040C4A7 0FBE5003              movsx edx, byte ptr [eax+03]
:0040C4AB 8D0C49                 lea ecx, dword ptr [ecx+2*ecx]
:0040C4AE 8BC5                     mov eax, ebp
:0040C4B0 D1E8                     shr eax, 1
:0040C4B2 8D94CA67216BFB  lea edx, dword ptr [edx+8*ecx-0494DE99]
:0040C4B9 8BCD                     mov ecx, ebp
:0040C4BB 2555555555           and eax, 55555555
:0040C4C0 81E155555555        and ecx, 55555555
:0040C4C6 8D0C48                  lea ecx, dword ptr [eax+2*ecx]
:0040C4C9 8D0489                  lea eax, dword ptr [ecx+4*ecx]
:0040C4CC C1E008                 shl eax, 08
:0040C4CF 2BC1                     sub eax, ecx
:0040C4D1 8D04C0                  lea eax, dword ptr [eax+8*eax]
:0040C4D4 8D0441                  lea eax, dword ptr [ecx+2*eax]
:0040C4D7 8D0C40                  lea ecx, dword ptr [eax+2*eax]
:0040C4DA 3BD1                     cmp edx, ecx
:0040C4DC 0F85B3000000       jne 0040C595  ;9th jump to the 'Beggar off routine'
:0040C4E2 8B0D10EC4B00      mov ecx, dword ptr [004BEC10]
:0040C4E8 55                          push ebp
:0040C4E9 6828744A00           push 004A7428    ;Reference to string 'Key"
:0040C4EE 6820744A00           push 004A7420   ;Reference to string "Install"
:0040C4F3 E8AFA20600           call 004767A7
:0040C4F8 C605D0E44B0000      mov byte ptr [004BE4D0], 00
:0040C4FF 8D8C2444010000       lea ecx, dword ptr [esp+00000144]
:0040C506 C68424B40400000B    mov byte ptr [esp+000004B4], 0B
:0040C50E E845140600               call 0046D958
:0040C513 8D8C2440010000        lea ecx, dword ptr [esp+00000140]
:0040C51A C68424B40400000A   mov byte ptr [esp+000004B4], 0A
:0040C522 E831140600               call 0046D958
:0040C527 8D8C2404010000        lea ecx, dword ptr [esp+00000104]
:0040C52E C68424B404000009    mov byte ptr [esp+000004B4], 09
:0040C536 E8ABF30600              call 0047B8E6
:0040C53B 8D8C24A8000000       lea ecx, dword ptr [esp+000000A8]
:0040C542 C68424B404000003     mov byte ptr [esp+000004B4], 03
:0040C54A E8C7010600               call 0046C716
:0040C54F 8D4C2458                   lea ecx, dword ptr [esp+58]
:0040C553 C68424B404000002     mov byte ptr [esp+000004B4], 02
:0040C55B E8E0620500               call 00462840
:0040C560 8D4C2458                   lea ecx, dword ptr [esp+58]
:0040C564 E887630500                call 004628F0
:0040C569 8D4C241C                   lea ecx, dword ptr [esp+1C]
:0040C56D C68424B404000000     mov byte ptr [esp+000004B4], 00
:0040C575 E8DE130600                call 0046D958
:0040C57A 8D4C2414                    lea ecx, dword ptr [esp+14]
:0040C57E C78424B4040000FFFFFFFF  mov dword ptr [esp+000004B4], FFFFFFFF
:0040C589 E8CA130600                call 0046D958
:0040C58E B001                           mov al, 01
:0040C590 E9AA000000                jmp 0040C63F

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040C3A9(C), :0040C3BD(C), :0040C3D9(C), :0040C3F5(C), :0040C411(C)
|:0040C42D(C), :0040C449(C), :0040C465(C), :0040C4DC(C)
|
:0040C595 6AFF                    push FFFFFFFF
:0040C597 6A10                    push 00000010
:0040C599 68BD0B0000         push 00000BBD
:0040C59E E88C8E0600         call 0047542F
:0040C5A3 0F842A010000      mov al,[esp+13]

Our first job is to locate ALL nine condition jumps to our 'begger off' routine, we don't want to miss any, else we could end up back to square one. For those impetuous crackers among us we might see that if we changed the line:
 
:0040C398 0F8505020000            jnz 0040C5A3
TO
:0040C398 EB8505020000          jp 0040C5A3
 
Then we could jump over all nine of these beggar off jumps and let the program carry on from there so lets try this:-

First, clear all Softice's previous breakpoints by typing bc *

Next,  locate the line we're about to change at memory location [0040C398] and then double-click on it. This tells Softice to treat this line as a breakpoint.

Type x to leave Softice then re-run Xara3D2 so we're back into the 'Unlock Screen' with a dummy unlock code already typed in.  Now press the 'OK' button.

Ok, so far so good, we're now at the memory location we suspect that can bypass the protection system and so we now type:

r eip=0040C5A3
 
* What  we're telling Softice is to 'forget' what part of the program it is about to execute and instead, execute the program's code at the new memory location we've just given it. This is exactly the same procedure that would happen IF we had changed the jnz instruction to a jp instruction. *
 
We can now type x to leave softice and let the program run as normal to see what happens next.

"You have entered an invalid Unlock code, this program has not been unlocked."

Hmmm, that's a shame, it did look promising and would have saved me further investigation and to be honest, I'm already getting sick of looking at this program's code looking for that elusive patch.. Time to re-think my strategies.. Head's back to the program's manual, looking for a clue, hint to where to try and attack the program..

hey, what's this, the program prevents access to the change background function as long as it's unregistered!. Why?.. Ah, I see, it's default background has the company's logo spread all over it, so if you try and use any logo's created with this program then the Xara3D2 logo gets shown as well..Hmm, nice touch. But how does this help us?. Can you feel it yet?, I certainly did..

I'll explain, each time you try and change the background border a message box pops up and tells you that you can't use this facility until the program has been registered.. Here's a nice messagebox we can trap.. Also, if we can't access the change background facility then the program must store a 'flag' of some sort in memory when it can't find it's unlock code in the system registry file.. This flag will usually consist of a byte value of '1' meaning not registered or a value of '0' meaning registered.

Press Ctrl-D and within Softice type bc * to clear all it's breakpoints then x to exit.

Run up Xara3D2 and progress into the main screen, with the default background showing.  Now re-enter Softice (Ctrl-D) and type: bpx messageboxa then x to leave Softice.
 
Now select the 'View' option then chose 'Change Background Texture' and straight away we're in Softice, we should see:-

:004753C6 E875A30000              call 0047F740 ;checks to see if memory
                                                ;location 004F2B08 is '0'
                                                ;0= program Registered
                                                ;1= program Unregistered

:004753CB 8B45F8                  mov eax, dword ptr [ebp-08]
:004753CE 85C0                    test eax, eax
:004753D0 7403                    je 004753D5
:004753D2 8B401C                  mov eax, dword ptr [eax+1C]
:004753D5 FF750C                  push [ebp+0C]
:004753D8 FF7778                  push [edi+78]
:004753DB FF7508                  push [ebp+08]
:004753DE 50                      push eax

* Reference To: USER32.MessageBoxA, Ord:0195h
                                  |
:004753DF FF15046B4F00            Call [User32!MessageBoxA]
:004753E5 891E                    mov dword ptr [esi], ebx ;We land here
:004753E7 837DFC00                cmp dword ptr [ebp-04], 00000000
:004753EB 89450C                  mov dword ptr [ebp+0C], eax
:004753EE 740B                    je 004753FB

 After checking out where the call 0047F740 went I quickly found that at memory location 004F2B08 contained a single byte that when checked through a 'Dead Listing' played a very important part within the whole program. When the program is first run this memory location contains a value of '0', but after it has checked to see if there is an Unlock code in the system registry file it places a value of '1' in this location, signify that the program is unregistered.  Clearing all of Softice's breakpoints and creating a memory access breakpoint: bpm 0157:004F2B08 gave me a number of locations within the program that handled this memory location.  One such area of interest concerns the initialization of the program itself, where, having found that it is unregistered, goes on to display how many days left you have before the 30 day time limit expires!. This certainly is of interest to us because if we can somehow make the program 'forget' to check memory location: 0157:004F2B08 at the start of the program then the rest of the program will follow suit!.
 
After a lot of trial and error I found the block of code that works out how many days we have left to evaluate this program, amongst other things and a little more work soon gave me the actual call that would do this check.

:004105D0 68AC774A00              push 004A77AC
:004105D5 8BCE                    mov ecx, esi
:004105D7 E859FD0600              call 00480335
:004105DC 6A05                    push 00000005
:004105DE 8BCE                    mov ecx, esi
:004105E0 E8AA060700              call 00480C8F
:004105E5 E846B3FFFF              call 0040B930 ;If the program is unreg'd
                                                ;then check no of days
                                                ;remaining and set the
                                                ;'unregistered' flag to 1
:004105EA 84C0                    test al, al
:004105EC 7512                    jne 00410600
 
Can you see where my patch will be placed?.

All we need to do is change:-

:004105E5 E846B3FFFF              call 0040B930

*** TO ***

:004105E5 EB19                    jmp 00410600
:004105E7 90                      nop
:004105E8 90                      nop
:004105E9 90                      nop
 

Job Done.....
 
The 'Crack' 
 
Using any hex editor load up the file X3D.exe then:-

SEARCH FOR    :-  E846B3FFFF84C07512
REPLACE WITH:-  EB1990909084C07512
 
  
Final Notes 
 
This was a hard crack, had to really work for this one, but what really bugs me was that I learnt very little from this program. This program also seems to use 'Self modifying' code in places which sometimes confuses matters when trying to 'dead list' it's code.
 
 

** Self modifying code is where while running, the program will change some of it's own code to something totally different and therefore, changes the way it runs based on any number of conditions **
 
The first time I cracked this program I managed to get it to correctly register itself in the Window's Registry file, where it inserted the 'Unlock Code' at:-

My Computer\HKEY_CURRENT USER\Software\Xara\XD2\Install
Key    0xb180b679 (2978002553)

This key was based on the program's serial No: WHPWRNCXP

I haven't explained how I did this because I can't re-create the steps I took to make this happen, but perhaps someone might find out how to do this and let me know..:)
 
My thanks and gratitude goes to:

Fravia+ for providing possibly the greatest source of Reverse Engineering
knowledge on the Web.
 
+ORC for showing me the light at the end of the tunnel.

 
Ob Duh 
 
Do I really have to remind you all that by buying and NOT stealing the software you use will ensure that these software houses will continue to  produce even *better* software for us to use and more importantly, to continue offering even more challenges to breaking their often weak protection systems.
 
If your looking for cracks or serial numbers from these pages then your wasting your time, try searching elsewhere on the Web under Warze, Cracks etc.
 


 
 
 Next   Return to Essay Index   Previous 
 


Essay by:          The Sandman
Page Created: 24th May 1998