+HCU Strainer - september 1997
by .MaLaTTiA.

MICRO$OFT PROJECT 95

> *** Quick Crack for Micro$oft's Project 95, by +ORC, 04/1997***
> search for the string
> :05167FA6 66C745FE0100       mov [ebp-02], 0001 ;BAD FLAG****
> and change it to:
> :05167FA6 66C745FE0000       mov [ebp-02], 0000 ;always good
> *** now find some stupid yuppies and give them this app ***
>
> pupils... why did we not search for
> :05168015 7C2B         jl 05168042        ;beggar off
> in order to noop everything say with an inc/dec:
> :05168015 40        inc eax   
> :05168016 48        dec eax 
> Why? 

Let's suppose we've just nooped the instruction at 5168015 and let's give a
look to the code:

:05167F6A E808CFEBFF          call 05024E77 
:05167F6F 66817DF4C007        cmp [ebp-0C], 07C0  
:05167F75 722F                jb 05167FA6         ** GO BADFLAG
:05167F77 66817DF40108        cmp [ebp-0C], 0801  
:05167F7D 7727                ja 05167FA6         
:05167F7F FF75F4              push [ebp-0C]       
:05167F82 660FB645F7          movzx byte ptr ax, [ebp-09] 
:05167F87 660FB64DF6          movzx byte ptr cx, [ebp-0A] 
:05167F8C 50                  push eax
:05167F8D 51                  push ecx
:05167F8E E84EC10B00          call 052240E1       
:05167F93 663B45E4            cmp ax, [ebp-1C]    ** HERE the program makes
                                                     another check which can
                                                     cause a badflag
:05167F97 7D0D                jge 05167FA6        ;go badflag
:05167F99 668B4DE4            mov cx, [ebp-1C]
:05167F9D 662BC8              sub cx, ax
:05167FA0 66894DF2            mov [ebp-0E], cx
:05167FA4 EB06                jmp 05167FAC        ;do not badflag

:05167FA6 66C745FE0100     mov [ebp-02], 0001     ** badflag!

   :no_bad_flag
:05167FAC FF75DC           push [ebp-24]
:05167FAF FF151C5C3C05     Call dword ptr[053C5C1C]  
:05167FB5 EB25             jmp 05167FDC              

   :only_5167DE5_calls_this_second_GetLocalTime
:05167FB7 66C745EC0000     mov [ebp-14], 0000
:05167FBD 8D45F4           lea eax, [ebp-0C]
:05167FC0 50               push eax                  
:05167FC1 E8B1CEEBFF       call 05024E77 GetLocalTime
:05167FC6 66817DF4C007     cmp [ebp-0C], 07C0        
:05167FCC 7208             jb 05167FD6               
:05167FCE 66817DF40108     cmp [ebp-0C], 0801        
:05167FD4 7606             jbe 05167FDC              
   :flag_unvalid_year
:05167FD6 66C745FE0100     mov [ebp-02], 0001        

   :valid_year_almost_everybody_calls_here
:05167FDC 66837DFE00       cmp [ebp-02], 0000   ;valid_flag?
:05167FE1 755F             jne 05168042         *** beggar off
:05167FE3 66837DF000       cmp [ebp-10], 0000   ;valid ebp-10?
:05167FE8 750E             jne 05167FF8         ;jmp unvalid_e10
:05167FEA 66837DEC00       cmp [ebp-14], 0000   ;valid ebp-14?
:05167FEF 7451             je 05168042          *** beggar off
:05167FF1 66837DF000       cmp [ebp-10], 0000   ;sure thatebp-10=0?
:05167FF6 7407             je 05167FFF          ;continue ifso

  :unvalid_e10
:05167FF8 66837DEC00       cmp [ebp-14], 0000   ;check if e14valid
:05167FFD 7543             jne 05168042         *** beggar off

  :valid_e10_and_e14       ** ebp-10 can be unvalid, I think...
:05167FFF 0FBF4DF2         movsx word ptr ecx, [ebp-0E] ;e10 and e14 true
:05168003 66833D58192C0501 cmp dword ptr [052C1958], 0001 
:0516800B 1BC0             sbb eax, eax
:0516800D 83E05A           and eax, 0000005A    ;pretty obvious
:05168010 83C05A           add eax, 0000005A    ;isnt it?
:05168013 3BC1             cmp eax, ecx         ;HERE********
:05168015 7C2B             jl 05168042          *** beggar off
:05168017 66837DF21E       cmp [ebp-0E], 001E   ;0x1E = 30
:0516801C 7F4A             jg 05168068          ;good guy jump
:0516801E 8D45F4           lea eax, [ebp-0C]

