Industrial Training




TSR To Write-Protect The Hard Disk



TSR To Write-Protect The Hard Disk

We can easily protect a floppy disk from being written to. All we need to do is paste a write protect label on the write protect notch. However, a hard disk cannot be similarly protected. If we are to protect a hard disk, we will have to write a TSR which prevents any write operations from being performed on the hard disk. This program is given below.

#pragma inline

#include "dos.h"

struct INTERRUPT

{

unsigned bp, di, si, ds, es, dx, cx, bx, ax, ip, cs, fl ;

} ;

void interrupt ( *prev )( ) ;

void interrupt our ( struct INTERRUPT ) ;

main( )

{

prev = getvect ( 0x13 ) ;

setvect ( 0x13, our ) ;

keep ( 0, 1000 ) ;

}

void interrupt our ( struct INTERRUPT r )

{

if ( _AH == 3 || _AH == 5 || _AH == 0x0b )

{

/* stc : set carry flag to 1 to indicate error */

asm stc

asm pushf

asm pop r.fl

return ;

}

_ES = r.es ;

_DX = r.dx ;

_CX = r.cx ;

_BX = r.bx ;

_AX = r.ax ;

( *prev )( ) ;

asm pushf

asm pop r.fl

r.ax = _AX ;

r.bx = _BX ;

r.cx = _CX ;

r.dx = _DX ;

r.es = _ES ;

}

Quit out of Turbo C, compile the program using

C>> TCC -Emasm.exe protect.c at the DOS prompt and then execute the PROTECT.EXE file.

To understand this program let us first see what happens when a normal disk operation is performed. Firstly interrupt 19 occurs and the address of the ROM-BIOS routine which performs disk Input/Output is picked up from IVT. Next, the current contents of

registers are pushed on the stack and then the control is passed to the ROM-BIOS routine. The ROM-BIOS performs the actual I/O, the stack contents are popped back into CPU registers and the control returns to the place from where interrupt 19 was issued. With our TSR active in memory this normal process would be changed slightly. Instead of the ROM-BIOS routine being called, the function our( ) gets called. This function being an ISR, values of all registers except SS and SP are pushed on the stack. The values pushed to the stack are also available in the formal parameters of the function our( ). Thus the variable r.bp will now contain the value pushed on the stack from register BP, the variable r.di would contain the value from register DI and so on. Soon you will see why this assigning of values on the stack to the formal arguments is necessary. To understand the working of the function our( ) let us state a few facts:

  1. Any time a condition is evaluated the current contents of the CPU register may get disturbed, since a condition is evaluated with the help of CPU registers.

  2. When the control goes back from an ISR, contents of the stack are popped back into CPU registers.

  3. Whenever a write operation is performed on the disk, the AH register contains either a value 3, 5 or Bh signifying a write, format or a long write operation respectively.

  4. Any change in the formal arguments of our( ) would effect a corresponding change in the contents of the stack.

  5. The values of actual CPU registers are accessible through pseudo variables _AH, _AL etc. provided in Turbo C.

  6. Turbo C doesn't provide a pseudo variable _FLAGS (Borland C/C++ does).

  7. When a write operation is performed, the ROM-BIOS routine reports the success or failure of the write operation by setting the carry flag bit in the flags register. DOS considers that the write operation has been successful if the carry flag bit contains a value 0, failure otherwise.

In our( ) , to begin with the condition in if is evaluated. If the condition is satisfied it means that a write is being attempted on the disk. We wish to prevent this, hence we execute the following set of statements

asm stc

asm pushf

asm pop r.fl

Let us understand these statements. The word asm indicates that the line represents an assembly language statement. It is necessary to use assembly language statements since there is no pseudo variable to access the flags register directly.

For DOS to believe that the attempt to write has failed we must just set the carry flag to 1. asm stc achieves this. asm pushf then pushes the flags register value on the stack and asm pop r.fl pops this set value from stack into r.fl . Note that by changing the value of r.fl a corresponding change also takes place in the value of flags register pushed earlier (when the control reached here) on the stack. Now the control returns from our( ). our( ) being an ISR all values from the stack are now popped into CPU registers and the control returns to DOS. DOS looks at the flags register, finds that it has been set up and hence promptly displays an error message "Write protect failure". In effect we have been able to prevent writing to the disk.

Let us now see what happens if a read operation is being performed. Once again the register values would be pushed on the stack and the control would reach our( ). In our( ) the condition would fail hence our( ) must permit the read operation. For this, our( ) has to pass control to the ROM-BIOS routine. Before it hands over the control, it must restore the values of the registers which may have been disturbed while checking the conditions. This is what is done through the statements

_ES = r.es ;

_DX = r.dx ;

_CX = r.cx ;

_BX = r.bx ;

_AX = r.ax ;

Once this is done the disk I/O routine in the ROM-BIOS is called through the statement ( *prev )( ) ;

Once this routine has performed disk I/O, it sets up certain registers (AX, BX, CX, DX, ES, flags) and passes the control back to our( ) .

If we now return the control to the place from where the interrupt was issued, the current contents of stack would be popped into CPU registers. This would be only partly correct, because by doing this we would not be able to communicate the values setup by the ROM-BIOS routine in AX, BX, CX, DX, ES and flags. Hence in our( ), after the call to prev the values set up by the ROM-BIOS routine in AX, BX, CX, DX, ES and flags are placed on the stack through the statements

asm pushf

asm pop r.fl

r.ax = _AX ;

r.bx = _BX ;

r.cx = _CX ;

r.dx = _DX ;

r.es = _ES ;

Now when the control returns from our( ) the desired values from the stack would be popped into CPU registers.

Note the preprocessor directive #pragma inline used at the beginning of our program. This directive tells the compiler that there is inline assembly language code in our program. If not used then a warning appears but the program still gets successfully compiled and linked. However, this time the compilation and linking takes a little more time.

We can't compile and link the program from within the TC environment using Ctrl-F9 or from the compile menu. This is because we have used assembly language statements within our program. We have to compile the program as shown below.

C>>TCC -mt -Emasm.exe protect.c

Where TCC is the command line TC compiler. The meanings of switches used in compilation is given below:

  • mtsignifies that our program is to be compiled in the tiny memory model

  • Emasm.exesignifies that an external assembler masm.exe should be used to convert the .asm file into an object file. In place of masm.exe if Borland's tasm.exe is used then we can use the switch -e in place of -Emasm.exe . protect.cis the name of the file we want to compile and link.



Hi I am Pluto.