        page    ,132                    ;
        title   EXEPATCH.ASM -

;** EXEPATCH.ASM
;
;       Contains the foll:
;
;               - code to find and overlay buggy unpack code
;               - new code to be overlayed on buggy unpack code
;               - old code sequence to identify buggy unpack code
;
;       Revision history:
;
;               Created: 5/14/90
;
;
;
;----------------------------------------------------------------------------
;
; M020 : Fix for rational bug - for details see routine header
; M028 : 4b04 implementation
; M030 : Fixing bug in EXEPACKPATCH (EXEC_CS is an un-relocated value)
; M032 : set turnoff bit only if DOS in HMA.
; M033 : if IP < 2 then not exepacked.
; M046 : support for a 4th version of exepacked files.
; M068 : support for copy protected apps.
; M071 : use A20OFF_COUNT of 10.
; TAK1 : T. Kirschner, Defect 65613 - Packed file error when DOS loaded high 
;
;----------------------------------------------------------------------------
;

.xlist
include v86seg.inc
.xcref
include demver.inc                      ;false definition
include DosSymC.inc                     ;{DOSINC} dos5=dossym
include exe.inc
include pdb.inc                         ;{DOSINC}
include struc30.inc                     ;{INC}
.cref
.list


PATCH1_COM_OFFSET       EQU     06CH
PATCH1_OFFSET           EQU     028H
PATCH1_CHKSUM           EQU     0EF4EH
CHKSUM1_LEN             EQU     11CH/2

			; @TAK1 - 07/06/93 T. Kirschner
			;	  A variation of common pattern one exists in
			;         the Microsoft overlay linker for DOS v3.65.
			;	  It matches the Patch1 pattern starting
			;	  2 bytes further into the file, and has
			;	  a slightly different instruction sequence
			;	  and checksum starting at 28h bytes after
			;         the 'RB' signature.

PATCHTK_COM_OFFSET      EQU     06Eh                                ;@TAK1
PATCHTK_OFFSET          EQU     028h	    			    ;@TAK1
PATCHTK_CHKSUM          EQU     0AAB9h                              ;@TAK1
CHKSUMTK_LEN            EQU     11CH/2                              ;@TAK1
                                                           
PATCH2_COM_OFFSET       EQU     076H
PATCH2_OFFSET           EQU     032H

        ; The strings that start at offset 076h have two possible
        ; check sums that are defined as PATCH2_CHKSUM PATCH2A_CHKSUM

PATCH2_CHKSUM           EQU     78B2H
CHKSUM2_LEN             EQU     119H/2
PATCH2A_CHKSUM          EQU     1C47H           ; M046
CHKSUM2A_LEN            EQU     103H/2          ; M046

PATCH3_COM_OFFSET       EQU     074H		; 
PATCH3_OFFSET           EQU     032H
PATCH3_CHKSUM           EQU     4EDEH
CHKSUM3_LEN             EQU     117H/2


V86LoL  SEGMENT
        EXTRN   UNPACK_OFFSET   :WORD
        EXTRN   A20OFF_COUNT    :byte
        EXTRN   A20OFF_PSP      :word
        EXTRN   DOS_FLAG        :byte
V86LoL  ENDS


V86DATA SEGMENT
        EXTRN   RationalPatchPtr: word  ;lmstub.asm
        EXTRN   DosHasHMA       : byte  ;lmstub.asm
        EXTRN   RatBugCode      : FAR   ;lmstub.asm
        EXTRN   fixexepatch     : word  ;lmstub.asm
V86DATA ENDS

V86TABLE SEGMENT
        EXTRN   Special_Version : word  ;MsTable.asm
V86TABLE ENDS


V86CODE SEGMENT

        EXTRN   Scan_Execname1:near
        EXTRN   Scan_Special_Entries:near

        ASSUME  CS:V86GROUP, SS:nothing


;-------------------------------------------------------------------------
;
;       Procedure Name          : ExecReady
;
;       Input                   : DS:DX -> ERStruc (see exe.inc)
;
;--------------------------------------------------------------------------

                public  ExecReady
