Sender: rezident@mail.geocities.com
Subject: my 1998 +HCU entry essay
Hello +ORC,

Firstly, may I thank you for all the terrific tutorials you have written to
date, they have been a massive help to myself and others in learning not only
how to crack, but how to *understand* the target code as well.

Here is my solution to your 1998 +HCU strainer - since you requested that 
submitted solutions be written in a style that newbies can understand, I 
have tried to write in 'tutorial' style, making things as easy to follow as 
possible. I therefore apologise if I may have oversimplified in any area 
for this reason.

By the way, you were right about MS Money 3 demo - it was *very* hard to
find. I eventually found it after a lot of intensive and careful searching
via FIDONet, rather ironic considering it's decline due to the incredible
success of the Internet.

- Part 1 of my solution to the 1998 +HCU strainer:
  The reason for the crack +ORC used for MS Project

- Part 2 of my solution to the 1998 +HCU strainer:
  Cracking MS Money 97 demo

- Part 3 of my solution to the 1998 +HCU strainer:
  Cracking MS Money 3 demo

---------------------------------------------------------------------


Part 1 of my solution to the 1998 +HCU strainer - The reason for the crack
+ORC used for MS Project

NOTE: Since I was unable to locate a copy of MS Project, I'm using only the
code snippets included by +ORC in the strainer...

The reason +ORC used the crack he did for MS Winproject, rather then 'nooping
the alternative location' is as follows:

By setting the 'badflag' location to zero (rather the one, the 'badflag'), we
can be sure that the program will always jump over the protection, allowing
us to use it regardless of the date. If you look closely at the code snippets
included in the strainer by +ORC you will see that the 'badflag' loaction is
checked *before* a call to the beggar-off routine...

:05167FA6 66C745FE0100     mov [ebp-02], 0001  ; set badflag
:05167FAC FF75DC           push [ebp-24]
:05167FAF FF151C5C3C05     Call dword ptr[053C5C1C]
:05167FB5 EB25             jmp 05167FDC        ; jump from here...
:05167FB7 66C745EC0000     mov [ebp-14], 0000
:05167FBD 8D45F4           lea eax, [ebp-0C]
:05167FC0 50               push eax
:05167FC1 E8B1CEEBFF       call 05024E77
:05167FC6 66817DF4C007     cmp [ebp-0C], 07C0
:05167FCC 7208             jb 05167FD6   
:05167FCE 66817DF40108     cmp [ebp-0C], 0801
:05167FD4 7606             jbe 05167FDC  
:05167FD6 66C745FE0100     mov [ebp-02], 0001
:05167FDC 66837DFE00       cmp [ebp-02], 0000  ; to here...
:05167FE1 755F             jne 05168042        ; beggar off if (badflag)!
:05167FE3 66837DF000       cmp [ebp-10], 0000
:05167FE8 750E             jne 05167FF8    
:05167FEA 66837DEC00       cmp [ebp-14], 0000
:05167FEF 7451             je 05168042      
:05167FF1 66837DF000       cmp [ebp-10], 0000
:05167FF6 7407             je 05167FFF      
:05167FF8 66837DEC00       cmp [ebp-14], 0000
:05167FFD 7543             jne 05168042     
:05167FFF 0FBF4DF2         movsx word ptr ecx, [ebp-0E]
:05168003 66833D58192C0501 cmp dword ptr [052C1958], 0001
:0516800B 1BC0             sbb eax, eax
:0516800D 83E05A           and eax, 0000005A
:05168010 83C05A           add eax, 0000005A
:05168013 3BC1             cmp eax, ecx     
:05168015 7C2B             jl 05168042        ; beggar off
:05168017 66837DF21E       cmp [ebp-0E], 001E
:0516801C 7F4A             jg 05168068        ; else good guy...
:0516801E 8D45F4           lea eax, [ebp-0C]


So if we had simply nooped the jump at 05168015, we could not have been
certain that the protection would be circumvented on all occasions, since the
'badflag' location is checked *before* this jump:

:05167FDC 66837DFE00       cmp [ebp-02], 0000  ; is badflag set?
:05167FE1 755F             jne 05168042        ; beggar off if (badflag)


It is also possible that this 'badflag' location may be checked again by
other parts of the program, so cracking the way +ORC is undoubtedly the best
method.


---------------------------------------------------------------------


Part 2 of my solution to the 1998 +HCU strainer - Cracking MS Money 97 demo


Before we start, we need to step back and think about what the protection
does. Firstly, there is a 90-day time limit on MS Money, before it 'expires'.
Secondly, when we get towards the end of this 90-day period, MS Money will
nag us when we leave the program - 'This program will expire in 1 day' etc.
Finally, we are only allowed to enter transaction dates that fall within a
3-month time span from the current date. Oh yes, we also are not allowed to
enter transactions without dates, MS Money insists that we enter a date for
every transaction (so that it may check to see whether the date falls within
the 3-month limit etc).

In the strainer, +ORC starts off the crack for us, telling us to search for
the 'GetFileTime' command in the listing from MS Money. So we do (using a
*fast* word processor or text editor, perhaps something like UltraEdit -
don't even think about trying your new copy of MS Word 97, it can't handle
it. The same goes for most of the new and therefore 'bloated' commercial word
processors), and this is what we find:


