Hi +orc,
I'm really happy you're back ( ya era hora tio ). Reading the letter you sent reverser, it was like you being out of "+our" world soon. I hope you'll not ( por la gloria de tu madre ). I miss at +hcu-world a little bit of philosophy and a little bit of humor, something I always find inside your lessons.

This lack is very usual (IMHO) in the Science-world. Sadly that cannot be taught in a lesson. I would like to try-to-solve this problem (mi granito de arena, vamos) writing different things ( I love to write things ), but is very hard for me to write good stuff in English (though i am improving ).

Well, the strainer. I've done it, though I had not enough time to do it as brilliantly as I would want. You know, I do everything the last day (casi me pilla el toro). I have hermitian operators pressing me :-).

Hope to see you (figuradamente claro) again. bye.

SiuL+Hacky ( Under your permission )
 
 
 
 
 
 

(Best view with fixed width fonts )

BUST OPENING MICROSOFT MONEYS (HOPE)
=============================
Well, here we are with two little stupid programs from microsoft. History is really important in cracking, and after this experience I am more convinced, as you will be after reading this. About the searching, well, to find money 97 was easy, just some days before the strainer was published. But money 94 is really hard to find in the web, very hard (at least for me). Yeah, Yeah, I learnt in the process, but probably my call provider is happier than me with this searching :-). I found all ms money versions in the history but 3.0, very sad.

So I cracked first money 97. I know that this is not chronological, but as if you were in a good film you can imagine this first crack as a flashback of the whole story. One advise, the first tut is written in a way all newbies can understand it, so I decided to take very few things for granted. Like I didn't want to repeat practically the same, the second essay is shorter and I'm giving the solution and the way to follow ( but I don't go step by step ). I could say this essay is more of a teaching exercise than a cracking one, specially 'cause an experienced cracker is not learning any STATE-OF-THE-ART protection scheme with this couple of programs.

money 97
========
The first thing you do after installing it is looking the big monster of money.exe, almost 5 Mb, What would do these programers with the old spectrum ? They would be selling sand in some deserted island probably. I ran it 'cause I wanted to fire the protection mechanism, but it was not easy to be aware of it. You are advised to be over the 90 days limit, but you can use the problem, so yo must get your fingers inside this rubbish for finding some disabled part. Ok, the date in the database must be inside the allowed limit (though it happen even if you are using it in the first 90 days ). I decided to remove the sample sample.mny, and the limit was restarted ( que chapuza tan impresentable ). So I knew the current database has a lot to do with the (supposed) installation date.

+orc's clues about the GetLocalTime, leads us to the frequently-called function :46a308. Just have a look at it, we are not going to get further details (by now), 'cause I cracked it without understanding it perfectly, so I think if you can crack it without "deciphering" the encoding, it is somehow imperfect. Well, look at it, feel it, can you smell the coding routine ? Later I'll go through it:

: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]
:0046A30E 668B442406              mov ax, [esp + 06]
:0046A313 6648                    dec ax
:0046A315 66C1E005                shl eax, 05
:0046A319 6633442402              xor ax, [esp + 02]
:0046A31E 6625E001                and ax, 01E0
:0046A322 6631442402              xor [esp + 02], ax
:0046A327 668B44240A              mov ax, [esp + 0A]
:0046A32C 6648                    dec ax
:0046A32E 6633442402              xor ax, [esp + 02]
:0046A333 66251F00                and ax, 001F
:0046A337 6631442402              xor [esp + 02], ax
:0046A33C 8B442404                mov eax, [esp + 04]
:0046A340 25FFFF0000              and eax, 0000FFFF
:0046A345 2D9C070000              sub eax, 0000079C
:0046A34A 83F87F                  cmp eax, 0000007F
:0046A34D 7E1F                    jle 0046A36E
:0046A34F B89CFFFFFF              mov eax, FFFFFF9C

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0046A36C(C)
|
:0046A354 6601442404              add [esp + 04], ax
:0046A359 8B4C2404                mov ecx, [esp + 04]
:0046A35D 81E1FFFF0000            and ecx, 0000FFFF
:0046A363 81E99C070000            sub ecx, 0000079C
:0046A369 83F97F                  cmp ecx, 0000007F
:0046A36C 7FE6                    jg 0046A354

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0046A34D(C)
|
:0046A36E 668B442404              mov ax, [esp + 04]
:0046A373 66C1E009                shl eax, 09
:0046A377 6633442402              xor ax, [esp + 02]
:0046A37C 6625FF01                and ax, 01FF
:0046A380 668B4C2404              mov cx, [esp + 04]
:0046A385 6683E91C                sub cx, 001C
:0046A389 66C1E109                shl ecx, 09
:0046A38D 6633C1                  xor ax, cx
:0046A390 6689442402              mov [esp + 02], ax
:0046A395 83C414                  add esp, 00000014
:0046A398 C3                      ret