ExecReady       proc    near
        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing

        mov     si, dx                  ; move the pointer into a friendly one
        .if     <BIT <[si].ER_Flags> and ER_EXE>   ; COM or EXE ?
                                                   ; only setver for .COM files
          mov     ax, [si].ER_PSP
          add     ax, 10h
          mov     es, ax

          mov     cx, word ptr ds:[si].ER_StartAddr
          mov     ax, word ptr ds:[si+2].ER_StartAddr

          call    [fixexepatch]
        .endif

        .if     <BIT <[si].ER_Flags> nand ER_OVERLAY> ;Is this an overlay?
          ; No, set up lie version first
          push    ds
          push    si
          LDS     SI, [si].ER_ProgName
          call    Scan_Execname1
          call    Scan_Special_Entries
          pop     si
          pop     ds
          mov     es, [si].ER_PSP
          mov     ax, [Special_Version]
          mov     es:[0].PDB_Version, ax
        .endif

        .if     <<[DosHasHMA]> ne 0>    ; M032: Does dos in HMA (M021)
          ;Yes, Dos has the HMA
          mov     ax, [si].ER_PSP       ; ax = PSP
          or      [DOS_FLAG],EXECA20OFF ; Set bit to signal int 21
                                        ; ah = 25 & ah= 49. See dossym.inc
                                        ; under TAG M003&M009 for explanation
          .if     <BIT <[si].ER_Flags> nand ER_EXE>  ; Q: COM file
                                        ; N: inc a20off_count , set
                                        ;    a20off_psp and ret
            push    ds
            mov     ds, ax              ; DS = load segment of com file.
            call    IsCopyProt          ; check if copy protected
            pop     ds
          .endif
          ; We need to inc the A20OFF_COUNT here. Note that if the count
          ; is non-zero at this point it indicates that the A20 is to be
          ; turned off for that many int 21 calls made by the app. In
          ; addition the A20 has to be turned off when we exit from this
          ; call. Hence the inc.
          inc     [A20OFF_COUNT]
          mov     [A20OFF_PSP], ax        ; set the PSP for which A20 is to be
                                          ; turned OFF.
        .endif
        xor     ax, ax
        ret
ExecReady       endp




public          exepatch_start
exepatch_start  label   byte

        ;
        ; The following is the code that'll be layed over the buggy unpack
        ; code.
        ;
str1    label   byte
        db      006h                    ;          push   es
        db      08Ch, 0D8h              ;          mov    ax,ds
first_stop      equ     $ - str1

        db      02Bh, 0C2h              ;          sub    ax, dx

first   label   byte
        db      08Eh, 0D8h              ;          mov    ds,ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      057h                    ;          push   di
        db      0B9h, 010h, 000h        ;          mov    cx,0010H
        db      0B0h, 0FFh              ;          mov    al,0FFH
        db      0F3h, 0AEh              ;          repz   scasb
        db      047h                    ;          inc    di
        db      08Bh, 0F7h              ;          mov    si,di
        db      05Fh                    ;          pop    di
        db      058h                    ;          pop    ax
second_stop     equ     $ - first

        db      02Bh, 0C2h              ;          sub    ax, dx

second  label   byte
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0B9h, 004h, 002h        ;NextRec:  mov    cx, 0204h
        db      08Bh, 0C6h              ;norm_agn: mov    ax,si
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 013h              ;          jz     SI_ok
        db      08Ch, 0DAh              ;          mov    dx,ds
        db      083h, 0CEh, 0F0h        ;          or     si,0FFF0H
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      073h, 008h              ;          jnc    SItoDS
        db      0F7h, 0DAh              ;          neg    dx
        db      0D3h, 0E2h              ;          shl    dx,cl
        db      02Bh, 0F2h              ;          sub    si,dx
        db      033h, 0D2h              ;          xor    dx,dx
        db      08Eh, 0DAh              ;SItoDS:   mov    ds,dx
        db      087h, 0F7h              ;SI_ok:    xchg   si,di
        db      01Eh                    ;          push   ds
        db      006h                    ;          push   es
        db      01Fh                    ;          pop    ds
        db      007h                    ;          pop    es
        db      0FEh, 0CDh              ;          dec    ch
        db      075h, 0DBh              ;          jnz    norm_agn
        db      0ACh                    ;          lodsb
        db      092h                    ;          xchg   dx,ax
        db      04Eh                    ;          dec    si
        db      0ADh                    ;          lodsw
        db      08Bh, 0C8h              ;          mov    cx,ax
        db      046h                    ;          inc    si
        db      08Ah, 0C2h              ;          mov    al,dl
        db      024h, 0FEh              ;          and    al,0FEH
        db      03Ch, 0B0h              ;          cmp    al,RPTREC
        db      075h, 005h              ;          jne    TryEnum
        db      0ACh                    ;          lodsb
        db      0F3h, 0AAh              ;          rep stosb
        db      0EBh, 006h              ;          jmp    TryNext
        db      03Ch, 0B2h              ;TryEnum:  cmp    al,ENMREC
        db      075h, 06Ch              ;          jne    CorruptExe
        db      0F3h, 0A4h              ;          rep movsb
        db      092h                    ;TryNext:  xchg   dx,ax
        db      0A8h, 001h              ;          test   al,1
        db      074h, 0B9h              ;          jz     NextRec
        db      090h, 090h              ;          nop, nop