* Referenced by a CALL at Addresses:
|:00408FFA   , :00411E74   , :00415149   , :004219EC   , :0042B945   ,
|:0042C0C6   , :00430688   , :0043BD16   , :0043C0BD   , :0043C0E8   ,
|:0043E0DD   , :0043F005   , :004415DC   , :00458FC9   , :00458FDD
|:0046A031   , :0046AE13   , :0046B3CA   , :0046BB13   , :0046BC90   ,
|:0046BE8E   , :0046BF83   , :00470507   , :0047208C   , :00472608   ,
|:00474316   , :0047490A   , :00485B68   , :0048C4E7   , :0048CC84
|:0048EE63   , :00491755   , :0049F6FB   , :004A05BC   , :004A18D7   ,
|:004A190C   , :004A1938   , :004A1A77   , :004B18B5   , :004C8BDB   ,
|:004C943A   , :004F1AC2   , :004F754A   , :004F7722   , :00520D32
|:00522C56   , :0052504A   , :005326DD   , :005327A4   , :0054EE33   ,
|:0054EE4C   , :005524B3   , :005524DA   , :0055416F   , :00554291   ,
|:0055F525   , :0056309E   , :0056CA58   , :00579333   , :0057ACC8
|:0057FE95   , :005914A2   , :0059A17F   , :005A32A4   , :005A3CB3   ,
|:005A58BA   , :005A5DD9   , :005A5E1A   , :005B5F97   , :005B6BC4   ,
|:005B6CC3   , :005B6F55   , :005CC9A3   , :005CF412   , :005D9364
|:005D9388   , :005D9393   , :005DA035   , :005E1285   , :005E140A   ,
|:005E545E
|
:0046A300 83EC14                  sub esp, 00000014
:0046A303 8D442404                lea eax, [esp + 04]
:0046A307 50                      push eax

* Reference To: KERNEL32.GetLocalTime, Ord:00E2h    
                                  |
:0046A308 FF15B0006300     *****  Call dword ptr [006300B0] ; here it is!
:0046A30E 668B442406              mov ax, [esp + 06]  ; load month
:0046A313 6648                    dec ax              ; subtract 1
:0046A315 66C1E005                shl eax, 05         ; multiply by 32
:0046A319 6633442402              xor ax, [esp + 02]  ; next lines stores
result
:0046A31E 6625E001                and ax, 01E0
:0046A322 6631442402              xor [esp + 02], ax      
:0046A327 668B44240A              mov ax, [esp + 0A]  ; load day
:0046A32C 6648                    dec ax              ; subtract 1
:0046A32E 6633442402              xor ax, [esp + 02]  ; next lines add
encoded day to encoded month
:0046A333 66251F00                and ax, 001F
:0046A337 6631442402              xor [esp + 02], ax
:0046A33C 8B442404                mov eax, [esp + 04] ; load year
:0046A340 25FFFF0000              and eax, 0000FFFF       
:0046A345 2D9C070000              sub eax, 0000079C   ; subtract 1948
:0046A34A 83F87F                  cmp eax, 0000007F   ; is year <= 2075?
:0046A34D 7E1F                    jle 0046A36E        ; jump from here if so
:0046A34F B89CFFFFFF              mov eax, FFFFFF9C


Wow! The above routine is called from a lot of places - using a very small
amount of intelligence, it is quite obvious that this is the main routine
used by MS Money to encode and return the date. The above code "first.class" tppabs="http://www.fravia.org/solutions/first.class"
retrieves the current date (using GetLocalTime) and then begins to encode it
into a 4-digit hexadecimal number. This hex 4-digit encoded date format is
used throughout MS Money - so it's probably worth looking at it a little more
closely. The current day and month are first encoded before the year is
checked to see whether it is after 2075. If it is 2075 or before, the date
encoding continues as below:


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0046A34D(C)
|
:0046A36E 668B442404              mov ax, [esp + 04] ; load year
:0046A373 66C1E009                shl eax, 09      
:0046A377 6633442402              xor ax, [esp + 02]
:0046A37C 6625FF01                and ax, 01FF
:0046A380 668B4C2404              mov cx, [esp + 04] ; load year again
:0046A385 6683E91C                sub cx, 001C       ; subtract 28
:0046A389 66C1E109                shl ecx, 09        ; multiply by 512
(discarding MSB if necessary)
:0046A38D 6633C1                  xor ax, cx         ; add to (encoded day +
encoded month)
:0046A390 6689442402              mov [esp + 02], ax  ; store encoded date
:0046A395 83C414                  add esp, 00000014
:0046A398 C3                      ret		      ; return to caller


So, the date encode routine shown above basically uses the following equation
to create the encoded date:

(month - 1) * (2^5 or 32)  +
(day - 1)                  +
(year - 28) * (2^9 or 512, discarding MSB if necessary) = encoded date!

Remember, the 'SHL' instruction is a quick way to perform multiplication by
powers of two, but the *most significant bit* (MSB) is shifted out of the
operand (if necessary) and into the carry flag, and a 0 is shifted into the
*least significant bit* (LSB). For example, 'SHL AX, 1' multiplies AX by 2
(eg 2^1), shifting out the MSB if necessary and 'SHL AX, 2' multiplies AX by
4 (eg 2^2), shifting out the MSB if necessary. BTW, the 'SHR' instruction is
a quick way of doing unsigned division by powers of two - I suggest you get a
good ASM book or tutorial if you want a better and more detailed explanation.

Now let's try it, we'll encode Christmas Day 1997 (25/12/1997) and see what
we get:

Year:

0x7CD (1997) - 0x1C (28) * 0x200 (512) = 0x6200 or 25088 (remember to discard
the MSB, as we only want a WORD sized value)

Month:

0xC (12) - 0x1 (1) * 0x20 (32) = 0x160 or 352

Day:

0x19 (25) - 0x1 (1) = 0x18 or 24


Now add them together:

0x6200 (25088) + 0x160 (352) + 0x18 (24) = 0x6378 or 25464

So 0x6378 (or 25464 in decimal) should, according to our equation, be
Christmas Day 1997, but can we be sure we have it right?

Hmm...let's make a little program to decode the encoded date format (this
will also be useful later on). The below code was written using Borland C
4.52 but should compile fine with any Borland C/C++ compiler (eg Turbo C). If
you use a Micro$oft compiler, tough! ;-)


/* ----- Start listing of decode.c ----- */

/* This program DOES NOT check for incorrect input! */
/* Remember to use the DECIMAL rather than the hex value */

#include
#include
#include

