Lockless Trial CrackMe
Volume #2 Revision A (yep!)
Written by Sphinx


Introduction


Well this CrackMe is obsolote (ie. expired) so this could be just a supplemental paper on cracking it. But be informed that there'll be a new one to be released soon. And in this paper, there'll be a lot of words instead of dasms. Since dasms aren't really the highlights :-)


Tools required


SoftICE 3.25
TASM 5.0
cRACKER'S nOTES (August 2000 Release)


Target's URL

http://www.lockless.com


Essay


Ok. Since there's an included readme file, we snoop it first for some quick infos.. hehe.. l33t text eh? Anyway, the author
said these things we should crack in the CrackMe (which, btw, are the tasks):

1. The ADT that doesn't work under WinNT
2. Nag
3. Name, serial and trackbars
4. Keyfile

Task 1: ADT

Ok. Since we can't patch directly the EXE (I think), we've to fool it instead. And running it you'll get a "softice-detected" messagebox. I've a BIG hunch that it's MeltICE only. So we could breakpoint CreateFileA. But there will be a lot of
occurences of this (btw, try snooping a little and you'll see the CRC check and the keyfile's name :-) But I changed my breakpoint to MessageBoxA for a faster pace in this regard. Now run the app. And break!. Hit F12 once to get back.
And since it already has presented us with the error, we backtrace a bit: (yep, dasms are coming :-)

 cs:00435A84  PUSH    EBX                      ; push ebx first
 cs:00435A85  XOR     EBX,EBX                  ; ebx = 0 (goodflag!)
 cs:00435A87  PUSH    00                       ; push a 00 too :)
 cs:00435A89  PUSH    00000080                 ; normal attribute
 cs:00435A8E  PUSH    03                       ; open existing
 cs:00435A90  PUSH    00                       ; no security-attrib
 cs:00435A92  PUSH    03                       ; share-mode stat (FILESHARE_READWRITE)
 cs:00435A94  PUSH    C0000000                 ; acces mode (GENERIC_READWRITE)
 cs:00435A99  PUSH    00435AD4                 ; offset of "\\.\SICE"
 cs:00435A9E  CALL    KERNEL32!CreateFileA     ; try opening the VxD
 cs:00435AA3  CMP     EAX,-01                  ; found? (eax=-1 if none, else eax=handle)
 cs:00435AA6  JZ      00435AB0                 ; no? jump, else...
 cs:00435AA8  PUSH    EAX                      ; save handle
 cs:00435AA9  CALL    KERNEL32!CloseHandle     ; then close it
 cs:00435AAE  MOV     BL,01                    ; bl = 1 (badflag!)
 cs:00435AB0  CMP     BL,01                    ; so, is bl = 1?
 cs:00435AB3  JNZ     00435AD0                 ; no? jump partial goodboy, else...
 cs:00435AB5  PUSH    00002030                 ; push type of messagebox
 cs:00435ABA  PUSH    00435AE0                 ; push offset of the caption
 cs:00435ABF  PUSH    00435AFC                 ; push offset of the text
 cs:00435AC4  PUSH    00                       ; save the handle
 cs:00435AC6  CALL    USER32!MessageBoxA       ; draw the messagebox
 cs:00435ACB  CALL    0040360C                 ; ExitProcess
 cs:00435AD0  POP     EBX                      ; pop ebx back
 cs:00435AD1  RET                              ; get out
Ok. Technical eh? Anyway, to crack it, we could just patch the conditional jump to an unconditional one. Yeah, that
would work. But look, there's even a better way. Since it opened it and returning the handle, I want it to close it, so that
the handle will be of no use anymore. BUT, we should make it eat a goodflag by making ebx = 0 right before comparing it
with 1. So we could patch the instruction at cs:00435AAE from mov bl,01 to xor bl,bl. Yep, more elegant, I guess :-)
Note that the new instruction also occupies 2 bytes as the old one. So how to automate this crack? Ok. I would do this:

 bpx cs:00435AAE do "e eip 32 DB"   ; 32 DB = hexcode of "xor bl,bl"
Task done. Next?

Task 2: Nag

Btw, before anything else, if you want to cheat a little, you could easily fool it into thinking that we've defeated a nag. How? Yeah, while the nag is drawn. Press ALT+F4 instead of clicking "Proceed". But that's not a real crack (and besides, at the end, you have to enter the offsets and bytes you've patched as proof :-) My guess is at startup, the memflag which handles
the nag defaults to the defeated one. And clicking "Proceed" moves the undefeated flag there. So since we've not clicked it
at all, it was never changed from the default. Well, just a thought :-)

