===================================================================

                        **** 1998 +HCU strainer ****
        *********************************************************
        *                                                       *
        *         Cracking M$ Money 3 Demo by iNCuBuS++         *
        *                                                       *
        *********************************************************


 Well, this was quite an exercise. Before I could start cracking
the code, I had to "crack" the French language (MsMoney ,French version)
in order to understand what I was doing ;) This useless app involves
nagscreen, year limitation and 60 days time limitation protection.
It was not particularly hard to crack this program, but it was not a game
either...I guess that's why it was chosen to be +HCU strainer ;)

On to the business!

Well, first we should put a BPX on some time acquiring function.
BPX-ing on GetLocalTime, GetSystemTime, GetSystemTimeAsFileTime (and so
on) gave no results: this is an old 16 bit app. and uses DOS services to
get current time and date. So we put a

  BPX DOS3CALL IF AH==2A

That would break execution only when DOS 2A (Curent Time) fuction is
called. We start the program and a nagscreen appears (we will deal with
this later). We click on OK button, and the ICE pops up at the beginning
of DOS3Call function. We exit the func with F11 and find ourselves in the
middle of date manipulation routine. We will start tracing from here.
After a few RETs we get to this piece of code:

:0008.14E8 9A52052815             call 0081.0552  ;WE CAME FROM HERE
:0008.14ED 8BD8                   mov bx, ax      ;Get the ptr to date
:0008.14EF 368B07                 mov ax, ss:[bx] ;Get the date
:0008.14F2 8946FE                 mov [bp-02], ax ;Store the date
:0008.14F5 8A66FF                 mov ah, [bp+FF] ;Get the year part
:0008.14F8 80E4FE                 and ah, FE      ;Mask out the year
:0008.14FB 80FC5C                 cmp ah, 5C      ;Year 1994 ?
:0008.14FE 741C                   je 151C         ;If it IS, jump !
:0008.1500 8A66FF                 mov ah, [bp+FF] ;Get the year part
:0008.1503 80E4FE                 and ah, FE      ;Mask out the year
:0008.1506 80FC5E                 cmp ah, 5E      ;Year 1995 ?
:0008.1509 7411                   je 151C         ;If it IS, jump !
:0008.150B 681709                 push 0917       ;Otherwise you are
                                                  ;a bad guy !

Now the above code needs some explanation. The year is calculated like
this: (YEAR - 1C) << 9 , so the year 1994 is coded as
(07CA - 001C) << 9 = 5C00 , and 1995 as (07CB - 001C) << 9 = 5E00.

It is obvious that we have to change one of the conditional branches into
unconditional. Logicaly, we'll take the first one:

:0008.14FE 741C       JZ     151C

and change it into

:0008.14FE EB1C       JMP    151C

Now the program will start without error. BUT! The program has created
a file named "msmoney.mny" which contains the date information among other
things. So, the next time the program is run, the check will be somewhere
else. Again, we will set a

 BPX DOS3CALL IF AH == 2A

and start M$Money. The ICE will pop up and we'll do as before - exit func
with F11 and find ourselves in the same date manipulation code "as.class" tppabs="http://www.Fravia.org/solutions/as.class" before.
So we'll exit nested subroutines using F12 until we get to this:

:0008.16A1 9A52051018   call 0081.0552    ;WE CAME FROM HERE (as before)
:0008.16A6 8BD8         mov bx, ax        ;Get the ptr. to the date
:0008.16A8 368B07       mov ax, ss:[bx]   ;Get the date
:0008.16AB 8946F0       mov [bp-10], ax   ;Store the date
:0008.16AE 8A66F5       mov ah, [bp+F5]   ;Get the year part of date
:0008.16B1 2500FE       and ax, FE00      ;Mask out the year
:0008.16B4 89868CFE     mov [bp-0174], ax ;Store it
:0008.16B8 3D005C       cmp ax, 5C00      ;Year 1994 ?
:0008.16BB 7503         jne 16C0          ;If it is NOT, check another
:0008.16BD E94401       jmp 1804          ;Jump, good guy!
:0008.16C0 3D005E       cmp ax, 5E00      ;Year 1995 ?
:0008.16C3 7503         jne 16C8          ;If it is NOT, check another
:0008.16C5 E93C01       jmp 1804          ;Jump, good guy!
:0008.16C8 3D0060       cmp ax, 6000      ;Year 1996 ?
:0008.16CB 7503         jne 16D0          ;If it is NOT, you are a bad guy
:0008.16CD E93401       jmp 1804          ;Jump, good guy!

Like the first time, we'll "persuade" the program that current date is
correct:

:0008.16BB 7503        JNZ    16C0