void main()
{

	int date, day, month, year;
	char buf[6];
	char *monthname[] = {"January", "February", "March", "April", "May",
        "June", "July", "August", "September", "October", "November", 
        "December"};

	printf("Enter (decimal) encoded date: ");
	fgets(buf, 6, stdin);
	if(strlen(buf) < 5)
		{
		printf("Enter 5 digits");
		exit(0);
		}
	date = atoi(buf);

	asm {
		mov ax, date;	/* decode month */
		and ax, 0x1E0;
		shr ax, 5;
		mov month, ax;

		mov ax, date;	/* decode day */
		and ax, 0x1F;
		inc ax;
		mov day, ax;

		mov ax, date;	/* decode year */
		shr ax, 9;
		add ax, 1948;
		mov year, ax;
		}

	printf("Date is %d %s %d", day, monthname[month], year);
}

/* ----- End listing of decode.c ----- */


Now run the above decode.c program and enter 25464 - what happens? Yes, it
returns 25 December 1997! So we now understand quite well how this encoded
date format used by MS Money works...

OK, so we are pretty sure that this routine is 'near' to where we need to
crack, but now we need to find out where the nag screen telling us that MS
Money has expired is called from. For this task we need to load SoftICE,
first setting a breakpoint on the beginning of the above date encode routine.
Load MS Money with the Loader32 utility. When SoftICE snaps at the beginning
of the MS Money program code, set the breakpoint:

Ctrl-D			; enter SoftICE
bpx 0046A300    	; set the breakpoint
Ctrl-D			; leave SoftICE

*** NOTE ***
You may find that MS Money will not run properly when loaded with the
Loader32 utility (I'm not sure why this is, anti-debugger code or perhaps a
display driver problem?). If this is the case, just use Loader32 to start MS
Money and set the breakpoint - then exit from Loader32 and close MS Money
(Ctrl-Alt-Delete...End Task etc). You can then run MS Money without the
Loader32 utility and it will snap fine.
*** END NOTE ***

Set the system date a few months in the future (so MS Money will display the
expiration nag) and run MS Money. We need to locate the *last* time that the
above routine is called before the nag is displayed. You'll find that this
routine is called just twice before the expiry nag screen pops up. Run MS
Money again, and when SoftICE snaps for the second time press 'F12' or type
'P RET'. This returns us to the code that called the routine, listed below:


:00472608 E8F37CFFFF              call 0046A300 ; calls the date encode
routine
:0047260D 668944246C              mov [esp + 6C], ax       ; store encoded
date
:00472612 6689842478010000        mov [esp + 00000178], ax ; store it again
:0047261A 668B442414       *****  mov ax, [esp + 14] ; here!
:0047261F 662500FE                and ax, FE00		
:00472623 663D0060                cmp ax, 6000	     ; checking	
:00472627 0F8446020000            je 00472873	     ; carry on...	
:0047262D 663D0062                cmp ax, 6200       ; carry on...	
:00472631 0F843C020000            je 00472873	     ; checking	
:00472637 663D0064                cmp ax, 6400	     ; carry on...	
:0047263B 0F8432020000            je 00472873         
:00472641 8D442414                lea eax, [esp + 14]
:00472645 6A01                    push 00000001
:00472647 50                      push eax
:00472648 E803FAFFFF              call 00472050
:0047264D 83C408                  add esp, 00000008
:00472650 85C0                    test eax, eax
:00472652 0F85A2010000            jne 004727FA
:00472658 66B8E407                mov ax, 07E4
:0047265C 5D                      pop ebp
:0047265D 5F                      pop edi
:0047265E 5E                      pop esi
:0047265F 5B                      pop ebx
:00472660 81C468020000            add esp, 00000268
:00472666 C3                      ret


Take a look at the value loaded into AX at line 0047261A. It looks just like
an MS Money encoded date doesn't it? Let's see if it is...set your clock back
to a working date. Run MS Money again, breaking at 0047261A ('bpx 0047261A').
Now convert the hex encoded date in AX to decimal ('? EAX') and enter the
result into the decode program we made in C previously. What does it say?
Make a note of the date and then run MS Money - select 'Help...About' from
the menu and what do you see? The expiry date given is the same as the date
loaded into AX! What the program is actually doing here is checking to see
firstly whether an *expiry* date has been calculated, and secondly whether
the *expiry* date is in 1996, 1997 or 1998. If the *expiry* date is in one of
these three years, everything is fine and MS Money jumps on the the next
routine (see below). If the *expiry* date is *not* in 1996, 1997 or 1998, MS
Money retrieves the *current* date again. If the current date is *not* 1996
or 1997 you get an error message saying 'The year must be 1996 or 1998 to run
MS Money'. So if you install MS Money 1997 when your system date is set to,
say, 1999, MS Money will not run. For that reason we should probably crack
this check as well (I'll look more at this protection later, but for now
let's just crack it quickly).

To crack this check simply replace these lines:

:0047261F 662500FE                and ax, FE00		
:00472623 663D0060                cmp ax, 6000	  ; is AX 1996?   	
:00472627 0F8446020000            je 00472873     ; jump if yes...

with these:

:0047261F 66250000                and ax, 0000	; set AX to 0	
:00472623 663D0000                cmp ax, 0000	; compare AX with 0	
:00472627 0F8446020000            je 00472873   ; jump if AX == 0 (eg
always!)

Now AX will always be set to zero, so MS Money will always jump, regardless
of the expiry date! Let's hex edit these changes into msmoney.exe to make
them permanent...

search for:   662500FE663D00600F84
replace with: 66250000663D00000F84


OK, now set your clock forward again (past the expiry date) and step over the
above code till you reach the following:


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00472627(C), :00472631(C), :0047263B(C)
|
:00472873 8B44246C                mov eax, [esp + 6C]
:00472877 6A5A                    push 0000005A    ; aha! 0x5A == 90!
:00472879 50                      push eax	   ; can't you 'feel' it?
:0047287A E8217BFFFF              call 0046A3A0	   ; what happens here?
:0047287F 83C408                  add esp, 00000008  
:00472882 6639442414              cmp [esp + 14], ax ; check if current date
is previous to install date...
:00472887 768E                    jbe 00472817     ; go onto the next jump!
:00472889 6A00                    push 00000000	   ; otherwise display
message -
:0047288B 6A00                    push 00000000    ; 'current date previous
to install date'	
:0047288D 6A00                    push 00000000
:0047288F 6A00                    push 00000000
:00472891 6A00                    push 00000000
:00472893 680F090000              push 0000090F
:00472898 E883330000              call 00475C20
:0047289D 66B8E407                mov ax, 07E4
:004728A1 83C418                  add esp, 00000018
:004728A4 5D                      pop ebp
:004728A5 5F                      pop edi
:004728A6 5E                      pop esi
:004728A7 5B                      pop ebx
:004728A8 81C468020000            add esp, 00000268
:004728AE C3                      ret


Hehe! Look at that - 0x5A (90 in decimal! Can you 'feel' it?) is pushed and
then a routine is called, followed by a JBE. This looks interesting...the
value stored in [esp + 14] is today's date (look at it with the decode
program). The value in AX has been computed in the previous call. Here MS
Money is checking to see whether the current date is previous to the install
date - if it is, a message telling you so appears and you are kicked out of
the the program. So this will need to be cracked, since we want to be able to
set the system date to whatever we want and still have the program run. To
crack this, simply replace:

:00472887 768E                    jbe 00472817 ; jump if below or equal...

with:

:00472887 EB8E                    jmp 00472817 ; jump always!


Remember to hex edit msmoney.exe to make the changes permanent:

search for:   6639442414768E
replace with: 6639442414EB8E


Now continue tracing until the following code is reached:


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00472887(C)
|
:00472817 668B442414              mov ax, [esp + 14] ; load expiry date
:0047281C 6639842478010000  ***** cmp [esp + 00000178], ax  ; compare with
current date
:00472824 721C              ***** jb 00472842	; Aha! jmp nice guy!
:00472826 6A00                    push 00000000 ; else beggar-off
:00472828 A1D0306200              mov eax, [006230D0]
:0047282D 6A00                    push 00000000
:0047282F 50                      push eax
:00472830 68003F4D00              push 004D3F00
:00472835 685F2F0000              push 00002F5F
:0047283A E8D1B0FFFF              call 0046D910
:0047283F 83C414                  add esp, 00000014


Now look! First the expiry date is loaded into AX, then it is compared with
[esp + 00000178] - what do you think is stored in [esp + 00000178]? The
current date! So if (current date < expiry date) MS Money loads with no nag.
If (current date >=  expiry date) the nag pops up. So cracking this should be
fairly straightforward. Simply replace:

:00472824 721C                    jb 00472842	; jump if below...

with:

:00472824 EB1C                    jmp 00472842	; jump always!


Well, that was easy enough wasn't it? All you have to do is hex edit
msmoney.exe to make the changes permanent.

search for:   842478010000721C
replace with: 842478010000EB1C


OK, we've cracked the first part of the protection scheme, the expiry nag
screen at the beginning of the program. Now we need to stop the nag screen
that pops up when you exit MS Money, telling you how many days you have
before the program expires. Let's think a little about how how this nag might
work, perhaps apply a little 'Zen', if we may. As +ORC teaches, it's very
important to *think* about and *feel* what the program is doing. Probably MS
Money compares the expiry date with the current date - if there are only a
few days till the expiry date, the nag is displayed. So we can use the date
encode routine as our entry-point, since MS Money will use it to retrieve the
current date. So set the system date to the day before the expiry date, and
then set a breakpoint on the first line of the date encode routine (as we did
before):

bpx 0046A300

When you exit from MS Money SoftICE will snap twice before displaying the
nag. The second time SoftICE snaps press 'F12' ('P RET') to return to the
calling code, listed below:


:00470507 E8F49DFFFF              call 0046A300 ; get the current date
(encoded)
:0047050C 668945EC                mov [ebp-14], ax   ; store date
:00470510 66A164866200            mov ax, [00628664] ; load expiry date
:00470516 668945EA                mov [ebp-16], ax   ; store expiry date
:0047051A 663945EC                cmp [ebp-14], ax   ; compare
:0047051E A1DCFC6100              mov eax, [0061FCDC]
:00470523 735D                    jnb 00470582 ; jump if current date >=
expiry date!
:00470525 8B45EA                  mov eax, [ebp-16] ; else carry on...
:00470528 50                      push eax
:00470529 E852A2FFFF              call 0046A780
:0047052E 83C404                  add esp, 00000004
:00470531 668BF0                  mov si, ax
:00470534 8B45EC                  mov eax, [ebp-14]
:00470537 50                      push eax
:00470538 E843A2FFFF              call 0046A780
:0047053D 83C404                  add esp, 00000004
:00470540 662BF0                  sub si, ax
:00470543 6683FE0F                cmp si, 000F
:00470547 A1DCFC6100              mov eax, [0061FCDC]
:0047054C 7F34                    jg 00470582
:0047054E 0FBFC6                  movsx word ptr eax, esi
:00470551 50                      push eax

* Possible StringData Ref from Data Obj ->"%d"
                                  |
:00470552 6828FA6100              push 0061FA28
:00470557 8D45E0                  lea eax, [ebp-20]
:0047055A 50                      push eax

* Reference To: USER32.wsprintfA, Ord:0249h
                                  |
:0047055B FF15080A6300            Call dword ptr [00630A08]
:00470561 83C40C                  add esp, 0000000C
:00470564 6A00                    push 00000000
:00470566 6A00                    push 00000000
:00470568 6A00                    push 00000000
:0047056A 6A00                    push 00000000
:0047056C 8D45E0                  lea eax, [ebp-20]
:0047056F 50                      push eax
:00470570 680C090000              push 0000090C
:00470575 E8A6560000              call 00475C20 ; and display the nag!