the jump identified with "*** beggar off" is the "bad" jump we want to avoid.
So, if we just noop the last jump at 5168015, we don't avoid the one at
5167FE1 which is triggered by the flag at ebp-02. That is why we have to
change the badflag in a goodflag!
(I know I should just give answers, but after all I'm here to learn, so I
have a question for you: why just changing the flag without nooping the jump
at 5168015? Does the change affect the compare too? I'll be honest: I didn't
have the time to find and try the program, because I started to work quite
late, so I just worked on this piece of code O:-) Thanx in advance...)

-------------------------------
HOW TO CRACK MS MONEY 3.0 & 97

In this essay I tried to use a mix of "live" and dead listing cracking. Maybe
in some point you'll find this text even TOO easy, but I think it can be very
useful for all the new crackers and, why not, perhaps future +HCUkers too. So
please be patient... I'm a neophyte too! :)
First of all, let's give a look to the problems we will have to deal with
(they are the same for msmoney 3.0 and 97):

1) NAGSCREEN: well, this isn't a real problem... now we're interested in TIME
			protections :)

2) DATE CHECK AT START: the date must be 1994 or 1995 in money3, 1996 or 1997
			in money97.

3) DATE "DELTA" CHECK: the transactions must be below a limit of 60 days for
			money3, 90 days for money97.

4) BLANK DATE CHECK: the transactions must have a date in both the versions.

5) END OF EVALUATION PERIOD: 60 days for money3, 90 for money97.



2) Ok, let's start with Money 3.0: I used the French version provided by Fravia
(thanx!). The first check is the date check at the start of the program. If 
you run the program with a wrong date a nagscreen appears telling you the
date must be 1994 or 1995, otherwise you can't use the program. In this crack
I'll use a "backtrace" method, starting from the errors and going back to the
piece of code which generated them. When the dialog box appears, call winice
and type

  TASK

You will see that the task we're interested in is called MNYDEMO. If you type 

  HWND MNYDEMO

the lines that will appear will look like these:

05CC (1)    0c47    32    MNYDEMO    #32770 (Dialog)	17D7:00004757
 05D0       0c47    32    MNYDEMO    Button             17D7:0000102E
 05D4       0c47    32    MNYDEMO    Static             17D7:000052FA
 05D8       0c47    32    MNYDEMO    Static             17D7:000052FA

In the first column you'll see the handles of the objects you find in the
5th column. The "Button" is the OK button in the "Erreur" dialog box. Now
let's try to break the program in this point, with

  BMSG 0788 wm_gettext wm_command

then press CTRL-D. The control will return automatically to winice because 
the breakpoint has been immediately triggered. Now if you type

  STACK

you'll see this called procedures (starting from the LAST call):

USER!SYSERRORBOX+0A46 AT 1807:1CC8 [?]
USER!SYSERRORBOX+0AB5 AT 1807:1D37 [?]
USER!MESSAGEBOX+0063 AT 1807:0063 [?]
MNYDEMO (40) AT 0D0F:B445 [?]
MNYDEMO (40) AT 0D0F:AD3E [?]
MNYDEMO (08) AT 30E7:1510 [?]
MNYDEMO (08) AT 30E7:16D6 [?]
MNYDEMO (08) AT 30E7:02EC [?]
MNYDEMO (08) AT 30E7:00C1 [?]

and so on.
What do we have here? A message box called by some code in the segment 40
of the main program, which is called before by segment 8... uhmm... 
probably the date check is in segment 8, and then a part of the window 
handling in seg 40, but I'm just trying to guess... :) Now let's run the
good old wdasm 8.5 and look at the dead listing code. (Ah, I usually run
Wdasm just ONE time to disasm the progs, then I save the file and read it
with LIST.COM... it's very fast!)
40 hex is 64 dec, so let's look first at 0064.b445: here we can see the
call to USER.MESSAGEBOX, but there aren't apparently date checks... and
also we are TOO near to the "bad" call... maybe it's just intuition (maybe
zen? I hope so! :) but I think we have to go a bit upper with the calls...
0064.ad3e doesn't return anything interesting, just a procedure without 
jumps: it can't be the right spot.

Now let's look at 0008.1510:

:0008.14E8 9A52052815             call 0081.0552
:0008.14ED 8BD8                   mov bx, ax
:0008.14EF 368B07                 mov ax, ss:[bx]
:0008.14F2 8946FE                 mov [bp-02], ax
:0008.14F5 8A66FF                 mov ah, [bp-01]
:0008.14F8 80E4FE                 and ah, FE
:0008.14FB 80FC5C                 cmp ah, 5C
:0008.14FE 741C                   je 1515         ;JUMP TO GOOD GUY!
:0008.1500 8A66FF                 mov ah, [bp-01]
:0008.1503 80E4FE                 and ah, FE
:0008.1506 80FC5E                 cmp ah, 5E
:0008.1509 7411                   je 1515         ;JUMP TO GOOD GUY!
:0008.150B 681709                 push 0917
:0008.150E 6A00                   push 0000
:0008.1510 9A1AADBB0E             call 0064.AD1A  ;BAD GUY!!!
:0008.1515 33C0                   xor ax, ax
:0008.1517 C9                     leave
:0008.1518 C20400                 ret 0004
...
:0008.1515 here is the code for the "good guy"! :)

