Target Program: W3Filer(32) Version 1.1.3
Protection: Nags
Cracked by: drlan [Me'97/C4N]!
Location: http://www.windows95.com

Tools needed:
- W32Dasm 8.9 (any version will do)
- Hex Editor (I like PSEdit and Hex Workshop)

Thanks niabi for recommending this target.  It's a small download and makes
for a very nice tutorial on building a KeyGenerator.  So, let's get started.

Download the target.  Run it a few times to get a feel for its behavior.  It
pops up a registration nag every time you run it.  The nag displays a serial
number and asks you for a name and registration number.  You'll also get a
nag after each file download.  I assume the (correct) registration number is
built by somehow manipulating the name we enter with the serial number given.
Enter a bogus serial number, click on OK.  Jot down the message.  It should
be something like "Invalid registration ID."  Also, scribble down some of the
text from the registration nag window.  These strings provide valuable clues
when you start poking around in a dead listing of the target.

Ok, now we know the behavior; we have a feel for the protection; and we have
written down some of the anoying messages we receive when we put in the wrong
registration number.  Time to "dead list" the target.  Load up W32Dasm 8.9
and disassemble the main executable (wfiler32.exe).  Save your disassembled
listing so you can load it up into your favorite word processor or good old
list.com.  Once you've loaded it up, have a look around for some of the nag
strings you wrote down.  NOTE: you can do this all inside W32Dasm 8.9, but
I prefer the speed of something a little leaner like list.com.  But anyway,
search for "Invalid registration" and see what you find.  It should drop you
into an area of code that looks like this:

* Reference To: KERNEL32.WriteProfileStringA, Ord:0000h
                        |
:00404DA6 E8C9860000	Call 0040D474
:00404DAB 8D8558FAFFFF  lea eax, dword ptr [ebp+FFFFFA58]
:00404DB1 50            push eax
:00404DB2 8D95A8FAFFFF  lea edx, dword ptr [ebp+FFFFFAA8]
:00404DB8 52            push edx
:00404DB9 E80B550000    call 0040A2C9		; this calls the protection
:00404DBE 83C408        add esp, 00000008
:00404DC1 85C0          test eax, eax		; check results of protection
:00404DC3 7D17          jge 00404DDC		; jump if eax is >= 0 (good)
:00404DC5 6810200000    push 00002010		; else, bitch about invalid...

* Possible StringData Ref from Data Obj ->"Error"
                        |
:00404DCA 6893E94000    push 0040E993

* Possible StringData Ref from Data Obj ->"Invalid registration ID"
                         |
:00404DCF 687BE94000     push 0040E97B
:00404DD4 53             push ebx

* Reference To: USER32.MessageBoxA, Ord:0000h
                         |
:00404DD5 E83C870000     Call 0040D516
:00404DDA EB27           jmp 00404E03

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00404DC3(C)
|
:00404DDC 6840200000     push 00002040		; if the reg code was good,
						; we would have jge'd here
* Possible StringData Ref from Data Obj ->"Info"
                         |
:00404DE1 68BAE94000     push 0040E9BA

* Possible StringData Ref from Data Obj ->"You have successfully registered"
                         |
:00404DE6 6899E94000     push 0040E999
:00404DEB 53             push ebx

* Reference To: USER32.MessageBoxA, Ord:0000h
                         |
:00404DEC E825870000     Call 0040D516

Now look up a few lines prior to the string reference.  See the CALL 0040A2C9,
then the test eax, eax and finally the conditional jump?  Look where that jump
would have landed if eax was greater than or equal to zero!  We could try to
patch things up right here, but I'll save you the trouble.  It won't work.  It
will say registered and essentially will be registered, but only for your
current session.  When you exit and run it again, you are back to being an
unregistered user.  What we really need to do is trace that CALL 0040A2C9 and
see what kind of magic takes place there.

* Referenced by a CALL at Addresses:
|:00404DB9   , :00407F76
|
:0040A2C9 55            push ebp
:0040A2CA 8BEC          mov ebp, esp
:0040A2CC 81C4B0FEFFFF  add esp, FFFFFEB0
:0040A2D2 53            push ebx
:0040A2D3 56            push esi
:0040A2D4 57            push edi
:0040A2D5 8B5508        mov edx, dword ptr [ebp+08]
:0040A2D8 8DB500FFFFFF  lea esi, dword ptr [ebp+FFFFFF00]
:0040A2DE 33C0          xor eax, eax
:0040A2E0 EB16          jmp 0040A2F8

Well, nothing too exciting here, but let's follow on (to the jmp 0040A2F8).

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040A2E0(U), :0040A2F5(U)
|
:0040A2F8 803A00	cmp byte ptr [edx], 00
:0040A2FB 75E5          jne 0040A2E2
:0040A2FD 56            push esi		; push our name onto the stack

* Reference To: USER32.CharUpperA, Ord:0000h
                                  |
:0040A2FE E80F330000    Call 0040D612		; convert name to upper case
:0040A303 56            push esi		; push ucase(name) onto stack

* Reference To: cw3220mt._strlen, Ord:0000h
                                  |
:0040A304 E86F300000    Call 0040D378		; get length of name entered
:0040A309 59            pop ecx
:0040A30A 8BC8          mov ecx, eax
:0040A30C 83F904        cmp ecx, 00000004	; name at least 4 characters?
:0040A30F 7D05          jge 0040A316		; if so, carry on
:0040A311 83C8FF        or eax, FFFFFFFF	; if not, make eax -01
:0040A314 EB67          jmp 0040A37D		; and jmp to RETurn to caller

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040A30F(C)
|
:0040A316 33D2          xor edx, edx		; zero out edx
:0040A318 33C0          xor eax, eax		; zero out eax
:0040A31A 3BC8          cmp ecx, eax		; name length > 0?
:0040A31C 7E17          jle 0040A335

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040A333(C)
|
:0040A31E 0FBE1C06	movsx ebx, byte ptr [esi+eax]	; ASCII value of char.
:0040A322 C1E303        shl ebx, 03			; shift left 3 bits
:0040A325 0FBE3C06      movsx edi, byte ptr [esi+eax]	; ASCII value of char.
:0040A329 0FAFF8        imul edi, eax			; mutiply * array index
:0040A32C 03DF          add ebx, edi			; add ebx + edi
:0040A32E 03D3          add edx, ebx			; edx is accumulator
:0040A330 40            inc eax				; next char. in name
:0040A331 3BC8          cmp ecx, eax			; end of name, yet?
:0040A333 7FE9          jg 0040A31E			; back to the top.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040A31C(C)
|
:0040A335 A120674100	mov eax, dword ptr [00416720]	; serial number given
:0040A33A C1F803        sar eax, 03			; shift left 3 bits
:0040A33D 03D0          add edx, eax			; name magic + serial
:0040A33F 52            push edx

* Possible StringData Ref from Data Obj ->"%lx"
                                  |
:0040A340 685EF54000	push 0040F55E
:0040A345 8D95B0FEFFFF  lea edx, dword ptr [ebp+FFFFFEB0]
:0040A34B 52            push edx

* Reference To: USER32.wsprintfA, Ord:0000h
                                  |
:0040A34C E8E5320000    Call 0040D636
:0040A351 83C40C        add esp, 0000000C
:0040A354 8D8DB0FEFFFF  lea ecx, dword ptr [ebp+FFFFFEB0]
:0040A35A 51            push ecx

* Reference To: USER32.CharLowerA, Ord:0000h
                                  |
:0040A35B E8B8320000    Call 0040D618
:0040A360 8D85B0FEFFFF  lea eax, dword ptr [ebp+FFFFFEB0]
:0040A366 50            push eax		; push the correct code
:0040A367 FF750C        push [ebp+0C]		; push the code entered

* Reference To: cw3220mt._strcmp, Ord:0000h
                                  |
:0040A36A E875300000    Call 0040D3E4		; compare reg code
:0040A36F 83C408        add esp, 00000008	; entered with the
:0040A372 85C0          test eax, eax		; correct reg code
:0040A374 7405          je 0040A37B		; jump if codes matched
:0040A376 83C8FF        or eax, FFFFFFFF	; make eax -01 (for RETurn)
:0040A379 EB02          jmp 0040A37D		; jump over "zero" eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040A374(C)
|
:0040A37B 33C0          xor eax, eax		; make eax 0 (for RETurn)

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040A314(U), :0040A379(U)
|
:0040A37D 5F            pop edi			; clearing off the stack
:0040A37E 5E            pop esi
:0040A37F 5B            pop ebx
:0040A380 8BE5          mov esp, ebp
:0040A382 5D            pop ebp
:0040A383 C3            ret			; RETurn to caller

Ok, we've exposed the routine that caluculates the correct registration code.
Armed with this information and a compiler of choice, we could set out to code
a KeyGenerator.  We'll get there, but first.  Have a close look at these two
lines of code.

:0040A366 50            push eax		; push the correct code
:0040A367 FF750C        push [ebp+0C]		; push the code entered

Here lies all the makings for a nice little crack.  This is where it pushes
the correct code and the code we entered onto the stack in preparation for
a call to cw3220mt._strcmp, which compares them to see if they're the same.

We could simply change this into:

:0040A366 50            push eax		; push the correct code
:0040A367 50		push eax		; push the correct code
:0040A368 40		inc eax			; take up some space
:0040A369 48		dec eax			; take up more space

Now we have it pushing the correct code twice, no matter what we entered.
This should have a desireable result! :-)  We had to add the inc eax, dec eax
to fill the remaining two bytes of code space, because the old instruction
was 3 bytes long, and our new push eax was only 1.  So we pad it with two
instructions that essentially do nothing.  Yes, I know, we could have also
used two NOPs (No OPerations) here, but I like following the teachings of the
Master (+ORC).  Some programs "sniff out" NOPs and recognize such tampering.

