; PROTECT.ASM
;
; This program prevents fixed disks from being reformatted. 
; It replaces the BIOS and DOS disk access interrupt. The DOS
; FORMAT command starts by rewriting the boot record. If an
; attempt to do so is made, a fatal error is returned, thus
; stopping the format. This program also protects against
; low level BIOS formats by also returning a fatal error
; if there is an attempt to format a hard disk.
;
; For all other cases, the old, disk routine is called.
;
; This program deinstalls when the system is rebooted.
;
; This program stays resident. It is set up to be a .COM file.
;
; Interrupts 13h, l9h, and 26h are redirected.
;
;
; Note: This program is only set up to protect the C drive 
; against DOS reformatting. Thus, external disks can be
; formatted without problem. To protect more drives, modify
; the procedure new_dos_write. You might want to specify
; the drive to be protected through command line parameters.

;----------------CONSTANTS----------------- 

DISK_INTR          equ 13h
REBOOT_INTR        equ 19h
DOS_WRITE          equ 26h
FREE_INTl          equ 60h
FREE_INT_LAST      equ 67h
DISK_ENTRY         equ DISK_INTR   * 4
REBOOT_ENTRY       equ REBOOT_INTR * 4
DOS_WRITE_ENTRY    equ DOS_WRITE   * 4
FORMAT_CMD         equ 5
C_DRIVE            equ 2
BAD_TRACK          equ 40h
BAD_TRACK_LO       equ 6
FORE_COLOR         equ 3
RETURN             equ 0dh
LINEFEED           equ 0ah

;-------------DUMMY SEGMENT---------------- 

; Set up a dummy segment heading pointing to the interrupt table.

int_table               segment at 0 
int_table               ends


;----------------BEGINNING----------------- 

code               segment
                   assume cs: code
                   org 44        ;here is the environment
env_adrs           dw ?          ;pointer
                   org 100h      ;make this a .COM file
protect:           jmp install         ;install interrupt


;-----------------DATA--------------------- 

communicate:            jmp com_rtn
program_name            db  'PROTECT',0
old_disk_intr           dw  ?,?
old_reboot_intr         dw  ?,?
old_dos_write           dw  ?,?
ident_intr              db  DOS_WRITE
IDENT_LENGTH            equ OFFSET  old_disk_intr - OFFSET program_name


    ;---------------NEW INTERRUPT ROUTINES------------

; This routine replaces the BIOS disk interrupt. If the interrupt
; call is to format a hard disk, it returns a bad track error
; otherwise, the control is passed along the interrupt chain.
;

new_disk_intr proc      far

	      cmp       ah,FORMAT_CMD   ;is it a format command?
	      jne       do_old_int      ;nope, pass control
	      test      dl,80h          ;is it for a hard disk?
	      jz        do_old_int      ;nope, pass control
	      mov       ah,BAD_TRACK    ;yes, fake a bad track error
	      stc                       ;indicate error
	      sti                       ;allow interrupts
	      ret       2               ;return, clearing old flags

do_old_int:   jmp dword ptr cs:old_disk_intr
					;pass control to old int
new_disk_intr endp

; This routine replaces the DOS disk write interrupt.
; If it is used to change sector 0 (the boot record), a bad track error
; is returned; otherwise, the control is passed along the interrupt
; chain.

 new_dos_write     proc  far
		   cmp dx,0             ;write to boot record?
		   jne do_old_write     ;no, do old interrupt
		   cmp al,C_DRIVE       ;Writing to hard disk?
		   jne do_old_write     ;no, do old interrupt
                   sti
		   mov ah,BAD_TRACK     ;fake a bad track error
                   mov al, BAD_TRACK_LO
		   stc                  ;signal error
		   ret                  ;exit, keep stack

do_old_write:      jmp   dword ptr cs:old_dos_write
                                                         
					;pass control to old int
new_dos_write      endp

; The following deinstalls the redirected interrupts if 
; there is a soft reboot. This keeps the vectors from pointing
; to an area of memory that no longer contains program code.

new_reboot_intr    proc far
		   cli                  ;don't allow interrupts
		   push ds              ;save old values
                   push     bx
                   assume ds:int_table 
					;point to interrupt table
                   xor bx,bx
                   mov ds,bx
		   mov      bl,ident_intr ;find markerinterrupt
                   xor      bh,bh
		   shl      bh,1
		   shl      bh,1
		   mov      [bx], word ptr 0      ;clear it
		   mov      [bx+2],word ptr 0
		   mov      bx,cs:old_disk_intr   ;replace disk
		   mov      ds:DISK_ENTRY,bx      ;interrupt
                   mov      bx,cs:old_disk_intr+2
                   mov      ds:DISK_ENTRY+2,bx
		   mov      bx,cs:old_reboot_intr ;replace reboot
		   mov      ds:REBOOT_ENTRY,bx    ;interrupt
                   mov      bx,cs:old_reboot_intr+2
		   mov      ds:REBOOT_ENTRY+2,bx
		   mov      bx,cs:old_dos_write   ;replace DOS write
		   mov      ds:DOS_WRITE_ENTRY,bx ;interrupt
                   mov      bx,cs:old_dos_write+2
		   mov      ds:DOS_WRITE_ENTRY+2,bx
		   pop bx                         ;restore registers
                   assume ds:nothing 
                   pop ds 
                   jmp dword ptr cs:old_reboot_intr 

new_reboot_intr   endp

; The following allows for communication between PROTECT, and
; outside programs.

com_rtn       proc near
                   iret 
com_rtn       endp

;-----------INSTALLATION SECTION-------------- 

