program giant_black_book_integ_checker; uses dos,crt; const MAX_ENTRIES =2000; {Max number of files this can handle} type LogRec_Type =record Name :string[80]; Time :longint; Size :longint; Checksum :longint; Found :boolean; end; var LstFile :text; {listing file} LogFile :file of LogRec_Type; {log file} LogEntries :longint; {# entries in log file} Log :array[1..MAX_ENTRIES] of ^LogRec_Type; {log entries} j :word; SearchDir :string; {directory to check} CurrDir :string; {directory program called from} {This routine just makes a string upper case} function UpString(s:string):string; var i :word; begin for i:=1 to length(s) do s[j]:=UpCase(s[j]); UpString:=s; end; {This function searches the log in memory for a match on the file name. To use it, pass the name of the file in fname. If a match is found, the function returns true, and FN is set to the index in Log[] which is the proper record. If no match is found, the function returns false.} function SearchLog(fname:string;var FN:word):boolean; var j :word; begin fname:=UpString(fname); if LogEntries>0 then for j:=1 to LogEntries do begin if fname=Log[j]^.Name then begin SearchLog:=true; FN:=j; exit; end; end; SearchLog:=false; end; {This function calcuates the checksum of the file whose name is passed to it. The return value is the checksum.} function Get_Checksum(FName:string):longint; var F :file; cs :longint; j,x :integer; buf :array[0..511] of byte; begin cs:=0; assign(F,FName); reset(F,1); repeat blockread(F,buf,512,x); if x>0 then for j:=0 to x-1 do cs:=cs+buf[j]; until eof(F); close(F); Get_Checksum:=cs; end; {This routine checks the integrity of one complete subdirectory and all its subdirectories. The directory name (with a final \) is passed to it. It is called recursively. This checks all COM and EXE files.} procedure check_dir(dir:string); var SR :SearchRec; {Record used by FindFirst} Checksum :Longint; {temporary variables} FN :word; cmd :char; begin dir:=UpString(dir); FindFirst(dir+'*.com',AnyFile,SR); {first check COM files} while DosError=0 do begin if SearchLog(dir+SR.Name,FN) then begin Checksum:=Get_Checksum(dir+SR.Name); if (Log[FN]^.Time<>SR.Time) or (Log[FN]^.Size<>SR.Size) or (Log[FN]^.Checksum<>Checksum) then begin write(dir+SR.Name,' has changed!',#7,#7,#7,' Do you want to '); write('update its record? '); write(LstFile,dir+SR.Name,' has changed! Do you want to update '); write(LstFile,'its record? '); repeat cmd:=UpCase(ReadKey) until cmd in ['Y','N']; if cmd='Y' then begin Log[FN]^.Time:=SR.Time; Log[FN]^.Size:=SR.Size; Log[FN]^.Checksum:=Checksum; Log[FN]^.Found:=True; end; writeln(cmd); writeln(LstFile,cmd); end else begin writeln(dir+SR.Name,' validated.'); Log[FN]^.Found:=True; end; end else begin if LogEntriesSR.Time) or (Log[FN]^.Size<>SR.Size) or (Log[FN]^.Checksum<>Checksum) then begin write(dir+SR.Name,' has changed!',#7,#7,#7, ' Do you want to update its record? '); write(LstFile,dir+SR.Name, ' has changed! Do you want to update its record? '); repeat cmd:=UpCase(ReadKey) until cmd in ['Y','N']; if cmd='Y' then begin Log[FN]^.Time:=SR.Time; Log[FN]^.Size:=SR.Size; Log[FN]^.Checksum:=Checksum; Log[FN]^.Found:=True; end; writeln(cmd); writeln(LstFile,cmd); end else begin writeln(dir+SR.Name,' validated.'); Log[FN]^.Found:=true; end; end else begin if LogEntries 0) and (SR.Name[1]<>'.') then begin ChDir(SR.Name); check_dir(dir+SR.Name+'\'); ChDir('..'); end; FindNext(SR); end; end; {This procedure checks the master boot sector and the boot sector's integrity} procedure check_boot; var FN,j :word; cs :longint; buf :array[0..511] of byte; r :registers; cmd :char; currdrv :byte; begin r.ah:=$19; intr($21,r); currdrv:=r.al; if currdrv>=2 then currdrv:=currdrv+$80-2; if currdrv=$80 then begin r.ax:=$201; {read boot sector/master boot sector} r.bx:=ofs(buf); r.es:=sseg; r.cx:=1; r.dx:=$80; intr($13,r); r.ax:=$201; intr($13,r); cs:=0; for j:=0 to 511 do cs:=cs+buf[j]; if SearchLog('**MBS',FN) then begin Log[FN]^.Found:=True; if Log[FN]^.Checksum=cs then writeln('Master Boot Sector verified.') else begin write('Master Boot Sector has changed! Update log file? '); write(LstFile,'Master Boot Sector has changed! Update log file? '); repeat cmd:=UpCase(ReadKey) until cmd in ['Y','N']; if cmd='Y' then Log[FN]^.Checksum:=cs; writeln(cmd); writeln(LstFile,cmd); end; end else begin writeln('Master Boot Sector data ADDED to log.'); writeln(LstFile,'Master Boot Sector data ADDED to log.'); LogEntries:=LogEntries+1; new(Log[LogEntries]); Log[LogEntries]^.Name:='**MBS'; Log[LogEntries]^.Checksum:=cs; Log[LogEntries]^.Found:=True; end; j:=$1BE; while (j<$1FE) and (buf[j]<>$80) do j:=j+$10; if buf[j]=$80 then begin r.dx:=buf[j]+256*buf[j+1]; r.cx:=buf[j+2]+256*buf[j+3]; end else exit; end else begin r.cx:=1; r.dx:=currdrv; end; if CurrDrv<$81 then begin r.ax:=$201; r.bx:=ofs(buf); r.es:=sseg; intr($13,r); r.ax:=$201; intr($13,r); cs:=0; for j:=0 to 511 do cs:=cs+buf[j]; if SearchLog('**BOOT',FN) then begin Log[FN]^.Found:=True; if Log[FN]^.Checksum=cs then writeln('Boot Sector verified.') else begin write('Boot Sector has changed! Update log file? '); write(LstFile,'Boot Sector has changed! Update log file? '); repeat cmd:=UpCase(ReadKey) until cmd in ['Y','N']; if cmd='Y' then Log[FN]^.Checksum:=cs; writeln(cmd); writeln(LstFile,cmd); end; end else begin writeln('Boot Sector data ADDED to log.'); writeln(LstFile,'Boot Sector data ADDED to log.'); LogEntries:=LogEntries+1; new(Log[LogEntries]); Log[LogEntries]^.Name:='**BOOT'; Log[LogEntries]^.Checksum:=cs; Log[LogEntries]^.Found:=True; end; end; end; {This procedure removes files from the log that have been deleted on the system. Of course, it allows the user to decide whether to remove them or not.} procedure PurgeFile(j:word); var cmd :char; i :word; begin write(Log[j]^.Name,' was not found. Delete from log file? ',#7,#7,#7); write(LstFile,Log[j]^.Name,' was not found. Delete from log file? '); repeat cmd:=UpCase(ReadKey) until cmd in ['Y','N']; if cmd='Y' then begin for i:=j to LogEntries-1 do Log[i]^:=Log[i+1]^; LogEntries:=LogEntries-1; end; writeln(cmd); writeln(LstFile,cmd); end; begin writeln('GB-INTEG Ver 1.00, (C) 1995 American Eagle Publications, Inc.'); assign(LogFile,'\GBINTEG.DAT'); {Load the log file into memory} {$I-} reset(LogFile); {$I+} if IOResult<>0 then LogEntries:=0 else begin for LogEntries:=1 to FileSize(LogFile) do begin new(Log[LogEntries]); read(LogFile,Log[LogEntries]^); end; close(LogFile); end; assign(LstFile,'GBINTEG.LST'); {Create the listing file} rewrite(LstFile); {Take care of directory maintenance} if ParamCount=1 then SearchDir:=ParamStr(1) else SearchDir:='\'; GetDir(0,CurrDir); ChDir(SearchDir); if SearchDir[length(SearchDir)]<>'\' then SearchDir:=SearchDir+'\'; check_boot; {check the boot sectors} check_dir(SearchDir); {check integrity} j:=1; while j<=LogEntries do {handle missing files} begin if Log[j]^.Found then j:=j+1 else PurgeFile(j); end; ChDir(CurrDir); rewrite(LogFile); {update log file} for j:=1 to LogEntries do begin Log[j]^.Found:=False; {reset these flags before writing to disk} write(LogFile,Log[j]^); end; close(LogFile); writeln(LogEntries,' files in current log file.'); writeln(LstFile,LogEntries,' files in current log file.'); close(LstFile); end.