home *** CD-ROM | disk | FTP | other *** search
- Unit SelfChk;
-
- {$I-}
-
- (* *)
- (* The SelfChk Unit, Release 1.1, 02/05/1990. *)
- (* *)
- (* This UNIT for Borland's Turbo Pascal version 4.0+ provides your *)
- (* programs with a means of checking themselves for changes at run- *)
- (* time as both an anti-viral and anti-hack measure. *)
- (* *)
- (* This UNIT is (C) 1990 by Michael S. Durkin and is distributed as *)
- (* SHAREWARE. Registration information is within the documentation, *)
- (* and is requested if you use The SelfChk Unit in your programs. *)
- (* You may NOT distribute modified source code. Do NOT modify this *)
- (* or any other copyright notice. *)
- (* *)
- (* Please forward any examples/suggestions for improvement or bug *)
- (* reports to me for consideration or fixes. *)
- (* *)
- (* Mike Durkin The TeleSoft RBBS-PC *)
- (* P.O. Box 1021 Data: (415) 969-8238 *)
- (* Mt. View, CA Four Lines (CAPAL) *)
- (* 94042-1021 FidoNet: (1:143/204) *)
- (* *)
- (* Place SelfChk in your Uses statement. Execute SCHKINST against *)
- (* your executable after compilation. Within your program use the *)
- (* DoSelfChk procedure to generate the CRC. SelfChkResult is bit *)
- (* mapped as noted below with the result of the SelfChk. Or, call *)
- (* ChkIntegrity (as an example) to check your executable and it'll *)
- (* take appropriate action. The low order Word of the FileSize of *)
- (* your program is used as an initial seed value for the 16 bit CRC *)
- (* routine (rather than $0000) for additional security. *)
- (* *)
- (* The SelfChk Unit will add, typically from 0.8 to 1.8k to your *)
- (* programs filesize depending on how many of the standard procedures *)
- (* this unit uses, that your program already used. Memory usage *)
- (* depends in part on the size of the disk buffer you choose. I *)
- (* recommend sharing the disk buffer with your main program to cut *)
- (* down on memory usage. *)
- (* *)
- (* And a final note: A condition of your use of The SelfChk Unit is *)
- (* that you don't make fun of my programming! It may be awful, but *)
- (* it gets the job done, and with a minimum of resources and time. *)
- (* If there is any part of this unit that confuses you, I'll be glad *)
- (* to clear it up. *)
- (* *)
-
- INTERFACE
-
- Uses Dos;
-
- Const
- SelfChkResult : Byte = 0; { Bit Mapped result after call to DoSelfChk (bit) }
- { 0 := SelfChk appears fine - }
- { 1 := IOError - couldn't check 0 }
- { 2 := 16bit CRC (w/filesize seed) failed 1 }
-
- (* Note: The following 4 constants must remain at minimum in this order *)
- (* to ensure that SCHKINST can find them during data installation and *)
- (* change 'file_crc' to reflect the actual CRC (seed=lo_word(filesize)). *)
-
- data_ofs : LongInt = $FFFFFFFF; { OffSet to DATA installed by SCHKINST }
- file_crc : Word = $FFFF ; { crc of exec installed by SCHKINST }
- crc_ccitt : Word = $0000 ; { runtime generation vs. file_crc }
- schk_sign : LongInt = $4B484353; { verification for SCHKINST. Ensures that }
- { SCHKINST is looking at the right data }
-
- Var { Share these with your main program, if you like, just don't }
- { call a procedure from this unit if you need the value of }
- { these variables to stay the same. }
- disk_buf : Array[1..2048] of Byte; { disk buffer, ok to change size }
- table_ccitt : Array [0..255] of Word;
- f : File;
- counter, result,
- counter2 : Integer;
- bytesCRCed : LongInt;
- exec_fname : String; { initiliazed to the executables drive/path/filename }
-
- Procedure DoSelfChk; { Compute seeded CRC and set SelfChkResult. }
-
- Procedure ChkIntegrity; { Calls DoSelfChk, examines SelfChkResult, and acts }
- { accordingly. Use this or write your own. }
-
- Procedure UpdSelfChkData;
- { Computes CRC and installs the value into itself. }
- { If your program updates itself, then call this }
- { procedure after you do your update to update the }
- { SelfChk CRC data. Note: Minimal error checking }
- { is done. Please see the Documentation for special }
- { considerations if you use this procedure. It is }
- { also expected that you modify bytes and/or append }
- { to the file only, so 'data_ofs' should not change. }
-
- Procedure Crc_gen( Var s; length : Integer);
- { Generally NOT called by the user. It's placed here }
- { for use by SCHKINST. If you do wish to generate }
- { the CRC of a buffer, set crc_ccitt := 0, and result}
- { to the length of the buffer. Then call this }
- { procedure as crc_gen(buf_name, length_buf); The }
- { CRC of the buffer is placed in crc_ccitt. For a }
- { file, read the next block, and leave crc_ccitt set }
- { at the CRC of the previous block. }
-
- IMPLEMENTATION
-
- {$I crc_gen.inc}
-
- Procedure DoSelfChk;
-
- Begin { Self Check code. }
-
- {$I-}
- FileMode := 0; { ReadOnly Mode }
- Assign(f,exec_fname);
- Reset(f,1);
- If IOResult <> 0 Then Begin
- SelfChkResult := SelfChkResult OR $01;
- Exit;
- End;
-
- (* Compute the 16 bit CRC using the low order word of *)
- (* the programs FileSize as an initial seed value. *)
-
- crc_ccitt := FileSize(f);
- bytesCRCed := 0;
- REPEAT
- BlockRead(f,disk_buf,SizeOf(disk_buf),result);
-
- (* The next IF..THEN restores the 6 bytes which were altered by SCHKINST *)
- (* during data installation to the value ($FF) SCHKINST saw them as *)
- (* when it computed the CRC. This is necessary, and does NOT sacrifice *)
- (* the integrity of the self check. Any byte(s)/bit(s) changed will *)
- (* still generate a bad self check result. *)
-
- If ((data_ofs OR (data_ofs+5)) >= bytesCRCed) AND ((data_ofs OR (data_ofs+5)) <= (bytesCRCed+result)) Then
- For counter := 1 to result Do
- If ((data_ofs+1) <= (bytesCRCed + counter)) AND
- ((bytesCRCed + counter) <= (data_ofs+6)) Then disk_buf[counter] := $FF;
-
- crc_gen(disk_buf,result);
- bytesCRCed := bytesCRCed + result;
- UNTIL Eof(f);
- Close(f);
-
-
- (* Compare the runtime CRC computation with the CRC installed by SCHKINST *)
- (* If the two don't match, set the result bit for a bad self check. *)
-
- If (crc_ccitt <> file_crc) Then SelfChkResult := SelfChkResult OR $04;
-
- FileMode := 2; { Read/Write Mode }
-
- End; { Procedure }
-
- Procedure ChkIntegrity;
-
- Begin
-
- DoSelfChk;
- If (SelfChkResult AND $01) = $01 Then WriteLn(' Note: I/O Error during CRC SelfChk!');
- If SelfChkResult > 1 Then Begin
- WriteLn(#7,'WARNING: ',exec_fname,' fails CRC SelfChk!',#7);
- Halt;
- End;
-
- End; { Procedure ChkIntegrity }
-
- Procedure UpdSelfChkData;
-
- Var
- fattr : Word;
-
- Begin
-
- DoSelfChk;
- Assign(f,exec_fname);
- GetFAttr(f,fattr);
- SetFAttr(f,$00);
- Reset(f,1);
- Seek(f,data_ofs+4);
- BlockWrite(f,crc_ccitt,2);
- SetFAttr(f,fattr);
- Close(f);
-
- End; { Procedure UpdSelfChkData }
-
- {$I+}
-
- Begin { Unit Init Code }
-
- exec_fname := ParamStr(0);
-
- (* build the crc table at runtime - saves approx. *)
- (* 400 bytes in the .EXE w/ minimal runtime overhead. *)
- (* Modified from the Public Domain program - FILETEST *)
-
- For counter := 0 to 255 Do
- Begin
- result := counter;
- For counter2 := 1 to 8 Do
- Begin
- If (result and 1) = 1
- then result := (result shr 1) xor $8404 { $8404 = CCITT polynomial }
- else result := result shr 1;
- End;
-
- table_ccitt[counter] := result;
- End
-
- End. { of Unit }
-