Ok, I agree, dead listing is more "sane", but I ( do you? )cannot crack it without softice. Who calls this function, and, as a result of it, fires the messageboxes ? What now I'm going to do is very easy, to follow the conection between the execution of this MesBox (stop now bad guy !) and the reason (some kind of comparison install-date and current date). This is really obvious, so good programers try to "hide" this conection. Nothing new, just the old back tracing. Many times it is not possible to do a dead back trace, so we must use Softice. Let's see what happen here ( you can trace also the identifier of the nag, as it's done in money 94 crack):

bpx MessageBoxA

Nothing happen, this is not our function. The nag window is fired with some other function. Take the dead list and inspect the imported funcions for some suspicious name:

bpx DialogBoxParamA

ok, it is our function. Write down the address and this is what we find in the dead list. Before showing it to you. What do I look for ? something like what we may call "bridge over the nag":

...
the famous cmp
jne GOOD_GUY
...
preparing nag window
...
call USER32.DialogBoxParamA
...
some lines (not a lot)
...
GOOD_GUY

and what i get in money 97 is:
 
( ... function called from one million places )
:0046D910 64A100000000            mov eax, fs:[00000000]; first line

... lines and lines with no near "cmp"

:0046D9B3 50                      push eax
:0046D9B4 8B4510                  mov eax, [ebp+10]
:0046D9B7 50                      push eax
:0046D9B8 56                      push esi
:0046D9B9 A1C8BD6200              mov eax, [0062BDC8]
:0046D9BE 50                      push eax

* Reference To: USER32.DialogBoxParamA, Ord:008Ah
                                  |
:0046D9BF FF15140B6300            Call dword ptr [00630B14]
:0046D9C5 668945EE                mov [ebp-12], ax
:0046D9C9 8B45E8                  mov eax, [ebp-18]

( ... lines and lines )

Ok, we don't have what i told you,  **** don't try to find something that do not exist **** , so look for variations from the basic scheme. Use softice to get the real caller and its neighborhood:

:00472817 668B442414              mov ax, [esp + 14]
:0047281C 6639842478010000        cmp [esp + 00000178], ax
:00472824 721C                    jb 00472842; <- CAN YOU FEEL IT !!
:00472826 6A00                    push 00000000
: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;  <<< CALLING nag!!!
:0047283F 83C414                  add esp, 00000014

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00472824(C)
|
:00472842 668B5506                mov dx, [ebp+06]

Now you understand Softice is a must. We want to jump for ever. If we look the registers window bx is zero at :47281c, so change it:
 
old ----
:0047281C 6639842478010000        cmp [esp + 00000178], ax
new ----
:0047281C 663b9c2478010000        cmp [esp + 00000178], bx

I know it is comparing limit-date and today-date, but I don't know yet the details ( that is what I was telling before ). Now pull from the string. It is important to know how the program gets the installation date. There are two possibilities:

1) Get it once and store it (even with copies in memory).
2) Get it whenever you need it (even checking it with some copies).

The second one is safer, but less common. Try the first one, which i am going to explain thoroughtly. Remember the code:

:00472817 668B442414          mov ax, [esp + 14]; <-- get limit day
:0047281C 6639842478010000    cmp [esp + 00000178], ax;
:00472824 721C                jb 00472842;

