MSPROJECT REPRESENTATION OF TIME: The time in this protection is represented as the number of days passed since 1983-DEC-31. For example the decimal number 1 means 1984-Jan-1 and 4337 means 1995-Aug-17. The routine which converts the date to the number of passed days starts at 052240E1. It gets day, month and year as parameters (1983 < year < 2050). :052240E1 mov cx, word ptr [esp + 0C] ;get year :052240E6 push esi :052240E7 movzx eax, cx ;put in eax :052240EA movzx edx, word ptr [esp + 0C] ;get month :052240EF imul eax, eax, 000005B5 ;year * 1461 ;(1641=number of days in 4 year ,+ a leap day) :052240F5 sub eax, 002C3ABD ;minus 4 * the number of days ;passed till 1983-dec-31 ;(correction included for missing ;leap years like 1000) :052240FA sar eax, 00000002 ;divide by 4 to get the day number ;represented by the year ;lets see the month :052240FD cmp edx, 00000001 ;January? :05224100 je 05224125 ;yes, go and add the days :05224102 cmp edx, 00000002 ;February? :05224105 je 0522412C ;yes, no leap day problem go on :05224107 and cx, 0003 ;year modulo 4 (0 means leap year) :0522410B cmp cx, 0001 ;set carry if leap year :0522410F sbb esi, esi ;use carry to compensate :05224111 neg esi ;for leap day :05224113 add si, word ptr [2*edx + 053C317C] ;look up from table the ;number of days passed ;from the year till the ;begining of the actual ;month :0522411B add si, word ptr [esp + 08] ;add date day :05224120 add ax, si ;finally, sum all things up in ax :05224123 jmp 0522413B ;and return * Referenced by Jump at Address: |:05224100(C) | :05224125 add ax, word ptr [esp + 08] ;If Jan then just add the days :0522412A jmp 0522413B ;return * Referenced by a Jump at Address: |:05224105(C) | :0522412C mov dx, word ptr [053C3180] ;If Feb take the days of Jan :05224133 add dx, word ptr [esp + 08] ;plus the day :05224138 add ax, dx ;sum all up in ax * Referenced by Jump at Addresses: |:05224123(U), :0522412A(U) | :0522413B pop esi :0522413C ret 000C ;return The time limit (installation date + 90 days) is stored in the last digits of the registry key HKCR\Eced.2\CLSID as a normal decimal number. (The key also containes a checksum to prevent messing with it.) Here is the protection routine, with your comments. My comments are marked with !! :05167F6A call 05024E77 ;CALL GetLocalTime :05167F6F cmp [ebp-0C], 07C0 ;is 1984? :05167F75 jb 05167FA6 ;go badflag :05167F77 cmp [ebp-0C], 0801 ;is 2049? :05167F7D ja 05167FA6 ;go badflag :05167F7F push [ebp-0C] ;save validyear :05167F82 movzx byte ptr ax, [ebp-09] ;pushday :05167F87 movzx byte ptr cx, [ebp-0A] ;pushmonth :05167F8C push eax :05167F8D push ecx :05167F8E call 052240E1 ;call here !!This call converts the current date to the number of days passed since 1983-dec-31 :05167F93 cmp ax, [ebp-1C] !!Compares it to the time limit which is stored in the registry and read by the routine at 502538A This is the crucial compare. :05167F97 jge 05167FA6 ;go badflag :05167F99 mov cx, [ebp-1C] :05167F9D sub cx, ax !!Calculate the remaining days :05167FA0 mov [ebp-0E], cx !!store it :05167FA4 jmp 05167FAC ;do not badflag * Referenced by a Jump at Addresses:05167E13(C), :05167ED7(C), :05167F1B(C), :05167F64(C), :05167F75(C), :05167F7D(C), :05167F97(C); lotta routines badflag :here... this is BADFLAG INTENSIVE :05167FA6 mov [ebp-02], 0001 ;BAD FLAG !!!*** :no_bad_flag :05167FAC push [ebp-24] :05167FAF Call dword ptr[053C5C1C] ;RegCloseKey :05167FB5 jmp 05167FDC ;jump 2nd getlocaltime :only_5167DE5_calls_this_second_GetLocalTime :05167FB7 mov [ebp-14], 0000 :05167FBD lea eax, [ebp-0C] :05167FC0 push eax ;SECOND GetLocalTime :05167FC1 call 05024E77 GetLocalTime ;call it :05167FC6 cmp [ebp-0C], 07C0 ;between 1984... :05167FCC jb 05167FD6 ;jump flag_unvalid_year :05167FCE cmp [ebp-0C], 0801 ;...and 2049? :05167FD4 jbe 05167FDC ;jump_to_valid_year :flag_unvalid_year :05167FD6 mov [ebp-02], 0001 ;flag "unvalid_year" :valid_year_almost_everybody_calls_here :05167FDC cmp [ebp-02], 0000 ;valid_flag? :05167FE1 jne 05168042 ;beggar off:unvalid something :05167FE3 cmp [ebp-10], 0000 ;valid ebp-10? :05167FE8 jne 05167FF8 ;jmp unvalid_e10 :05167FEA cmp [ebp-14], 0000 ;valid ebp-14? :05167FEF je 05168042 ;beggar off,unvalid !!Problem with registry key :05167FF1 cmp [ebp-10], 0000 ;sure that ebp-10=0? :05167FF6 je 05167FFF ;continue ifso :unvalid_e10 :05167FF8 cmp [ebp-14], 0000 ;check if e14valid :05167FFD jne 05168042 ;no? be damned! :valid_e10_and_e14 :05167FFF movsx word ptr ecx, [ebp-0E] ;e10 and e14 true !!Get remaining days :05168003 cmp dword ptr [052C1958], 0001 :0516800B sbb eax, eax :0516800D and eax, 0000005A ;pretty obvious :05168010 add eax, 0000005A ;isnt it? :05168013 cmp eax, ecx ;HERE******** !!Remaining days more than 90 means system date is set back before the installation time :05168015 jl 05168042 ;beggar off :05168017 cmp [ebp-0E], 001E ;0x1E = 30 :0516801C jg 05168068 ;good guy jump :0516801E lea eax, [ebp-0C] !!Go and send message less than 30 days remained The crack at 05168013 can not work well, because it does not check if we passed the 90 day limit, but it checks whether the system time is set back before the installation date. If we patch at 5167FA6 then the main check (at 5167F93) is cracked and ebp-0E (the remaining days) is not recalculated, but remains as initialized to 5A meaning for the rest of the protection that not a single day has passed since the installation. MSMONEY In the cracking of MSMONEY the most difficult for me was finding the targets. While I could collect a lot of different versions I have to admit I could not find the exact files you were talking about (noticing the differences in the addresses compared to the code pieces in your tutorial). While I expected it in the case of MSMONEY 3.0, because mine is a french version (thanks to Fravia), I was surprised that even MSMONEY97 has different versions. You did not gave the exact file version number (my claims to be 5.0p) or file size, so I don't know who's the later version. I just can hope that if yours the later one the protection scheme has not changed. (On the other hand, I would be really surprised keeping in mind that the protection has not changed a bit since MSMONEY 1.0 (1992)). Well, I described the situation and present my work, you have to judge, whether cracking different versions disqualify me automatically, from applying to the +HCU membership. Here, I present the analysis of the protections in MSMONEY 3.0 and MSMONEY97. I have actually done the cracking on MSMONEY 1.0 and just simply checked these the later versions if they have the same protection routines. (Of course they have the same protections! Amazing!) REPRESENTATION OF TIME: In MSMONEY the time is represented in a word in the following packed format: bits 0-4 day (0 representing day 1 of the month) bits 5-8 month (0 representing January) bits 9-15 year (counted from 1948 which is represented by 0) Therefore, 0000h is 1948-Jan-1, 62B2h is 1997-Jun-19 and FF7Eh being the latest date which can be represented 2075-Dec-31. The routine which codes the system date to the word starts at (51):552, in my MSMONEY 3.0 and at 470640 in MSMONEY97. The date encoding function of my MSMONEY 3.0. :0081.0552 mov ax, ds :0081.0554 nop :0081.0555 inc bp ;function entering set up :0081.0556 push bp :0081.0557 mov bp, sp :0081.0559 push ds :0081.055A mov ds, ax :0081.055C sub sp, 000A :0081.055F lea ax, [bp-0A] ;where to put the date :0081.0562 push ax :0081.0563 call 0010.08C0 ;get system date through dos int21/2A :0081.0568 add sp, 0002 :0081.056B mov al , [bp-09] ;ok, lets start with month :0081.056E sub ah, ah ;clear AH :0081.0570 dec ax ;count month from 0 (Jan=0) :0081.0571 shl ax, 05 ;shift to position (bit 5-8) :0081.0574 xor ax, [bp-04] ;? :0081.0577 and ax, 01E0 ;clear everything except month bits ;01E0 is the mask to isolate month :0081.057A xor [bp-04], ax ;store result :0081.057D mov al , [bp-04] ;? :0081.0580 mov cl , [bp-0A] ;get days :0081.0583 sub ch, ch ;clear ch :0081.0585 dec cx ;count days from 0 :0081.0586 xor al , cl ;? :0081.0588 and ax, 001F ;clear everything except day bits ;001F is the mask to isolate days :0081.058B xor [bp-04], ax ;combine it with the stored month :0081.058E mov ax, [bp-08] ;get year :0081.0591 sub ax, 079C ;minus 1948 :0081.0594 cmp ax, 007F ;compare to the highest number we can ;represent (7F means 2075) :0081.0597 jbe 05A8 ;less, ok go on * Referenced by a Jump at Address: |:0081.05A6(C) | :0081.0599 sub word ptr [bp-08], 0064 ;decrease year by 100 :0081.059D mov ax, [bp-08] :0081.05A0 sub ax, 079C :0081.05A3 cmp ax, 007F :0081.05A6 ja 0599 ;until it is less then 2075 :) * Referenced by Jump at Address: |:0081.0597(C) | :0081.05A8 mov ax, [bp-08] ;so get the year again :0081.05AB sub ax, 001C ;minus 1948 ;if you shift to the left 1948 by 9 ;0011100 will be in bits 15-9 which ;is exactly 1C before the shift :0081.05AE shl ax, 09 ;shift to position :0081.05B1 mov cx, [bp-08] ;? :0081.05B4 shl cx, 09 ;? :0081.05B7 xor cx, [bp-04] ;get month and day :0081.05BA and ch, 01 ;clear the year bits (this year is wrong) :0081.05BD xor cx, ax ;combine valid year with month and day :0081.05BF mov [bp-04], cx ;store the final result :0081.05C2 mov ax, cx ;result in ax too :0081.05C4 mov bx, [bp+06] :0081.05C7 mov ss:[bx], ax ;return the final result :0081.05CA mov ax, bx :0081.05CC mov dx, ss :0081.05CE lea sp, [bp-02] :0081.05D1 pop ds :0081.05D2 pop bp :0081.05D3 dec bp :0081.05D4 retf 0002 I don't know what the instructions marked by ? mean. Clearly bp-04 is the position which is used to combine the month, day and year bits, but the initial value in it has no effect on the final result, and it always containes the result at the end of the routine. Here is the corresponding function of my MSMONEY97. It does everything exactly like the old function. :004689D0 sub esp, 00000014 ;locals :004689D3 lea eax, dword ptr [esp + 04] ;where to put the date :004689D7 push eax * Reference To: KERNEL32.GetLocalTime, Ord:00E2h | :004689D8 Call dword ptr [00620140] ;get date :004689DE mov ax, word ptr [esp + 06] ;get month :004689E3 dec ax ;Jan will be 0 :004689E5 shl ax, 0005 ;shift to position :004689E9 xor ax, word ptr [esp + 02] ;? :004689EE and ax, 01E0 ;isolate month bits :004689F2 xor word ptr [esp + 02], ax ;store month :004689F7 mov ax, word ptr [esp + 0A] ;get day :004689FC dec ax ;first of month will be 0 :004689FE xor ax, word ptr [esp + 02] ;? :00468A03 and ax, 001F ;isolate days :00468A07 xor word ptr [esp + 02], ax ;combine days with month :00468A0C mov eax, dword ptr [esp + 04] ;get year :00468A10 and eax, 0000FFFF ;clear upper part of eax :00468A15 sub eax, 0000079C ;minus 1948 :00468A1A cmp eax, 0000007F ;over 2075 :00468A1D jle 00468A3E :00468A1F mov eax, FFFFFF9C ;-100 * Referenced by a Jump at Address: |:00468A3C(C) | :00468A24 add word ptr [esp + 04], ax ;add to year :00468A29 mov ecx, dword ptr [esp + 04] :00468A2D and ecx, 0000FFFF :00468A33 sub ecx, 0000079C :00468A39 cmp ecx, 0000007F :00468A3C jg 00468A24 ;until its less then 2075 * Referenced by a Jump at Address: |:00468A1D(C) | :00468A3E mov ax, word ptr [esp + 04] ;? :00468A43 shl ax, 0009 ;? :00468A47 xor ax, word ptr [esp + 02] ;get month and day :00468A4C and ax, 01FF ;clear year bits :00468A50 mov cx, word ptr [esp + 04] ;get year :00468A55 sub cx, 001C ;correct with 1948 :00468A59 shl cx, 0009 ;shift to position :00468A5D xor ax, cx ;combine with month and day :00468A60 mov word ptr [esp + 02], ax ;store :00468A65 add esp, 00000014 :00468A68 ret THE WORKING OF THE PROTECTION: Every time a new MSMONEY data file (.MNY) is created the actual system date + the time limit (either 60 or 90 days) is written into the data file at offset 224h in MSMONEY 3.0 and 228h in MSMONEY97. When the data file is opened this file limit date is compared to the system date to see if 60 days have passed since the creation of the file and also it is copied to the fix memory position [64D6] in MSMONEY 3.0 and [00619244] in MSMONEY97, to be used for the check of the date of every entered transaction. The routine which calculates this limit date of a file is also responsible for checking the year of the system date being the year when the demo is valid. This routine is the heart of the protection and starts at (08):14BC and 00470640 in the two versions, respectively. This routine is called when a new file is created or the year of an opened file does not match the year of the demo version (MNY files without a date limit or with date limit from other years can be opened in the demo and they get a new date limit). The function from my MSMONEY 3.0. (*** indicates the patches I made in my crack. They are explained at the end of the essay.) * Referenced by a CALL at Addresses: |:0008.0889, :0008.16D6, :0008.172B | :0008.14BC enter 0006, 00 ;allocate locals :0008.14C0 cmp word ptr [bp+04], 0000 ;calculate limit rigth away? :0008.14C4 je 14E4 ;yes, go and calculate :0008.14C6 mov ah, [64D7] ;no, lets see what we have already :0008.14CA and ax, FE00 ;isolate year bits with mask :0008.14CD mov [bp-06], ax ;store it :0008.14D0 cmp ax, 5C00 ;1994? :0008.14D3 je 14DF ;ok, valid year, short way out :0008.14D5 cmp ax, 5E00 ;1995? :0008.14D8 je 14DF ;ok, valid year, short way out :0008.14DA cmp ax, 6000 ;1996? (still valid year because the 60 ;day limit might reach into 1996) :0008.14DD jne 14E4 ;none, go and calculate new ;time limit for the file * Referenced by a Jump at Addresses: |:0008.14D3(C), :0008.14D8(C) | :0008.14DF mov ax, [64D6] ;lets fetch the current time limit :0008.14E2 jmp 152F ;short way out * Referenced by a Jump at Addresses: |:0008.14C4(C), :0008.14DD(C) | ;this calculates a new time limit :0008.14E4 lea ax, [bp-04] ;where to put the return value :0008.14E7 push ax :0008.14E8 call 0081.0552 ;get the coded system date with ;the previously described function :0008.14ED mov bx, ax :0008.14EF mov ax, ss:[bx] ;the coded date in ax :0008.14F2 mov [bp-02], ax ;store it *** mov [bp-02], F400 *** :0008.14F5 mov ah, [bp-01] ;take upper byte *** jmp 151C *** :0008.14F8 and ah, FE ;isolate year bits with mask :0008.14FB cmp ah, 5C ;1994? :0008.14FE je 151C ;ok, valid year :0008.1500 mov ah, [bp-01] ;get upper half again :0008.1503 and ah, FE ;isolate year bits :0008.1506 cmp ah, 5E ;1995? :0008.1509 je 151C ;ok, valid year ;notice that 1996 is not valid here ;because this is pure system date :0008.150B push 0917 ;prepare for bad system date message :0008.150E push 0000 :0008.1510 call 0064.AD1A ;send it :0008.1515 xor ax, ax ;set flag "not successful" :0008.1517 leave :0008.1518 ret 0004 ;leave :0008.151B nop * Referenced by a Jump at Addresses: |:0008.14FE(C), :0008.1509(C) | ;valid year, lets calculate time limit :0008.151C push word ptr [bp-02] ;push date :0008.151F push 003C ;push 60 days :0008.1521 lea ax, [bp-04] ;where to put the result :0008.1524 push ax :0008.1525 call 0081.05D8 ;calculate sytem date + 60 days ;we need a whole routine for this ;because the date is coded :0008.152A mov bx, ax :0008.152C mov ax, ss:[bx] ;get the new time limit date * Referenced by a Jump at Address: |:0008.14E2(U) | :0008.152F mov bx, [bp+06] :0008.1532 mov [bx], ax ;Store new time limit at [64D6] :0008.1534 mov ax, 0001 ;Flag set "successful" :0008.1537 leave :0008.1538 ret 0004 ;leave The similar routine in my MSMONEY 97 does everything like the old one: * Referenced by a CALL at Addresses: |:0046F068 , :00470B1E , :00470C38 , :00470CCE | :00470640 sub esp, 00000004 ;locals :00470643 cmp dword ptr [esp + 0C], 0 ;calculate new limit right away? :00470648 je 0047067C ;yes, go calculate :0047064A mov ax, [00619244] ;no, let's fetch what we have :00470650 and ax, FE00 ;isolate year bits :00470654 cmp ax, 6000 ;1996? :00470658 je 00470666 ;yes, don't recalculate :0047065A cmp ax, 6200 ;1997? :0047065E je 00470666 ;ok, don't recalculate :00470660 cmp ax, 6400 ;1998? :00470664 jne 0047067C ;none, of them go and calculate new * Referenced by a Jump at Addresses: |:00470658(C), :0047065E(C) | :00470666 mov ax, [00619244] ;we have a valid year so take the limit :0047066C mov ecx, dword ptr [esp + 08] ;where to put the result :00470670 mov word ptr [ecx], ax ;return the current limit :00470673 mov eax, 00000001 ;set flag "successful" :00470678 add esp, 00000004 :0047067B ret * Referenced by a Jump at Addresses: |:00470648(C), :00470664(C) | :0047067C call 004689D0 ;Get coded time :00470681 mov word ptr [esp + 02], ax ;save it *** mov [esp+02],F400 *** :00470686 and ax, FE00 ;take year*** jmp 004706B8 *** :0047068A cmp ax, 6000 ;1996? :0047068E je 004706B8 ;ok, valid go on :00470690 cmp ax, 6200 ;1997? :00470694 je 004706B8 ;ok, go on :00470696 push 00000000 ;prepare to bad system year message :00470698 mov eax, [00613FF0] :0047069D push 00000000 :0047069F push eax :004706A0 push 004CF510 :004706A5 push 00002FDC :004706AA call 0046BF10 ;send it :004706AF add esp, 00000014 :004706B2 xor eax, eax ;set flag to "unsuccessful" :004706B4 add esp, 00000004 :004706B7 ret ;leave * Referenced by a Jump at Addresses: |:0047068E(C), :00470694(C) | :004706B8 mov eax, dword ptr [esp + 02] ;get the ystem date :004706BC push 0000005A ;and 90 days :004706BE push eax :004706BF call 00468A70 ;calculate new time limit :004706C4 mov ecx, dword ptr [esp + 10] :004706C8 add esp, 00000008 :004706CB mov word ptr [ecx], ax ;return it :004706CE mov eax, 00000001 ;set flag "success" :004706D3 add esp, 00000004 :004706D6 ret As I told before, the time limit which is calculated by this function is stored in each money data file. The routine which checks this time limit upon opening the file starts at (08):1969 in my MSMONEY 3.0. It first compares the year of the file date to the year of the demo version, then goes on to check if the system timer is set back before the file creation date and finally checks the 60 day limit. ;File time limit year check :0008.1696 mov ax, [si+0224] ;get the time limit of the file :0008.169A mov [bp-0C], ax ;store it :0008.169D lea ax, [bp-02] :0008.16A0 push ax :0008.16A1 call 0081.0552 ;Get system date :0008.16A6 mov bx, ax :0008.16A8 mov ax, ss:[bx] :0008.16AB mov [bp-10], ax ;Store system date :0008.16AE mov ah, [bp-0B] ;Take file time limit :0008.16B1 and ax, FE00 ;Isolate year :0008.16B4 mov [bp+FE8C], ax ;Store it :0008.16B8 cmp ax, 5C00 ;1994? :0008.16BB jne 16C0 ;no, *** jmp 16D0 *** :0008.16BD jmp 1804 ;validhecks * Referenced by a Jump at Address: |:0008.16BB(C) | :0008.16C0 cmp ax, 5E00 ;1995? :0008.16C3 jne 16C8 ;no, :0008.16C5 jmp 1804 ;valid year, go on to further checks * Referenced by a Jump at Address: |:0008.16C3(C) | :0008.16C8 cmp ax, 6000 ;1996? (if we started to use the demo in 1995-dec) :0008.16CB jne 16D0 ;none, go to create new time limit :0008.16CD jmp 1804 ;ok, go on to further checks * Referenced by a Jump at Address: |:0008.16CB(C) | :0008.16D0 lea ax, [bp-0C] ;where to put new time limit :0008.16D3 push ax :0008.16D4 push 0001 ;force current time limit check :0008.16D6 call 14BC ;Go and create new file time limit ;with the above described function :0008.16D9 or ax, ax ;successful? :0008.16DB je 16E0 ;no, leave short way with error code (07E4) which ;means that you are not allowed to work with ;this file, but have to open another one :0008.16DD jmp 17D0 ;yes, go on to checks (pretty useless here) . . . :0008.17D0 mov ax, [bp-0C] ;get new time limit :0008.17D3 mov [si+0224], ax ;store it :0008.17D7 mov byte ptr [si+0223], 00 ;? :0008.17DC mov word ptr [195C], 0001 ;? * Referenced by a Jump at Address: |:0008.1820(C) | ;60 day limit check :0008.17E2 mov ax, [bp-0C] ;get stored file time limit :0008.17E5 cmp [bp-10], ax ;compare to stored system date :0008.17E8 jb 1830 ;still some day left go on * Possible Reference to Dialog: DialogID_0494 | :0008.17EA push 0494 ;Prepare to message 60 day is over :0008.17ED push SEG ADDR of Segment 0028 :0008.17F0 push 15C0 :0008.17F3 push word ptr [0B54] :0008.17F7 push 0000 :0008.17F9 push 0000 :0008.17FB push 0000 :0008.17FD call 0005.013A ;send it :0008.1802 jmp 186C ;and leave * Referenced by a Jump at Addresses: |:0008.16BD(U), :0008.16C5(U), :0008.16CD(U) | ;System timer set back check :0008.1804 push word ptr [bp-10];Push system date :0008.1807 push 003C ;Push 60 days :0008.1809 lea ax, [bp-04] ;Where to put result :0008.180C push ax :0008.180D call 0081.05D8 ;Add system date + 60 days :0008.1812 mov bx, ax :0008.1814 mov ax, ss:[bx] :0008.1817 mov [bp-02], ax ;Store system+60 :0008.181A mov ax, [bp-0C] ;get file time limit :0008.181D cmp [bp-02], ax ;Compare the two :0008.1820 jnb 17E2 ;File time limit smaller, ok go ;on to further check :0008.1822 push 0918 ;File time limit higher :0008.1825 push 0000 ;Send system timer set back :0008.1827 call 0064.AD1A ;message :0008.182C jmp 1607 :0008.182F nop * Referenced by a Jump at Address: |:0008.17E8(C) | ;last days warning check :0008.1830 push ax ;push file time limit :0008.1831 call 0081.08E6 ;convert packed time limit to plain ;number of days passed since 1948 :0008.1836 push word ptr [bp-10];push system date :0008.1839 mov si, ax ;store time limit day form :0008.183B call 0081.08E6 ;convert packed system date to passed days :0008.1840 sub si, ax ;calculate the difference :0008.1842 mov [bp-02], si ;store :0008.1845 cmp si, 0007 ;more than 1 week? :0008.1848 jg 186C ;yes, just go on :0008.184A mov al , [bp-02] ;Prepare for last days message :0008.184D add al, 30 ;number of remaining days in ASCII :0008.184F mov [bp-12], al :0008.1852 mov byte ptr [bp-11], 00 :0008.1856 push 4919 :0008.1859 push 07D2 :0008.185C lea ax, [bp-12] :0008.185F push ss :0008.1860 push ax :0008.1861 push 0000 :0008.1863 push 0000 :0008.1865 push 0040 :0008.1867 call 0064.B144 ;send last days warning This function is quite long and I intend to patch at the very begining of the checks, therefore I only cite the first part of the same MSMONEY97 routine which does everything like the old one. :00470BEC mov ax, word ptr [ebp+0228] ;get file time limit :00470BF3 mov word ptr [esp + 14], ax ;store it :00470BF8 call 004689D0 ;getsystem date :00470BFD mov word ptr [esp + 6C], ax ;store it :00470C02 mov word ptr [esp + 00000178], ax ;store it :00470C0A mov ax, word ptr [esp + 14] ;get file time limit :00470C0F and ax, FE00 ;isolate year :00470C13 cmp ax, 6000 ;1996? :00470C17 je 00470E63 ;ok, valid *** jmp 00470C31 *** :00470C1D cmp ax, 6200 ;1997? :00470C21 je 00470E63 ;ok, valid go on to further checks :00470C27 cmp ax, 6400 ;1998? :00470C2B je 00470E63 ;ok, valid go on to further checks :00470C31 lea eax, dword ptr [esp + 14] ;where to put new time limit :00470C35 push 00000001 ;force current time limit check :00470C37 push eax :00470C38 call 00470640 ;go and get new time limit :00470C3D add esp, 00000008 ;with the first described routine :00470C40 test eax, eax ;success? :00470C42 jne 00470DEA ;ok we have new time limit :00470C48 mov ax, 07E4 ;problem, leave with error code 07E4 :00470C4C pop ebp ;which means we cannot use this file :00470C4D pop edi :00470C4E pop esi :00470C4F pop ebx :00470C50 add esp, 00000268 :00470C56 ret The third component of the protection is the checking if the time of an entered transaction is over the date limit of the file. This comparison is done at (0E):2B63 in MSMONEY 3.0 and uses [64D6] where the limit is. :0014.2B4C cmp word ptr [bp+08], 0000 ;flag to check limit or not ;(set at (35):2234 push 01) :0014.2B50 je 2B6F ;just go on *** jmp 2B6F *** :0014.2B52 test byte ptr [17AC], 01 ;?flag (only in MSMONEY 3.0) :0014.2B57 jne 2B6F ;just go on :0014.2B59 cmp word ptr [70D8], FFFF ;test if there is a date :0014.2B5E jne 2B63 ;yes, go and check the limit :0014.2B60 jmp 35F8 ;no, send missing date warning * Referenced by a Jump at Address: |:0014.2B5E(C) | :0014.2B63 mov ax, [64D6] ;get time limit :0014.2B66 cmp [70D8], ax ;compare to transaction date :0014.2B6A jb 2B6F ;transaction lower ok, accept :0014.2B6C jmp 3606 ;over the limit don't accept * Referenced by a Jump at Addresses: |:0014.2B50(C), :0014.2B57(C), :0014.2B6A(C) | :0014.2B6F cmp word ptr [bp-10], 0000 ;go on with transaction The similar check in MSMONEY97 starts at 005B550D and uses [619244] where the limit is stored. :005B5504 cmp dword ptr [ebp+30], 0 ;flag to time check or not ;(the flag is set at 558BB7 push 01) :005B5508 je 005B552B ;just go on *** jmp 005B552B *** :005B550A mov ecx, dword ptr [ebp+3C] :005B550D mov ax, word ptr [ecx+01A9] ;get the current transaction date :005B5514 cmp ax, FFFF ;check if there is any date :005B5518 je 005B6214 ;go and send missing date message :005B551E cmp word ptr [00619244], ax ;compare to the time limit :005B5525 jbe 005B622D ;over, don't accept * Referenced by a Jump at Address: |:005B5508(C) | :005B552B cmp dword ptr [ebp+1C], 0 ;fine accept the transaction :005B552F push 00000000 :005B5531 je 005B5551 THE CRACK FOR MSMONEY 3.0: In MSMONEY there are three levels of protection and we have the three corresponding code parts. I tried for a long time to devise a single patch which eliminates all parts of the protection, but could not find it. Finally, I decided to do a major patch which eliminates most of the protection, and two other auxillary patches which eliminates the remaining small parts of the protection. The major patch is done in the time limit creating routine and forces it to create a time limit well into the next century. In MSMONEY 3.0 we change :0008.14F2 mov [bp-02], ax > mov [bp-02], F400 ;use 2070-01-01 :0008.14F5 mov ah, [bp-01] > jmp 151C ;go and create limit F400 represents 2070-Jan-1 and the routine uses this walue instead of the current system date. Therefore, the time limit which is writen to the MNY data file and also used for the transaction check will be 2070-Jan-01 plus 60 days. (We could use an even later date like FExx which is 2075, but it is not a good idea to push a patch till the limit.) This patch eliminates the 1994,1995 year check for a new file by jumping over the check,makes the transactions valid till 2070, and because the 60 day limit check on a opened MNY file is done only when the year of the limit (written into the file) is 1994-1996 the patch eliminates it as well. If we open a MNY file which limit is not 1994-1996 then this function is called and the limit is replaced by our patched limit. The only problem arise if we want to open a MNY file which is not created with the patched MSMONEY and the limit year in the file happens to be 1994-1996, then instead of the patched routine the 60 day-over check is called. To eliminate this possibility we have to patch the routine where the file time limit is checked. This can be done by changing the next instruction: :0008.16BB jne 16C0 > jmp 16D0 ;always go and get new time limit This eliminates all checks and directs the program flow to the patched time limit creation routine where it is set to the 2070 date. With these two patches whether we open a money file or create a new one the time limit which always will be 2070 is copied into [64D6] and used for the transaction check, therefore that protection is also cracked. This is useful, because while it seems that all transaction checks are done at one place both in MSMONEY 3.0 and MSMONEY97, there are so many functions (most of them I don't even understand) that I can not be sure. Even if there are multiple transaction check points they all must use the patched date so they are automatically cracked. On the other hand, if we would like to enter transactions without a date we have to patch the transaction check routine, too. This can be done by changing: :0014.2B50 je 2B6F > jmp 2B6F ;always accept transaction which eliminates all kinds of transaction checks. THE CRACK FOR MSMONEY97: Because the protection of MSMONEY97 operates exactily the same way like the older version. The crack can be done along the same lines. The change in the time limit calculation routine: :00470681 mov word ptr [esp + 02], ax > mov word ptr [esp + 02], F400 :00470686 and ax, FE00 > jmp 004706B8 in the file time limit check routine: :00470C17 je 00470E63 > jmp 00470C31 in the transaction check routine: :005B5508 je 005B552B > jmp 005B552B I hope you could follow my analysis despite my poor english. Thank you for your attention. ZER0