Основы разработки антивирусного сканера

Дата публикации 24 дек 2003

Основы разработки антивирусного сканера — Архив WASM.RU

Содержание

  1. Введение
  2. Простейшие типы вирусов
  3. Методы детектирования
  4. Контрольные суммы и технология их расчета
  5. Использование контрольных сумм для детектирования вирусов
  6. Алгоритм поиска файлов
  7. Основы построения вирусной базы
  8. Основы работы с вирусной базой
  9. Заметки по лечению вирусов
  10. Заключение

1. Введение

 В этой статье речь пойдет о разработке примитивного антивирусного программного обеспечения, точнее сказать антивирусного сканера.

 Сканнерами называют программы, которые проверяют файлы на предмет зараженности их известными программе вирусами.

 Прочитав эту статью, я не гарантирую, того, что вы в тот же момент станете умнейшим вирусным аналитиком, эта область программирования достаточно необычна и тяжела для понимания. Сталкиваясь с ней (областью) «вплотную» в первый раз очень тяжело сразу «взять быка за рога». Но в этой статье я попытаюсь изложить основы, так что вполне возможно, то, что будет описано ниже вы, уже знали.

 Прежде чем читать статью, необходимо удостовериться, что ваш уровень знаний ассемблера не ограничивается умением писать красивые интерфейсы и менюшки, в антивирусных программах это не главное. Конечно без удобного (дружелюбного) интерфейса ваша разработка не будет востребована. Но все же, необходимо иметь некоторые понятия о:

  • Контрольные суммы участков данных, что это такое и примерный алгоритм расчета
  • Вирусы и троянские кони в бинарных файлах (исполняемых файлах), а так же написанные на скриптовых языках (VBS, JavaScript)
  • Методы работы с файлами (поиск, запись, чтение и тд)

2. Простейшие вирусы

 Вирус – программа очень маленьких размеров, основной задачей которой является распространение по компьютерам пользователей, за счет различных хитрых алгоритмов и ошибок пользователей.

 Вирусы это очень сложные программы, их можно разделить на множество видов, но мы ограничимся всего двумя. Разделим вирусы на простейшие и файловые вирусы. Файловые вирусы это те, которые умеют заражать файлы различных форматов (загрузочные – PE, документы, файлы помощи и еще множество других), обычно эти вирусы написаны на языке ассемблера, и для их обнаружения необходимо очень хорошо знать структуру файлов, которые они поражают. Под понятием простейших вирусов я подразумеваю различных червей и сюда же можно приписать троянских коней.

 Файловый вирус – вирус, который распространяется через зараженные файлы различных форматов, обычно исполняемых. Т.е. кто-то кому-то передал программу, один из исполняемых файлов которой был заражен вирусом.

 Червь – вирус, который распространяется самостоятельно (не поражая какие либо файлы), обычно через глобальные сети, путем рассылки себя в письмах; авторами червей часто являются хакеры и для своего распространения черви используют различные дырки в операционных системах.

 Троянские кони – программы, у которых отсутствуют функции самостоятельного распространения (т.е. они распространяются различными людьми специально), но обязательно присутствуют различные деструктивные функции.

 Деструкция – алгоритм, причиняющий вред компьютеру и пользователи, иногда не только моральный, но и материальный. Деструктивные алгоритмы часто используются в вирусах и всегда используются в троянских конях.

 Деструкция в троянских конях – это алгоритмы, которые могут совершать различные пакости, начиная от форматирования жесткого диска или перезаписи flashbios и заканчивая воровством «важных» файлов (паролей для доступа в интернет) с компьютера пользователя.

 3. Методы детектирования

 Главное, что черви и троянские кони не умеют заражать другие файлы, по этому всегда распространяются в одном и том же виде. Т.е. в виде не изменяющегося файла. Допустим, у нас имеется файл с вирусом-червем, который занимает 10 килобайт. Червь распространяется в виде исполняемого файла PortableExecutable. Нам необходимо написать программу антивирус против этого червя.

  • Программа будет искать PE-файлы в указанном каталоге
  • Каждый найденный файл размером 10 килобайт и больше будет открываться
  • Первые 10кб будут считываться, и сверяться с теми, которые были взяты из «тела» вируса
  • Если содержимое будет совпадать, то значит перед нами червь и нужно его вылечить

 Мы составили примитивный алгоритм детектирования вируса червя. Теперь допустим, таким способом ваша программа опознает, и лечит 100 вирусов. В качестве сигнатуры (данных, по которым определяется зараженность объекта тем или иным вирусом) используется полный вирусный код, допустим для каждого вируса по 10 кб. В результате вирусная база (база данных программы содержащая алгоритмы по детектированию и лечению вирусов) программы будет занимать 1 мегабайт, а это очень много. Крупнейшие антивирусные программы детектируют несколько десятков тысяч самых разнообразных вирусов, и их вирусные базы занимают всего несколько мегабайт.

 Детектирование (обнаружение) вирусов, не изменяющих своей структуры, является примитивнейшим занятием. И так сигнатура для детектирования вирусов может занимать всего 3 двойных слова, т.е. 12 байт и быть очень надежной, если использовать контрольные суммы