When placed at cmp (for example) in Softice do:

bpm [esp+178] w; <-----> suppose ss:(esp+178) == ADDR1

disable it, restart money and enable it. Probably it is a copy of a previous variable with the right value, then breakpoint it and so on. If the variable
is located inside stack, softice will break one million times, so use in this case:

bpm [esp+178] w if bplog

Then instead of breaking, the hit will be logged. But, think, we must stop the loggin when the var remains assigned to the right value, so USE ALSO:

bpx Last line known where the var has the right value (limit date)

for example bpx on any of the 3 lines of code above, then when it stops, look at the last match ( each one is logged by bplog ). More or less you'll find:

(... one million loggins)
loggin bp 1 bpm ADDR1 w dr3 if bplog (#137:472608)
Break due to 137:47281c

Ok, let's have a look to 472608:

:004725FC 668B8528020000          mov ax, [ebp+00000228]; <<< GET
:00472603 6689442414              mov [esp + 14], ax; <<< COPY
:00472608 E8F37CFFFF              call 0046A300

You can realize that softice logs the next instruction executed after the breakpoint. Continue our "searching-job". Edit our bpm and change it to:

bpm ebp+228 w if bplog

Restart Money 97 and ... what happen ? Softice crashes while loggin: WHY ? PLEASE WRITE THIS IN YOUR FACE, OR WHEREVER. I use to forget it and lose time wondering what's going on. Some actions should not be interrupted, one of them is DISK TRANSFER, so when your bpm crashes THINK THAT THIS MEMORY LOCATION IS WRITTEN DIRECTLY FROM A READ FILE OPERATION ( _hread _lread and so on). What does it mean ? the limit day is got from a file: yes, yes sample.mny. I told you.

Solution: bpx _lread ( look at imported functions )

enable it and land in the right _lread. Just after it is read, bpm (now for read-write operations ) the same location that crashes the program, and all the new copies the program may do afterwards (with the right value; actually, in money 97 it is copied once, and the original location is removed). If you trace them, they are gonna be compared before the nag (we know where) and before the second nag (when filling the database). Trace this location: ds:[628664] (the copy I told you), and try to enter an operation on 1/1/99. It'll break here:

:005C2C77 837D3000                cmp [ebp+30], 00000000
:005C2C7B 7421                    je 005C2C9E
:005C2C7D 8B4D3C                  mov ecx, [ebp+3C]
:005C2C80 668B81B5010000          mov ax, [ecx+000001B5]
:005C2C87 663DFFFF                cmp ax, FFFF
:005C2C8B 0F842F0D0000            je 005C39C0
:005C2C91 66390564866200          cmp [00628664], ax; <<< HERE !!
:005C2C98 0F863B0D0000            jbe 005C39D9; << jump bad guy

We don't want to jump. Look again at the reg. window and change it to:

:005C2C91 66391d64866200          cmp [00628664], bx;

Is it possible to jump always in 5c2c7b ?. Good question, Try it !
 
Just one more thing, not very important. You cannot use the program if the installation date is older than today (for example you start the program on 2050, and want to use it forever back to the present, just another approach). You just have to trace some calls to GetLocalTime, and very soon there's a comparison with installation-date. Exactly here:
 
; in this moment ax == today
:00472882 6639442414              cmp [esp + 14], ax;
:00472887 768E                    jbe 00472817;

So, jump always again. Change it to:
:00472882 663dffff                cmp ax, ffff;
:00472886 90                      nop
:00472887 768E                    jbe 00472817;

And use it like a tribute to our beloved (but lately hated) nop.
Now we have it cracked. What happen with date encryption ? ok, let's see.
I'm gonna show what's going on ax and the memory location, but first I have to tell you: I think it is absolutly stupid, why they don't just take the days since 1970 ? Perharps, the programmer wanted to tell us his/her born-date. this is ax:
 
       ah              al
  =============   =============
| , , , | , , , | , , , | , , , |   ---- > ax register

Now the routine step by step:
                |
* Reference To: KERNEL32.GetLocalTime, Ord:00E2h
                                  |
:0046A308 FF15B0006300            Call dword ptr [006300B0]
:0046A30E 668B442406              mov ax, [esp + 06]; << get month

|0,0,0,0|0,0,0,0|0,0,0,0|m,m,m,m|   ---- > ax register
                |         month
( ... a month takes in the worst case 4 bits )

:0046A313 6648                    dec ax

( it adjusted month info, because January is mounth 1 instead of 0 )

:0046A315 66C1E005                shl eax, 05

|0,0,0,0|0,0,0,m|m,m,m,0|0,0,0,0|   ---- > ax register
                |
We can know how many days are left in a year by: month*30 + days, taking care that January is month number 0. Now in ax we got the first part of the sum. Nota that in this case a month is gonna have 31 days always.

:0046A319 6633442402              xor ax, [esp + 02]; << [esp+02]=ffff;

|1,1,1,1|1,1,1,M|M,M,M,1|1,1,1,1|   ---- > ax register
                |
I mean M as month xor f, or other way MMMM=(15 - month)

:0046A31E 6625E001                and ax, 01E0

|0,0,0,0|0,0,0,M|M,M,M,0|0,0,0,0|   ---- > ax register
                |

:0046A322 6631442402              xor [esp + 02], ax

|1,1,1,1|1,1,1,m|m,m,m,1|1,1,1,1|   ---- > [esp + 2 ] location
                |
:0046A327 668B44240A              mov ax, [esp + 0A]; << get days

|0,0,0,0|0,0,0,0|0,0,0,d|d,d,d,d|   ---- > ax register
                |
( days info takes as much as 5 bits )

:0046A32C 6648                    dec ax
:0046A32E 6633442402              xor ax, [esp + 02]

|1,1,1,1|1,1,1,1|1,1,1,D|D,D,D,D|   ---- > ax register
                |

:0046A333 66251F00                and ax, 001F

|0,0,0,0|0,0,0,0|0,0,0,D|D,D,D,D|   ---- > ax register
                |
:0046A337 6631442402              xor [esp + 02], ax

|1,1,1,1|1,1,1,m|m,m,m,d|d,d,d,d|   ---- > [esp + 02]
                |
( so we have how many days left this year, with months of 31 days )

:0046A33C 8B442404                mov eax, [esp + 04]; << get year

|0,0,0,0|0,y,y,y|y,y,y,y|y,y,y,y|   ---- > ax register
                |
( I can't think of anybody using money needing more than 11 bits )

:0046A340 25FFFF0000              and eax, 0000FFFF
:0046A345 2D9C070000              sub eax, 0000079C;
:0046A34A 83F87F                  cmp eax, 0000007F; << year 2075 ?
:0046A34D 7E1F                    jle 0046A36E       ---|
:0046A34F B89CFFFFFF              mov eax, FFFFFF9C     |
                                                        |
                                                        |
:0046A354 6601442404              add [esp + 04], ax    |
:0046A359 8B4C2404                mov ecx, [esp + 04]   |
:0046A35D 81E1FFFF0000            and ecx, 0000FFFF     |
:0046A363 81E99C070000            sub ecx, 0000079C     |
:0046A369 83F97F                  cmp ecx, 0000007F     |
:0046A36C 7FE6                    jg 0046A354           |
                                                        |
                                                        |
:0046A36E 668B442404              mov ax, [esp + 04] <--|

( loading year again )

:0046A373 66C1E009                shl eax, 09

|y,y,y,y|y,y,y,0|0,0,0,0|0,0,0,0|   ---- > ax register
                |

:0046A377 6633442402              xor ax, [esp + 02]

|Y,Y,Y,Y|Y,Y,Y,m|m,m,m,d|d,d,d,d|   ---- > ax register
                |
( coding the year ? not yet )

:0046A37C 6625FF01                and ax, 01FF

|0,0,0,0|0,0,0,m|m,m,m,d|d,d,d,d|   ---- > ax register
                |
( more of stupid software )

:0046A380 668B4C2404              mov cx, [esp + 04]; << load year again

|0,0,0,0|0,y,y,y|y,y,y,y|y,y,y,y|   ---- > cx register
                |
:0046A385 6683E91C                sub cx, 001C

|0,0,0,0|0,Y,Y,Y|Y,Y,Y,Y|Y,Y,Y,Y|   ---- > cx register
                |
( now YYYYYYYYYYY= year - 28 = 1969, more absurd )

:0046A389 66C1E109                shl ecx, 09

|Y,Y,Y,Y|Y,Y,Y,0|0,0,0,0|0,0,0,0|   ---- > cx register
                |

now the year has been stripped, now we have something like

   ( year - 28 ) % 128

What the hell is it ? They are coding somehow the year information, the same way they did with "month" and "days", but now it's not so perferct. For relating our "year", with "money97-year", first think of a year with 512 days/year and second no Gregorian Callendar, year 0 is no more Jesus' born-date, Oh ! Sinners ! . Like the modified year is divided by 128, this callendar is stable during 128 years before it is reset, but for money 97 is enough. Now year 0 is 1948.
 

:0046A38D 6633C1                  xor ax, cx

|Y,Y,Y,Y|Y,Y,Y,m|m,m,m,d|d,d,d,d|   ---- > ax register
                |
now we got the final compose. Call it STUPID_DATE

:0046A390 6689442402              mov [esp + 02], ax
:0046A395 83C414                  add esp, 00000014
:0046A398 C3                      ret
 
Now you'll agree is stupid. Well if you want to translate from DATE of this stupid programmer born in 1948 to OUR DATE:

year = 1948 + (INT) STUPID_DATE/512
month = (int) (STUPID_DATE % 512 ) /31
year =  (STUPID_DATE % 512 ) % 31

You may see there's a lot of 16 bits code. Is the protection taken from old 16 bits money 94 ? Very close, very close, not exactly, but they are very similar. I'm not gonna explain the other one like this, because there's nothing new. Here I'm giving you the code of money 94 coding (now that money97's is recorded in your mind ). It's up to you to delve inside it and realize it's practically the same I explained before.

:0081.0563 9AC008FFFF             call 0010.08C0
:0081.0568 83C402                 add sp, 0002
:0081.056B 8A46F7                 mov al , [bp-09]; << get month
:0081.056E 2AE4                   sub ah, ah
:0081.0570 48                     dec ax
:0081.0571 C1E005                 shl ax, 05      << shift month
:0081.0574 3346FC                 xor ax, [bp-04]  << xor against 00 !!
:0081.0577 25E001                 and ax, 01E0     << filter it
:0081.057A 3146FC                 xor [bp-04], ax
:0081.057D 8A46FC                 mov al , [bp-04]  << month coded
:0081.0580 8A4EF6                 mov cl , [bp-0A]  << get days
:0081.0583 2AED                   sub ch, ch
:0081.0585 49                     dec cx
:0081.0586 32C1                   xor al , cl
:0081.0588 251F00                 and ax, 001F
:0081.058B 3146FC                 xor [bp-04], ax
:0081.058E 8B46F8                 mov ax, [bp-08]
:0081.0591 2D9C07                 sub ax, 079C
:0081.0594 3D7F00                 cmp ax, 007F     << year 2047 ?
:0081.0597 760F                   jbe 05A8
 

:0081.0599 836EF864               sub word ptr [bp-08], 0064
:0081.059D 8B46F8                 mov ax, [bp-08]
:0081.05A0 2D9C07                 sub ax, 079C
:0081.05A3 3D7F00                 cmp ax, 007F
:0081.05A6 77F1                   ja 0599
 

:0081.05A8 8B46F8                 mov ax, [bp-08]  << get year
:0081.05AB 2D1C00                 sub ax, 001C     << substract again
:0081.05AE C1E009                 shl ax, 09      << shifting
:0081.05B1 8B4EF8                 mov cx, [bp-08]
:0081.05B4 C1E109                 shl cx, 09
:0081.05B7 334EFC                 xor cx, [bp-04]
:0081.05BA 80E501                 and ch, 01
:0081.05BD 33C8                   xor cx, ax
:0081.05BF 894EFC                 mov [bp-04], cx
:0081.05C2 8BC1                   mov ax, cx
:0081.05C4 8B5E06                 mov bx, [bp+06]
:0081.05C7 368907                 mov ss:[bx], ax
: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
 
 

MONEY 94
=========

Three years before. First of all THANX A LOT reverser for the link, I was desperate. Do you think the protection is very different ? Of course not.
+orc gave some clues, but they were a little bit tricky

1) In the version I cracked the addresses were slightly sled, for example:

  * lesson 4.2 *
:0008.17CA 8B46F4                 mov ax, [bp-0C]
:0008.17CD 3946F0                 cmp [bp-10], ax
:0008.17D0 7246                   jb 1818
  * money 94 frech version *
:0008.17E2 8B46F4                 mov ax, [bp-0C]
:0008.17E5 3946F0                 cmp [bp-10], ax
:0008.17E8 7246                   jb 1830

So, be careful and don't Bp without thinking ! Second and more suspicious, is the supposed crack ( taken from lesson 4.2 ):

********************* the hot code ********************

:0008.17CA 8B46F4                 mov ax, [bp-0C]
:0008.17CD 3946F0                 cmp [bp-10], ax
:0008.17D0 7246                   jb 1818

* Possible Reference to Dialog: DialogID_0494
                                  |
:0008.17D2 689404                 push 0494 ;HERE! NOTICE OF EXPIRATION
: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 ;

********************* SUPPOSED +ORC CRACK ******************

instead of
:0008.17D0 7246            jb 1818
let's have
:0008.17D0 55              push bp
:0008.17D0 5D              pop bp
(nooping it)
                                                                              ************************************************************

Is it a joke or I don't understand anything ? Well it's a joke ( the addresses, the crack ...  ). If you change it that way, the nag snaps always, so you must jump always, IT IS EXACTLY the scheme I told you ( bridge over the nag ) at the begining. So the cmp:

cmp limitday, today;

You just need to trace limitday and you have the crack. But what about today ? It is 16 bits, so no GetLocalTime, ... , or yes, they use the old-styled GetLocalTime, a service of Int 21h. In that moment I remembered a 1996 program: Secure Shell or something, that used a rather interesting function kernel!dos3call for getting the current date. Then I tried it and it was the one used in money 94 fv. Very funny, look at microsoft docs, now they recommend NOT TO USE this function. BTW, they use it a lot.

I'm not gonna explain the "tracing" process the way I did with money 97, it is just the same thing. Ok, we are in front of +orc's routine, but let's look some instructions before:
 

: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]; <<< get today
:0008.181D 3946FE                 cmp [bp-02], ax; <<< some checking
:0008.1820 73C0                   jnb 17E2

... if this secret checking is right jump

:0008.17E2 8B46F4                 mov ax, [bp-0C] ; <<< get today
:0008.17E5 3946F0                 cmp [bp-10], ax; <<  limitday ?
:0008.17E8 7246                   jb 1830; <<< the code given by +orc

Play a little bit with the carry flag, and you'll realize that the first one checks limit-date with today+63. It is a way of checking today and installation date, of course today should be later or you'll be nagged as usual. Modify BOTH jumpings and jump always. These are the MODIFIED instructions:

:0008.181D 663bc0                 cmp eax, eax; << new cmp

(... improve old money 94 with the powerful 32 bits technology )

:0008.17E5 3b7ef0                 cmp di, [bp-10]; << second cmp

What else ? Ah, the database. So easy ( este crack ya lo podria terminar cualquier politico ), so easy that I cannot repeat it. Follow my instructions in money 97 (tracing back a value with the famous limitdate, taken from :0008.17E5, for instance). You'll discover that it is copied again in a fixed location, just like money 97 scheme, and you will land in the middle of this incredibly tough piece of code:

:0014.2B63 A1D664                 mov ax, [64D6]; << get limit-date
:0014.2B66 3906D870               cmp [70D8], ax; << date entered ?
:0014.2B6A 7203                   jb 2B6F; << below, jump good girl
:0014.2B6C E9970A                 jmp 3606; << nobody fools microsft !

This way you just can have fun wondering a new way of changing the code. There are one million ways, here there's one:

:0014.2B66 3906d664               cmp [64d6], ax;
:0014.2B6A 7403                   je 2B6F;
 
I would like you to enjoy these two programs, but it's practically imposible to enjoy esta BOBERIA.
 
 

... JUST ONE MOMENT ... OHH, THE QUESTION
===========================================
A little bit more of Microsoft Crapping. Microsoft Project 4.1, the last trap of our good old slick +orc. I'm giving you the last part of the code given by +him (even his comms). I'll comment it beautifully:
 

:05167F6A E808CFEBFF          call 05024E77 ;CALL GetLocalTime
:05167F6F 66817DF4C007        cmp [ebp-0C], 07C0  ;is 1984?
:05167F75 722F                jb 05167FA6       ;go badflag
:05167F77 66817DF40108        cmp [ebp-0C], 0801  ;is 2049?
:05167F7D 7727                ja 05167FA6         ;go badflag
:05167F7F FF75F4              push [ebp-0C]    ;save validyear
:05167F82 660FB645F7          movzx byte ptr ax, [ebp-09] ;pushday
:05167F87 660FB64DF6          movzx byte ptr cx, [ebp-0A] ;pushmonth
:05167F8C 50                  push eax
:05167F8D 51                  push ecx

============
Not a great thing. A flag is used for pointing the naughty guys. It will be set at :5167fa6. Let's suppose we pass this first test over. What else ?
============
 

:05167F8E E84EC10B00          call 052240E1       ;call here
:05167F93 663B45E4            cmp ax, [ebp-1C]
: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 ;BAD FLAG !!!***
   :no_bad_flag
:05167FAC FF75DC           push [ebp-24]
:05167FAF FF151C5C3C05     Call dword ptr[053C5C1C];RegCloseKey
:05167FB5 EB25             jmp 05167FDC ;jump 2nd getlocaltime
 

======================================================
A mistirious call not commented by +orc. Je, Je. Softice a little bit, and chan tata chan, TODAY-DATE ( coded in a way i don't really care ) IS RETURNED IN AX REGISTER. And it is compared with... LIMIT-DATE, of course. If you read lesson 4.2, the supposed holly "cmp" was place later ... pretty suspicious. The flag will be set if you passed over the time limit, but look, if you are a GOOD GUY you'll enjoy some bonus-instructions:

; firstly it is important for you to know that at this point
; [ebp-e]=0x5e and ax=today_date
 

:05167F99 668B4DE4            mov cx, [ebp-1C]; << load limit-date
:05167F9D 662BC8              sub cx, ax; << days to end
:05167FA0 66894DF2            mov [ebp-0E], cx; << save days to end

Thus, if you are a bad guy, [ebp-e] will remain with 0x5e, it is, YOU WILL BE IN YOUR FIRST DAY OF USING THE PROGRAM. Obviously [bp-e] will not keep information about the timeout, so we just have the flag.
==========================================================
 

... in the last snippet everybody jumped here
 

   :valid_year_almost_everybody_calls_here
:05167FDC 66837DFE00       cmp [ebp-02], 0000   ;valid_flag?
:05167FE1 755F             jne 05168042  ;beggar off:unvalid something
: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,unvalid
: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         ;no? be damned!
  :valid_e10_and_e14
: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  ;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]

===============================
At :5168015 we had the suppossed holly "cmp", but we got one before, which set bad-flag to 1, and if it's set, we'll jump at :05167FE1 to the ugly world of nagg windows, and no further execution of this second holly "cmp". But if we do not jump (setting bad-flat to 0, as +he tells), this second "cmp" will be good always, 'cause remember that we shall be ALWAYS in our first day.

If we want to use anyway the failed crack, you should change also the first cmp/jge located at :05167F93.
================================
 

I don't know. Were they trying a NEW protection scheme ? Something BIG ?. Sorry the only BIG, HUGE, thing around is Msproject itself.