last_stop       equ     $ - second

size_str1       equ     $-str1


;
; The following is the code that we need to look for in the exe
; file.
;
public Scan_patch1
scan_patch1     label   byte

        db      08Ch, 0C3h              ;          mov    bx,es
        db      08Ch, 0D8h              ;          mov    ax,ds
        db      02Bh, 0C2h              ;          sub    ax, dx
        db      08Eh, 0D8h              ;          mov    ds,ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B9h, 010h, 000h        ;          mov    cx,0010H
        db      0B0h, 0FFh              ;          mov    al,0FFH
        db      0F3h, 0AEh              ;          repz   scasb
        db      047h                    ;          inc    di
        db      08Bh, 0F7h              ;          mov    si,di
        db      08Bh, 0C3h              ;          mov    ax,bx
        db      02Bh, 0C2h              ;          sub    ax, dx
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B1h, 004h              ;NextRec:  mov    cl,4
        db      08Bh, 0C6h              ;          mov    ax,si
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 009h              ;          jz     SI_ok
        db      08Ch, 0DAh              ;          mov    dx,ds
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0DAh              ;          mov    ds,dx
        db      083h, 0CEh, 0F0h        ;          or     si,0FFF0H
        db      08Bh, 0C7h              ;SI_ok:    mov    ax,di
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 009h              ;          jz     DI_ok
        db      08Ch, 0C2h              ;          mov    dx,es
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0C2h              ;          mov    es,dx
        db      083h, 0CFh, 0F0h        ;          or     di,0FFF0H
                                        ;DI_ok:
size_scan_patch1        equ     $-scan_patch1

public Scan_patchTK					; @TAK1 - 07/06/93
                                                        
scan_patchTK  	label   byte                            	    ;@TAK1
								    ;@TAK1	 
	db	08Ch, 0C3h           	;mov     bx,es              ;@TAK1
	db	08Ch, 0D8h           	;mov     ax,ds              ;@TAK1
	db	02Bh, 0C2h           	;sub     ax,dx              ;@TAK1
	db	08Eh, 0D8h           	;mov     ds,ax              ;@TAK1
	db	08Eh, 0C0h           	;mov     es,ax              ;@TAK1
	db	0BFh, 00Fh, 000h       	;mov     di,DGROUP (000f)   ;@TAK1
	db	0B9h, 010h, 000h       	;mov     cx,0010            ;@TAK1
	db	0B0h, 0FFh           	;mov     al,ff              ;@TAK1
	db	0F3h, 0AEh           	;repe 	 scasb              ;@TAK1
	db	047h               	;inc     di                 ;@TAK1
	db	08Bh, 0F7h           	;mov     si,di              ;@TAK1
	db	08Bh, 0C3h           	;mov     ax,bx              ;@TAK1
	db	02Bh, 0C2h           	;sub     ax,dx              ;@TAK1
	db	08Eh, 0C0h           	;mov     es,ax              ;@TAK1
	db	0BFh, 00Fh, 000h       	;mov     di,DGROUP (000f)   ;@TAK1
	db	0B1h, 004h           	;mov     cl,04              ;@TAK1
	db	08Bh, 0C6h           	;mov     ax,si              ;@TAK1
	db	0F7h, 0D0h           	;not     ax                 ;@TAK1
	db	0D3h, 0E8h           	;shr     ax,cl              ;@TAK1
	db	074h, 00Ah           	;jz      SI_OK              ;@TAK1
	db	08Ch, 0DAh           	;mov     dx,ds              ;@TAK1
	db	02Bh, 0D0h           	;sub     dx,ax              ;@TAK1
	db	08Eh, 0DAh           	;mov     ds,dx              ;@TAK1
	db	081h, 0CEh, 0F0h, 0FFh 	;or      si,fff0            ;@TAK1
	db	08Bh, 0C7h           	;SI_OK:	 mov     ax,di      ;@TAK1
	db	0F7h, 0D0h           	;not     ax                 ;@TAK1
	db	0D3h, 0E8h           	;shr     ax,cl              ;@TAK1
	db	074h, 00Ah           	;jz      0080               ;@TAK1
	db	08Ch, 0C2h           	;mov     dx,es              ;@TAK1
	db	02Bh, 0D0h           	;sub     dx,ax              ;@TAK1
	db	08Eh, 0C2h           	;mov     es,dx              ;@TAK1
	db	081h, 0CFh, 0F0h, 0FFh  ;or      di,fff0            ;@TAK1
                                        ;DI_ok:                     ;@TAK1
                                                                    ;@TAK1