we change into

:0008.16BB 0BC0        OR     AX,AX

in order to allow the program to reach JMP 1804 instruction.

Now we come to the "60 days trial" time protection:

:0008.1804 FF76F0     push word ptr [bp+F0] ;Push the date onto stack
:0008.1807 6A3C       push 003C             ;3Ch = 60 (hmm,what could
:0008.1809 8D46FC     lea ax, [bp-04]       ;this be ? ;)
:0008.180C 50         push ax
:0008.180D 9AD8053418 call 0081.05D8        ;Calc. expiration date
:0008.1812 8BD8       mov bx, ax            ;relative to the curr.date
:0008.1814 368B07     mov ax, ss:[bx]
:0008.1817 8946FE     mov [bp-02], ax
:0008.181A 8B46F4     mov ax, [bp-0C]       ;Get the expected expir.date
:0008.181D 3946FE     cmp [bp-02], ax       ;If the calculated expir.date
:0008.1820 73C0       jnb 17E2              ;is lower than the expected
:0008.1822 681809     push 0918             ;one, you are a bad guy!
:0008.1825 6A00       push 0000

and it continues here:

:0008.17E2 8B46F4     mov ax, [bp-0C]       ;Get expected expiration date
:0008.17E5 3946F0     cmp [bp-10], ax       ;Is the current date lower?
:0008.17E8 7246       jb 1830               ;If it IS, jump good guy!

The above comparation at .181D checks if the user has turned the clock
back. Program calculates expiration date as current date + 60 and it
compares it with expected expiration date written somewhere inside
"msmoney.mny" file. The calculated expiration date can be equal (if it is
a first day of the trial period) or greater than the expected expir. date.
If it is not, the program will consider that as if the trial period has
expired and it will terminate. Obviously we have to make the program
always jump at .17E2 as everything was OK. Note that this is not absolutely
necessary because under normal circumstances we don't have the reason to
turn the clock back (since we'll make this work after the trial period
expiration), but we'll do it anyway since we want our target to be date
independant at all times. The other check (at .17E2) is a standard
expiration check. Program gets the expected expiration date and compares
it with the current date. If the current date exceeds the expiration date,
we will be considered as bad guys and the program will terminate (after
annoying messages).

We have to change

:0008.1820 73C0        JAE    17E2

into

:0008.1820 EBC0        JMP    17E2

and

:0008.17E2 7246        JB     1830

into

:0008.17E2 EB46        JMP    1830

That way the program will always proceed without error. But there is one
more check! Message box will open telling us that we have ? days of trial
period left. It was intended to warn the users when they have less than
7 days to use this demo. We will continue tracing after the jump at .1830
and get to the following piece of code:

:0008.1840 2BF0       sub si, ax            ;Exiration date - current date
:0008.1842 8976FE     mov [bp-02], si       ;Store the days left
:0008.1845 83FE07     cmp si, 0007          ;Do we have more than a week?
:0008.1848 7F22       jg 186C               ;If we DO, don't show the message
:0008.184A 8A46FE     mov al , [bp+FE]
:0008.184D 0430       add al, 30
:0008.184F 8846EE     mov [bp+EE], al
:0008.1852 C646EF00   mov byte ptr [bp-11], 00
:0008.1856 681949     push 4919
:0008.1859 68D207     push 07D2
:0008.185C 8D46EE     lea ax, [bp-12]
:0008.185F 16         push ss
:0008.1860 50         push ax
:0008.1861 6A00       push 0000
:0008.1863 6A00       push 0000
:0008.1865 6A40       push 0040
:0008.1867 9A44B11315 call 0064.B144        ;POP THE MESSAGE BOX
:0008.186C 8B5E08     mov bx, [bp+08]

Again it's a matter of turning conditional into unconditional jump:

:0008.1848 7F22         JG     186C

we change into:

:0008.1848 EB22         JMP    186C

and that's it. Now the program will start with or without "msmoney.mny"
file. BUT! When you try to enter the date past the expiration date
anywhere in the app, the message will pop up saying that this is not
allowed in the demo version. The blank date fields are not allowed also.
So, we'll have to crack this as well...

We click on a date field within the "Operations Futures" window and enter
some date. The program will automaticaly put the current date, so we can
take that as well. Now, before we hit enter, we'll switch into the ICE and
set a BMSG on that edit box waiting for WM_GETTEXT message. Now we return
to Windoze and hit enter. The ICE pops up and we look at the lParam of the
message. It is a SEG:OFF address of a buffer into which the entered date
will be read. We DB that address and see what's in there. Still nothing.
Now we start quick tracing, stepping over the subroutines and system calls
and not far from where we started, the buffer fills with entered date. We
put a BPR on that string and continue running the program. The ICE will
pop up once inside USER!LSTRCMP, several times at some unimportant CMPs,
once within KERNEL!LSTRLEN and then we get to the important part: date
string conversion into an integer array (one WORD for month, one for day
and one for the year). Let's look at the code snippet (it is presented in
the order of the execution):