Well, that looks simple enough doesn't it? MS Money retrieves the current
date and compares it with the expiry date. If the expiry date has *passed*
(eg if MS Money has *already* expired) NO nag is shown! Hehe, no problems
cracking this - simply replace

:00470523 735D                    jnb 00470582 ; jump if <=

with

:00470523 EB5D                    jmp 00470582 ; jump always!


Now MS Money will fly over the nag routine every time...no more nag at the
end of the program. Hex edit msmoney.exe to make the changes permanent:

search for:   A1DCFC6100735D
replace with: A1DCFC6100EB5D


Well, that takes care of the nags in MS Money, now we need to turn our
attention to the 3-month transaction limit. This part of the crack is
somewhat more challenging than the really very simple tasks we've already
completed. Let us think a little about the behaviour of this protection and
it's visible manifestations. Go into MS Money and create a new account (in
Account Manager), if you haven't already. Now open the account and enter a
few transactions, perhaps write a few cheques (pay those bills ;-)). You'll
see that if you enter a date *within* the allowed 3-month time span
everything is fine. However, if you try to enter a date that is *outside*
this time limit you get a nag saying 'The entered date is outside the 90-day
range' etc. If you try to enter a transaction with *no* date you get another
nag - this time it says 'This trial edition of MS Money requires that you
input a valid date. The full version of MS Money accepts transactions without
dates'. What a bore, we'll have to crack this too. OK, go into MS Money and
into the account manager. Enter a new cheque transaction, but before pushing
the 'Enter' button (or pressing return) enter SoftICE ('Ctrl-D') and type the
following:

BPX USER!GetWindowText IF(BPCOUNT==05)

This will cause SoftICE to snap every *five* times that the GetWindowText
routine is called. Now exit from SoftICE ('Ctrl-D' again) and press return.
SoftICE will snap (on the fifth occurrence of GetWindowText) and set BPCOUNT
to zero (Read the SoftICE documentation for more info, as +ORC suggests. It's
available from the Numega web site - www.numega.com - in PDF format).
You'll see that you're in USER32.DLL code",.class" tppabs="http://www.Fravia.org/solutions/,.class" so press 'F12' ('P RET') three
times to get back to the MS Money code that called GetWindowText for the
fifth time. This is what you should see:


:005C2DE5 E896D8FFFF              call 005C0680 ; makes the call to
GetWindowText
:005C2DEA 85C0                    test eax, eax
:005C2DEC 7431                    je 005C2E1F
:005C2DEE 8B4528                  mov eax, [ebp+28]
:005C2DF1 8D8D34FEFFFF            lea ecx, [ebp+FFFFFE34]
:005C2DF7 40                      inc eax
:005C2DF8 50                      push eax
:005C2DF9 E8D244E4FF              call 004072D0
:005C2DFE 66C1E008                shl eax, 08
:005C2E02 8B4D0C                  mov ecx, [ebp+0C]
:005C2E05 668B4904                mov cx, [ecx+04]
:005C2E09 6633C1                  xor ax, cx
:005C2E0C 6625003F                and ax, 3F00
:005C2E10 6633C1                  xor ax, cx
:005C2E13 8B4D0C                  mov ecx, [ebp+0C]
:005C2E16 66894104                mov [ecx+04], ax
:005C2E1A E96F0B0000              jmp 005C398E     ; jumps here...


This is followed my a few lines of unimportant (for us) code, just step
('F10') over it until you see the following:


:005C2C63 8B4DEC                  mov ecx, [ebp-14]
:005C2C66 51                      push ecx
:005C2C67 E8A4DAFFFF              call 005C0710
:005C2C6C 83C404                  add esp, 00000004
:005C2C6F 85C0                    test eax, eax
:005C2C71 0F85170D0000            jne 005C398E
:005C2C77 837D3000                cmp [ebp+30], 00000000
:005C2C7B 7421                    je 005C2C9E   ; go ahead nice buyer (if
[ebp+30] == 0)
:005C2C7D 8B4D3C                  mov ecx, [ebp+3C]
:005C2C80 668B81B5010000          mov ax, [ecx+000001B5] ; load date entered
:005C2C87 663DFFFF                cmp ax, FFFF ; was no date entered?
:005C2C8B 0F842F0D0000            je 005C39C0  ; beggar off if no date
entered
:005C2C91 66390564866200          cmp [00628664], ax ; compare with expiry
date
:005C2C98 0F863B0D0000            jbe 005C39D9 ; beggar off if expiry date <=
date entered

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:005C2C7B(C)
|
:005C2C9E 837D1C00                cmp [ebp+1C], 00000000 ; carry on good
guy...
:005C2CA2 6A00                    push 00000000
:005C2CA4 741E                    je 005C2CC4
:005C2CA6 8D45F2                  lea eax, [ebp-0E]
:005C2CA9 8B4D3C                  mov ecx, [ebp+3C]
:005C2CAC 50                      push eax
:005C2CAD E83ED9FFFF              call 005C05F0
:005C2CB2 668B00                  mov ax, [eax]
:005C2CB5 8B4D3C                  mov ecx, [ebp+3C]
:005C2CB8 6689819A000000          mov [ecx+0000009A], ax
:005C2CBF E9CA0C0000              jmp 005C398E


OK, that looks easy enough to crack. Let's replace the first check:

:005C2C8B 0F842F0D0000            je 005C39C0  ; beggar off if no date
entered

with:

:005C2CB 6650                     push ax      ; save ax on stack
:005C2CD 6640                     inc ax       ; increment ax
:005C2CF 6658                     pop ax       ; restore ax

and replace the second check:

:005C2C98 0F863B0D0000            jbe 005C39D9 ; beggar off if expiry date <=
date entered

with the same:

:005C2C98 6650                     push ax      ; save ax on stack
:005C2C9A 6640                     inc ax       ; increment ax
:005C2C9C 6658                     pop ax       ; restore ax


Alternatively, instead of the two above changes, we could just change:

