Beginner level essay follows.

                 -----
                 A CDRom check with a twist
                 (by grasshopper)

                 One of the most annoying things is to find out
                 that a software that you've purchased with your
                 hard-earned money refuses to work until you stick
                 its CDRom into the drive.  Such was the case
                 with a (several hundred dollar) program I recently
                 purchased.  At least with dongles you don't have to
                 reach out for the CD, remove the current one in the
                 drive, select drive, click, remove cd, insert the
                 previous CD you were working and continue with your
                 work.  If the lame-o program crashes, it's back to
                 the 2 minute drill.  Eject, insert, click, click,
                 eject, insert.

                 I've been out of the (cracking) scene ever since
                 I retired my Apple ][, a looong time ago.  Having
                 been emboldened by reverser's pages, the past few
                 weeks, I decided to give it a whirl a get rid of
                 this annoyance.

                 The protection check was a "standard" CDRom check.
                 So, the "standard" sice, BPX on GetDriveAttributeA,
                 GetVolumeInformationA, GetFileAttributes, CreateFileA.
                 Step, step, step.  After spending a few nights (I'm
                 out of shape) tracing through the protection routines,
                 I patch the tail end of the protection check changing
                 a jne to a relative jmp.  Hardly anything worth writing
                 about until . . .

                 Fire up the program, it seems to work sometimes, but most
                 of the time not.  Wierd.  What is that you say? "zen."

                 After poking around sice a little bit more, I try
                 a different approach.  I run the executable through
                 a disassembler and check the dead listing.  As I page
                 through the listing, a wierd thing catches my eye.  It
                 seems that when I hit pagedown, my editor is redisplaying
                 the same portion over and over.  Right at the spot where
                 the protection code is.  I trace calls into the routine
                 and find this little gem:

                   jmp dword ptr [4*eax+00419198]  ; jump to one of n random checks
                   call 004841C0                   ; where eax holds a random value
                   jmp 00418F17                    ; from 1 to n
                   call 00484CF0
                   jmp 00418F17
                   call 00485820
                   
                   jmp 00418F17
                   call 00489010

                 I check the reference to 00419198 and sure enough,
                 it's a jump table to the calls below.  The program
                 was randomizing the call to the protection check.
                 So tracing through one and applying a patch to "fix"
                 the return value would only work if it happened to
                 randomly pick the location that's been patched.  A
                 simple jmp around to the correct place fixed the
                 program so it's no longer annoying to start-up.

                 What threw me off was the fact that I was starting the
                 trace on the GetDriveTypeA call and stepping through
                 the code, setting bp's at later system calls.  Changing
                 eax before the end of the protection worked for that
                 one instance of the protection check but not for the
                 others, so when I patched that piece, it only worked
                 if it hit that correct random location.

                 How could it have been improved?

                 1. Bias the randomization so that it goes to the same
                    location everyday.  The 30-second crack would then
                    work as many times as needed for testing during that
                    day but fail on other days.
                 2. Spread out the checks to different parts of the code.
                    It's obvious that the check was a cut and paste job
                    to the same file.