В.В.Водолазкий Индикатор заражения программы компьютерным вирусом Статья содержит описание программы, позволяющей обнаружить факт вирусной атаки по изменению длины исполняемой программы. Языки программирования - ТурбоПаскаль 5.0+ и ТурбоАссемблер 2.0+. "На Аллаха надейся, а верблюда привязывай." Синдбад-мореход. Вот уже несколько лет нас пугают всепожирающей "компьютерной чумой" - эпидемией компьютерных вирусов. Но все средства защиты, как правило, ограничиваются применением одной из версий AIDSTEST или -V (иногда довольно старой) и какого-нибудь резидентного фильтра типа FLUSHOT или ANTI4US2. Созданием этих программ занимается небольшая кучка энтузиастов, а основная масса пользователей и программистов занимает выжидательную позицию - кто кого. В самом деле, чем-то ведь эта борьба должна кончится: или с вирусами будет покончено раз и навсегда, или будет поставлен крест на использовании ЭВМ. Так что, поживем, увидим... Но ведь, с точки зрения здравого смысла, разработчик программного обеспечения должен нести полную ответственность за свое произведение. И если программа не в состоянии сама восстановить себя в случае ее инфицирования, то, по крайней мере, она должна сигнализировать пользователю о факте заражения. Это означает, что при разработке прикладной программы необходимо включить в нее средства, позволяющие обнаружить появление в ней чужеродного кода. Рассмотрим основные пути решения этой задачи. Обнаружение вирусной атаки, вообще говоря, может быть осуществлено путем сравнения некоторых характеристик исполняемой программы с их эталонными значениями. Примерами таких характеристик могут служить : длина программы, ее контрольная сумма, окрестности точки входа в программу и даже стиль программирования. Но поскольку данная процедура носит вспомогательный характер и не призвана полностью заменить специализированные антивирусные средства, используемые в ней признаки инфицирования программы должны вычисляться с минимальными затрататми вычислительных ресурсов и в то же время достаточно надежно. В наибольшей степени этим противоречивым условиям удовлетворяет проверка длины выполняемого файла. Казалось бы, что проще? Открыть файл, переместить указатель в конец (функция DOS 4202) и считывай длину файла из регистровой пары DX:AX. Но вопрос в том, какой файл? Если ввести в программу проверку длины файла с фиксированным именем, например, SUPERPRO.EXE, то где гарантия, что пользователь, изнуренный необходимостью вводить такое длинное и неудобное название, не переименует программу, например в SP.EXE, или просто не попытается вызвать его из другого текущего каталога? К счастью, DOS сохраняет информацию о полном (включая имя устройства и путь доступа) имени исполняемой программы. Необходимо только воспользоваться этой информацией. Для чего мы должны получить доступ к окружению DOS. Окружение DOS представляет собой последовательность ASCIIZ-строк (строк в коде ASCII, завершающихся нулевым байтом), в которых хранятся так называемые переменные среды. Кроме того, в окружение, начиная с версии DOS 3.0, включено и полное имя исполняемой программы. На рис. 1 приведен типовой состав окружения, записанный в мнемонике ассемблера. Обратите внимание, что имя загружаемой программы в отличие от переменных среды выделено словом $0001. Для доступа к окружению DOS вам необходимо получить сегментный адрес PSP текущей программы (функция DOS $62), а затем считать адрес окружения из PSP по смещению $2C. Пример реализации описанного подхода приведен в листинге 1. db 'COMSPEC=C:\4DOS.COM',0 db 'PROMPT=$p$g',0 db 'PATH=E:\WIN3;C:\DOS;D:\4DOS;C:\TASM20;C:\TDEBUG;E:\TP;D:\ME',0 db 'TEMP=e:\WIN3\TEMP',0 db 0 dw 1 db 'C:\hlup\bimbom\superpro.exe',0 db 0 Рис.1. Представление окружения DOS в мнемонике ассемблера. С целью экономии объема памяти, процедура определения длины файла реализована на ассемблере. Приведенная в Листинге 1 программа ориентирована на использование компилятора TurboAssembler 2.0 фирмы Borland, поддерживающего модель данных TurboPascal. Необходимая интерфейсная часть и оформление индикации на экране дисплея показаны на Листинге 2. Обратите внимание на управляющую директиву {$F+}. Дело в том, что при использовании модели TPASCAL в программе на ассемблере автоматически генерируются процедуры типа FAR (дальний межсегментный вызов и возврат). Сам же ТурбоПаскаль вызовы оптимизирует, и если указанная директива будет отсутствовать, это неизбежно приведет к зависанию программы. Установка процедуры в программу осуществляется следующим образом. Вначале вы отлаживаете программу без включения модуля VIRCHECK. Когда программа полностью отлажена, добавьте к списку используемых модулей в директиве USES имя модуля VIRCHECK. Первым оператором в основной процедуре программы сделайте вызов: SelfTest(0); При запуске программы процедура обнаружит несоответствие эталонной длины программы (0) фактическому значению, которое будет выдано на дисплей. После этого вы вновь входите в редактор и заменяете аргумент процедуры SelfTest на значение, выданное системой. В примере 1 приведен пример простой программы-"Нарцисса", которая любуется собой и проверяет свою собственную длину. program test; uses Vircheck; begin SelfTest(7502); end. Пример 1. Обращение к процедуре проверки длины загружаемого файла Предложенные в данной статье решения ориентированы на пользователей ТурбоПаскаль 5.0 и 5.5. Программисты, работающие на ТурбоПаскале 6.0 в более выгодном положении - они могут использовать встроенный ассемблер, поместив текст, приведенный на Листинге 1, в конструкцию, представленную в примере 2. Procedure CalcFile( var LL : Longint); begin asm ..... ..... текст процедуры из Листинга 1 ..... end end; Пример 2. Использование встроенного ассемблера ТурбоПаскаль 6.0 Необходимо только исключить оператор RET из текста на ассемблере, так как он будет добавлен автоматически при компиляции программы. Если вас очень пугает "огромная" длина модуля (с текстовым оформлением - около 1 Кбайта), вы можете сделать его оверлейным. И еще одно последнее замечание. Если вы используете пакет PKLITE для динамического архивирования исполняемого файла, вам придется устанавливать в SelfTest длину файла ПОСЛЕ его архивирования. Господа, не забывайте, что успех и признание пользователей обеспечены только НАДЕЖНОМУ программному обеспечению. NAME VIRCHECK PAGE 58,120 ; .286C ; только для программ, предназначенных ; для эксплуатации с 80286 .RADIX 16 ;устанавливаем по умолчанию 16-ную систему ;счисления ; ;╔══════════════════════════════════════╗ ;║ ║ ;║ В.В.Водолазкий, (С) "Монитор" 1991 ║ ;║ ║ ;║ Процедура определения длины ║ ;║ текущего исполняемого файла ║ ;║ ║ ;║ Входит в состав модуля VirCheck ║ ;║ совместима с Turbo Pascal 5.0+ ║ ;║ ║ ;║ Для получения объектного файла ║ ;║ используйте командную строку: ║ ;║ ║ ;║ TASM VIRCHECK ║ ;║ ║ ;╚══════════════════════════════════════╝ ; MODEL TPASCAL LOCALS CODESEG ; ; Вызов процедуры из программы на TurboPascal ; Procedure CalcFile(var L : Longint); PUBLIC CALCFILE PROC CALCFILE L : DWORD PUSHF CLD ;сброс флага направления MOV AH,62 INT 21 ;читаем адрес активного PSP MOV ES,BX MOV BX,2CH ;es:bx - адрес среды MOV AX,WORD PTR ES:[BX] MOV ES,AX XOR DI,DI ;теперь пара ES:DI настроена на ;поиск в переменных среды MOV CX,8000 ;размер среды в байтах MOV AL,1 ;это слово является индикатором ;имени исполняемого файла REPNE SCASB ;ищем байт $01 INC DI ;коррекция указателя ; теперь ES:DI указывают на начало ASCIIZ-строки ; с полным именем (например, C:\hlup\bimbom.exe), ; исполняемого в данный момент файла PUSH DS ; определение DS глобально для прог- ; раммы и должно быть сохранено PUSH ES POP DS PUSH DI POP DX ; DS:DX := ES:DI MOV AX,3D00 ; открываем файл INT 21 JNC @@1 ;если не было ошибки MOV AX,4CF1 ;если файл открыть не удалось INT 21 ;выход из программы @@1: MOV BX,AX ;сохраняем номер канала в/в MOV AX,4202 ;перемещаем указатель в конец файла XOR CX,CX MOV DX,CX ; В DX:AX возвращается длина файла INT 21 ; (т.е. максимальное смещение указа- ; теля) PUSH AX MOV AH,3EH ;закрываем файл INT 21 POP AX POP DS LES DI,[L] ; передаем результат измерений в STOSW ; переменную связи MOV AX,DX STOSW POPF RET ENDP ENDunit VirCheck; { В.В.Водолазкий (C) "Монитор" 1991 г. Модуль проверки длины текущего исполняемого файла. Для работы требует наличия объектного файла VIRCHECK.OBJ. Совместим с TurboPascal 5.0+ } {$F+} {$O+} INTERFACE uses CRT; Procedure SelfTest(Len : LongInt); IMPLEMENTATION {$L vircheck.obj} Procedure CalcFile(var L: LongInt); external; Procedure Buzzer; var i : byte; BEGIN for i := 1 to 5 do begin Sound(((i mod 2)+1)*500); Delay(300) end; NoSound END; Procedure SelfTest(Len : LongInt); Var LL : LongInt; BEGIN CalcFile(LL); Writeln(ll); IF LL <> Len then begin Buzzer; { включаем ревун } TextColor(LightRed); Writeln; Writeln('Внимание! Длина файла изменена.'); TextColor(Yellow); Write(' Эталонное значение.... : '); TextColor(LightBlue); Writeln(Len:8); TextColor(Yellow); Write(' Фактическое........... : '); TextColor(LightGreen); Writeln(LL:8); Writeln; Halt($F0) end END; END.