YEAH! I WAS RIGHT!!! :))))
We have reached the right point! Now let's see the comparisons... First
ah is ANDed with FE, then it is compared with 5C and 5E. So the (decimal)
values ah can have are 92 and 94! Uhmm... the values are similar, but
they are not the right ones! I think we will have to give a look to that
0081.0552 call now... 

:0081.0552 8CD8                   mov ax, ds
:0081.0554 90                     nop
:0081.0555 45                     inc bp
:0081.0556 55                     push bp
:0081.0557 8BEC                   mov bp, sp
:0081.0559 1E                     push ds
:0081.055A 8ED8                   mov ds, ax
:0081.055C 83EC0A                 sub sp, 000A
:0081.055F 8D46F6                 lea ax, [bp-0A]
:0081.0562 50                     push ax
:0081.0563 9AC008FFFF             call 0010.08C0

This call will find the current date and put it in the locations that
go from ds:af10 to ds:af14. To understand better what's going on, 
give a look to the following table:

Current date: 12-08-1997 (Tuesday)

	 day: 12       ->   0c
       month: 08       ->   08
        year: 1997     -> 07cd
    day/week: Tuesday  ->   02

Location:   AF10      AF11      AF12      AF13      AF14
           (bp-A)    (bp-9)    (bp-8)    (bp-7)    (bp-6)
    Type:    day     month     yearl     yearh       d/w
   Value:     0C        08        CD        07        02

Ok, now we can return to the dead listing...

:0081.0568 83C402                 add sp, 0002
:0081.056B 8A46F7                 mov al , [bp-09]  ;move month in AL
:0081.056E 2AE4                   sub ah, ah        ;ax=0008
:0081.0570 48                     dec ax            ;ax=0007
:0081.0571 C1E005                 shl ax, 05        ;ax=00E0
:0081.0574 3346FC                 xor ax, [bp-04]   ;[AF16]=0  ?
:0081.0577 25E001                 and ax, 01E0      ;mask=0000000111100000

The value of the month is put in the positions from 6 to 9. The maximum value
of ax (december) is 0000000101100000, or 160 hex.

:0081.057A 3146FC                 xor [bp-04], ax   ;[AF16]=E0  saves value
:0081.057D 8A46FC                 mov al , [bp-04]  
:0081.0580 8A4EF6                 mov cl , [bp-0A]  ;move day in CL
:0081.0583 2AED                   sub ch, ch        ;cx=000C
:0081.0585 49                     dec cx            ;cx=000B
:0081.0586 32C1                   xor al , cl       ;ax=00EB  (it's like a sum
                                                     but faster, I think)
:0081.0588 251F00                 and ax, 001F      ;mask=0000000000011111

The value of the days is put in the positions from 1 to 5. The maximum value
of ax is 160h+1eh=17eh (note: there's one high bit).

:0081.058B 3146FC                 xor [bp-04], ax   ;[AF16]=EB
:0081.058E 8B46F8                 mov ax, [bp-08]   ;move year in AX
:0081.0591 2D9C07                 sub ax, 079C      ;079c=1948
:0081.0594 3D7F00                 cmp ax, 007F      ;007F=127 
                                                     is the year >2075?
:0081.0597 760F                   jbe 05A8          ;it should jump :)

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

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0081.0597(C)
|
:0081.05A8 8B46F8                 mov ax, [bp-08]    ;move year in AX
:0081.05AB 2D1C00                 sub ax, 001C       ;ax=0761  001c=28
:0081.05AE C1E009                 shl ax, 09         ;ax=6200  

The value of the year (minus 1C) is put in the positions >9.

:0081.05B1 8B4EF8                 mov cx, [bp-08]    ;move year in CX
:0081.05B4 C1E109                 shl cx, 09         ;cx=9A00
:0081.05B7 334EFC                 xor cx, [bp-04]    ;cx=9aEB (why?)
:0081.05BA 80E501                 and ch, 01         ;cx=00EB
                                                     (keeps just the high bit
                                                      of the month)
:0081.05BD 33C8                   xor cx, ax         ;cx=62EB
                                                     (faster than sum)