size_scan_patchTK 	equ   	$-scan_patchTK                      ;@TAK1

public scan_patch2
scan_patch2     label   byte
        db      08Ch, 0C3h              ;          mov    bx,es
        db      08Ch, 0D8h              ;          mov    ax,ds
        db      048h                    ;          dec    ax
        db      08Eh, 0D8h              ;          mov    ds,ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B9h, 010h, 000h        ;          mov    cx,0010H
        db      0B0h, 0FFh              ;          mov    al,0FFH
        db      0F3h, 0AEh              ;          repz   scasb
        db      047h                    ;          inc    di
        db      08Bh, 0F7h              ;          mov    si,di
        db      08Bh, 0C3h              ;          mov    ax,bx
        db      048h                    ;          dec    ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B1h, 004h              ;NextRec:  mov    cl,4
        db      08Bh, 0C6h              ;          mov    ax,si
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 00Ah              ;          jz     SI_ok
        db      08Ch, 0DAh              ;          mov    dx,ds
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0DAh              ;          mov    ds,dx
        db      081h, 0CEh, 0F0h, 0FFh  ;          or     si,0FFF0H
        db      08Bh, 0C7h              ;SI_ok:    mov    ax,di
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 00Ah              ;          jz     DI_ok
        db      08Ch, 0C2h              ;          mov    dx,es
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0C2h              ;          mov    es,dx
        db      081h, 0CFh, 0F0h, 0FFh  ;          or     di,0FFF0H
                                        ;DI_ok:
size_scan_patch2        equ     $-scan_patch2


public Scan_patch3
scan_patch3     label   byte
        db      08Ch, 0C3h              ;          mov    bx,es
        db      08Ch, 0D8h              ;          mov    ax,ds
        db      048h                    ;          dec    ax
        db      08Eh, 0D8h              ;          mov    ds,ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B9h, 010h, 000h        ;          mov    cx,0010H
        db      0B0h, 0FFh              ;          mov    al,0FFH
        db      0F3h, 0AEh              ;          repz   scasb
        db      047h                    ;          inc    di
        db      08Bh, 0F7h              ;          mov    si,di
        db      08Bh, 0C3h              ;          mov    ax,bx
        db      048h                    ;          dec    ax
        db      08Eh, 0C0h              ;          mov    es,ax
        db      0BFh, 00Fh, 000h        ;          mov    di,000FH
        db      0B1h, 004h              ;NextRec:  mov    cl,4
        db      08Bh, 0C6h              ;          mov    ax,si
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 009h              ;          jz     SI_ok
        db      08Ch, 0DAh              ;          mov    dx,ds
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0DAh              ;          mov    ds,dx
        db      083h, 0CEh, 0F0h        ;          or     si,0FFF0H
        db      08Bh, 0C7h              ;SI_ok:    mov    ax,di
        db      0F7h, 0D0h              ;          not    ax
        db      0D3h, 0E8h              ;          shr    ax,cl
        db      074h, 009h              ;          jz     DI_ok
        db      08Ch, 0C2h              ;          mov    dx,es
        db      02Bh, 0D0h              ;          sub    dx,ax
        db      08Eh, 0C2h              ;          mov    es,dx
        db      083h, 0CFh, 0F0h        ;          or     di,0FFF0H
                                        ;DI_ok:
size_scan_patch3        equ     $-scan_patch3


                public  scan_com
scan_com        label   byte
        db      0ACh                    ;          lodsb
        db      08Ah, 0D0h              ;          mov    dl,al
        db      04Eh                    ;          dec    si
        db      0ADh                    ;          lodsw
        db      08Bh, 0C8h              ;          mov    cx,ax
        db      046h                    ;          inc    si
        db      08Ah, 0C2h              ;          mov    al,dl
        db      024h, 0FEh              ;          and    al,0FEH
        db      03Ch, 0B0h              ;          cmp    al,RPTREC
        db      075h, 006h              ;          jne    TryEnum
        db      0ACh                    ;          lodsb
        db      0F3h, 0AAh              ;          rep stosb
        db      0EBh, 007h, 090h        ;          jmp    TryNext
        db      03Ch, 0B2h              ;TryEnum:  cmp    al,ENMREC
        db      075h, 06Bh              ;          jne    CorruptExe
        db      0F3h, 0A4h              ;          rep movsb
        db      08Ah, 0C2h              ;TryNext:  mov    al,dl
        db      0A8h, 001h              ;          test   al,1