Ok. Since we've kicked the ADT, we should finish up the RET and continue on tracing. Anyway, after sometime, you
should end up here (after 6 RET's).
 cs:004384FF  CALL    0042AD6C         ; yep, which called the above
 cs:00438504  MOV     ECX,[00439B40]
 cs:0043850A  MOV     EAX,[00439AA8]
 cs:0043850F  MOV     EAX,[EAX]
 cs:00438511  MOV     EDX,[004351CC]
 cs:00438517  CALL    0042AD6C         ; draws the nag
 cs:0043851C  MOV     EAX,[00439AA8]
 cs:00438521  MOV     EAX,[EAX]
 cs:00438523  CALL    0042ADF8         ; main program
 cs:00438528  CALL    0040360C         ; ExitProcess
So we could try NOPing that entire call out which calls the nag. I don't know if that would work coz i haven't tried it :-)
And besides, that calls seems to be more than what you think. So let's trace into the call. Anyway, after sometime, you
should end up here, where the program starts to display the nag which is at these relevant memory addresses:
 cs:00425F8F  CMP     WORD PTR [EAX+000001CE],00
 cs:00425F97  JZ      00425FAB
 cs:00425F99  MOV     EBX,[EBP-04]
 cs:00425F9C  MOV     EDX,[EBP-04]
 cs:00425F9F  MOV     EAX,[EBX+000001D0]
 cs:00425FA5  CALL    [EBX+000001CC]     ; Here!
 cs:00425FAB  XOR     EAX,EAX
If we invert that only jump, we'll pass the nag. But doing this, we must be specific. Why? Look at the call, notice that it
calls the procedure based on the value of ebx and this whole routine is being called several times. And that call will display some dialogs including the main program and the nag. So we so how to kick the nag only without bothering the main prog? We've got to know what address that call will call :-) Then we'll just "prevent" the nag from being shown :-) Ok. Here's the address that call calls when it's the time for the nag to be drawn:
 cs:0043531C  MOV     EAX,[0043A7A4]
 cs:00435321  CALL    00428F48           ; really display the nag now!
 cs:00435326  RET
So we could NOP (again) the entire call (occupies 4 bytes). But hey! Instead, why not simply replace the first byte of the first line with a RET so that it won't continue drawing the nag. How? Ok. While EIP's at cs:0043531C, type:
 e eip C3       ; C3 = hexcode for RET (duh!)
Sure, we could've have assembled the new instruction but this is faster :-) Ok, task done. Next?

Task 3: Name, Serial and Trackbars

Ok. Notice there's no "Check" button of whatever, which means that the username is being read and maybe even generated the valid serial for and checked as you type it in. Note also that the trackbars are kept disabled until you've entered a name with at least 6 chars. These little things help :-) So what now? First, I would type Sphin as name and any dummy serial. And since GetDlgItemTextA or whatever doesn't work (if NOT always) in Delphi apps (yep, it's in Delphi :-) we've to breakpoint hmemcpy instead. Yeah, it would work in Win9x. After that, goto the name field and since I'll register it using my nick (of course), I'd hit x. Then break! Now F12 a couple of times unti you're in a good spot.


Hey! If you haven't noticed it, there's an easter egg included which tells you your valid serials for every name! But I haven't been able to figure out how to make it appear without faking the relevant memlocs :-) But, I admit, it helped me easily figure out where and what to sniff :-)
 cs:00436EB3  MOV     EAX,[EBP-04]          ; eax = name offset
 cs:00436EB6  CALL    004039B8              ; eax = length(name)
 cs:00436EBB  CMP     EAX,06                ; so was it >= 6?
 cs:00436EBE  JGE     00436F0E              ; yes? jump, else...
If Length(Name) < 6, then continue on, disabling the serial field and the trackbars. You'll notice it when you see it. Trust me :-) So if you want to know how trackbars are going to be compared later, simply put a BPM at the second to the last locations, coz the first is for the serial field.

Anyway, if name is valid...
 cs:00436F0E  LEA     EDX,[EBP-04]          ; these just determines the
 cs:00436F11  MOV     EAX,[EBX+000001E4]    ; offset of the string to use
 cs:00436F17  CALL    0041B248              ; (as this is Delphi :)
 cs:00436F1C  MOV     EAX,[EBP-04]          ; eax = name offset
 cs:00436F1F  CALL    004039B8              ; eax = length(name) again
 cs:00436F24  CMP     EAX,06                ; length check again
 cs:00436F27  JL      00436F7C              ; less? jump and east a badflag, else...
 cs:00436F29  MOV     DL,01                 ; dl = 1 (enable flag)
 cs:00436F2B  MOV     EAX,[EBX+000001DC]    ; eax = serial field
 cs:00436F31  CALL    0041B1E0              ; enable it
 cs:00436F36  CALL    00435DC8              ; main calc routine!
Next lines simply enable all of the trackbars. So note that here, when edx = 0, is disable and edx = 1 is enable :-) Ok. Since
I already know how these goes, I'll say that the serial is NOT only name dependant but Harddisk Serial Number dependant also. Why so? coz if you'll trace the calcs, you'll notice that the dword at memloc [0043A99C] stores our HD serial. I recognized it quickly coz I already memorized mine since I've used it when I keygenned a CrackMe before :-) So how to check yours? Simply put a BPM at 43A99C. Notice that if you've already ran the app, the only breaks you'll get will be on the parts where that memloc is read. But we're not after that. So still no hits. So I tried running anew and lo! It breaks! And see where it lands. Yep, you'll know what I mean when you see it :-) And I won't include here the dasms. If you want to
sniff the serials, it won't be a prob. as well as if you want to code a keygen coz it doesn't have complex stuff going on
in there. It's just quite long :-) But since I wanted to keygen (or maybe waste time), I did it. Mail me for it :-)