4. Контрольные суммы и технология их расчета

  

 Контрольная сумма это 32 битное число (очень редко 16 битное), которое характеризует определенный участок кода. Есть множество способов подсчета контрольной суммы, для лучшего восприятия этого термина рассмотрим пример примитивнейшего подсчета:

 Например, у нас есть участок кода, состоящий из 5 байт (десятичная система): 001 004 000 005 100

 По нашему примитивному подсчету, контрольная сумма его будет равняться 1+4+0+5+100=110. Т.е. прочитав контрольную сумму другого участка, мы получим другое значение. Однако, используя столь примитивный алгоритм расчета, контрольные суммы совершенно отличающихся участков могут совпадать, для этого используются более продвинутые процедуры подсчета.

Код (Text):
  1.  
  2.   -----------------------
  3.   ; подсчет контрольной суммы участка кода “sbuf”, длины “dlen”
  4.   ; после подсчетов контрольная сумма будет
  5.   «положена» в “crc_buf”
Код (Text):
  1.  
  2.   <u>calculate_crc</u>
Код (Text):
  1.  
  2.   proc  crc_buf: dword, sbuf:
  3.   dword, dlen: dword
Код (Text):
  1.  
  2.   push  eax ecx edx ebx esi edi
  3.   cld
  4.   mov  esi,sbuf
  5.   mov  edi,dlen
  6.   mov  ecx,-1
  7.   mov  edx,ecx
Код (Text):
  1.  
  2.   next_byte:
Код (Text):
  1.  
  2.   sub    eax,eax
  3.   sub    ebx,ebx
  4.   lodsb
  5.   xor    al,cl
  6.   mov  cl,ch
  7.   mov  ch,dl
  8.   mov  dl,dh
  9.   mov  dh,8
Код (Text):
  1.  
  2.   next_bit:
Код (Text):
  1.  
  2.   shr    ebx,1
  3.   rcr     eax,1
  4.   jnc    no_carry
  5.   xor    eax,8320h
  6.   xor    ebx,0edb8h
Код (Text):
  1.  
  2.   no_carry:
Код (Text):
  1.  
  2.   dec   dh
  3.   jnz    next_bit
  4.   xor    ecx,eax
  5.   sub    edx,ebx
  6.   dec    edi
  7.   jnz    next_byte
  8.   not    edx
  9.   not    ecx
  10.   mov   eax,edx
  11.   ror    eax,cl
  12.   add   eax,ecx
  13.   mov  edi,crc_buf
  14.   mov  word ptr [edi],dx
  15.   mov   word ptr [edi+2],cx
  16.   pop   edi esi ebx edx ecx eax
  17.   ret
Код (Text):
  1.  
  2.   <b><u>endp</u></b>

 Комментарии к алгоритму расчета контрольной суммы (crc) отсутствуют потому, что достаточно понимать смысл подсчета crc, стандарта подсчета не существует, а описывать операции, производимые в этой процедуре достаточно тяжело и бессмысленно. Со временем будет появляться опыт в подобных вещах и если будет необходимо, то вы и сами разберетесь в коде.

5. Использование контрольных сумм для детектирования вирусов

 Если необходимо подсчитать контрольную сумму участка в несколько килобайт, то это конечно не мгновенная процедура. Если считать контрольную сумму каждого найденного файла то процесс проверки не будет проходить так быстро как хотелось бы.

 Возьмем в качестве примера вирус BAT.Sys.602, распространяющийся в виде файла BAT, написанного на примитивном языке Batch, входящим в комплект операционной системы DOS. Внешне вирус-червь представляет собой обычный текст.

 

 Для опознавания наличия этого вируса в BAT-файлах совсем не обязательно считать контрольную сумму всего вирусного кода, достаточно взять участок кода, состоящий из нескольких строк. Так же просто необходимо запомнить их расположение в вирусном файле и длину этих строк (всех вместе). Допустим, нам приглянулись строки 4 и 5.

 Как мы видим, строка 4 начинается в файле со смещения 75 (4Bhex) и заканчивается 150 (96 hex). Т.е. размер двух строк составляет 75 байт.

 Но брать в качестве сигнатуры определенные строки не совсем обязательно, можно взять любой кусок кода, размер желательно до 100 байт, что бы время на расчет контрольной суммы затрачивалось минимальное.

 Вашему вниманию предлагается пример программы, которая считает контрольную сумму участка кода выбранного выше (смещение 75, длина 75 байт)  в файле ‘sys.bat’ …