size_scan_com   equ     $-scan_com


        public  exepatch
        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing
ExePatch proc   near
        call    ExePackPatch
        call    word ptr [RationalPatchPtr]
        ret
ExePatch endp



;---------------------------------------------------------------------------
;
; Procedure Name        : ExePackPatch
;
; Inputs                : DS                    -> DOSDATA
;                         ES:0                  -> read in image
;                         ax:cx = start cs:ip of program
; Output                :
;
;        IF  (ES <= 0FFFh)              //If image is below 64K
;          & (IP >= 2)                  //ExePacked files are also IP>=2
;          & ExePack signature ('RB') found
;        if common code to patch compares (for 3 diff. versions)
;        if rest of the code & checksum compares
;        overlay buggy code with code in v86group:str1.
;
;
; Uses                  : NONE
;
;---------------------------------------------------------------------------


        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing
        public  ExePackPatch
ExePackPatch    proc    near

        push    bx

;------ Is the image loaded below 64K, if not, no patch is needed.
        mov     bx, es                  ; bx has load segment
        .if     <bx a 0FFFh>            ; Q: is the load segment > 64K
          pop     bx                    ; Y: no need to patch
          ret
        .endif

;------ save registers
        push    ds
        push    es
        push    ax
        push    cx
        push    si
        push    di

;------ ExePacked prgrams always have an IP of 12h (>=2)
        sub     cx, 2                   ; Q: is IP >=2
        .if     b
          jmp     ep_done               ; N: exit
        .endif

;
; AX:CX now points to location of 'RB' if this is an exepacked file.
;

;------ look for 'RB' ExePack signature
        mov     di, cx
        mov     es, ax
        mov     cs:[unpack_offset], di  ; save pointer to 'RB' in unpack_offset
        .if     <<word ptr es:[di]> ne 'BR'>
          jmp     ep_done
        .endif


;------ addressability
        push    cs
        pop     ds                      ; set ds to cs
        assume  ds:V86GROUP


;------ Look for Patch1
        add     di, PATCH1_COM_OFFSET   ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.

        call    chk_common_str          ; check for match
        ;exit:  ZF=match found
        ;       NZ=no match

        .if     z                       ; Q: does the patch match
                                        ; Y: check for rest of patch string

          mov     si, offset V86GROUP:scan_patch1
                                        ; ds:si -> scan string
          mov     di, [unpack_offset]   ; restore di to point to 'RB'

          add     di, PATCH1_OFFSET     ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.
          mov     cx, size_scan_patch1
          mov     bx, CHKSUM1_LEN
          mov     ax, PATCH1_CHKSUM
          call    chk_patchsum          ; check if patch and chk sum compare
          ;exit:  NC=checksum compare is ok
          ;       CF=compare failed
          .if     nc                    ; Q: did we pass the test
                                        ; N: exit
                                        ; Y: overlay code with new

            mov     si, offset V86GROUP:str1
            mov     cx, size_str1
            rep     movsb
          .endif

          jmp     ep_done
        .endif

;------ Look for variation on Patch1: PatchTK		  	;@TAK1 Start

	mov  	di, [unpack_offset]    	; restore di to point to 'RB'
        add     di, PATCHTK_COM_OFFSET  ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.

        call    chk_common_str          ; check for match
        ;exit:  ZF=match found
        ;       NZ=no match

        .if     z                       ; Q: does the patch match
                                        ; Y: check for rest of patch string

          mov     si, offset V86GROUP:scan_patchTK
                                        ; ds:si -> scan string
          mov     di, [unpack_offset]   ; restore di to point to 'RB'

          add     di, PATCHTK_OFFSET    ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.
          mov     cx, size_scan_patchTK
          mov     bx, CHKSUMTK_LEN
          mov     ax, PATCHTK_CHKSUM
          call    chk_patchsum          ; check if patch and chk sum compare
          ;exit:  NC=checksum compare is ok
          ;       CF=compare failed
          .if     nc                    ; Q: did we pass the test
                                        ; N: exit
                                        ; Y: overlay code with new

            mov     si, offset V86GROUP:str1
            mov     cx, size_str1
            rep     movsb
          .endif

          jmp     ep_done
        .endif                                                  ;@TAK1 End