:0081.0266 AC           lodsb                ;Get chr from the buffer
:0081.0267 2AE4         sub ah, ah           ;Clear the high byte of the WORD
:0081.0269 3D2F00       cmp ax, 002F         ;chr = "/" ?
:0081.026C 7F10         jg 027E              ;If greater, jump!
:0081.026E 3D2D00       cmp ax, 002D         ;chr = "-" or "." ?
:0081.0271 7D48         jge 02BB             ;If it is, jump!
:0081.0273 2D2000       sub ax, 0020
:0081.0276 EB16         jmp 028E
 .
 .
 .
:0081.027E 2D3000       sub ax, 0030         ;Convert ASCII to byte integer
:0081.0281 7C0D         jl 0290
:0081.0283 2D0900       sub ax, 0009
:0081.0286 7F03         jg 028B
:0081.0288 E99300       jmp 031E
 .
 .
 .
:0081.031E 83F9FF       cmp cx, FFFF
:0081.0321 7502         jne 0325
:0081.0323 33C9         xor cx, cx
:0081.0325 8A44FF       mov al , [si+FF]     ;Get the chr again
:0081.0328 2AE4         sub ah, ah           ;Clear the high byte
:0081.032A 8BD1         mov dx, cx           ;Get previous converted digits
:0081.032C C1E202       shl dx, 02           ;Adjust the sum of previously
:0081.032F 03D1         add dx, cx           ;converted digits for adding to
:0081.0331 D1E2         shl dx, 01           ;the ASCII of the current digit
:0081.0333 03C2         add ax, dx           ;and then convert them together
:0081.0335 2D3000       sub ax, 0030         ;into an word integer.
:0081.0338 8BC8         mov cx, ax           ;Save currently converted sum
:0081.033A EB95         jmp 02D1             ;for the next iteration.
 ...
:0081.02BB 8D46F0       lea ax, [bp-10]
:0081.02BE 3BF8         cmp di, ax
:0081.02C0 7503         jne 02C5
:0081.02C2 E96F02       jmp 0534
:0081.02C5 83C702       add di, 0002         ;Next word of the array
:0081.02C8 894DFE       mov [di-02], cx      ;Store the converted number.
:0081.02CB FF46FA       inc word ptr [bp+FA] ;Inc the counter of words
:0081.02CE B9FFFF       mov cx, FFFF
:0081.02D1 3976F8       cmp [bp-08], si      ;End of string ?
:0081.02D4 7790         ja 0266              ;If NOT, go back to the top
                                             ;of the loop !
:0081.02D6 8B76FA       mov si, [bp-06]      ;Get the cnt of conv.nums.
:0081.02D9 83FE03       cmp si, 0003         ;If it's NOT 3,
:0081.02DC 7503         jne 02E1             ;we're not done yet!
:0081.02DE E95302       jmp 0534             ;We're done, so jump!


 This routine converts multi-digit decimal numbers represented in
ASCII into word-sized integers and stores them into the array. The code
converts the number by adding an adjusted sum of previously converted
digits to the current digit chr and then converts the whole sum by
subtracting 30h (the basis of ASCII-to-int conversion). Converted sum is
saved in CX for the next loop iteration. Number end is marked by "/","-"
or "." (date format "mm/dd/yy") or by the end of the string. When
the number end is reached, the converted number is stored into the array
and the next number is taken (if any). We can BPR on that array and follow
what is happening with the converted date. The array being copied at
another location which we will BPR as well and from there the date words
are being passed to a routine that converts them into a single-word date
format used in previous date checks.Here's the code snippet:

:0081.0528 9AAE101306 call 0081.10AE  ;Convert date into a single word
:0081.052D 8BD8       mov bx, ax      ;Get a ptr to date
:0081.052F 368B07     mov ax, ss:[bx] ;Get the date
:0081.0532 EB08       jmp 053C
 .
 .
 .
:0081.053C 8B5E06     mov bx, [bp+06] ;Get a ptr to storage word
:0081.053F 368907     mov ss:[bx], ax ;Store the date
:0081.0542 8BC3       mov ax, bx      ;Return ptr in AX
:0081.0544 8CD2       mov dx, ss
:0081.0546 5E         pop si
:0081.0547 5F         pop di
:0081.0548 8D66FE     lea sp, [bp-02]
:0081.054B 1F         pop ds
:0081.054C 5D         pop bp
:0081.054D 4D         dec bp
:0081.054E CA0600     retf 0006       ;Exit the subroutine