Код (Text):
  1.  
  2.   ; компилировать: tasm32 –ml getcrc32.asm
  3.   ;                             tlink32 -Tpe -c -x getcrc32.obj,,, import32
  4.   ;
  5.   .386P
  6.   .model flat, stdcall
Код (Text):
  1.  
  2.   extrn
  3.   extrn
  4.   extrn
  5.   extrn
  6.   extrn
  7.   extrn
  8.   <b><u>include</u></b>
  9.  
  10.   FILE_BEGIN
  11.   OPEN_EXISTING
  12.   GENERIC_READ
Код (Text):
  1.  
  2.   ExitProcess:near
  3.   ReadFile:near
  4.   CreateFileA:near
  5.   CloseHandle:near
  6.   GetFileSize:near
  7.   SetFilePointer:near
  8.   <b><u>crc_proc.inc</u></b>
  9.  
  10.   equ 0
  11.   equ 3
  12.   equ 80000000h
Код (Text):
  1.  
  2.   ; список API использующихся программой
  3.   ; здесь находится вышеуказанная процедура
  4.   ; подсчета контрольной суммы
  5.   ; для перевода указателя
  6.   ; открыть файл уже существующий
  7.   ; чтение данных из файла
Код (Text):
  1.  
  2.   ENTRY_SIZE
Код (Text):
  1.  
  2.   equ 1+260+4+318
Код (Text):
  1.  
  2.   ; размер одной ячейки для процесса поиска
Код (Text):
  1.  
  2.   .data
Код (Text):
  1.  
  2.   ; сегмент данных
Код (Text):
  1.  
  2.   number
  3.   handle
  4.   crc32_buf
  5.   crc32_code_loc
  6.   crc32_code_len
  7.   file_name
  8.   buffer
Код (Text):
  1.  
  2.   dd ?
  3.   dd ?
  4.   dd ?
  5.   dd 75
  6.   dd 75
  7.   db ‘sys.bat’,0
  8.   db 1000 dup (?)
Код (Text):
  1.  
  2.  
  3.   ; файловый номер
  4.   ; для хранения посчитанной crc участка кода
  5.   ; местоположение участка кода в файле
  6.   ; длина участка кода
  7.   ; файл с которым будем работать
  8.   ; буфер для хранения участка кода
Код (Text):
  1.  
  2.   .code
Код (Text):
  1.  
  2.   ; сегмент кода
Код (Text):
  1.  
  2.   <b><u>start</u></b>:
Код (Text):
  1.  
  2.   push  0 0
  3.   push  OPEN_EXISTING
  4.   push  0 0
  5.   push  GENERIC_READ
  6.   push  offset file_name
  7.   call    <b>CreateFileA</b>
  8.   cmp   eax,-1
  9.   jnz     read_file
Код (Text):
  1.  
  2.   ; откроем существующий файл ‘sys.bat’
  3.   ; для чтения
  4.  
  5.   ; если все прошло успешно, продолжим
Код (Text):
  1.  
  2.   error_opening:
  3.   </td>
  4.   <td width=252 valign=top>
  5.   <code><pre>
  6.   --------------------------------------------
  7.   jmp   exit
Код (Text):
  1.  
  2.   ; «обработчик» ошибки открытия файла
Код (Text):
  1.  
  2.   read_file:
Код (Text):
  1.  
  2.   mov   handle,eax
  3.   push  0 eax
  4.   call    <b>GetFileSize</b>
  5.   mov   ecx,dword ptr [crc32_code_loc]
  6.   add    ecx,dword ptr [crc32_code_len]
  7.   cmp   ecx,eax
  8.   jle      set_pointer
  9.   push   handle
  10.   call    <b>CloseHandle</b>
  11.   jmp    exit
Код (Text):
  1.  
  2.   ; сохраним <i>файловый номер</i> в “handle”
  3.   ; eax – размер открытого файла в байтах
  4.   ; ecx – расположение участка в файле
  5.   ; ecx – смещение на конец участка
  6.   ; если смещение на конец участка меньше
  7.   ; чем размер файла, значит файл подходит
  8.   ; иначе не тот файл
  9.   ; закроем файл
  10.   ; выйдем в ОС
Код (Text):
  1.  
  2.   set_pointer:
Код (Text):
  1.  
  2.   push   FILE_BEGIN
  3.   push   0
  4.   push   crc32_code_loc
  5.   push   handle
  6.   call     <b>SetFilePointer</b>
Код (Text):
  1.  
  2.  
  3.  
  4.  
  5.   ; установим указатель на расположение
  6.   ; необходимого участка в файле
Код (Text):
  1.  
  2.   push   0
  3.   push   offset number
  4.   push   crc32_code_len
  5.   push   offset buffer
  6.   push   handle
  7.   call     <b>ReadFile</b>
  8.   push   handle
  9.   call     <b>CloseHandle</b>
