home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / antivir / stlthb22.zip / VIRCHECK.PAS < prev    next >
Pascal/Delphi Source File  |  1992-02-10  |  11KB  |  457 lines

  1. {$I-}
  2. {
  3. vircheck.pas
  4. Stealth Bomber Version 2.2
  5.  
  6. Kevin Dean
  7. Fairview Mall P.O. Box 55074
  8. 1800 Sheppard Avenue East
  9. Willowdale, Ontario
  10. CANADA    M2J 5B9
  11. CompuServe ID: 76336,3114
  12.  
  13. February 10, 1992
  14.  
  15.     This is the interface to the anti-virus system and CRC checks in the
  16. Stealth Bomber package.
  17.  
  18.     This code is public domain.
  19. }
  20.  
  21.  
  22. unit VirCheck;
  23.  
  24.  
  25. interface
  26.  
  27.  
  28. uses
  29.   DOS, DOSMCB, AllocBuf;
  30.  
  31.  
  32. type
  33.   crc32_t =
  34.     longint;
  35.  
  36.   FileCRC =
  37.     record
  38.     case boolean of
  39.       false:
  40.     (
  41.     SearchStr : array [1 .. 8] of char;    { Used by Stealth Bomber package only. }
  42.     );
  43.  
  44.       true:
  45.     (
  46.     Polynomial : crc32_t;            { Polynomial for this file. }
  47.     CRC : crc32_t;                { Calculated CRC for this file. }
  48.     );
  49.     end;
  50.  
  51.  
  52. { Anti-virus validation return code. }
  53. const
  54.   StealthOK          = $0000;    { No virus found. }
  55.   StealthIntrErr     = $0001;    { Interrupts set beyond the program's code space. }
  56.   StealthDOSMemErr   = $0002;    { DOS memory inconsistent with BIOS memory. }
  57.   StealthDOSHijacked = $0004;    { DOS interrupt hijacked by JMP FAR or CALL FAR. }
  58.   StealthFileErr     = $0001;    { File not found or unable to open. }
  59.   StealthFileDateErr = $0002;    { File date/time stamp invalid. }
  60.   StealthFileSizeErr = $0004;    { File size inconsistent between directory and file open checks. }
  61.   StealthCRCBadPoly  = $0008;    { CRC polynomial is invalid. }
  62.   StealthNoMem       = $0010;    { No memory to perform CRC check. }
  63.   StealthCRCInvalid  = $0020;    { CRC is invalid. }
  64.  
  65.  
  66. function StealthSysCheck : word;
  67. function StealthFileCheck(FileName : PathStr; FCRC : FileCRC) : word;
  68.  
  69.  
  70. implementation
  71.  
  72.  
  73. type
  74.   dWordRec =
  75.     record
  76.     Lo, Hi : word
  77.     end;
  78.  
  79.   BytePtr =
  80.     ^byte;
  81.  
  82.   WordPtr =
  83.     ^word;
  84.  
  85.   PtrPtr =
  86.     ^pointer;
  87.  
  88.  
  89. {***}
  90. function BIOSMemory : word;
  91.  
  92. var
  93.   Regs : Registers;    { Registers in memory check call. }
  94.  
  95. begin
  96. Intr($12, Regs);
  97. BIOSMemory := Regs.AX;
  98. end;
  99.  
  100.  
  101. {***}
  102. { Determine true segment address of pointer and check for wrap around memory. }
  103. function PtrSeg(Ptr : pointer) : word;
  104.  
  105. var
  106.   PSeg : word;    { True segment of pointer. }
  107.  
  108. begin
  109. PSeg := Seg(Ptr^) + Ofs(Ptr^) shr 4;
  110.  
  111. if PSeg < Seg(Ptr^) then
  112.   { Pointer points beyond standard 1M memory. }
  113.   PSeg := $FFFF;
  114.  
  115. PtrSeg := PSeg;
  116. end;
  117.  
  118.  
  119. {***}
  120. { Validate interrupt; make sure not beyond code space and not hijacked. }
  121. function ValidateIntr(IntrNum : integer; MemLimit : word) : word;
  122.  
  123. var
  124.   Result : word;        { Result of tests. }
  125.   I : record
  126.       case boolean of
  127.     false:
  128.       (
  129.       IPtr : pointer;    { Interrupt pointer. }
  130.       );
  131.  
  132.     true:
  133.       (
  134.       CodePtr : BytePtr;    { Pointer to interrupt; treat as data. }
  135.       );
  136.       end;
  137.   ISeg : word;            { Adjusted segment of I.IPtr. }
  138.   Target : pointer;        { Target of hijacked interrupt. }
  139.   IntrMCB : MCBPtr;        { MCB of hijacked interrupt. }
  140.   TargetMCB : MCBPtr;        { MCB of target of hijacking. }
  141.  
  142. begin
  143. { Assume interrupt is valid. }
  144. Result := StealthOK;
  145.  
  146. { Get interrupt and adjusted segment. }
  147. GetIntVec(IntrNum, I.IPtr);
  148. ISeg := PtrSeg(I.IPtr);
  149.  
  150. { Interrupt pointer invalid if between PSP and memory limit. }
  151. if (ISeg >= PrefixSeg) and (ISeg < MemLimit) then
  152.   Result := Result or StealthIntrErr;
  153.  
  154. { Check beginning of interrupt code for suspicious instructions. }
  155. case I.CodePtr^ of
  156.   $EA,        { JMP FAR <addr>. }
  157.   $9A:        { CALL FAR <addr>. }
  158.     begin
  159.     Target := PtrPtr(longint(I.CodePtr) + 1)^;
  160.     Result := Result or StealthDOSHijacked;
  161.     end;
  162.  
  163.   $2E:        { CS segment prefix. }
  164.     case WordPtr(longint(I.CodePtr) + 1)^ of
  165.       $2EFF,    { JMP FAR CS:[addr]. }
  166.       $1EFF:    { CALL FAR CS:[addr]. }
  167.     begin
  168.     Target := PtrPtr(Ptr(Seg(I.CodePtr^), WordPtr(longint(I.CodePtr) + 3)^))^;
  169.     Result := Result or StealthDOSHijacked;
  170.     end;
  171.       end;
  172.   end;
  173.  
  174. if Result and StealthDOSHijacked <> 0 then
  175.   begin
  176.   { Determine MCB's that own the interrupt and the target of the redirection. }
  177.   IntrMCB := MCBOwner(I.CodePtr);
  178.   TargetMCB := MCBOwner(Target);
  179.  
  180.   { Redirection is valid if it falls within the same MCB or falls outside memory limit. }
  181.   if (IntrMCB = TargetMCB) or (PtrSeg(target) >= MemLimit) then
  182.     Result := Result and not StealthDOSHijacked;
  183.   end;
  184.  
  185. ValidateIntr := Result;
  186. end;
  187.  
  188.  
  189. {***}
  190. { Perform anti-virus system check. }
  191. function StealthSysCheck : word;
  192.  
  193. var
  194.   Result : word;    { Result of tests. }
  195.   MCB : MCBPtr;        { Memory control block pointer. }
  196.   BIOSMem : word;    { Memory in paragraphs according to BIOS. }
  197.   DOSMem : word;    { Memory in paragraphs according to DOS. }
  198.   MemLimit : word;    { Limit of useable memory. }
  199.  
  200. begin
  201. { Assume system passes all tests. }
  202. Result := StealthOK;
  203.  
  204. BIOSMem := BIOSMemory * 64;
  205.  
  206. { Find last memory control block. }
  207. MCB := GetMCB;
  208. while MCB^.ID <> $5A do
  209.   MCB := NextMCB(MCB);
  210.  
  211. DOSMem := Seg(MCB^) + MCB^.Size + 1;
  212.  
  213. { DOS memory extenders may show more memory than BIOS and some versions of DOS may differ by up to 1k from BIOS memory. }
  214. if BIOSMem > DOSMem + 64 then
  215.   Result := Result or StealthDOSMemErr;
  216.  
  217. { Assume BIOS memory goes at least to 640k limit (may have been modified by virus). }
  218. if BIOSMem < $A000 then
  219.   MemLimit := $A000
  220. else
  221.   MemLimit := BIOSMem;
  222. if MemLimit < DOSMem then
  223.   MemLimit := DOSMem;
  224.  
  225. { Swap vectors taken over by Turbo Pascal. }
  226. SwapVectors;
  227.  
  228. Result := Result or ValidateIntr($21, MemLimit);    { DOS function interrupt. }
  229. Result := Result or ValidateIntr($24, MemLimit);    { Critical error interrupt. }
  230. Result := Result or ValidateIntr($25, MemLimit);    { Absolute disk read interrupt. }
  231. Result := Result or ValidateIntr($26, MemLimit);    { Absolute disk write interrupt. }
  232. Result := Result or ValidateIntr($1C, MemLimit);    { User timer interrupt. }
  233. Result := Result or ValidateIntr($28, MemLimit);    { DOS OK interrupt. }
  234.  
  235. { Restore vectors required by Turbo Pascal. }
  236. SwapVectors;
  237.  
  238. StealthSysCheck := Result;
  239. end;
  240.  
  241.  
  242. {***}
  243. { Extract the low word of a dword. }
  244. function LowW(DWord : longint) : word;
  245.  
  246. begin
  247. LowW := DWordRec(DWord).Lo;
  248. end;
  249.  
  250.  
  251. {***}
  252. { Extract the high word of a dword. }
  253. function HiW(DWord : longint) : word;
  254.  
  255. begin
  256. HiW := DWordRec(DWord).Hi;
  257. end;
  258.  
  259.  
  260. {***}
  261. { Calculate the CRC of a file.  The file is assumed to be open and the buffer is assumed to be valid. }
  262. function CalcCRC(var F : file; Buffer : BytePtr; BufSize : word; Polynomial : crc32_t) : crc32_t;
  263.  
  264. var
  265.   Table : array [0 .. 255] of crc32_t;    { CRC table. }
  266.   I : word;                { Byte counter. }
  267.   HalfI : ^crc32_t;            { Pointer to CRC of I div 2. }
  268.   CRC : crc32_t;            { Current CRC. }
  269.   BufPtr : BytePtr;            { Pointer to walk through Buffer. }
  270.  
  271. begin
  272. { Generate a CRC lookup table for faster calculation. }
  273. I := 0;
  274. HalfI := @Table[0];
  275. Table[0] := 0;
  276. while I < 256 do
  277.   begin
  278.   if Hi(HiW(HalfI^)) and $80 <> $00 then
  279.     begin
  280.     Table[I + 1] := HalfI^ shl 1;
  281.     Table[I] := Table[I + 1] xor Polynomial;
  282.     end
  283.   else
  284.     begin
  285.     Table[I] := HalfI^ shl 1;
  286.     Table[I + 1] := Table[I] xor Polynomial;
  287.     end;
  288.  
  289.   Inc(I, 2);
  290.   Inc(longint(HalfI), sizeof(crc32_t));
  291.   end;
  292.  
  293. { Calculate CRC. }
  294. CRC := 0;
  295. BlockRead(F, Buffer^, BufSize, I);
  296. while I <> 0 do
  297.   begin
  298.   BufPtr := Buffer;
  299.   while I <> 0 do
  300.     begin
  301.     CRC := (CRC shl 8) xor Table[Hi(HiW(CRC)) xor BufPtr^];
  302.  
  303.     Dec(I);
  304.     Inc(longint(BufPtr));
  305.     end;
  306.  
  307.   BlockRead(F, Buffer^, BufSize, I);
  308.   end;
  309.  
  310. CalcCRC := CRC;
  311. end;
  312.  
  313.  
  314. {***}
  315. { Check file header consistency and calculate CRC of file. }
  316. function StealthFileCheck(FileName : PathStr; FCRC : FileCRC) : word;
  317.  
  318. var
  319.   Result : word;    { Result of tests. }
  320.  
  321.   FN : PathStr;        { Complete file name with path. }
  322.   Dir : DirStr;        { Directory of ParamStr(0). }
  323.   Name : NameStr;    { Name of ParamStr(0). }
  324.   Ext : ExtStr;        { Extension of ParamStr(0). }
  325.  
  326.   DirInfo : SearchRec;    { File directory information. }
  327.   TimeStamp : DateTime;    { Time stamp within DirInfo. }
  328.  
  329.   OldFileMode : byte;    { Old file open mode. }
  330.   F : file;        { File handle for FN. }
  331.  
  332.   Buffer : BytePtr;    { Buffer for file's data. }
  333.   BufSize : word;    { Buffer size. }
  334.  
  335. begin
  336. { Assume file passes all tests. }
  337. Result := StealthOK;
  338.  
  339. { If name contains drive or directory, use unmodified, else build full name. }
  340. if (FileName[2] = ':') or (Pos('\', FileName) <> 0) then
  341.   FN := FileName
  342. else
  343.   begin
  344.   { Assume file not found. }
  345.   FN := '';
  346.  
  347.   { DOS version 3 and above save program name in ParamStr(0). }
  348.   if Lo(DOSVersion) >= 3 then
  349.     begin
  350.     { Split program name into its components. }
  351.     FN := ParamStr(0);
  352.     FSplit(FN, Dir, Name, Ext);
  353.  
  354.     { Merge drive and directory with file name. }
  355.     FN := Dir + FileName;
  356.  
  357.     { Attempt to access file; if failed, pass control onto path search. }
  358.     FindFirst(FN, ReadOnly or Hidden or SysFile or Archive, DirInfo);
  359.     if DosError <> 0 then
  360.       FN := '';
  361.     end;
  362.   end;
  363.  
  364. if FN = '' then
  365.   { Search path for file. }
  366.   FN := FSearch(FileName, GetEnv('PATH'));
  367.  
  368. if FN <> '' then
  369.   begin
  370.   FindFirst(FN, ReadOnly or Hidden or SysFile or Archive, DirInfo);
  371.   if DosError = 0 then
  372.     begin
  373.     UnpackTime(DirInfo.Time, TimeStamp);
  374.  
  375.     { Check file time, day, and year. }
  376.     if (TimeStamp.Sec > 59) or (TimeStamp.Min > 59) or (TimeStamp.Hour > 23) or
  377.        (TimeStamp.Day = 0) or (TimeStamp.Year >= 2080) then
  378.       Result := Result or StealthFileDateErr;
  379.  
  380.     case TimeStamp.Month of
  381.       4, 6, 9, 11:
  382.     { Thirty days hath September, April, June, and November. }
  383.     if TimeStamp.Day > 30 then
  384.       Result := Result or StealthFileDateErr;
  385.  
  386.       1, 3, 5, 7, 8, 10, 12:
  387.     { All the rest have thirty-one, excepting February alone. }
  388.     if TimeStamp.Day > 31 then
  389.       Result := Result or StealthFileDateErr;
  390.  
  391.       2:
  392.     { February hath twenty-eight days clear, and twenty-nine in each leap year. }
  393.     if TimeStamp.Year mod 4 <> 0 then
  394.       begin
  395.       if TimeStamp.Day > 28 then
  396.         Result := Result or StealthFileDateErr;
  397.       end
  398.     else
  399.       if TimeStamp.Day > 29 then
  400.         Result := Result or StealthFileDateErr;
  401.  
  402.       else
  403.         Result := Result or StealthFileDateErr;
  404.       end;
  405.  
  406.     { Save and set file mode to read-only. }
  407.     OldFileMode := FileMode;
  408.     FileMode := 0;
  409.  
  410.     Assign(F, FN);
  411.     Reset(F, 1);
  412.  
  413.     { Restore file mode. }
  414.     FileMode := OldFileMode;
  415.  
  416.     if IOResult = 0 then
  417.       begin
  418.       { Compare length to length returned by directory search. }
  419.       if FileSize(F) <> DirInfo.Size then
  420.     Result := Result or StealthFileSizeErr;
  421.  
  422.       { Make sure that polynomial has its last bit and at least one other set. }
  423.       if (FCRC.Polynomial and $00000001 = 0) or (FCRC.Polynomial = $00000001) then
  424.     Result := Result or StealthCRCBadPoly;
  425.  
  426.       { Allocate 16k buffer if possible, but get at least 512 bytes. }
  427.       BufSize := 16384;
  428.       Buffer := BufAlloc(BufSize, 512);
  429.  
  430.       if Buffer <> nil then
  431.     begin
  432.     { CRC is valid if calculated CRC matches what is stored in FCRC. }
  433.     if FCRC.CRC <> CalcCRC(F, Buffer, BufSize, FCRC.Polynomial) then
  434.       Result := Result or StealthCRCInvalid;
  435.  
  436.     FreeMem(Buffer, BufSize);
  437.     end
  438.       else
  439.     Result := Result or StealthNoMem;
  440.  
  441.       Close(F);
  442.       end
  443.     else
  444.       Result := Result or StealthFileErr;
  445.     end
  446.   else
  447.     Result := Result or StealthFileErr;
  448.   end
  449. else
  450.   Result := Result or StealthFileErr;
  451.  
  452. StealthFileCheck := Result;
  453. end;
  454.  
  455.  
  456. end.
  457.