В этой статье мы немного углубимся в дебри компьютерной безопасности. И на повестке дня повис вопрос, как вы уже догадались — «Инспектор файлов». Что это и для чего он нужен я расскажу по мере чтения вами этой статьи, но замечу сразу, что испокон веков компьютерной эры, с тех самых времен когда операционная система содрогнулась от вирусной эпидемии, на стражу ваших и системных файлов встал гроза контроля и целостности — «Инспектор». Прошу прощение за эпическое отступление ;).
Так вот, помимо установленных антивирусов и прочих специальных программ, которые выполняют кучу всяких полезностей, порой даже абсолютно не нужных в данный момент и только лишь отнимают наше драгоценное время. А мы то знаем, что время — деньги!
Что же меня побудило написать статью на эту тема? Во-первых я считаю, и вам даю на заметку, что пользоваться готовым софтом это утеха юзеров и прочих бездельников. Каждый уважающий себя программист должен иметь в своем арсенале собственноручно написанный инструментарий отвечающий его задачам и требованием, и не одной команды лишней. Да и кроме того вы сами знаете что делает ваша программа, и в любой момент можете вспороть ее и что то подправить, добавить или удалить, в отличие от чужих прграммулек. Во-вторых, зная, а кто не знал запомните, что ни один антивирус не может вас защитить на 100% от свеженькой вредоносной информационной субстанции, то бишь вирус и прочая нечисть, так как антивирусная индустрия всегда позади на шаг впереди идущих создателей этой дряни. Поэтому, вашу безопасность можете обеспечить только вы сами и одним из средств обеспечения в этом деле является «Инспектор файлов», который непоколебимо несет свою службу на полях информационного пространства и «своих» знает в «лицо».
После не долгих эпических отступлений, вы уж простите меня, без лишних слов приступаем к реализации нашего стражника «Инспектора». В первую очередь нам необходимо поставить задачу. Ей будет:
- реализовать поиск файлов,
- проверку файлов на наличие их в банке данных, по соответствующим признакам (путь к файлу, дата создания, изменения и контрольная сумма),
- добавление в новых кандидатов на прописку в банк данных,
- реализация интерфейса связывающего предыдущие описанные задачи и действия пользователя.
В целом, получается вот такое тех. задание. А теперь от слов к делу. Для начала определимся с дизайном формы. Здесь я полагаюсь на ваш вкус, но потребуется нам две кнопки и поле вывода информации (Memo или ListBox). Одну из кнопок определим под выбор папки — «Обзор», и для нее напишем следующий код:
1 2 3 4 5 6 7 8 9 10 11 | procedure TForm1.Button1Click(Sender: TObject); begin Path := GetPath('Выберите папку'); if Path <> '' then Label4.Caption := Path; end; |
А теперь опишем используемую функцию GetPath():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function GetPath(mes: string):string; var Root: string; // корневой каталог pwRoot : PWideChar; Dir: string; begin Root := ''; // по умолчанию корневой каталог - рабочий стол GetMem(pwRoot, (Length(Root)+1) * 2); pwRoot := StringToWideChar(Root,pwRoot,MAX_PATH*2); if SelectDirectory(mes, pwRoot, Dir) then if length(Dir) = 2 // пользователь выбрал корневой каталог then GetPath := Dir+'\' else GetPath := Dir else GetPath := ''; end; |
Итак, процедуру выбора каталога для проверки реализовано. Теперь приступим к реализации проверки файлов. Но сначала для второй кнопки опишем следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | procedure TForm1.Button2Click(Sender: TObject); begin Form1.ListBox1.Items.Add(TimeToStr(Time)+' :> Сканирование началось.'); ListFiles.Clear; FindF(Path); Scan_File; Form1.ListBox1.Items.Add('==================================================='); Form1.ListBox1.Items.Add(TimeToStr(Time)+' :> Сканирование завершено.'); end; |
А теперь последовательно опишем используемые функции и первой будет процедура FindF(). Эта процедура осуществляет рекурсивный поиск файлов относительно выбранного корневого каталога, и добавляет их в список для проверки. Смотрим листинг:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | procedure FindF(path_f:string); begin //Инициируем поиск if(FindFirst(path_f+'\*.*',faAnyFile,SearchRec) = 0) then begin repeat if not((SearchRec.name = '.')or(SearchRec.name = '..')) then begin if SearchRec.Attr <> faDirectory then begin ListFiles.Add(path_f+'\'+SearchRec.Name); //если файл, добавляем в список Inc(Count_file); end else FindF(path_f+'\'+SearchRec.Name); //если папка, запускаем рекурсию end; until (FindNext(SearchRec)<>0); FindClose(SearchRec); end; end; |
Далее рассмотрим процедуру Scan_File(). Ее задача проверить каждый найденный файл на наличие в своей базе данных, сверить контрольную сумму, дату создания и дату последнего изменения файла. В случае если будут обнаружены какие-либо не соответствия, немедля бьет тревогу. Смотрим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | procedure Scan_File; var crc32T,dataCT,dataMT:Cardinal; i:integer; f: THandle; SearchF:TWIN32FindData; temp:boolean; begin i:=0; //проверка на первый запуск, а точнее база была только что создана или нет. if newBase = true then //если да то заносим список найденых файлов с их //парамметрами(путь, контрольная сумма, дата создания, дата изменения) в базу while i <= Count_file-1 do begin WriteIB(ListFiles.Strings[i]); Inc(i); end; //далее производим проверку i:=0; while i <= Count_file-1 do begin try //проверяем наличие файла FS:=TFileStream.Create(ListFiles.Strings[i],fmOpenRead or fmShareDenyNone); except on Exception do begin Form1.ListBox1.Items.Add('не открывается '+ListFiles.Strings[i]); break; end; end; //Вычисляем контрольную сумму crc32T:=CRC32Stream(FS,0); FS.Free; f:=FindFirstFile(PChar(ListFiles.Strings[i]),SearchF); if f <> INVALID_HANDLE_VALUE then begin //определяем дату создания и дату изменения файла dataCT:=SearchF.ftCreationTime.dwLowDateTime; dataMT:=SearchF.ftLastWriteTime.dwLowDateTime; end; //запускаем процедуру поиска файла с найденными параметрами //по базе temp:=FindIB(ListFiles.Strings[i],crc32T,dataCT,dataMT); if (Not temp and (FindFlagB=0)) then begin //если файл отсутсвует то выводим тревожное сообщение Form1.ListBox1.Items.Add('ВНИМАНИЕ! Данный файл в базе отсутствует: '+ListFiles.Strings[i]); end; //Если файл в наличии то выводим результат проверки if temp and (FindFlagB=1) then Form1.ListBox1.Items.Add(ListFiles.Strings[i]+' - файл проверен. ОК.') else if (not temp) and (FindFlagB=1) then Form1.ListBox1.Items.Add(ListFiles.Strings[i]+' - файл был изменен.'); Inc(i); Form1.Update; end; end; |
Далее опишем функции для работы с базой. И первая функция FindIB(). Она осуществляет проверку наличия интересующего файла в базе и сверку переданных параметров искомого файла с теми что соответствуют ему.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | function FindIB(strS:string;crc32,dataC,dataM:Cardinal):Boolean; var fileS:TextFile; nameD,fn:string; crc32D,dataCD,dataMD:Cardinal; i,it:integer; begin FindFlagB:=0; AssignFile(fileS,'IFdb.db'); Reset(fileS); While Not EOF(fileS) do begin //считываем данные из базы ReadLn(fileS,nameD); // получаем имя файла it:=Pos('$',nameD); fn:=copy(nameD,1,it-1); Delete(nameD,1,it); //получаем контрольную сумму it:=Pos('$',nameD); crc32D:=StrToInt64(copy(nameD,1,it-1)); Delete(nameD,1,it); //получаем дату создания файла it:=Pos('$',nameD); dataCD:=StrToInt64(copy(nameD,1,it-1)); Delete(nameD,1,it); //получаем дату последнего изменения dataMD:=StrToInt64(copy(nameD,1,length(nameD))); if strS = fn then begin FindFlagB:=1; if crc32 = crc32D then if dataC = dataCD then if dataM = dataMD then begin FindIB:=true; CloseFile(fileS); exit; end; end; end; FindIB:=false; CloseFile(fileS); end; |
Теперь рассмотрим функцию WriteIB(). Которая заносит в базу путь к файлу, его контрольную сумму, дату создания и дату последнего изменения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | procedure WriteIB(name:string); var dbf:TextFile; i:Cardinal; s:string; f: THandle; SearchF:TWIN32FindData; begin //открываем базу AssignFile(dbf,'IFdb.db'); Append(dbf); //инициализируем переменные s:=name+'$'; try //проверяем наличие добавляемого файла FS:=TFileStream.Create(name,fmOpenRead or fmShareDenyNone); except on Exception do begin Form1.ListBox1.Items.Add('не открывается '+name); exit; end; end; //вычисляем контрольную сумму i:=CRC32Stream(FS,0); s:=s+IntToStr(i); f:=FindFirstFile(PChar(name),SearchF); if f <> INVALID_HANDLE_VALUE then //определяем дату создания и дату изменения файла s:=s+'$'+IntToStr(SearchF.ftCreationTime.dwLowDateTime)+'$'+IntToStr(SearchF.ftLastWriteTime.dwLowDateTime); writeln(dbf,s); CloseFile(dbf); FS.Free; end; |
Далее надо объявить необходимые глобальные переменные:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var SearchRec: TSearchRec; Count_file,FindFlagB,step: integer; Path: string; ListFiles: TStrings; FS:TFileStream; newBase:boolean; |
Теперь необходимо для события onCreate написать следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | procedure TForm1.FormCreate(Sender: TObject); var f: THandle; SearchF:TWIN32FindData; begin SendMessage(ListBox1.Handle,LB_SetHorizontalExtent,1000,0); newBase:= false; ListFiles:=TStringList.Create; Path:='*.*'; f:=FindFirstFile('IFdb.db',SearchF); if f = INVALID_HANDLE_VALUE then begin f:=CreateFile('IFdb.db', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0); if f <> INVALID_HANDLE_VALUE then begin CloseHandle(f); newBase:= true; end; end; end; |
На этом статью заканчиваю. Надеюсь статья и программный код описал максимально понятно. Но замечу что код требует оптимизации, так как был написан таким образом чтобы был понятен читателю. Премер может быть расширен, в него можно добавить кучу дополнительных возможностей. Все это зависит от вашей фантазии.