:0081.05BF 894EFC                 mov [bp-04], cx    ;[AF16]=62EB
:0081.05C2 8BC1                   mov ax, cx         ;ax=62EB DATECODE!
:0081.05C4 8B5E06                 mov bx, [bp+06]
:0081.05C7 368907                 mov ss:[bx], ax    ;backup datecode
:0081.05CA 8BC3                   mov ax, bx
:0081.05CC 8CD2                   mov dx, ss
:0081.05CE 8D66FE                 lea sp, [bp-02]
:0081.05D1 1F                     pop ds
:0081.05D2 5D                     pop bp
:0081.05D3 4D                     dec bp
:0081.05D4 CA0200                 retf 0002

NOTE: the maximum value of month+day has a high bit, which can change the
final value for the year (the one which is checked at 0008.14fb and 0008.1506).
This is why the values obtained from  this call are first ANDed with the mask
FE00. Now, we just have to try with 1994 & 1995 to see the date codes are
5C and 5E... wow! :) We now just have to change the jump at 0008.14fe from
je 1515 to jmp 1515 (7411 must change in eb11).
The searchstring is: 80 E4 FE 80 FC 5C 74 11
Before ending this section, let's give a look to the lines which are before
this piece of code:

:0008.14BC C8060000               enter 0006, 00
:0008.14C0 837E0400               cmp word ptr [bp+04], 0000
:0008.14C4 741E                   je 14E4
:0008.14C6 8A26D764               mov ah, [64D7]
:0008.14CA 2500FE                 and ax, FE00
:0008.14CD 8946FA                 mov [bp-06], ax
:0008.14D0 3D005C                 cmp ax, 5C00
:0008.14D3 740A                   je 14DF
:0008.14D5 3D005E                 cmp ax, 5E00
:0008.14D8 7405                   je 14DF
:0008.14DA 3D0060                 cmp ax, 6000
:0008.14DD 7505                   jne 14E4

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.14D3(C), :0008.14D8(C)
|
:0008.14DF A1D664                 mov ax, [64D6]
:0008.14E2 EB4B                   jmp 152F



* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.14C4(C), :0008.14DD(C)
|
:0008.14E4 8D46FC                 lea ax, [bp-04]
:0008.14E7 50                     push ax
:0008.14E8 9A52052815             call 0081.0552
... and here we reach the point previously examined.

What is this code? Why there are comparisons SO similar to the ones we have
seen before? Well, I didn't delve deep in this part, but I think it's saving
information about the end of the trial period, first checking if a date 
already exists in that [64D7] location: in fact, you just have to look to the
last comparison, 

 cmp ax, 6000 

(6000 is year 1996). We know the start date must be before the end of 1995,
so it should be the date of expiration (which can be in 1996).

3) DATE DELTA CHECK
4) BLANK DATE CHECK

If we use the same method to reach the check points in the program (HWND, BMSG
and STACK), we can see the two kind of date checks inside the program are quite
near. The blank date check shows this stack (from the LAST call):

MNYDEMO (40) AT 0D0F:B445 [?]
MNYDEMO (40) AT 0D0F:AD3E [?]
MNYDEMO (0E) AT ....:35FD [?]
MNYDEMO (35) AT ....:223A [?]
MNYDEMO (35) AT ....:1EDF [?]
MNYDEMO (35) AT ....:1837 [?]

and the delta (60 days) check shows this one:

MNYDEMO (40) AT 0D0F:B445 [?]
MNYDEMO (40) AT 0D0F:AF7E [?]
MNYDEMO (0E) AT ....:360F [?]
MNYDEMO (35) AT ....:223A [?]
MNYDEMO (35) AT ....:1EDF [?]
MNYDEMO (35) AT ....:1837 [?]

Uhmmm... the last three calls are the same, and so is for the fist one. If we
look at the date check at the start of the program we can see the first two
calls (at segment 40) are the same. So... let's guess :) maybe the two 
locations we're interested in are 0e:35fd and 0e:360f? Let's give a look to
these ones:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0014.2B60(U)
|
:0014.35F8 681A09                 push 091A
:0014.35FB 6A00                   push 0000
:0014.35FD 9A1AAD1236             call 0064.AD1A	;BAD CALL!!!
:0014.3602 E9B501                 jmp 37BA


:0014.3605 90                     nop

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0014.2B6C(U)
|
:0014.3606 681609                 push 0916
:0014.3609 6A00                   push 0000
:0014.360B 6A00                   push 0000
:0014.360D 6A00                   push 0000
:0014.360F 9A58AF1F30             call 0064.AF58	;BAD CALL!!!