:005C2C77 837D3000                cmp [ebp+30], 00000000
:005C2C7B 7421                    je 005C2C9E   ; go ahead nice buyer (if
[ebp+30] == 0)

to:

:005C2C77 837D3000                cmp [ebp+30], 00000000
:005C2C7B EB21                    jmp 005C2C9E   ; go ahead always!


Since this is the routine used throughout *all* of MS Money to check the
input date, this crack will allow you to enter *any* date *anywhere* in any
MS Money transaction! You'll no longer get any silly messages telling you
that the date is outside the allowed range, and you are no longer obliged to
enter a date at all - you may leave it blank if you like. Try it...it works.

Hex edit msmoney.exe yet again to make the changes permanent:

search for:   837D30007421
replace with: 837D3000EB21


Good, we've thoroughly cracked MS Money 97 - now we can at last delete the
Micro$oft abomination from our hard drive! :-)

Here's a list of the changes you'll need to hex edit into msmoney.exe to
crack MS Money 97 permanently (so you can see how it works before deleting it
ASAP :-))

1) search for:   662500FE663D00600F84
   replace with: 66250000663D00000F84

2) search for:   6639442414768E
   replace with: 6639442414EB8E

3) search for:   842478010000721C
   replace with: 842478010000EB1C

4) search for:   A1DCFC6100735D
   replace with: A1DCFC6100EB5D

5) search for:   837D30007421
   replace with: 837D3000EB21


---------------------------------------------------------------------


Part 3 of my solution to the 1998 +HCU strainer - Cracking MS Money 3 demo


To finish the crack for MS Money 3 demo that +ORC already started for us in
the strainer, I am going to use a different approach. Most of the MS Money 97
demo crack was done using SoftICE, aka the 'live' approach. However, now I'll
make greater use of the 'dead listing' approach - using the disassembled
listing of MS Money demo4 (mnydemo.exe) made with a Windoze disassembler such
as WCB (Windows Code Back, for Windows 3.X) or better yet, W32Dasm (latest
version 8.9, I think). The listing of mnydemo.exe is pretty sizeable (about
17MB's!), but at least a little more manageable than the MS Money 97 listing,
which is nearly 40MB! We'll use this listing to trace *backwards* through the
MS Money code - OK, make your disassembled listing and get ready.

+ORC has already found for us the (conditional) jump which decides whether or
not to display the expiry nag screen (see the strainer for the excellent
method +ORC teaches to find this location). What we are going to do is work
*backwards* from this point to finish the crack, using our disassembled
listing. Here is the jump +ORC shows us:


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.1808(C)
|
:0008.17CA 8B46F4                 mov ax, [bp-0C] ; load expiry date
:0008.17CD 3946F0                 cmp [bp-10], ax ; compare expiry date with
current date
:0008.17D0 7246                   jb 1818	; jump if (current date <
expiry date)

* Possible Reference to Dialog: DialogID_0494
                                  |
:0008.17D2 689404                 push 0494 ; else load expiry nag
:0008.17D5 685505                 push SEG ADDR of Segment 0028
:0008.17D8 684C15                 push 154C
:0008.17DB FF362C0A               push word ptr [0A2C]
:0008.17DF 6A00                   push 0000
:0008.17E1 6A00                   push 0000
:0008.17E3 6A00                   push 0000
:0008.17E5 9A34019911             call 0005.0134 ; show expiry nag
:0008.17EA EB68                   jmp 1854       ; then carry on...


Well, what do you suppose the values MS Money compares here are? Using just
the smallest amount of Zen (if we may), we can assume that these values being
compared are probably the current date and the install date. Set a breakpoint
on the first line of this routine in SoftICE and run MS Money to see if we're
right - of course we are! And look at that date format, doesn't it look
familiar? It should do (use the decode C program we made earlier), it is
*exactly* the same date format used in the MS Money 97 demo which we looked
at previously (I know, I'm doing this backwards, I should probably do this
version first and then go on to the 1997 version. The reason I'm doing it
like this is because I cracked MS Money 97 first, since it was easier to
find. MS Money 3 demo took *a lot* of intensive searching to locate - I
didn't download it from Fravia's page - +ORC was of course right about it
being difficult to find. Incidently, I didn't find MS Money 3 demo on the
Internet at all. Rather ironically, I found it via FIDOnet, thanks to a
helpful BBS sysop who was susceptible a little basic 'social engineering'
;-)). In fact, as you'll see, MS Money 3 uses nearly exactly the same
protection scheme as MS Money 97 - or rather, MS Money 97 uses the same
scheme as MS Money 3 :-). This is undoubtedly the reason why +ORC so rightly
stresses the importance of 'software history' - so often the protection
scheme used by a program is *identical* to the schemes used by earlier
versions of the same program.


Back to the crack - this is easy to patch, simply replace:

:0008.17D0 7246                   jb 1818 ; jump if below

with:

:0008.17D0 EB46                   jmp 1818 ; jump always!


Now MS Money will always fly over the nag. But wait a minute, before you get
out the hex editor and start changing things, let's investigate a little
further. As you can see, the above routine is called from just one location
via a *conditional* jump:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.1808(C)

Search through your listing for the caller, '0008.1808' and what do we find:


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.16A5(U), :0008.16AD(U), :0008.16B5(U)
|
:0008.17EC FF76F0                 push word ptr [bp-10]
:0008.17EF 6A3C                   push 003C       ; 0x3C == 60 in decimal!
:0008.17F1 8D46FC                 lea ax, [bp-04]
:0008.17F4 50                     push ax
:0008.17F5 9AD8051C18             call 0079.05D8
:0008.17FA 8BD8                   mov bx, ax
:0008.17FC 368B07                 mov ax, ss:[bx] ; load (current date + 60
days)
:0008.17FF 8946FE                 mov [bp-02], ax ; store (current date + 60
days)
:0008.1802 8B46F4                 mov ax, [bp-0C] ; load expiry date
:0008.1805 3946FE                 cmp [bp-02], ax ; compare expiry date with
(current date + 60 days)
:0008.1808 73C0                   jnb 17CA        ; jump if ((current date +
60 days) is >= expiry date)
:0008.180A 681809                 push 0918
:0008.180D 6A00                   push 0000
:0008.180F 9A64975218             call 0063.9764 ; else display 'current date
is previous to install date' nag
:0008.1814 E9D8FD                 jmp 15EF


