CRACKING CSE HTML VALIDATOR 3.03
or How To Make The Program Work For Us

by Johnny+X/C3'98 (johnny_x_@hotmail.com)
04.nov.98

TARGET SOURCE
http://www.htmlvalidator.com/


TOOLS USED
SoftIce 3.24
W32Dasm 8.93 (not strictly necessary)
HIEW 5.92 (or any hex editor)
PCOM 2.7 (only needed to produce a patch)


INTRODUCTION
Although Sandman has already written a tutorial on cracking this
program, I thought I'd approach it from a different perspective, this
time looking at a way of making the program into a keygen.


THE CRACK
The first part is to find and identify the protection system. It uses a
simple name/serial pair, nothing too difficult really. To find where it
is located in the code, set a breakpoint on GetDlgItemTextA and enter
any name/serial pair (I use Johnny+X/C3'98! and 1223334444). SoftIce
will break. This is where the program reads in our name. Press Ctrl+D
and SoftIce will instantly break again, this time where the program
reads the serial we entered (I'll call this the "fake" serial). Press
F11 to step out of GetDlgItemTextA and into the routine that called it,
and we'll be at the start of the serial calculation process. Set a
breakpoint on this address (431B93) and clear the one on GetDlgItemTextA
(this saves a little time in future). Now step through the code using
F10, checking each register that changes (if SoftIce isn't configured to
display the register window by default, type wr to toggle it, each
altered register is a different colour. You might also want to display
the data window by typing wd). Eventually you will end up at the
following code:

:00431BFA 8D8DD0F9FFFF            lea ecx, dword ptr [ebp+FFFFF9D0]
:00431C00 51                      push ecx
:00431C01 8D85D0FDFFFF            lea eax, dword ptr [ebp+FFFFFDD0]
:00431C07 50                      push eax
:00431C08 E88FE0FFFF              call 0042FC9C
:00431C0D 83C408                  add esp, 00000008
:00431C10 8D95D0FBFFFF            lea edx, dword ptr [ebp+FFFFFBD0]
:00431C16 52                      push edx
:00431C17 8D8DD0F9FFFF            lea ecx, dword ptr [ebp+FFFFF9D0]
:00431C1D 51                      push ecx
:00431C1E E8C9AA0000              call 0043C6EC
:00431C23 83C408                  add esp, 00000008
:00431C26 85C0                    test eax, eax
:00431C28 7502                    jne 00431C2C

Checking the registers at 431C07 and 431C1D will tell you that
[ebp+FFFFF9D0] contains the calculated serial (I'll call this the "real"
serial), [ebp+FFFFFDD0] contains your name, and [ebp+FFFFFBD0] contains
the fake serial. With a little bit of commenting you can see what this
code does:

:00431BFA lea ecx, dword ptr [ebp+FFFFF9D0] ; ecx = loc. of real serial
:00431C00 push ecx                          ; push real serial to stack
:00431C01 lea eax, dword ptr [ebp+FFFFFDD0] ; eax = location of name
:00431C07 push eax                          ; push name to stack
:00431C08 call 0042FC9C                     ; calculate real serial
:00431C0D add esp, 00000008                 ; stack correction
:00431C10 lea edx, dword ptr [ebp+FFFFFBD0] ; edx = loc. of fake serial
:00431C16 push edx                          ; push fake serial to stack
:00431C17 lea ecx, dword ptr [ebp+FFFFF9D0] ; ecx = loc. of real serial
:00431C1D push ecx                          ; push real serial to stack
:00431C1E call 0043C6EC                     ; compare serials
:00431C23 add esp, 00000008                 ; stack correction
:00431C26 test eax, eax                     ; eax = 0 if serials match
:00431C28 jne 00431C2C                      ; jump if eax <> 0

Now we can crack this in a number of ways:

1) We can just make a serial for our name. If we're cracking this for
our own use (which, of course, we shouldn't be ;) this would be fine,
but there are many people out there who don't like having software
registered to other people.

2) We can create a keygen, but this isn't the point of this tutorial ;)
Besides, the solution I present here is nearly as good.

3) We can reverse the jump immediately following the serial compare
routine (I should really say jumps, since there are a couple to take
care of). Unfortunately, if you try this then you'll soon discover that
although this causes our name and serial to be written to the registry,
it is our *fake* serial that is written, not the real one.

4) We can reverse all the jumps which follow the serial compare routine,
but this is rather inefficient. A better way would be to simply patch
the serial compare routine to return the value we want, which in this
case is 0, but we're going to be a little more sneaky.

There are a number of ways of going about this crack, but this is the
most efficient (i.e. it patches the fewest number of bytes).

Look at the first little bit of code:

:00431BFA lea ecx, dword ptr [ebp+FFFFF9D0] ; ecx = loc. of real serial
:00431C00 push ecx                          ; push real serial to stack
:00431C01 lea eax, dword ptr [ebp+FFFFFDD0] ; eax = location of name
:00431C07 push eax                          ; push name to stack
:00431C08 call 0042FC9C                     ; calculate real serial