Note that the ONLY way to reach these points is from the unconditional
jumps at 14.2B60 and 14.2B6C. Here they are:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0014.2B47(C)
|
:0014.2B4C 837E0800               cmp word ptr [bp+08], 0000
:0014.2B50 741D                   je 2B6F
:0014.2B52 F606AC1701             test byte ptr [17AC], 01
:0014.2B57 7516                   jne 2B6F
:0014.2B59 833ED870FF             cmp word ptr [70D8], FFFF
:0014.2B5E 7503                   jne 2B63
:0014.2B60 E9950A                 jmp 35F8	--==>> BLANK DATE

The jump MUST BE to 2b63 (NO blank date)

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0014.2B5E(C)
|
:0014.2B63 A1D664                 mov ax, [64D6]
:0014.2B66 3906D870               cmp [70D8], ax
:0014.2B6A 7203                   jb 2B6F
:0014.2B6C E9970A                 jmp 3606	--==>> WRONG DATE

The jump MUST BE to 2b6f (NO wrong date)

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0014.2B50(C), :0014.2B57(C), :0014.2B6A(C)
|
:0014.2B6F OK!!! THIS IS THE RIGHT PLACE!!!

Uhm... the fastest way to arrive here is to change the je 2b6f at 14.2b50 in
a jmp 2b6f (maybe some flag? It's a good shortcut, anyway!). So let's search
the string

83 7E 08 00 74 1D F6 

and change it in 

83 7E 08 00 EB 1D F6 
             +--------jump anyway

5) The last check we have to crack is the one for end of the evaluation
period. If we look at the stack we'll see something like this:

MNYDEMO (5) AT ...:03E8 [?]
MNYDEMO (8) AT ...:17FD [?]
MNYDEMO (8) AT ...:02EC [?]
MNYDEMO (8) AT ...:00C1 [?]
MNYDEMO (2) AT ...:1401 [?]
MNYDEMO (1) AT ...:0023	[?]

and so on.
Let's give a look to these calls: 5.03E8 is just the call for a 
USER.DIALOGBOXPARAM; before 8.17FD we have this code:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0008.1820(C)
|
:0008.17E2 8B46F4                 mov ax, [bp-0C]
:0008.17E5 3946F0                 cmp [bp-10], ax
:0008.17E8 7246                   jb 1830


* Possible Reference to Dialog: DialogID_0494
                                  |
:0008.17EA 689404                 push 0494
:0008.17ED 686D05                 push SEG ADDR of Segment 0028
:0008.17F0 68C015                 push 15C0
:0008.17F3 FF36540B               push word ptr [0B54]
:0008.17F7 6A00                   push 0000
:0008.17F9 6A00                   push 0000
:0008.17FB 6A00                   push 0000
:0008.17FD 9A3A01B111             call 0005.013A

If you have Borland Resource Workshop, give a look to dialog 0494hex,
that is 1172. It is the "Avertissement de limite de validé" we do not
want to have to deal with! So we don't want to reach this point... 
but how? Let's look at that 0008.1820 piece of code...

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0008.16BD(U), :0008.16C5(U), :0008.16CD(U)
|
:0008.1804 FF76F0                 push word ptr [bp-10]
:0008.1807 6A3C                   push 003C
:0008.1809 8D46FC                 lea ax, [bp-04]
:0008.180C 50                     push ax
:0008.180D 9AD8053418             call 0081.05D8
:0008.1812 8BD8                   mov bx, ax
:0008.1814 368B07                 mov ax, ss:[bx]
:0008.1817 8946FE                 mov [bp-02], ax
:0008.181A 8B46F4                 mov ax, [bp-0C]
:0008.181D 3946FE                 cmp [bp-02], ax
:0008.1820 73C0                   jnb 17E2

uhmm... I got a call to 0081.05d8, then a sequence of mov very similar to the
one used in the initial date check... there the call was 0081.0552, here 05d8.
Well, in bp-02 and bp-0c there are two values, and I jump to the "bad" location
only if bp-2 contains a value that is greater or equal to the one in bp-c. I
tried to change the jnb in two nops and found that if I don't do the jump the
program tells me that the date of the system is previous than the date of
installation :)
Well, I'm not perfect... O:-)
Ok, so I HAVE to jump to 17E2, and the only way to skip that damned dialog box
is the

:0008.17E8 7246                   jb 1830

Doesn't it sound like "Jump if you haven't reached the LAST date"? Here,
[bp-10] should be the actual date, which should be less than [bp-c], the LAST
date. In fact, if you look at the code at 8.1830, you'll see this:

:0008.1830 50                     push ax               PUSH LAST DATE
:0008.1831 9AE6083E18             call 0081.08E6
:0008.1836 FF76F0                 push word ptr [bp-10] PUSH ACTUAL DATE

 (the call should return in ax a value corresponding to the date pushed)

:0008.1839 8BF0                   mov si, ax      move days value in si
:0008.183B 9AE608EB14             call 0081.08E6
:0008.1840 2BF0                   sub si, ax      sub actual days from
                                                  LAST DATE days