and we get here:

:0014.1888 9A3402F810 call 0081.0234  ;WE CAME FROM HERE
:0014.188D 8BD8       mov bx, ax      ;Get a ptr to date
:0014.188F 368B07     mov ax, ss:[bx] ;Get the date
:0014.1892 8946F0     mov [bp-10], ax ;Store the date.
:0014.1895 3DFFFF     cmp ax, FFFF
:0014.1898 7512       jne 18AC
 .
 .
 .
:0014.18AC A3D870     mov [70D8], ax  ;HERE! The entered date is being
:0014.18AF E9F708     jmp 21A9        ;placed at fixed location !!!
                                      ;(a global variable)

Now we will put a BPMW DS:70D8 RW and resume the program. It will break at:

:0014.2B59 833ED870FF cmp word ptr [70D8], FFFF ;Is the date field blank?
:0014.2B5E 7503       jne 2B63                  ;If NOT, you're good guy!
:0014.2B60 E9950A     jmp 35F8                  ;Otherwise, jump bad guy!
:0014.2B63 A1D664     mov ax, [64D6]            ;Get the expiration date
:0014.2B66 3906D870   cmp [70D8], ax            ;If the entered date is
:0014.2B6A 7203       jb 2B6F                   ;lower than expiration
:0014.2B6C E9970A     jmp 3606                  ;date jump, otherwise,
                                                ;you're a bad guy!

And there it is - We have to change

:0014.2B5E 7503       JNE    2B63

into

:0014.2B5E EB03       JMP    2B63

and

:0014.2B6A 7203       JB     2B6F

into

:0014.2B6A EB03       JMP    2B6F

and we're done. The final date check is used in the "Saisir les operations
futures" option in "Outils" menu (I don't have a faintest idea what that
means). We again set a BPMW DS:70D8 RW and enter the date where we're
supposed to, hit enter and the ICE will eventualy pop up here:

:0088.1277 A1D870     mov ax, [70D8]            ;Get entered date
:0088.127A 894406     mov [si+06], ax           ;Store it
:0088.127D 833ED870FF cmp word ptr [70D8], FFFF ;If the date field was not
:0088.1282 750E       jne 1292                  ;left blank, jump.
    .                                           ;Otherwise, you're
    .                                           ;a bad guy!
    .
:0088.1292 A1D664      mov ax, [64D6]           ;Get the expiration date
:0088.1295 3906D870    cmp [70D8], ax           ;If the entered date is
:0088.1299 722F        jb 12CA                  ;lower than expiration
                                                ;date, jump. Otherwise,
                                                ;you're a bad guy!
Again we'll replace

:0088.1282 750E        JNE    1292

with

:0088.1282 EB0E        JMP    1292

and

:0088.1299 722F        JB     12CA

with

:0088.1299 EB2F        JMP    12CA


Now the program will accept any date as well as no date entered. And we're
done with the time protection. Period! Now the only thing left is to get
rid of the nagscreen and a message box which pops up when the program
starts.

 We set a BPX USER!SHOWWINDOW and start M$ Money. We'll break into
the ICE several times before we get to the right call:

:0002.0ECB 9AF20E0000 call USER.SHOWWINDOW           ;Show main window
:0002.0ED0 689304     push 0493
:0002.0ED3 68FFFF     push SEG ADDR of Segment 0028
:0002.0ED6 68C015     push 15C0
:0002.0ED9 FF36540B   push word ptr [0B54]
:0002.0EDD 6A00       push 0000
:0002.0EDF 6A00       push 0000
:0002.0EE1 6A00       push 0000
:0002.0EE3 9A3A01FFFF call 0005.013A                 ;Open the nagscreen
:0002.0EE8 EB0C       jmp 0EF6

All we have to do is to bypass the call to nagscreen opening routine. We
can't just noop it because it is referenced by the relocation table and
the system will try to correct the segment address of the call when it
loads the program thus corrupting any instruction we put there and the
program will not run. So, we will put a JMP immediately after the call to
ShowWindow:

:0002.0ECB 9AF20E0000 CALL   USER.SHOWWINDOW
:0002.0ED0 EB24       JMP    0EF6
:0002.0ED2 90         NOP
:0002.0ED3

And the last thing is removing the message box which tells us that Minitel
is not operational in the demo version. We put a BPX USER!MESSAGEBOX and
run the program. Since we previously removed other protections, the ICE
will pop up immediately at the right call. We exit the func with F11 and
nested subroutine calls with F12 until we get to the main calling code:

:0002.1451 837EB400   cmp word ptr [bp-4C], 0000
:0002.1455 7508       jne 145F
:0002.1457 68E666     push 66E6
:0002.145A 9A7A287C15 call 0002.287A
:0002.145F 9A520AFFFF call 0030.0A52 ;WE CAME FROM HERE
:0002.1464 0BC0       or ax, ax      ;AX = 0 ?
:0002.1466 7503       jne 146B       ;If it is NOT, jump!
:0002.1468 E9CB00     jmp 1536       ;And we should proceed here.

We neutralize this call by changing

:0002.1455 7508       JNE    145F
:0002.1457 68E666     PUSH   66E6

into

:0002.1455 E9DE00     JMP    1536
:0002.1458 0BC0       OR     AX,AX


That's it. No more nagscreen, no annoying messages, no time limitation.
We're done. Let's now modify the EXE file and test our crack.We'll use
HIEW for this purpose.

 - HIEW mnydemo.exe
 - F4 to change mode, F3 to choose asm
 - now we'll go one by one:

 1.SEARCH FOR:   74 1C 8A 66 FF 80 E4 FE
   REPLACE WITH: EB 1C .....

 2.SEARCH FOR:   75 03 E9 44 01
   REPLACE WITH: 0B C0 .....

 3.SEARCH FOR:   73 C0 68 18 09 6A 00
   REPLACE WITH: EB C0 .....

 4.SEARCH FOR:   72 46 68 94 04 68 6D 05
   REPLACE WITH: EB 46 .....

 5.SEARCH FOR:   7F 22 8A 46 FE 04 30
   REPLACE WITH: EB 22 .....

 6.SEARCH FOR:   75 03 E9 95 0A
   REPLACE WITH: EB 03 .....

 7.SEARCH FOR:   72 03 E9 97 0A
   REPLACE WITH: EB 03 .....

 8.SEARCH FOR:   75 0E 68 1A 09
   REPLACE WITH: EB 0E .....

 9.SEARCH FOR:   72 2F 68 16 09
   REPLACE WITH: EB 2F .....

 10.SEARCH FOR:  68 93 04 68 FF FF
   REPLACE WITH: EB 24 90 .....

 11.SEARCH FOR:  75 08 68 E6 66
   REPLACE WITH: E9 DE 00 0B C0

Done! Now we could test it, and when we're convinced that it works, we
can relax and forget all about this useless prog (but not the protection
scheme !!! ;) ....

iNCuBuS++, August 1997  

        **** 1998 +HCU strainer ****
        *********************************************************
        *                                                       *
        *   Answer to +ORC's Question On Cracking M$ Project    *
        *                     by iNCuBuS++                      *
        *                                                       *
        *********************************************************

        First, let's look at the code snippet commented in more detail:

:05167F6A E808CFEBFF       call 05024E77      ;Get the date
:05167F6F 66817DF4C007     cmp [ebp+F4], 07C0 ;1984?
:05167F75 722F             jb 05167FA6        ;If below, jump to BAD
:05167F77 66817DF40108     cmp [ebp+F4], 0801 ;2049?
:05167F7D 7727             ja 05167FA6        ;If above, jump to BAD 
:05167F7F FF75F4           push [ebp+F4]      ;Push the date word           
:05167F82 660FB645F7       movzx byte ptr ax,[ebp+F7] ;Get month
:05167F87 660FB64DF6       movzx byte ptr cx,[ebp+F6] ;Get day
:05167F8C 50               push eax           ;Push month
:05167F8D 51               push ecx           ;Push the year and the day
:05167F8E E84EC10B00       call 052240E1      ;Convert date into a single
                                              ;word-integer
:05167F93 663B45E4         cmp ax, [ebp+E4]   ;If the current date is above
:05167F97 7D0D             jge 05167FA6       ;or equal to the expiration
                                              ;date, jump to BAD!