;------ Look for Patch2
;------ The "LINK /E" from "MASM/2", Link/2 v1.00
        mov     di, PATCH2_COM_OFFSET   ; es:di -> possible loaction of patch
                                        ; in another version of unpack
        call    chk_common_str          ; check for match
        ;exit:  ZF=match found
        ;       NZ=no match

        .if     z                       ; Q: does the patch match
                                        ; Y: check for rest of patch string

          mov     si, offset V86GROUP:scan_patch2
                                        ; ds:si -> scan string
          mov     di, PATCH2_OFFSET     ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.
          mov     cx, size_scan_patch2
          mov     bx, CHKSUM2_LEN
          mov     ax, PATCH2_CHKSUM
          call    chk_patchsum          ; check if patch and chk sum compare
          ;exit:  NC=checksum compare is ok
          ;       CF=compare failed
          ;------ if the CheckSum failed, look for Patch2A (046)
          .if     c
            mov     si, offset V86GROUP:scan_patch2  ; ds:si -> scan string
            mov     cx, size_scan_patch2
            mov     bx, CHKSUM2A_LEN
            mov     ax, PATCH2A_CHKSUM
            call    chk_patchsum        ; check if patch and chk sum compare
            ;exit:  NC=checksum compare is ok
            ;       CF=compare failed
          .endif


          ;------ Did Patch2 or Patch2A compare pass?
          .if     nc
            ;--- Yes, overlay code with new

            mov     si, offset V86GROUP:str1
            mov     cx, first_stop
            rep     movsb

            mov     ax, 4890h           ; ax = opcodes for dec ax, nop
            stosw

            add     si, 2
            mov     cx, second_stop
            rep     movsb

            stosw                       ; put in dec ax and nop

            add     si, 2
            mov     cx, last_stop
            rep     movsb

          .endif
          jmp     short ep_done
        .endif


;------ Look for Patch3
        mov     di, PATCH3_COM_OFFSET   ; es:di -> possible loaction of patch
                                        ; in another version of unpack
        call    chk_common_str          ; check for match
        ;exit:  ZF=match found
        ;       NZ=no match

        .if     z                       ; Q: does the patch match
                                        ; Y: check for rest of patch string

          mov     si, offset V86GROUP:scan_patch3
                                        ; ds:si -> scan string

          mov     di, PATCH3_OFFSET     ; es:di -> points to place in packed
                                        ;          file where we hope to find
                                        ;          scan string.
          mov     cx, size_scan_patch3
          mov     bx, CHKSUM3_LEN
          mov     ax, PATCH3_CHKSUM
          call    chk_patchsum          ; check if patch and chk sum compare
          ;exit:  NC=checksum compare is ok
          ;       CF=compare failed
          .if     nc                    ; Q: did we pass the test
                                        ; N: exit
                                        ; Y: overlay code with new

            mov     si, offset V86GROUP:str1
            mov     cx, first_stop

            rep     movsb

            mov     al, 48h               ; al = opcode for dec ax
            stosb
            add     si, 2

            mov     cx, second_stop

            rep     movsb

            stosb                         ; put in dec ax
            add     si, 2

            mov     cx, last_stop

            rep     movsb
          .endif
        .endif

ep_done:

        pop     di
        pop     si
        pop     cx
        pop     ax
        pop     es
        pop     ds
        pop     bx
        ret

ExePackPatch    endp

;-------------------------------------------------------------------------
;
;       Procedure Name  : chk_common_str
;
;       Input           : DS = V86GROUP
;                       ; ES:DI points to string in packed file
;
;       Output          ; ZF if match
;                       ; NZ if no match
;
;-----------------------------------------------------------------------

public chk_common_str
chk_common_str  proc    near
        assume  cs:V86GROUP,ds:V86GROUP,es:nothing,ss:nothing

        mov     si, offset V86GROUP:scan_com
        mov     cx, size_scan_com
                                        ; ds:si -> scan string
                                        ; es:di -> EXE file image string
        repe    cmpsb

                                        ; M046 - start
        ; a fourth possible version of these exepacked programs have a
        ; 056h instead of 06bh. See scan_com above
        ;
        ;       db  75h, 6Bh            ;jne    CorruptExe
        ;
        ; If the mismatch at this point is due to a 56h instead of 6bh
        ; we shall try to match the rest of the string
        ;

        .if     ne and
        .if     <<byte ptr es:[di-1]> eq 56h>
          repe    cmpsb
        .endif
                                        ; M046 - end
        ret

chk_common_str  endp