:0008.1842 8976FE                 mov [bp-02], si
:0008.1845 83FE07                 cmp si, 0007    Less than 7 days?
:0008.1848 7F22                   jg 186C

If the evaluation copy is going to expire in less than 7 days, a message
appears telling us how many days we have left. We want to disable this too...
ok, so to crack this check we have to:
 - change the "jb 1830" at 8.17e8 with a "jmp 1830"
 - change the "jg 186c" at 8.1848 with a "jmp 186c"

the hex strings are:
 - 8B 46 F4 39 46 F0 72 46
   8B 46 F4 39 46 F0 EB 46
 - 83 FE 07 7F 22
   83 FE 07 EB 22

Ok, the work is done! :)

--------------------------
MONEY 97:

With this version we will have to use another trick, 'cause the bmsg method is
not enough... anyway, I'll explain you how to reach the right places in the
progam step by step. 
This version is very similar to the previous one, so sometimes we will use some
old information to crack the program. I used another mix of dead listing and
live cracking, but here the dead listing approach is quite harder, 'cause the
program is very large and you'll need a lot of free resources. To disassemble
the file (WITHOUT jmp and call references) I needed more than 50 megs free on
my C:\ drive, and to view the source I had to split the resulting file (more
than 35 megs!) in three pieces of about 12 megs. List can handle files until
about 16 megs, above that it truncates the file automatically: anyway, I
continued to use it 'cause it's SOOO fast! :)
Now, let's proceed like in money3 (ignore the nagscreen, we are interested in
time protections now):

2) DATE CHECK AT START - remember? the date must be 1996 (or 1997)

Uhmmm... if only the guys at Micro$oft were so silly to use the same date
algorithm... maybe I can check if... naaa... it isn't possible! Let's use
another method... if you wait for the dialog box to appear telling you the
date is wrong and use the same task-hwnd-stack trick, you'll see something
like this:

KERNEL.Alloc    at 0187:0024 [?]
USER!DIALOGBOXPARAM+003B at 17D7:4558 [?]
and so on.

Hey, but where's the calling program? Here is why I said the bmsg trick alone
is not enough. But, of course, it helps :) You see that the first call is
inside user!dialogboxparam, so why don't we try to clear all the old
breakpoints and type

 bpx dialogboxparama

(the "a" at the end of the procedure means we are dealing with 32-bit code).
If we try to make the nag window appear again, the program will break in the
right point and show you this stack (from the LAST call):

(NOTE: here we have the RETURN values and not the call addresses)
USER32!DIALOGBOXPARAMA at 014f:BFF62DC9
MSMONEY!.TEXT+6F6AF at 014f:004706AF
MSMONEY!.TEXT+77730 at 014f:00478730
and so on.

Well, let's give immediately a look to that last 4706af call:

:0047064A 66A144926100            mov ax, [00619244]
:00470650 662500FE                and ax, FE00
:00470654 663D0060                cmp ax, 6000           ;1996!
:00470658 740C                    je 00470666
:0047065A 663D0062                cmp ax, 6200           ;1997!
:0047065E 7406                    je 00470666
:00470660 663D0064                cmp ax, 6400           ;1998!
:00470664 7516                    jne 0047067C
:00470666 66A144926100            mov ax, [00619244]
:0047066C 8B4C2408                mov ecx, dword ptr [esp + 08]
:00470670 668901                  mov word ptr [ecx], ax
:00470673 B801000000              mov eax, 00000001
:00470678 83C404                  add esp, 00000004
:0047067B C3                      ret


:0047067C E84F83FFFF              call 004689D0
:00470681 6689442402              mov word ptr [esp + 02], ax
:00470686 662500FE                and ax, FE00
:0047068A 663D0060                cmp ax, 6000           ;1996!
:0047068E 7428                    je 004706B8
:00470690 663D0062                cmp ax, 6200           ;1997!
:00470694 7422                    je 004706B8
:00470696 6A00                    push 00000000
:00470698 A1F03F6100              mov eax, [00613FF0]
:0047069D 6A00                    push 00000000
:0047069F 50                      push eax
:004706A0 6810F54C00              push 004CF510
:004706A5 68DC2F0000              push 00002FDC
:004706AA E861B8FFFF              call 0046BF10      ;BAD CALL!!!
:004706AF 83C414                  add esp, 00000014
:004706B2 33C0                    xor eax, eax
:004706B4 83C404                  add esp, 00000004
:004706B7 C3                      ret


:004706B8 8B442402                good guy, you're welcome! :)


Hey... I can't believe it... it's just the SAME IDENTICAL ALGORITHM! You
know what to do now:

FIRST: look at west and say "THANK YOU MICRO$OFT! YOU MAKE ME FEEL A GOOD
       CRACKER!"