Well, well, are you feeling a little 'deja vu'? This is just like MS Money
97! Use SoftICE to investigate the values being compared here, if you really
need to. Use the date decode program we made in C previously and you'll find
out exacly which dates are being compared where. Just as in MS Money 97, this
part of the code is checking to see whether the current date is *previous* to
the install date. If it is, you see a nasty nag telling you so and are not
allowed to use the program.

Again, to crack this part of the protection and remove the check is simple.
Replace:

:0008.1808 73C0                   jnb 17CA        ; jump if ((current date +
60 days) is >= expiry date)

with:

:0008.1808 EBC0                   jmp 17CA        ; jump always!


Once again, before making these changes permanent, continue on with the
investigation. We can see that this routine is called from *three* locations
via unconditional jumps, but all the callers are just a few instructions
apart - 0008.16A5, 0008.16AD and 0008.16B5, as show below:

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.16A5(U), :0008.16AD(U), :0008.16B5(U)

Hmm...a jump tree perhaps? Let's look and find out. Search your listing for
'0008.16A5' and you'll find the following snippet of code:


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.1679(C)
|
:0008.167E 8B842402               mov ax, [si+0224] ; load expiry date
:0008.1682 8946F4                 mov [bp-0C], ax   ; store expiry date
:0008.1685 8D46FE                 lea ax, [bp-02]
:0008.1688 50                     push ax
:0008.1689 9A5205F817             call 0079.0552 ; get current date
:0008.168E 8BD8                   mov bx, ax
:0008.1690 368B07                 mov ax, ss:[bx] ; load current date
:0008.1693 8946F0                 mov [bp-10], ax ; store current date
:0008.1696 8A66F5                 mov ah, [bp-0B] ; load *year* of expiry
date
:0008.1699 2500FE                 and ax, FE00  ; looks familiar eh? :-)
:0008.169C 89868CFE               mov [bp-0174], ax
:0008.16A0 3D005C                 cmp ax, 5C00  ; is year 1994?
:0008.16A3 7503                   jne 16A8      ; go on to next check
:0008.16A5 E94401                 jmp 17EC      ; else go on nice guy!



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.16A3(C)
|
:0008.16A8 3D005E                 cmp ax, 5E00 ; is year 1995?
:0008.16AB 7503                   jne 16B0     ; go on to next check
:0008.16AD E93C01                 jmp 17EC     ; else go on nice guy!



* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.16AB(C)
|
:0008.16B0 3D0060                 cmp ax, 6000 ; is year 1996?
:0008.16B3 7503                   jne 16B8     ; beggar off...
:0008.16B5 E93401                 jmp 17EC     ; else go on nice guy!


Once again, this code should look familiar - it is almost a mirror image of
the code in MS Money 97. Here the program is checking to see whether the year
of the *install* date is 1994, 1995, or 1996 (in MS Money 97 it checked for
the years 1996, 1997 and 1998). If the *install* date falls in one of these
three years everything is fine and we jump to the routine we were
investigating prior to this one (remember, we're working our way *backwards*
through the code). If, however, the install year is *not* 1994, 1995 or 1996,
we get a nag saying 'The year must be 1994 or 1995 to run MS Money 3 demo'.
Again, cracking this check is really very simple, just replace:

:0008.1699 2500FE                 and ax, FE00
:0008.169C 89868CFE               mov [bp-0174], ax
:0008.16A0 3D005C                 cmp ax, 5C00  ; is year 1994?
:0008.16A3 7503                   jne 16A8      ; go on to next check
:0008.16A5 E94401                 jmp 17EC      ; else go on nice guy!

with:

:0008.1699 250000                 and ax, 0000  ; set AX to 0
:0008.169C 89868CFE               mov [bp-0174], ax
:0008.16A0 3D0000                 cmp ax, 0000  ; is AX == 0?
:0008.16A3 7503                   jne 16A8      ; go on to next check if
*not* 0 (eg never!)
:0008.16A5 E94401                 jmp 17EC      ; else go on nice guy!


Once again, don't hex edit mnydemo.exe to make the changes permanent just
yet...let's continue our investigation...as the listing shows us, this
routine is called by a *conditional* jump from just *one* location. In other
words, the *entire* protection scheme we've just been looking at is called
from *one conditional* jump!

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.1679(C)
  ^^^^^^^^^

Well, this looks very interesting! Search your listing for the single caller,
'0008.1679', and just look at what we find:


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.175A(U), :0008.177C(U), :0008.17B1(U)
|
:0008.1674 F606201601             test byte ptr [1620], 01 ; hmm!
:0008.1679 7403                   je 167E       ; jump to year check
:0008.167B E9D601           ***** jmp 1854      ; else fly over all checks!!

In case you haven't noticed, 1854 is where MS Money continues to execute
after all the nags have been implemented:

:0008.17E5 9A34019911             call 0005.0134 ; show expiry nag
:0008.17EA EB68                   jmp 1854       ; then carry on...


Well, well...so if the memory location [1620] is set to 0, MS Money 3 demo
goes through the protection scheme we've just been looking at...but if [1620]
is set to 1, it flies right over all the protection checks and runs MS Money,
*regardless* of the date, with no nags at all! It certainly seems ('feels')
as though [1620] is some sort of 'magic' location...let's see whether the
other part of the protection in MS Money 3 demo (the 60 day transaction date
limit) uses the same 'flag'. Trace to the above location (0008.1674) in
SoftICE, and type the following:

A                          ; assemble intruction in memory
MOV BYTE PTR [1620], 01    ; load [1620] with 01 instead of comparing!
NOP                        ;
NOP                        ; nop out the 'je' instruction...


