
Lockless Trial CrackMe
Volume #2 Revision A (yep!)
Written by Sphinx
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 :-)
SoftICE 3.25
TASM 5.0
cRACKER'S nOTES (August 2000 Release)
http://www.lockless.com
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]
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
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.