install:           assume   ds:code
		   push     cs                  ;set up segments
                   pop      ds

;See if PROTECT is installed already           
                          
		   mov      al,FREE_INTl        ;scan interrupt table
check_loop:        mov      ah,35h              ;free range for PROTECT
		   int      21h                 ;marker

		   cmp      bx,0                ;see if interrupt
						;is in use
		   jne      compare_strings
						;yes, see if it is marker
		   mov      cx,es               ;now check segment
		   cmp      cx,0
		   jne      compare_strings
						;else,vector is free
		   mov      ident_intr,al       ;remember it
		   jmp      cont_loop

compare_strings:   mov      di,bx               ;es:di points to target
		   add      di,3                ;don't lock at jump
		   lea      si,program_name     ;ds:si points to name
		   mov      cx,IDENT_LENGTH     ;see if target points to
		   repe     cmpsb               ;same string as name
		   jcxz     already_installed   ;matched => installed

cont_loop:         inc      al                  ;look at next interrupt
		   cmp      al,FREE_INT_LAST    ;time to stop?
		   jne      check_loop          ;no, keep checking
		   cmp ident_intr,DOS_WRITE     ;a free interrupt?
		   jne not_installed            ;yes, so install

; No free room to install communications and identifier 
; interrupt ; vector. Print a message and install anyway.
;
; ident_intr is initialized to the DOS write interrupt. 
; Thus, if no marker is installed, the eections which redirect during
; installation and upon rebooting will change the DOS write 

; interrupt when modifying the identifier interrupt, then 
; correct it when modifying the DOS write interrupt.
;
		   lea      dx,no_ident_warning
                   call     disp_msg
		   jmp      not_installed

; Already installed, so print a message and terminate.
 
already_installed:
		   lea      dx,already_msg ;display message
		   call     disp_msg
		   mov      al,1                ;indicate error
		   mov      ah,4ch              ;terminate
		   int      21h
              
; Not installed, so continue

not_installed:     lea      dx,start_up_msg     ;print message that
		   call     disp_msg            ;protect is installed

; Read old interrupts

		   mov      al,DISK_INTR        ;read old interrupt
		   mov      ah,35h
		   int      21h
		   mov      old_disk_intr,bx    ;save it
		   mov      old_disk_intr+2,es
		   mov      al,REBOOT_INTR
			    ;read old reboot interrupt
		   mov      ah,35h
		   int      21h

		   mov      old_reboot_intr, bx ;save it
		   mov      old_reboot_intr+2,es
		   mov      al,DOS_WRITE
		   mov      ah,35h
		   int      21h

		   mov      old_dos_write,bx
		   mov      old_dos_write+2,es

; Free environment

		   mov      ax,env_adrs     ;find environment
		   mov      es,ax               ;free it
		   mov      ah,73
		   int      21h

; Redirect interrupts

		   lea      dx,communicate      ;set up communicatlon
		   mov      al,ident_intr       ;identifier interrupt
		   mov      ah,25h
		   int      21h

		   lea      dx,new_disk_intr    ;redirect interrupt
		   mov      al,DISK_INTR
		   mov      ah,25h
		   int      21h

		   lea      dx,new_reboot_intr  ;redirect reboot
						;interrupt
		   mov      al,REBOOT_INTR
		   mov      ah,25h
		   int      21h

		   lea      dx,new_dos_write    ;redlrect DOS dlsk
		   mov      al,DOS_WRITE        ;write interrupt
		   mov      ah,25h
		   int      21h

; Now terminate and stay resident

		   lea      dx,install
		   mov      cl,4
		   shr      dx,cl
		   inc      dx
		   mov      al,0                ;no error
		   mov      ah,31h               ;terminate
		   int      21h

;------------UTILITIES (non-resident) ----------- 

; disp_msg
;
; displays an ASCIIZ message 
; offset of mes6age in DS:DX 

disp_msg           proc     near
		   push     ax
		   push     bx
		   push     si
		   mov      bl, FORE_COLOR
		   mov      si,dx               ;point to string

d_m_loop:          mov      al, [si]            ;load Character
		   cmp      al,0                ;check for end
		   je       d_m_end
		   mov      ah,0eh
		   int      10h                 ;print char

		   inc      si
		   jmp      d_m_loop
d_m_end:           
		   pop      si
		   pop      bx
		   pop      ax
                   ret
disp_msg           endp


;-------------------MESSAGES (non-resident)-------------- 

start_up_msg       db  LINEFEED,RETURN
                   db  '------------------------------'       

                   db LINEFEED,RETURN
                   db 'PROTECT is installed',LINEFEED
                   db LINEFEED,RETURN
		   db 'Your hard disk is safe from reformattlng'
                   db LINEFEED,LINEFEED,RETURN
                   db '------------------------------'  
                   db LINEFEED,LINEFEED,RETURN,0

already_msg        db LINEFEED,RETURN 
                   db '------------------------------'  
                   db LINEFEED,RETURN 
		   db 'Sorry, PROTECT was already installed'
                   db LINEFEED,LINEFEED,RETURN
                   db '------------------------------'  
                   db LINEFEED,LINEFEED,RETURN,0
                   
no_ident_warning   db LINEFEED,RETURN
                   db '------------------------------'  
                   db LINEFEED,RETURN
		   db 'Warning, Many resident utilities are'
                   db ' already installed.'
                   db LINEFEED,RETURN
		   db 'PROTECT will not warn you if you install'
                   db ' twice'
                   db LINEFEED,RETURN  
                   db '------------------------------'  
                   db LINEFEED,LINEFEED,RETURN,0

code               ends
                   end protect