Now exit from SoftICE ('Ctrl-D') and MS Money 3 demo will run *without* the
nags. Input a few transactions, with dates that are outside the 60 day limit,
and transaction with no dates at all. What do you find? MS Money accepts them
without any fuss at all! We've found the main 'magic' flag used throughout
the MS Money 3 demo to activate *all* of the the protection schemes -
incredible! It just shows how incredibly *stupid* Micro$haft and their greedy
minions really are! Well, let's make these changes permanent, by replacing:

:0008.1674 F606201601             test byte ptr [1620], 01 ; hmm!
:0008.1679 7403                   je 167E       ; jump to year check
:0008.167B E9D601                 jmp 1854      ; else fly over all checks!!

with:

:0008.1674 C606201601             mov byte ptr [1620], 01 ; set [1620] to 1
:0008.1679 90                     nop         ; do nothing
:0008.167A 90                     nop         ; do nothing
:0008.167B E9D601                 jmp 1854    ; fly over all checks!!


seach mnydemo.exe for: F6062016017403E9D601
and replace with:      C6062016019090E9D601
                       ^^        ^^^^	

Just change three bytes to crack the entire protection! Now you can also
delete this Micro$oft monster from your hard drive. :-)

BTW, you may have tried to crack this version of MS Money by BPX'ing on
GetLocalTime. If so, you'll already know that this doesn't work. The reason
for this is because MS Money 3 demo uses an INT 21 function 2A or the below
function instead:

/*
procedure DOS3Call;

The DOS3Call function allows an application to call an MS-DOS Interrupt 21h
function. DOS3Call can be called only from assembly-language routines. It is
exported from KRNL286.EXE and KRNL386.EXE and is not defined in any Windows
header or include files.

Target

Windows, DOS Protected Mode (WinAPI unit)

Parameters

Registers must be set up as required by the desired Interrupt 21h function
before the application calls the DOS3Call function.

Returns

The register contents are preserved as they are returned by the Interrupt 21h
function.

Comments

Applications should use this function instead of a directly coded MS-DOS
Interrupt 21h function. The DOS3Call function runs somewhat faster than the
equivalent MS-DOS Interrupt 21h function running in Windows.
*/


Here is the main routine in MS Money 3 that gets the current date and returns
it in encoded form:

:0079.0552 8CD8                   mov ax, ds
:0079.0554 90                     nop
:0079.0555 45                     inc bp
:0079.0556 55                     push bp
:0079.0557 8BEC                   mov bp, sp
:0079.0559 1E                     push ds
:0079.055A 8ED8                   mov ds, ax
:0079.055C 83EC0A                 sub sp, 000A
:0079.055F 8D46F6                 lea ax, [bp-0A]
:0079.0562 50                     push ax
:0079.0563 9AC008FFFF             call 0010.08C0 ; get time using
KERNEL.DOS3CALL or INT 21 function 2A
:0079.0568 83C402                 add sp, 0002
:0079.056B 8A46F7                 mov al , [bp-09]
:0079.056E 2AE4                   sub ah, ah
:0079.0570 48                     dec ax
:0079.0571 C1E005                 shl ax, 05
:0079.0574 3346FC                 xor ax, [bp-04]
:0079.0577 25E001                 and ax, 01E0
:0079.057A 3146FC                 xor [bp-04], ax
:0079.057D 8A46FC                 mov al , [bp-04]
:0079.0580 8A4EF6                 mov cl , [bp-0A]
:0079.0583 2AED                   sub ch, ch
:0079.0585 49                     dec cx
:0079.0586 32C1                   xor al , cl
:0079.0588 251F00                 and ax, 001F
:0079.058B 3146FC                 xor [bp-04], ax
:0079.058E 8B46F8                 mov ax, [bp-08]
:0079.0591 2D9C07                 sub ax, 079C
:0079.0594 3D7F00                 cmp ax, 007F  
:0079.0597 760F                   jbe 05A8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0079.05A6(C)
|
:0079.0599 836EF864               sub word ptr [bp-08], 0064
:0079.059D 8B46F8                 mov ax, [bp-08]
:0079.05A0 2D9C07                 sub ax, 079C
:0079.05A3 3D7F00                 cmp ax, 007F
:0079.05A6 77F1                   ja 0599

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0079.0597(C)
|
:0079.05A8 8B46F8                 mov ax, [bp-08]
:0079.05AB 2D1C00                 sub ax, 001C
:0079.05AE C1E009                 shl ax, 09
:0079.05B1 8B4EF8                 mov cx, [bp-08]
:0079.05B4 C1E109                 shl cx, 09
:0079.05B7 334EFC                 xor cx, [bp-04]
:0079.05BA 80E501                 and ch, 01
:0079.05BD 33C8                   xor cx, ax
:0079.05BF 894EFC                 mov [bp-04], cx
:0079.05C2 8BC1                   mov ax, cx
:0079.05C4 8B5E06                 mov bx, [bp+06]
:0079.05C7 368907                 mov ss:[bx], ax
:0079.05CA 8BC3                   mov ax, bx
:0079.05CC 8CD2                   mov dx, ss
:0079.05CE 8D66FE                 lea sp, [bp-02]
:0079.05D1 1F                     pop ds
:0079.05D2 5D                     pop bp
:0079.05D3 4D                     dec bp
:0079.05D4 CA0200                 retf 0002

It's almost identical to the code in MS Money 97 isn't it? It encodes the
date in exactly the same way as the newer MS Money 97. This (unusual?)
consistency by Micro$oft has made it very easy for us to crack *both*
versions wide open, proving that, as +ORC teaches, software history is *very*
important. In fact, that was almost too easy, it makes me wonder whether
Micro$oft use this pathetic protection scheme on purpose, in order to acheive
their meglomaniac plans for world domination...a worrying thought...so make
sure *not* to use these sad Micro$oft products! Delete then *now* (once
you've cracked them, of course ;-))!


ReZiDeNt, August 1997