;---------------------------------------------------------------------------
;
;       Procedure Name  : chk_patchsum
;
;       Input           : DS:SI -> string we're looking for
;                       : ES:DI -> offset in packed file
;                       : CX    = scan length
;                       : BX    = length of check sum
;                       : AX    = value of check sum
;
;       Output          : NC=if patch & check sum compare
;                       ; CY=no compare
;
;       Uses            : AX, BX, CX, SI
;----------------------------------------------------------------------------

public chk_PatchSum
chk_patchsum    proc    near
        assume  cs:V86GROUP,ds:V86GROUP,es:nothing,ss:nothing

        push    di

        repe    cmpsb

        .if     z                       ; Q: does the patch match
                                        ; Y: continue the compare

          ; we do a check sum starting from the location of the
          ; exepack signature 'RB' up to 11c/2 bytes, the end of the
          ; unpacking code.

          mov     di, [unpack_offset]     ; di -> start of unpack code
          mov     cx, bx                  ; cx = length of check sum

          mov     bx, ax                  ; save check sum passed to us in bx
          xor     ax, ax

          .repeat
            add     ax, word ptr es:[di]
            add     di, 2
          .loop

          pop     di                      ; restore di

          .if     <ax eq bx>              ; Q: does the check sum match
                                          ; N: exit
            clc
            ret
          .endif

        .endif

        stc
        ret

chk_patchsum    endp


;----------------------------------------------------------------------------
;
; procedure : RationalPatch
;
; A routine (in Ration DOS extender) which is invoked at hardware interrupts
; clobbers CX register on 286 machines. (123 release 3 uses Rational DOS
; extender). This routine identfies Buggy Rational EXEs and fixes the bug.
;
; THE BUG is in the following code sequence:
;
;8b 0e 10 00    mov     cx, ds:[10h]            ; delay count
;90             even                            ; word align
;e2 fe          loop    $                       ; wait          CLOBBERS CX
;e8 xx xx       call    set_A20                 ; enable A20
;
; This patch routine replaces the mov & the loop with a far call into a
; routine in DOS data segment which is in low memory (because A20 line
; is off). The routine (RatBugCode) in DOS data saves & restores CX around
; a mov & loop.
;
; Identification of Buggy Rational EXE
; ====================================
;
; (ALL OFFSETS ARE IN THE PROGRAM SECTION - EXCLUDING THE EXE HEADER)
;
; OFFSET                                Contains
; ------                                --------
; 0000h                 100 times Version number in binary
;                       bug exists in version 3.48 thru 3.83 (both inclusive)
;
; 000ah                 the WORDS : 0000h, 0020h, 0000h, 0040h, 0001h
;
; 002ah                 offset where version number is stored in ASCII
;                               e.g. '3.48A'
;
; 0030h                 offset of copyright string. Copyright strings either
;                       start with "DOS/16M Copyright...." or
;                       "Copyright.....". The string contains
;                       "Rational Systems, Inc."
;
; 0020h                 word : Paragraph offset of the buggy code segment
;                               from the program image
; 0016h                 word : size of buggy code segment
;
;       Buggy code is definite to start after offset 200h in its segment
;
;----------------------------------------------------------------------------
;
RScanPattern1   db      0, 0, 20h, 0, 0, 0, 40h, 0, 1, 0
RLen1           equ     $ - offset RScanPattern1

RScanPattern2   db      8bh, 0eh, 10h, 00h, 90h, 0e2h, 0feh, 0e8h
RLen2           equ     $ - offset RScanPattern2

RScanPattern3   db      8bh, 0eh, 10h, 00h, 0e2h, 0feh, 0e8h
RLen3           equ     $ - offset RScanPattern2
;
;----------------------------------------------------------------------------
;
; INPUT : ES = segment where program got loaded
;
;----------------------------------------------------------------------------

public  RationalPatch
RationalPatch   proc    near
        cld
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        push    ds                      ; we use all of them
        mov     di, 0ah                 ; look for pat1 at offset 0a
        push    cs
        pop     ds
        assume  ds:nothing
        mov     si, offset RScanPattern1
        mov     cx, RLen1
        rep     cmpsb                   ; do we have the pattern ?
        jne     rpexit
        mov     ax, es:[0]
        cmp     ax, 348                 ; is it a buggy version ?
        jb      rpexit
        cmp     ax, 383                 ; is it a buggy version
        ja      rpexit

        call    VerifyVersion
        jne     rpexit

        mov     cx, es:[16h]            ; Length of buggy code seg
        sub     cx, 200h                ; Length we search (we start
                                        ;  at offset 200h)
        mov     es, word ptr es:[20h]   ; es=buggy code segment
        mov     si, offset RScanPattern2
        mov     dx, RLen2
        call    ScanCodeSeq             ; look for code seq with nop
        .if     nz
          mov     si, offset RScanPattern3
          mov     dx, RLen3
          call    ScanCodeSeq           ; look for code seq w/o nop
          jnz     rpexit
        .endif