Now, what if we were to change it to this?

:00431BFA lea ecx, dword ptr [ebp+FFFFFBD0] ; ecx = loc. of fake serial
:00431C00 push ecx                          ; push real serial to stack
:00431C01 lea eax, dword ptr [ebp+FFFFFDD0] ; eax = location of name
:00431C07 push eax                          ; push name to stack
:00431C08 call 0042FC9C                     ; calculate real serial

Notice how instead of pushing the location of the real serial to the
stack, the location of the fake serial is pushed. What does this mean?
It means that our fake serial will be overwritten with the real one. So
far, so good. Now onto the next bit:

:00431C10 lea edx, dword ptr [ebp+FFFFFBD0] ; edx = loc. of real serial
:00431C16 push edx                          ; push fake serial to stack
:00431C17 lea ecx, dword ptr [ebp+FFFFF9D0] ; ecx = nothing now
:00431C1D push ecx                          ; push real serial to stack
:00431C1E call 0043C6EC                     ; compare serials

(Notice how [ebp+FFFFFBD0] now holds the real serial, and that
[ebp+FFFFF9D0] doesn't hold anything any more, as it is not written to
during the last section)

Patch it to this:

:00431C10 lea edx, dword ptr [ebp+FFFFFBD0] ; edx = loc. of real serial
:00431C16 push edx                          ; push fake serial to stack
:00431C17 lea ecx, dword ptr [ebp+FFFFFBD0] ; ecx = loc. of real serial
:00431C1D push ecx                          ; push real serial to stack
:00431C1E call 0043C6EC                     ; compare serials

What happens now? The real serial is now compared to itself. Obviously
the serial compare routine will decide that the two serials match :) The
real serial will then be written to the registry, meaning we've turned
the program into a keygen :)

The only problem is, even though we are registered, we don't know what
our serial is! (Our serial is replaced with "(private)" if we return to
the serial entry box) We can get round this with creative use of the
message box that pops up to tell us that we're registered. The piece of
code which generates the box is this (edited slightly):

:00431C35 68CB0B4600              push 00460BCB ; Title of message box
:00431C3A 681B0B4600              push 00460B1B ; Contents of message
:00431C3F 53                      push ebx

* Reference To: USER32.MessageBoxA, Ord:0000h
                                  |
:00431C40 E84A6A0100              Call 0044868F ; display message box

Since the value of [ebp+FFFFFBD0] is usually 6BE5A8 (I say usually
because it isn't always, it seems to be 9 times out of 10 though), if we
replace push 00460BCB with push 006BE5A8 then our serial will be
displayed as the title of the message box.


CRACK SUMMARY
We've made the following changes to the program:

431BFA: [ebp+FFFFF9D0] becomes [ebp+FFFFFBD0], meaning that our fake
        serial is overwritten by the real one.

431C17: [ebp+FFFFF9D0] becomes [ebp+FFFFFBD0], meaning that the memory
        location that now holds the real serial is compared against
        itself; this guarantees that it passes the serial comparison
        check.

431C3A: push 00460B1B becomes push 006BE5A8, which causes the title of
        the "Registration Accepted" message box to be replaced with the
        real serial.

The bytes to be patched are:

311FDh:  FB -> F9
3121Ah:  FB -> F9
31236h:  A8E56B -> CB0B46


CREATING A PATCH

Using the excellent PCOM (Private COMpiler), we can easily produce a
patch file. Copy the following to a text file and save it as
hv303-c.src:

-----8< cut here 8<-----

; Patcher for CSE HTML Validator 3.03
;
; First clear the screen, then print info.

$cls
@Patch for CSE HTML Validator 3.03
@Cracked by Johnny+X/C3'98!
@

; now ask if the user wishes to continue

@Press Y to apply patch, any other key to quit.
$y/n
@

; check to make sure htmlval30.exe is in the current directory

@Finding htmlval30.exe...
+htmlva~1.exe
@Found!
@

; check to make sure it's the correct size

@Checking size...
!524288
@Ok!
@

; check the bytes we're going to patch are correct

@Checking bytes...
>0003:11FD
=FB
>0003:121A
=FB
>0003:1236
=CB
=0B
=46
@Ok!
@

; run the dos command copy to create a backup

@Backing up to htmlval3.bak...
$run:copy htmlva~1.exe htmlval3.bak
@

; apply the patch

@Patching file...
>0003:11FD
'F9
>0003:121A
'A8E36B
>0003:12EC
'F9
@Ok!

; close the file and tell the user we're finished

-
@
@[Patch completed - press a key]
$key

-----8< cut here 8<-----

Now just compile it with 'pcom hv303-c' and you're done :)

Happy cracking,
J+X