But first, how to get that stinkin' serial number. We could use function 69h of interrupt 21h. But I'm trying to code in win32 now, so I'll leave this one (for now). Instead, I've got to find out how the hell GetVolumeInformationA works :-)

Btw, I've used cRACKER'S nOTES as my source for API function defintions since I can't download a 7-12M helpfile :-)
And besides, it provides fundamental infos about them. And here's what I got:
BOOL GetVolumeInformation(
      LPCTSTR  lpRootPathName,               // address of root directory of the file system 
      LPTSTR  lpVolumeNameBuffer,            // address of name of the volume 
      DWORD  nVolumeNameSize,                // length of lpVolumeNameBuffer 
      LPDWORD  lpVolumeSerialNumber,         // address of volume serial number 
      LPDWORD  lpMaximumComponentLength,     // address of system's maximum filename length
      LPDWORD  lpFileSystemFlags,            // address of file system flags 
      LPTSTR  lpFileSystemNameBuffer,        // address of name of file system 
      DWORD  nFileSystemNameSize             // length of lpFileSystemNameBuffer 
   );
So it returns a boolean. But we're not concern with that. We're only after that lpVolumeSerialNumber. So we should declare a dword in our DATA section that would store it. Oh yeah, I've declared the path for lpRootPathName to C:.
So declare the whole thing in TASM like this:

In the .DATA section:
 szPath       db "C:\",0
 szSerial     dd 0
And somewhere in the .CODE section (I opted at startup):
 call GetVolumeInformationA, offset szPath, 0, 0, offset szSerial, 0, 0, 0, 0
After calling it, szSerial will hold the serial number. Nice eh? Yeah. Anyway, here's mine (on my machine only):
 Name: Sphinx
 Serial: 280261256
 Trackbars: 26176
So task done. Next?

Task 4: Keyfile

Ok. This is a simple keyfile-check. Since it's a keyfile we're after, we've to breakpoint CreateFileA at startup coz there's
also no check button or something that would verify it whilst the app is running. Anyway, the keyfile name should be lockey.key. So I created a dummy and filled it with some bytes. So to automate this tedious (?) task, I've set up a
breakpoint like below (of course, there's another way :-)
 bpx CreateFileA  IF (*(EAX)==0x6B636F6C)       ; 0x6B636F6C == 'lock' (dword)
Now this breakpoint will only occur when the keyfile is read. Nice eh? On the first break... Bah! You know the drill right?
So I'll just tell you some things about the keyfile :-) The keyfile should be greater than or equal to 12 bytes long. And the setting of the fileptr where it'll read the necessary bytes are dependent on the on the filesize. Then read some bytes calculating
a somewhat CRC coz afterwards, it compares it with the hard-coded CRC checked two times! If equal, then you cracked it. Else, too bad :-P And here's just a hexdump of the keyfile I created:

53 70 68 69 6E 78 00 00 3F 82 62 5E 00 00 00 00
There. Crack done :-) Whoops! So what now? Hmmm, I tired playing with the app a bit more. Then when I clicked on the group logo, you'll be presented with the CrackMe infos then, as you've probably figure out, the "Ok" button brings us back
to the main program. And if we click on the "x" (exit) button instead to close the dialog, if completely registered (ie. all tasks are completed), we'll be presented with a nice verfication complementing our work. *sigh* :-) else, if either one is not yet cracked, you'll exit immediately.

Some text snippet:

You could have reached this file with or without
discovering the two correct keys so if you
discovered them please write them in the two fields
and you'll be better considered!

Hmmm, we could've reached this file even without discovering the two correct keys? But since we've discovered them,
we'll be better considered :-) Ok. Scrolling down and we see... hey! what's this:

(c) 1999 Lockless production - CRC check: 343151316

Guess what's that "crc" is all about. If you must know, it's just the decimal equivalent of my harddisk serial number.
Why? Of course this is for checking whether or not we really discovered the 2 correct keys. So you can't lie :-)

Dang! All that for nothing? Well, I learned something. And that's the most important part of cracking, IMHO. So cya!

Sphinx [sphinxter@edsamail.com.ph]

Final Notes



Greets to:

TRES2000
Lockless
TORN@DO


When ever there is a door,
there is an entrance.
And behind an entrance can no secret hide,
when a cracker takes his knowledge for a ride
    McCodEMaN



ObDuh

The information in this essay is for educational purpose only!
You are only allow to crack, reverse engineer, modify code and debugg programs that you legaly bought and then for personal use only!!
To ignore this warning is a criminell act and can result in lawful actions!

So please note!
I take no responebility for how you use the information in this essay, i take NO responebility for what might happen to you or your computer!
You use this information on your own risk!!









Essay written by Sphinx ŠTRES2000. All Rights Reserved.