:05167F99 668B4DE4         mov cx, [ebp+E4]   ;Get the expiration date
:05167F9D 662BC8           sub cx, ax         ;Expir.date - current date
:05167FA0 66894DF2         mov [ebp+F2], cx   ;Store the days left
:05167FA4 EB06             jmp 05167FAC       ;Skip setting the BAD FLAG
:05167FA6 66C745FE0100     mov [ebp+FE], 0001 ;** BAD FLAG **
:05167FAC FF75DC           push [ebp+DC]
:05167FAF FF151C5C3C05     call dword ptr [ADVAPI32!RegCloseKey]
:05167FB5 EB25             jmp 05167FDC
:05167FB7 66C745EC0000     mov [ebp+EC], 0000
:05167FBD 8D45F4           lea eax, [ebp+F4]
:05167FC0 50               push eax
:05167FC1 E8B1CEEBFF       call 05024E77
:05167FC6 66817DF4C007     cmp [ebp+F4], 07C0
:05167FCC 7208             jb 05167FD6
:05167FCE 66817DF40108     cmp [ebp+F4], 0801
:05167FD4 7606             jbe 05167FDC
:05167FD6 66C745FE0100     mov [ebp+FE], 0001 ;** BAD FLAG **
:05167FDC 66837DFE00       cmp [ebp+FE],0000  ;Is it GOOD FLAG ?
:05167FE1 755F             jne 05168042       ;No, jump bad guy!
:05167FE3 66837DF000       cmp [ebp+F0], 0000
:05167FE8 750E             jne 05167FF8
:05167FEA 66837DEC00       cmp [ebp+EC], 0000
:05167FEF 7451             je 05168042
:05167FF1 66837DF000       cmp [ebp+F0], 0000
:05167FF6 7407             je 05167FFF
:05167FF8 66837DEC00       cmp [ebp+EC], 0000
:05167FFD 7543             jne 05168042
:05167FFF 0FBF4DF2         movsx word ptr ecx, [ebp+F2] ;Get the days left
:05168003 66833D58192C0501 cmp dword ptr [052C1958], 0001
:0516800B 1BC0             sbb eax, eax
:0516800D 83E05A           and eax, 0000005A
:05168010 83C05A           add eax, 0000005A   ;EAX=90 days of trial per.
:05168013 3BC1             cmp eax, ecx        ;If there is more than 90
:05168015 7C2B             jl 05168042         ;days left, jump to BAD !!
:05168017 66837DF21E       cmp [ebp+F2], 001E  ;Less than 30 days left?
:0516801C 7F4A             jg 05168068         ;If not, jump!
:0516801E 8D45F4           lea eax, [ebp+F4]   ;Otherwise, warn the user
:05168021 50               push eax            ;that he has less than
:05168022 FF75F2           push [ebp+F2]       ;30 days of trial period.
:05168025 E895D2EBFF       call 050252BF
:0516802A 6A00             push 00000000
:0516802C 8D45F4           lea eax, [ebp+F4]
:0516802F 6A00             push 00000000
:05168031 6A00             push 00000000
:05168033 50               push eax
:05168034 6821150000       push 00001521
:05168039 6A01             push 00000001
:0516803B E86179EAFF       call 0500F9A1       ;Pop the warning
:05168040 EB26             jmp 05168068        ;Skip setting the BAD FLAG
:05168042 66C745FE0100     mov [ebp+FE], 0001  ;Set the BAD FLAG again
:05168048 6822150000       push 00001522
:0516804D 6A01             push 00000001
:0516804F E87D7BEAFF       call 0500FBD1       ;Open a nagscreen
:05168054 6A00             push 00000000
:05168056 A140432C05       mov eax, [052C4340]
:0516805B 6A72             push 00000072
:0516805D 6805050000       push 00000505
:05168062 50               push eax
:05168063 E8606CEEFF       call 0504ECC8
:05168068 668B45FE         mov ax, [ebp+FE]    ;Get the FLAG's status
:0516806C 5F               pop edi
:0516806D 5E               pop esi
:0516806E 5B               pop ebx
:0516806F 8BE5             mov esp, ebp
:05168071 5D               pop ebp
:05168072 C3               ret


In his lesson 4.2 +ORC poses a question:

> Well, just a moment... go back to the code "snippets" above,
> 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? 
>
> Because it would NOT have worked... or, as a matter of fact, it
> would have worked only in SOME cases... this is an example of a
> "protectionists' bait", please examine the code. 
>     You should -by now- know enough our art to be able to
> understand by yourself why the crack at 5167FA6 works and the
> crack at 5168015 (which is indeed part of the protection) will
> not work...

It wouldn't work because:

- FIRST, if the year isn't between 1984 and 2049, the program will set
  the BAD FLAG and skip the mentioned
        
        :5168013    CMP    EAX,ECX
        :5168015    JL     5168042

  part because, if the flag is SET, 

        :5167FDC    CMP    WORD PTR [EBP+FE],00 ; GOOD FLAG?
        :5167FE1    JNE    5168042              ; If NOT, jump bad guy! 

  will redirect execution to:

        :5168042    MOV    WORD PTR [EBP+FE],0001 ;Set the BAD FLAG again
        :5168048    PUSH   1522
        :516804D    PUSH   01
        :516804F    CALL   500FBD1                ;Pop the nagscreen

          .
          .
          .

  so nooping the above JL  5168042 would give no results.