;       we set up a far call into DOS data
;       dx has the length of the doce seq we were searching for

        mov     al, 9ah                 ; far call opcode
        stosb
        mov     ax, offset RatBugCode
        stosw
        mov     ax, ss
        stosw
        mov     cx, dx
        sub     cx, 6                   ; filler (with NOPs)
        mov     al, 90h
        rep     stosb
rpexit:
        pop     ds
        pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
RationalPatch   endp


;----------------------------------------------------------------------------
;
; ScanCodeSeq
;
; Looks for a pattern pointed to by DS:SI & len DX in ES:200 to ES:200+CX-1
;
; returns in ES:DI the start of the pattern if Zero flag is set
;
;----------------------------------------------------------------------------
;
public ScanCodeSeq
ScanCodeSeq     proc    near
        push    cx
        sub     cx, dx
        inc     cx
        mov     di, 200h
        .repeat
          push    si
          push    di
          push    cx
          mov     cx, dx
          rep     cmpsb
          pop     cx
          pop     di
          pop     si
          .leave  e
          inc     di
        .loop
        pop     cx
        ret
ScanCodeSeq     endp


;----------------------------------------------------------------------------
;
; VerifyVersion
;
; Checks whther the binary version
;----------------------------------------------------------------------------

public VerifyVersion
VerifyVersion   proc    near
        mov     si, es:[2ah]            ; offset of version number
                                        ;  in ascii
        mov     bl, 10
        add     si, 3                   ; point to last digit

        call    VVDigit
        .if     z
          call    VVDigit
          .if     z and
          .if     <<byte ptr es:[si]> eq '.'>
            dec     si
            call    VVDigit
          .endif
        .endif
        ret
VerifyVersion   endp


public  VVDigit
VVDigit proc    near
        div     bl
        add     ah, '0'
        dec     si
        cmp     es:[si+1], ah
        mov     ah, 0                   ; do not xor or sub we need Z
        ret
VVDigit endp

;---------------------------------------------------------------------------
;
;       M068
;
;       Procedure Name  : IsCopyProt
;
;       Inputs          : DS:100 -> start of com file just read in
;
;       Outputs         : sets the A20OFF_COUNT variable to 10 if
;                         the program loaded in DS:100 uses a MICROSOFT
;                         copy protect scheme that relies on the A20 line
;                         being turned off for it's scheme to work.
;
;                         Note: The int 21 function dispatcher will turn
;                               a20 off, if the A20OFF_COUNT is non-zero
;                               and dec the A20OFF_COUNT before iretting
;                               to the user.
;
;       Uses            : ES, DI, SI, CX
;
;---------------------------------------------------------------------------

CPStartOffset   EQU     0175h
CPID1Offset     EQU     011bh
CPID2Offset     EQU     0173h
CPID3Offset     EQU     0146h
CPID4Offset     EQU     0124h
ID1             EQU     05343h
ID2             EQU     05044h
ID3             EQU     0F413h
ID4             EQU     08000h

CPScanPattern   db      089h, 026h, 048h, 001h           ; mov [148], sp
                db      08ch, 00eh, 04ch, 001h           ; mov [14c], cs
                db      0c7h, 006h, 04ah, 001h, 0h, 01h  ; mov [14a], 100h
                db      08ch, 00eh, 013h, 001h           ; mov [113], cs
                db      0b8h, 020h, 001h                 ; mov ax, 120h
                db      0beh, 000h, 001h                 ; mov si, 100h
CPSPlen         EQU     $ - CPScanPattern


                public  IsCopyProt
IsCopyProt      proc    near

        .if     <<ds:[CPID1Offset]> eq ID1> and
        .if     <<ds:[CPID2Offset]> eq ID2> and
        .if     <<ds:[CPID3Offset]> eq ID3> and
        .if     <<ds:[CPID4Offset]> eq ID4>

          push    cs
          pop     es
          mov     di, offset CPScanPattern        ; es:di -> Pattern to find

          mov     si, CPStartOffset               ; ds:si -> possible location
                                                  ; of pattern

          mov     cx, CPSPlen                     ; cx = length of pattern

          repe    cmpsb

          .if     z
            mov     cs:[A20OFF_COUNT], 0AH          ; M071
          .endif
        .endif
        ret

IsCopyProt      endp


V86CODE ENDS

        END