; прочитаем в “buffer” данные размером ; “crc32_code_len” изфайла ; закроемфайл
Код (Text):
  1.  
  2.   push   crc32_code_len
  3.   push   offset buffer
  4.   push   offset crc32_buf
  5.   call     calculate_crc
Код (Text):
  1.  
  2.   ; длина участка crc которого нужно посчитать
  3.   ; размещение данных участка
  4.   ; куда «положить» посчитанную crc
  5.   ; считаем crc участка
Код (Text):
  1.  
  2.   exit:
  3.   </td>
  4.   <td width=252 valign=top>
  5.   <code><pre>
  6.   push   0
  7.   call     <b>ExitProcess</b>
  8.   end      <b><u>start</u></b>
Код (Text):
  1.  
  2.   ; выйдем в ОС

6. Алгоритм поиска файлов

 Как вы уже, наверное, знаете для поиска файлов? используются API: FindFirstFile, FindNextFile, FindClose.

 ПараметрамиFindFirstFile является маска для поиска и «место» под структуру для поиска (объявленную структуру официального типа или буфер, размер которого не меньше размера официальной структуры). Маской для поиска является обычная строка, содержащая путь к каталогу, в котором будет производиться поиск, а так же маску поиска (обычно используется “*.*”).

 Вид официальной структуры для поиска:

Код (Text):
  1.  
  2.   <i>fnd_struc           </i>
  3.    atr                    
  4.    cr_time            
  5.    ac_time             
  6.    wr_time              
  7.    size_high             
  8.    size_low          
  9.    reserved          
  10.    long_name      
  11.    dos_name        
  12.   <i>fnd_struc           </i>
Код (Text):
  1.  
  2.   <i>struc</i>
  3.    dd ?
  4.    dd 2
  5.   dup (?)
  6.    dd 2
  7.   dup (?)
  8.    dd 2
  9.   dup (?)
  10.    dd ?
  11.    dd ?
  12.    dd 2
  13.   dup (?)
  14.    db
  15.   260 dup (?)
  16.    db 14
  17.   dup (?)
  18.   <i>ends</i>
Код (Text):
  1.  
  2.  
  3.   <b>атрибут файла</b>
  4.   <b>время создания файла</b>
  5.   <b>время доступа к файлу</b>
  6.   <b>время модификации файла</b>
  7.   <b>размер файла</b>
  8.  
  9.   <b>резерв</b>
  10.   <b>длинное имя файла</b>
  11.   <b>короткое имя файла</b>

 Можно объявлять эту структуру, а можно использовать эквивалент “findbuf db 314 dup (?)”. Немного разбираясь в программировании можно догадаться, что имя найденного файла будет находиться по смещению 44 от начала findbuf.Используем “movesi,offsetfindbuf + 44” и регистр esi указывает на имя найденного файла или каталога.

 После выполнения API FindFirstFile, регистр eax будет содержать идентификатор поиска или в случае ошибки –1.

 Вызов FindFirstFile используется только один раз, далее в дело вступает FindNextFile. Параметрами этой апи являются идентификатор поиска (который мы получили от вызова FindFirstFile и должны были сохранить) и та же структура для поиска (которая так же использовалась при первом поиске). Поиск ведется до тех пор, пока регистр eax не будет равняться нулю.

 Эти API ищут файлы или каталоги по маске поиска, т.е. нельзя искать только файлы или только каталоги. Когда используется маска ‘*.*’ то будут найдены все каталоги и файлы, содержащиеся в указанном перед маской каталоге.

 Все конечно хорошо, но необходимо что бы наша антивирусная программа могла проверять файлы не только в указанном каталоге, но и в тех, что расположены ниже. Для этого мы должны писать рекурсивную процедуру, т.е. такую, которая умеет вызывать сама себя.

 Прежде всего, необходимо объявить переменную для поиска в каталогах содержащих множество других подкаталогов. Думаю, что пользователей, у которых количество подкаталогов в каталоге превышает 50 единицы, по этому ограничимся этим числом. И так, для процесса поиска в каждом каталоге должны быть индивидуальными (не использоваться в других процессах) маска поиска (максимум 260 символов), идентификатор поиска (двойное слово, 4 байта), структура для поиска (318 байт) и флаг (размером 1 байт, а для чего нужен этот флаг, я объясню позже). В результате мы должны объявить переменную а/ля ”buf db ( ( 1 + 260 + 4 + 318 ) * 50 )”. Ячейка для каждого процесса поиска будет занимать 1+260+4+318 = 583 байта и одновременно может вестись 50 процессов (не более). Конечно, можно использовать память из стека, но это будет тяжелее объяснить.

Структура размещения данных в ячейке может быть такой, какая удобна вам.

Я предлагаю структуру такого типа:

Код (Text):
  1.  
  2.   <i> </i>flag             
  3.    mask        
  4.    search_handle       
  5.    find_structure
      
Код (Text):
  1.  
  2.   <i> </i>db ?
  3.    db
  4.   260 dup (?)
  5.    dd ?
  6.    db 318 dup (?)
Код (Text):
  1.  
  2.   <b>флаг поиска (0 – ищем только файлы, 1 – только каталоги)</b>
  3.   <b>маска для поиска в каталоге</b>
  4.   <b>идентификатор поиска</b>
  5.   <b>структура для поиска</b>

 Теперь поговорим о алгоритме поиска. Сначала необходимо обработать все файлы текущего каталога, а затем уже подкаталоги. Но API сначала находят подкаталоги, а потом уже файлы. Вот для этого и необходим флаг.

При первом поиске флаг настраивается только на поиск файлов, те если значение флага не совпадает с необходимым, то найденные каталоги просто пропускаются. По окончанию поиска флаг перенастраивается на поиск только каталогов, и теперь уже пропускаются файлы.

Давайте рассмотрим пример программы обходящей дерево каталогов в поиске всех файлов…

Код (Text):
  1.  
  2.   ; компилировать: tasm32 -ml walk.asm
  3.   ;                             tlink32 -Tpe -c
  4.   -x walk.obj ,,, import32
  5.   ;
  6.   .386P
  7.   .model flat, stdcall
Код (Text):
  1.  
  2.   extrn
  3.   extrn
  4.   extrn
  5.   extrn
  6.   extrn
Код (Text):
  1.  
  2.   lstrlenA:near
  3.   ExitProcess:near
  4.   FindFirstFileA:near
  5.   FindNextFileA:near
  6.   FindClose:near
Код (Text):
  1.  
  2.   ; список API использующихся программой
Код (Text):
  1.  
  2.   ENTRY_SIZE
Код (Text):
  1.  
  2.   equ 1+260+4+318
Код (Text):
  1.  
  2.   ; размер одной ячейки для процесса поиска
Код (Text):
  1.  
  2.   .data
Код (Text):
  1.  
  2.   ; сегмент данных
Код (Text):
  1.  
  2.   mask
  3.   search_dir
  4.   buffer
Код (Text):
  1.  
  2.   db ‘\*.*’,0
  3.   db ‘c:\Program files’,0
  4.   db (ENTRY_SIZE*50)
  5.   dup (?)
Код (Text):
  1.  
  2.   ; <i>маска для поиска</i>
  3.   ; начальный каталог для поиска
  4.   ; буфер под 50 «одновременных» <i>процессов поиска</i>
Код (Text):
  1.  
  2.   .code
Код (Text):
  1.  
  2.   ; сегмент кода
Код (Text):
  1.  
  2.   <b><u>start:</u></b>
  3.   </td>
  4.   <td width=252 valign=top>
  5.  <code><pre>
  6.   mov  esi,offset <u>search_dir</u>
  7.   push esi
  8.   call   <b>lstrlenA</b>
  9.   xchg eax,ecx
  10.   inc    ecx
  11.   mov  edi,offset <u>buffer</u>
  12.   inc    edi
  13.   rep    movsb
Код (Text):
  1.  
  2.   ; esi – путь для поиска файлов
  3.  
  4.   ; получим размер пути в ASCII символах в eax
  5.   ; обменяем значениями ecx и eax
  6.   ; ecx = размер пути + NULL
  7.  
  8.   ; перенесем «полу готовую» маску для
  9.   ; первого процесса поиска
Код (Text):
  1.  
  2.   push  offset <u>buffer</u>
  3.   call    find
Код (Text):
  1.  
  2.   ; указатель на готовую структуру для поиска
Код (Text):
  1.  
  2.   push  0
  3.   call   <b>ExitProcess</b>
Код (Text):
  1.  
  2.  
  3.   ; выйдем в ОС
Код (Text):
  1.  
  2.   -----------------------
  3.   ; поиск файлов с использованием указанной в “soff” ячейки (и последующих)
Код (Text):
  1.  
  2.   <b><u>find</u></b>
Код (Text):
  1.  
  2.   <b><u>proc  <i>soff</i>: dword</u></b>