- SECOND, the check

        :5167F93    CMP    AX,[EBP+E4] ;If the current date is above or
        :5167F97    JGE    5167FA6     ;equal to the expir.date, jump to
                                       ;BAD FLAG

  would immediately jump to bad flag setting at :5167FA6 if the current
  date is above or equal to expiration date, thus skipping the check at
  :5168013 (like in our first case) , so nooping the JL wouldn't work.


AND NOW......

        +ORC's SOLUTION ALSO DOESN'T WORK IN ALL CASES !!! It works if the
current date is greater than expiration date or if it is outside the range
1984 to 2049 ! If the current date is lower than the installation date 
(but it doesn't go below 1984) protection will react - the nagscreen will
pop up and the program will terminate. Let's see why !

Let's ,for example, move the date to the day before the installation.
The routine will pass this part :

:05167F6A E808CFEBFF       call 05024E77      ;Get the date
:05167F6F 66817DF4C007     cmp [ebp+F4], 07C0 ;1984?
:05167F75 722F             jb 05167FA6        ;If below, jump to BAD
:05167F77 66817DF40108     cmp [ebp+F4], 0801 ;2049?
:05167F7D 7727             ja 05167FA6        ;If above, jump to BAD 
:05167F7F FF75F4           push [ebp+F4]      ;Push the date word           
:05167F82 660FB645F7       movzx byte ptr ax,[ebp+F7] ;Get month
:05167F87 660FB64DF6       movzx byte ptr cx,[ebp+F6] ;Get day
:05167F8C 50               push eax           ;Push month
:05167F8D 51               push ecx           ;Push the year and the day
:05167F8E E84EC10B00       call 052240E1      ;Convert date into a single
                                              ;word-integer
:05167F93 663B45E4         cmp ax, [ebp+E4]   ;If the current date is above
:05167F97 7D0D             jge 05167FA6       ;or equal to the expiration
                                              ;date, jump to BAD!
:05167F99 668B4DE4         mov cx, [ebp+E4]   ;Get the expiration date
:05167F9D 662BC8           sub cx, ax         ;Expir.date - current date
:05167FA0 66894DF2         mov [ebp+F2], cx   ;Store ** THE DAYS LEFT **
:05167FA4 EB06             jmp 05167FAC       ;Skip setting the BAD FLAG

Note that THE DAYS LEFT value is now greater than 5A (5Ah = 90) and it is
5B (5Bh = 91) ! So, we proceed here:

:05167FAC FF75DC           push [ebp+DC]
:05167FAF FF151C5C3C05     call dword ptr [ADVAPI32!RegCloseKey]
:05167FB5 EB25             jmp 05167FDC

and immediately here:

:05167FDC 66837DFE00       cmp [ebp+FE],0000  ;Is it GOOD FLAG ?
:05167FE1 755F             jne 05168042       ;No, jump bad guy!
:05167FE3 66837DF000       cmp [ebp+F0], 0000
:05167FE8 750E             jne 05167FF8
:05167FEA 66837DEC00       cmp [ebp+EC], 0000
:05167FEF 7451             je 05168042
:05167FF1 66837DF000       cmp [ebp+F0], 0000
:05167FF6 7407             je 05167FFF
:05167FF8 66837DEC00       cmp [ebp+EC], 0000
:05167FFD 7543             jne 05168042
:05167FFF 0FBF4DF2         movsx word ptr ecx, [ebp+F2] ;Get the days left
:05168003 66833D58192C0501 cmp dword ptr [052C1958], 0001
:0516800B 1BC0             sbb eax, eax
:0516800D 83E05A           and eax, 0000005A
:05168010 83C05A           add eax, 0000005A   ;EAX=90 days of trial per.

; ** THE FOLLOWING IS IMPORTANT **
:05168013 3BC1             cmp eax, ecx        ;If there is MORE THAN 90
:05168015 7C2B             jl 05168042         ;days left, jump to BAD !!
:05168017 66837DF21E       cmp [ebp+F2], 001E  ;Less than 30 days left?
:0516801C 7F4A             jg 05168068         ;If not, jump!


Now, we already said that we now have 91 days left (since we moved current 
date 1 day before installation) !! So, the above JL (the one +ORC was 
talking about) WILL jump to :05168042 ! And there is 

:05168042 66C745FE0100     mov [ebp+FE], 0001  ;SET THE BAD FLAG AGAIN
:05168048 6822150000       push 00001522
:0516804D 6A01             push 00000001
:0516804F E87D7BEAFF       call 0500FBD1       ;OPEN THE NAGSCREEN !!!
:05168054 6A00             push 00000000
:05168056 A140432C05       mov eax, [052C4340]
:0516805B 6A72             push 00000072
:0516805D 6805050000       push 00000505
:05168062 50               push eax
:05168063 E8606CEEFF       call 0504ECC8
:05168068 668B45FE         mov ax, [ebp+FE]    ;Get the FLAG's status
:0516806C 5F               pop edi
:0516806D 5E               pop esi
:0516806E 5B               pop ebx
:0516806F 8BE5             mov esp, ebp
:05168071 5D               pop ebp
:05168072 C3               ret

So, the BAD FLAG will be set after all - the nagscreen will pop up and 
the program will terminate ! You don't beleve me? Try it !

Another thing that was overlooked (or it was left out on purpose ;-) is 
another nagscreen that informs the user when he has less than 30 days of
the trial period left! Here is the same code snippet again: 

        :5168017    CMP    WORD [EBP+F2],1E ;Do we have more than a month?
        :516801C    JG     5168068          ;If we DO, jump!  
        :516801E    LEA    EAX,[EBP+F4]       ;Otherwise, inform the user
        :5168021    PUSH   EAX                ;that he has less than 30
        :5168022    PUSH   DWORD PTR [EBP+F2] ;days to use this demo.
        :5168025    CALL   50252BF
        :516802A    PUSH   00
        :516802C    LEA    EAX,[EBP+F4]
        :516802F    PUSH   00
        :5168031    PUSH   00
        :5168033    PUSH   EAX
        :5168034    PUSH   00001521
        :5168039    PUSH   01
        :516803B    CALL   500F9A1             ;Pop the warning

This warning would appear even with +ORC's crack because while the current
date is within the trial period range (installation date to expiration
date) the execution flows normaly as if the app wasn't cracked at all.
The crack is in effect ONLY when the current date exceeds the expiration
date or exceeds the year limit (goes below 1984 or above 2049). So, the
last 30 days of the trial period we would get a warning from our "beloved"
Micro$oft each time we run M$ Project. How can we solve the above two
problems? Let's look at the code AGAIN:

:05168013 3BC1             cmp eax, ecx        ;If there is MORE THAN 90
:05168015 7C2B             jl 05168042         ;days left, jump to BAD !!
:05168017 66837DF21E       cmp [ebp+F2], 001E  ;Less than 30 days left?
:0516801C 7F4A             jg 05168068         ;If not, jump!
                                               ;Otherwise, pop the warning

Let's see....the program should NEVER jump to :5168042 and it should
ALWAYS jump to :05168068 .... Noop the first and change the second 
conditonal jump into unconditional ? NAH!...

The more elegant solution is to change

:05168015 7C2B        JL     05168042

into

:05168015 EB51        JMP    05168068

That way we skipped the 30 days chech entirely AND we prevented the 
setting of the BAD FLAG.

So, the conclusion is that BOTH CRACKS +ORC HAS MENTIONED HAVE TO BE MADE
(though not exactly as he said) !!!

At the end, let's modify Winproj.exe ! We'll use HIEW for this purpose!

        - HIEW Winproj.exe
        - Hit F4, then F3 to change to ASM mode
        
        1. SEARCH FOR:   66 C7 45 FE 01 00 FF 75 DC
           REPLACE WITH: 66 C7 45 FE 00 00 ......

        2. SEARCH FOR:   7C 2B 66 83 7D F2 1E 7F 4A
           REPLACE WITH: EB 51 .......

That's all! Try out entire range of dates from 1980 to 2099 and see that
it works now!

 .
 .
 .

BUT, wait! There is even easier way to crack this! We can simply exit the
subroutine using F12 and see this:

:516845C E817F7FFFF   call 05167B78 ;WE CAME FROM HERE
:5168461 6685C0       test ax,ax    ;BAD FLAG ?
:5168464 0F8596000000 jne 05168500  ;yes, jump!
                                    ;Otherwise, you are a good guy!

If we look at the dead listing, we'll see that the expiration check routine
is called only here and that all references to BAD FLAG setting at
:5167FA2 are WITHIN the same routine, our expiration check routine. 
So, why wouldn't we change

:516845C E817F7FFFF   CALL   05167B78     ;Check for expiration 

into

:516845C B800000000   MOV    EAX,00000000 ;always GOOD FLAG


In HIEW we should:

        SEARCH FOR:   E8 17 F7 FF FF 66
        REPLACE WITH: B8 00 00 00 00 ...

and that should do it. The program will now start without problem no matter
the date, though I haven't tested all in-program options, so there may be
a catch (but I don't think so) ...

iNCuBuS++ , August 1998