SECOND: look at north and say "THANK YOU +ORC FOR THE KNOWLEDGE YOU GIVE ME
        EVERY DAY!"
THIRD: put a jmp 004706B8 (EB28) instead of je 004706B8 (7428) at 0047068E.
  

3-4) DATE "DELTA" AND BLANK DATE CHECK

Same method: try with a blank date, then bmsg on the button, then look at
the stack. This time we have USER!MESSAGEBOX so write

  bpx messageboxa

and make the message box appear again. This is the stack (from the LAST
call):

USER32!MessageBoxA at 014f:BFF638D9
MSMONEY!.text+... at 014f:0047426E
MSMONEY!.text+... at 014f:005B683E
MSMONEY!.text+... at 014f:00558BC2

Now do the same thing with an "out of limit" date:

USER32!MessageBoxA at 014f:BFF638D9
MSMONEY!.text+... at 014f:004742DE
MSMONEY!.text+... at 014f:005B683E
MSMONEY!.text+... at 014f:00558BC2

Ok, the difference is in the LAST call before the messagebox. In fact, the
kind of error is different in the two cases. We can guess something like
this: in the procedure called by 5b683e (or, better, by the instruction
immediately before 5b683e) there's a check for the transaction date: if the
date is blank then the program calls the procedure which "goes" to 47426e,
if it has a "wrong delta" the program calls the procedure which "goes" to
4742de. 
Ok, first of all we have to check what is the call preceding the instruction
at 5b683e:

:005B6839 E872EAFFFF              call 005B52B0
:005B683E 83C438                  add esp, 00000038

Well, now we know we have to look from address 5b52b0 for some addresses,
which are the starting addresses for the 47426e and 4742de procedures. If
we look at the code, we can see that the first procedure starts at 474240
and the second starts at 474280. Maybe there are too many addresses messing
around here, so let's put them together:

5b52b0  Here starts the transaction date check procedure. There should be a
        check for the blank value (remember? In money3 there was a comparison
        with value FFFF) and a check for the "wrong delta" (comparison
        between two values in memory).
474240  Here starts the "blank date error" procedure.
474280	Here starts the "wrong delta error" procedure.

Et voilà! These are the only addresses you have to remember! Now we can check
for a comparison with the value FFFF (remember the old money version?). If
you search for ", FFFF" in the dead listing you'll find this piece of code:

:005B5504 837D3000                cmp dword ptr [ebp+30], 00000000    ;look at
                                                                14.2b4c in the
                                                                  old version!
:005B5508 7421                    je 005B552B
:005B550A 8B4D3C                  mov ecx, dword ptr [ebp+3C]
:005B550D 668B81A9010000          mov ax, word ptr [ecx+01A9]
:005B5514 663DFFFF                cmp ax, FFFF                    ;HERE IT IS!
:005B5518 0F84F60C0000            je 005B6214                 ;blank date jump
:005B551E 66390544926100          cmp word ptr [00619244], ax
:005B5525 0F86020D0000            jbe 005B622D                ;wrong delta jump
:005B552B 837D1C00                cmp dword ptr [ebp+1C], 00000000 ;ok good guy

Well, it seems the right spot: in fact, if you look at 5b6214, you'll see:

:005B6214 6A00                    push 00000000
:005B6216 6A00                    push 00000000
:005B6218 6A00                    push 00000000
:005B621A 6A00                    push 00000000
:005B621C 6A00                    push 00000000
:005B621E 6810090000              push 00000910
:005B6223 E818E0EBFF              call 00474240 ; HA!!! Here is the "blank date
                                                  error" procedure!

and if you look at 5b622d, you'll see:

:005B622D 6A00                    push 00000000
:005B622F 680B090000              push 0000090B
:005B6234 E867E9EBFF              call 00474BA0 ; wow!!! I love this job :)

Ok, now you just have to jump immediately to 5B552B, changing the instruction

:005B5508 7421                    je 005B552B

in

:005B5508 EB21                    jmp 005B552B


5) END OF EVALUATION PERIOD - max 90 days

For the end of the evaluation period use the same trick, window, bmsg, stack,
and look at the procedure used for the nag window. In this case it is
dialogboxparama, so make a breakpoint on it, run the program again and look
at the new stack (as always from the LAST call):

USER32!DialogBoxParamA at 014F:BFF62DC9
 at 014f:00470E2F
 at 014f:0001BA8C

Let's give a look to 470e2f:

:00470DF1 668B442414              mov ax, word ptr [esp + 14]
:00470DF6 66898528020000          mov word ptr [ebp+0228], ax
:00470DFD C7057815610001000000    mov dword ptr [00611578], 00000001
:00470E07 668B442414              mov ax, word ptr [esp + 14]
:00470E0C 6639842478010000        cmp word ptr [esp + 00000178], ax
:00470E14 721C                    jb 00470E32                ; it sould jump!
:00470E16 6A00                    push 00000000
:00470E18 A1F03F6100              mov eax, [00613FF0]
:00470E1D 6A00                    push 00000000
:00470E1F 50                      push eax
:00470E20 6810F54C00              push 004CF510
:00470E25 685F2F0000              push 00002F5F
:00470E2A E8E1B0FFFF              call 0046BF10              ; bad call
:00470E2F 83C414                  add esp, 00000014
:00470E32 668B5506                mov dx, word ptr [ebp+06]
:00470E36 8B8C2488020000          mov ecx, dword ptr [esp + 00000288]
and so on.

so you just need to change

:00470E14 721C                    jb 00470E32                ; it sould jump!

in

:00470E14 EB1C                    jmp 00470E32               ; it jumps!

Ok, now the crack is finished... but wait! And what about those 7 days before
the end of the evaluation period? Maybe there is a check in this version too,
and it wouldn't be nice if we had that nagscreen... I tried to put the date 7
days before the expiration of Money95 and at the exit that dialog box appeared.
Grrr! Let's look at the OLD code:

:0008.1840 2BF0                   sub si, ax      
:0008.1842 8976FE                 mov [bp-02], si
:0008.1845 83FE07                 cmp si, 0007    ;Less than 7 days?

ok, now I search the NEW code for sub si, ax:

:0046EB30 662BF0                  sub si, ax
:0046EB33 6683FE0F                cmp si, 000F    ;Hey!!! Here there aren't 7
                                                   days!

this is the first sub followed by a cmp I've found, but if you look in all the
code you'll see it's also the ONLY one. So, do we have to trust those guys at
M$ or do we have to use the old method? Uhmmm.. I know I shouldn't, but let's
try to crack it! Look at the previous part of code:

:0046EAF7 E8D49EFFFF              call 004689D0        

we know this call, if you look at the code it's the same algorithm used in the
old version to make up the date codes.

:0046EAFC 668945EC                mov word ptr [ebp-14], ax ;new date in ebp-14
:0046EB00 66A144926100            mov ax, [00619244]        ;end date in ax
:0046EB06 668945EA                mov word ptr [ebp-16], ax ;end date in ebp-16
:0046EB0A 663945EC                cmp word ptr [ebp-14], ax ;new date>end date?
:0046EB0E A1AC0C6100              mov eax, [00610CAC]
:0046EB13 7360                    jnb 0046EB75              ;NO days checking
:0046EB15 8B45EA                  mov eax, dword ptr [ebp-16]
:0046EB18 50                      push eax
:0046EB19 E832A3FFFF              call 00468E50
:0046EB1E 83C404                  add esp, 00000004
:0046EB21 668BF0                  mov si, ax
:0046EB24 8B45EC                  mov eax, dword ptr [ebp-14]
:0046EB27 50                      push eax
:0046EB28 E823A3FFFF              call 00468E50
:0046EB2D 83C404                  add esp, 00000004
:0046EB30 662BF0                  sub si, ax
:0046EB33 6683FE0F                cmp si, 000F              ;days remaining>15?
:0046EB37 A1AC0C6100              mov eax, [00610CAC]
:0046EB3C 7F37                    jg 0046EB75               ;yes, >15
:0046EB3E 0FBFC6                  movsx eax, si             ;no, write nag
:0046EB41 50                      push eax

* Possible StringData Ref from Data Obj ->"%d"
                                  |
:0046EB42 68D8096100              push 006109D8
:0046EB47 8D45E0                  lea eax, dword ptr [ebp-20]
:0046EB4A 50                      push eax

* Reference To: USER32.wsprintfA, Ord:0249h
                                  |
:0046EB4B FF15080A6200            Call dword ptr [00620A08]
:0046EB51 83C40C                  add esp, 0000000C
:0046EB54 6A00                    push 00000000
:0046EB56 6A00                    push 00000000
:0046EB58 A1F03F6100              mov eax, [00613FF0]
:0046EB5D 50                      push eax
:0046EB5E 6810F54C00              push 004CF510
:0046EB63 68DD2F0000              push 00002FDD
:0046EB68 E8A3D3FFFF              call 0046BF10
:0046EB6D 83C414                  add esp, 00000014
:0046EB70 A1AC0C6100              mov eax, [00610CAC]
:0046EB75 85C0                    test eax, eax
:0046EB77 7438                    je 0046EBB1

Now we just have to change

:0046EB13 7360                    jnb 0046EB75              ;NO days checking

in

:0046EB13 EB60                    jmp 0046EB75              ;NO days checking

and the crack is complete!