Ok, as promised, here's the C source code for a KeyGenerator for this babe.
Keep in mind, I'm no C programmer.  I tried to keep variable names similar
to the register names where so you could follow the ASM dead list versus my
KeyGenerator.  This should help illustrate how I created it, and how you can
create other KeyGens in the future.  I compiled this with Borland C++ 3.1,
but it should compile under any standard C/C++ compiler.

 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <iostream.h>

 int main(void)
 {
	char name[80], sn[10];
	long serial, regcod;
	int eax, ecx, ebx, edx, edi;

	printf("\n\n");
	printf("W3Filer32 KeyGenerator\n");
	printf("By +drlan [Me'97/C4N]!\n\n");
	printf("Enter serial number given: ");
	serial = atol(gets(sn));
	printf("Enter name to register to: ");
	gets(name);
	ecx = strlen(name);
	edx = 0;

	for (eax = 0; eax < ecx; eax++) {
		ebx = toupper(name[eax]);
		ebx = ebx << 3;
		edi = toupper(name[eax]);
		edi = edi * eax;
		ebx = ebx + edi;
		edx = edx + ebx;
	}

	serial = serial >> 3;
	regcod = edx + serial;
	cout.setf(ios::hex);
	cout << "Your registration code is: " << regcod << "\n\n";

	return 0;
}

That's it for this lesson.  Hope this was fun and instructional.

Disclaimer: THIS ESSAY IS FOR EDUCATIONAL PURPOSES ONLY.  ANY USE, MIS-USE
OR ILLEGAL ACTIVITY IS THE SOLE RESPONSIBILITY OF THE READER.