Код (Text):
  1.  
  2.   mov  ebx, <i>soff</i>
  3.   mov  byte ptr [ebx],0
  4.   mov  edi,ebx
  5.   inc    edi
  6.   push  edi
  7.   call   <b>lstrlenA</b>
  8.   add   edi,eax
  9.   cmp  byte ptr [edi-1’,’*’
  10.   jz      first_find
Код (Text):
  1.  
  2.   ; ebx – указатель на текущую ячейку
  3.   ; установим флаг
  4.   (значение 0) на поиск файлов
  5.   ; edi–1 – текущий каталог
  6.  
  7.  
  8.   ; eax - длина пути в ASCII символах
  9.  
  10.   ; если маска уже присутствует в пути
  11.   ; ищем первый объект (файл или каталог)
Код (Text):
  1.  
  2.   check_slash:
Код (Text):
  1.  
  2.   mov  esi,offset <u>mask</u>
  3.   cmp  byte ptr [edi-1],’\’
  4.   jnz    nneed_slash
  5.   inc    esi
Код (Text):
  1.  
  2.   ; esi – смещение общей маски для поиска
  3.   ; проветим, есть ли слеш в конце пути
  4.  
  5.   ; если есть, то пропустим
Код (Text):
  1.  
  2.   nneed_slash:
  3.   </td>
  4.   <td width=252 valign=top>
  5.  <code><pre>
  6.   push  esi
  7.   call   lstrlenA
  8.   inc    eax
  9.   xchg  eax,ecx
  10.   rep    movsb
Код (Text):
  1.  
  2.  
  3.   ; eax – длина маски
  4.   ; прибавим NULL к длине
  5.  
  6.   ; дополним путь маской и слешем (если необходимо)
Код (Text):
  1.  
  2.   find_first:
Код (Text):
  1.  
  2.   mov  edx,ebx
  3.   add   edx,1+260+4
  4.   push  edx
  5.   mov   edx,ebx
  6.   inc     edx
  7.   push   edx
  8.   call    <b>FindFirstFileA</b>
  9.   cmp   eax,-1
  10.   jnz   save_fnd_hndl
Код (Text):
  1.  
  2.   ; edx – указывает на ячейку процесса поиска
  3.   ; edx – указывает на <i>структуру для поиска</i>
  4.   ; первый параметр для API <b>FindFirstFileA</b>
  5.   ; edx – указывает на ячейку процесса поиска
  6.   ; edx – указывает на <i>маску для поиска</i>
  7.   ; второй параметр
  8.   ; выполним API
  9.   ; если eax = -1 значит произошла ошибка
  10.   ; иначе проверим найденный объект
Код (Text):
  1.  
  2.   test_fod:
Код (Text):
  1.  
  2.   cmp   byte ptr [ebx],1
  3.   jz    end_find
Код (Text):
  1.  
  2.   ; если <i>флаг</i> = 1, то поиск подкаталогов был уже
  3.   ; проведен в этом каталоге, осталось закончить
  4.   ; процесс
Код (Text):
  1.  
  2.   set_filesfind:
Код (Text):
  1.  
  2.   inc     byte ptr [ebx]
  3.   jmp     first_find
Код (Text):
  1.  
  2.   ; если <i>флаг </i>= 0, то все файлы в каталоге
  3.   ; были обработаны, устанавливаем <i>флагу</i>
  4.   ; значение 1 и обрабатываем подкаталоги
Код (Text):
  1.  
  2.   end_find:
Код (Text):
  1.  
  2.   push   dword ptr [ebx+1+260]
  3.   call     <b>FindClose</b>
  4.   ret
Код (Text):
  1.  
  2.   ; ebx = ebx + 1 + 260 = <i>идентификатор  поиска</i>
  3.   ; прекратим процесс поиска в каталоге
Код (Text):
  1.  
  2.   ; ----------------------
  3.   ; процесс проверки найденного объекта
Код (Text):
  1.  
  2.   save_fnd_hndl:
Код (Text):
  1.  
  2.   mov    dword ptr
  3.   [ebx+1+260],eax
Код (Text):
  1.  
  2.   ; сохраним <i>идентификатор поиска</i>
Код (Text):
  1.  
  2.   lf:
Код (Text):
  1.  
  2.   cmp     byte
  3.   ptr [ebx+1+260+4+44],’.’
  4.   je      find_next
Код (Text):
  1.  
  2.   ; пропустим подкаталоги “.” и “..”
Код (Text):
  1.  
  2.   test_if_dir:
Код (Text):
  1.  
  2.   test      byte ptr
  3.   [ebx+1+260+4],10h
  4.   jz        found_file
Код (Text):
  1.  
  2.  
  3.   ; перейдем если нашли файл
Код (Text):
  1.  
  2.   proc_dir:
Код (Text):
  1.  
  2.   cmp    byte ptr [ebx],1
  3.   jnz    find_next
Код (Text):
  1.  
  2.   ; если ищем только файлы (<i>флаг</i> = 0), то
  3.   ; каталоги не трогаем
Код (Text):
  1.  
  2.   ;----------------------
  3.   ; если файлы каталога были проверены, то по очереди будут проверяться подкаталоги.
  4.   ; подготавливаем <i>маску поиска </i>для найденного подкаталога в следующей ячейке и запускаем себя
Код (Text):
  1.  
  2.   prepare_rec:
Код (Text):
  1.  
  2.   mov    edi,ebx
  3.   add     edi,( ( ENTRY_SIZE ) +  1 )
  4.   mov    esi,ebx
  5.   inc      esi
  6.   push    esi
  7.   call      <b>lstrlenA</b>
  8.   xchg    eax,ecx
  9.   sub      ecx,4
  10.   cmp     byte ptr [esi+ecx],’\’
  11.   jnz      not_root
  12.   inc      ecx
Код (Text):
  1.  
  2.   ; edi – ячейка текущего процесса поиска
  3.   ; edi – ячейка следующего процесса поиска+1
  4.   ; esi – ячейка текущего процесса поиска
  5.   ; esi – <i>маска поиска </i>текущего процесса
  6.  
  7.   ; eax - длина <i>маски поиска </i>текущего процесса
  8.  
  9.   ; отнимем от длины  4 символа (маску и слеш)
  10.  
  11.   ; корневой каталог, …
  12.   ; … оставим слеш
Код (Text):
  1.  
  2.   not_root:
Код (Text):
  1.  
  2.   rep      movsb
Код (Text):
  1.  
  2.   ; перенесем «полу готовую маску» в
  3.   ; следующую ячейку
Код (Text):
  1.  
  2.   mov    esi,ebx
  3.   add     esi,1+260+4+44
  4.   push    esi
  5.   call      <b>lstrlenA</b>
  6.   inc       eax
  7.   xchg    eax,ecx
  8.   rep       movsb
  9.   add      ebx,(ENTRY_SIZE)
  10.   push     ebx
  11.   call      <b><u>find</u></b>
  12.   sub       ebx,(ENTRY_SIZE)
  13.   jmp      find_next
Код (Text):
  1.  
  2.    
  3.   ; esi – имя найденного подкаталога
  4.  
  5.   ; eax – длина найденного подкаталога
  6.   ; eax – длина найденного подкаталога + 0
  7.  
  8.   ; дополнить маску найденным подкаталогом
  9.   ; ebx – ячейка следующего процесса поиска
  10.   ; параметр для вызова “себя”
  11.   ; ищем в подкаталоге
  12.   ; ebx – ячейка этого процесса поиска
  13.   ; ищем следующий объект
Код (Text):
  1.  
  2.   found_file:
  3.   </td>
  4.   <td width=252 valign=top>
  5.  <code><pre>
  6.   cmp     byte ptr [ebx],1
  7.   jz         find_next
  8.   --------------------------------------------
Код (Text):
  1.  
  2.   ; если ищем каталоги, то файлы не трогаем
  3.  
  4.   ; код для работы с найденными файлами
Код (Text):
  1.  
  2.   find_next:
Код (Text):
  1.  
  2.   mov    edx,ebx
  3.   add     edx,1+260+4
  4.   push    edx
  5.   push    dword ptr [ebx+1+260]
  6.   call      <b>FindNextFileA</b>
  7.   cmp     eax,0
  8.   jnz      
  9.   lf
  10.   jmp      test_fod
Код (Text):
  1.  
  2.  
  3.   ; edx – указывает на <i>структуру для поиска</i>
  4.   ; первый параметр для API <b>FindNextFileA</b>
  5.   ; второй параметр <i>идентификатор поиска</i>
  6.   ; выполним API
  7.  
  8.   ; если нашли объект, проведем его анализ
  9.   ; если ничего не нашли
Код (Text):
  1.  
  2.   <b><u>endp</u></b>
Код (Text):
  1.  
  2.   end       <b><u>start</u></b>

7. Основы построения вирусной базы

 Данные необходимые для поиска и лечения вирусов обычно хранятся в вирусной базе. Ее формат может быть любым, стандартов не существует. Мы рассмотрим пример вирусной базы для детектирования скрипт вирусов, которая будет содержаться в исходном коде программы, а не в отдельном файле.

Код (Text):
  1.  
  2.   vir_no           
  3.   vir_001_name 
  4.  
  5.    
  6.   vir_001_sig     
 
Код (Text):
  1.  
  2.   <i> </i>dd 3
  3.    db ‘BAT.Sys.602’,0
  4.    db 8 dup (?)
  5.    dd 161412090
Код (Text):
  1.  
  2.   <b>количество вирусов в базе данных</b>
  3.   <b>название вируса в </b><b>ASCII кодировке</b>
  4.   <b>размер названия 20 байт, оставшиеся байты</b>
  5.   <b>сигнатура участка длиной 75 байт, по смещению 75 кода вируса,
  6.      она же  сигнатура для детектирования</b>
Код (Text):
  1.  
  2.   vir_002_name
  3.  
  4.   vir_002_crc32
Код (Text):
  1.  
  2.    db ‘VBS.Links’,0
  3.    db 010 dup (?)
  4.    dd 1138651542
Код (Text):
  1.  
  2.   vir_003_name
  3.  
  4.   vir_003_sig
Код (Text):
  1.  
  2.    db ‘VBS.ILoveYou’,0
  3.    db 7 dup (?)
  4.    dd 3434909282

 Вот мы имеем простейшую вирусную базу для трех вирусов. Размер одной вирусной записи (данных об одном вирусе, необходимых для его детектирования и лечения) в данном случае составляет 20 + 4 = 24 байта.

 Если же вирусная база хранится в отдельном файле, то при старте антивирусный сканер должен загрузить ее в память, для того, чтобы к данным, хранящимся в ней, было быстрее и проще «обращаться» в необходимости. Для этого пишутся специальные процедуры разбора, ну и конечно же вирусные записи содержат гораздо больше данных, например таких как тип вируса, адреса процедур для детектирования и лечения … Но обо всем этом в следующий раз, в следующих статьях.

8. Основы работы с вирусной базой

 В нашем случае, необходимо писать процедуру для работы с найденными файлами, т.е. читать данные из файла по смещению 75 и размером 75 байт, считать контрольную сумму и сравнивать по очереди с указанной во всех доступных (в нашем случае 2) вирусных записях в вирусной базе. Если совпадает, выводит информацию о том, что найденный файл заражен определенным вирусом.

 Допустим, программа нашла файл, прочитала из него 75 байт по смещению 75 и подсчитала контрольную сумму этого участка. Контрольная сумма содержится в переменной “crc32_buf”. Вот как должен выглядеть процесс проверки на зараженность известными программе вирусами:

Код (Text):
  1.  
  2.   mov eax,dword ptr crc32_buf
  3.   mov ecx,dword ptr vir_no
  4.   mov esi,offset vir_001_name

Код (Text):
  1.  
  2.   ; eax – контрольная сумма участка
  3.   ; ecx – количество вирусов в базе
  4.   ; esi – начало первой <i>вирусной записи</i>

Код (Text):
  1.  
  2.   cmp_crc32_loop:

Код (Text):
  1.  
  2.   push ecx esi
  3.   cmp eax,dword ptr [esi+20]
  4.   jnz next_crc
  5.   pop   esi ecx

Код (Text):
  1.  
  2.   ; запомним данные регистров
  3.   ; проверим контрольную сумму с вирусной
  4.   ; если не совпадает перейдем к “next_crc”
  5.   ; востановим значение регистров

Код (Text):
  1.  
  2.   infected:
--------------------------------------------

Код (Text):
  1.  
  2.   ; если файл заражен вирусом …

Код (Text):
  1.  
  2.   next_crc:

Код (Text):
  1.  
  2.   pop esi ecx
  3.   add esi,24
  4.   loop 
  5.   cmp_crc32_loop

Код (Text):
  1.  
  2.   ; восстановим значение регистров
  3.   ; перейдем к следующей <i>вирусной записи</i>
  4.   ; цикл

9. Заметки по лечению вирусов

 В данном случае найденные файлы с вирусами можно просто удалять, но это самые простейшие вирусы. Обычно каждый вирус изменяет что-то в файлах настройках операционной системы, пытается спрятаться от чужих глаз. Даже для вирусов, файлы носители которых (дропперы) можно просто удалять это (скорее всего) не будет являться сто процентным лечением. Необходим анализ алгоритма работы вируса, вполне возможно он уже (например) успел прописать свой вызов в WIN.INI из «потайного места». Так как эти типы вирусов не заражают файлов, то от них мог бы избавиться любой начинающий пользователь. Авторы таких вирусов знают это и для этого предпринимают разнообразные хитрые методы для того, что бы вирус мог выжить после «чистки», вернуться «к жизни» второй раз.

10. Заключение

Соответственно если Вы заинтересовались этой областью программирования, придется разбираться и изучать (хотя бы основы) множество скрипт языков программирования. Я не говорю про основы строения исполняемых файлов написанных не на ассемблере, а на языках высокого уровня. Как раз на них и пишется большинство троянских коней. Необходимо будет разбираться с различными упаковщиками и шифровщиками исполняемых файлов, строением архивов …

 Все примеры программ, которые были представлены Вашему вниманию в статье, доступны в ZIP-архиве, но в немного измененном виде. Добавлен вывод «рабочей информации» через консоль и почти полностью отсутствуют комментарии к программе, но в статье комментариев изобилие.

 Готовое подобие антивирусного сканера так же доступно, а так же прилагаются КУСКИ вирусных дропперов, которые вставлены в базу антивируса (которую мы рассматривали выше). Полный вирусный код я не представляю, так как в этом нет необходимости.

 Если статья действительно кому-нибудь поможет, то я буду писать на эту тему еще … например про основы детектирования и лечения файловых вирусов, технологии анализа программного кода (кодо-анализаторов) и эмуляции программного кода (для детектирования полиморфных и шифрованных вирусов) …


0 1.846
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532