Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29217
Скачиваний: 1689
Глава 10. Введение в вирусологию. Обработчик прерываний
99
Это типичный COM-файл, структура которого вам уже известна. Можно создать
и EXE-файл, однако его размер будет больше.
В следующих разделах мы будем подробнее рассматривать работу вируса на
практике.
10.3. Резидент
Приступаем к самому сложному, но очень интересному разделу. Будет действи-
тельно сложно. Но мы уверены, что вы без особого труда разберетесь.
В
Н И МА Н И Е
!
Резидентные программы могут работать некорректно, если их запускать из файловых
оболочек, использующих API-функции. Таковой, например, является FAR Manager.
Если резидентные программы из данной книги действительно работают некорректно
с некоторыми оболочками, то запускайте их из командной строки или из DOS Navigator.
Резидентная программа (резидент) — программа, которая постоянно находится
в памяти. Примером резидента является драйвер мыши, Norton Guide, всевозмож-
ные антивирусы, которые следят за тем, что делает та или иная программа и сооб-
щают о ее действиях пользователю и пр. Естественно, любая резидентная програм-
ма всегда занимает часть оперативной памяти (в DOS — часть основной памяти).
Как правило, резидентная программа должна перехватывать то или иное преры-
вание, с тем, чтобы программист или другие программы могли обратиться к ней.
Давайте подробнее рассмотрим прерывания (немного мы затрагивали данную тему
в предыдущих главах).
10.3.1. Подробней о прерываниях
Прерывание — это своего рода процедура (подпрограмма), которая имеет не на-
звание (например, print_string), а номер. Всего существует 256 прерываний. Неко-
торые номера зарезервированы BIOS (ПЗУ) компьютера. Например,
16h
:
...
mov ah,10h
int 16h
...
Или операционной системой (MS-DOS):
...
mov ah,9
mov dx,offset String
int 21h
...
Тем не менее, ничто не мешает программисту перехватить, скажем, прерывание
21h
и таким образом проконтролировать, кто и что делает с ним. Например, вызо-
вем функцию
09h
прерывания
21h
:
...
mov ah,9
Часть III. Файловая оболочка, вирус, резидент
100
mov dx,offset Our_string
int 21h
...
Our_string db 'Привет!$'
...
В результате операционная система выведет на экран сообщение "Привет!".
Мы же можем перехватить прерывание
21h
, и если какая-то программа попытается
вывести на экран некую строку, то мы в регистры
ds:dx
подставим адрес нашей
строки. Теперь функция
09h
прерывания
21h
будет выводить на экран нашу и толь-
ко нашу строку. Это можно сделать следующим образом (листинг 10.3).
Листинг 10.3. Часть нашего обработчика прерывания 21h
...
cmp ah,9
je Out_str
Go_21h:
...
;Здесь передаем управление предыдущему (оригинальному) обработчику 21h.
...
Out_str:
push cs
pop ds
mov dx,offset My_string
jmp Go_21h
...
My_string db "Моя строка$"
...
В приведенном примере проверяем, вызывается ли функция вывода строки на
экран
09h
прерывания
21h
или какая-либо другая. Если вызывается иная функция,
то мы просто передаем управление оригинальному обработчику. В противном слу-
чае загружаем в
ds:dx
адрес некоторой нашей строки и опять-таки передаем управ-
ление оригинальному обработчику прерывания
21h
. Что произойдет, полагаем, до-
гадаться несложно.
Обработчик прерывания — это процедура, постоянно (или временно) находя-
щаяся в памяти. Обработчик прерывания первым получает управление, выполняет
некоторые действия, а затем передает управление оригинальному обработчику
(т. е. процедуре, которая уже находилась в памяти до загрузки нашего обработчи-
ка). Оригинальный обработчик может также выполнить некоторые действия, а за-
тем передать управление другому обработчику и т. д.
Глава 10. Введение в вирусологию. Обработчик прерываний
101
Вообще оригинальные (скажем так, первичные) обработчики MS-DOS прерыва-
ний
20h
—
2Fh
находятся в файлах IO.SYS/MSDOS.SYS. До того момента, пока они
не загрузились в память, эти прерывания, грубо говоря, "пустые". То есть при по-
пытке вызвать одно из прерываний DOS (начиная с
20h
) до загрузки указанных
выше программ либо ничего не произойдет, либо компьютер просто "зависнет".
Прерывания делятся на программные и аппаратные по способу их вызова.
Программные прерывания вызывает непосредственно программа при помощи
команды
int
(отсюда и название — программные), например, для того чтобы
получить код клавиши, которую нажмет пользователь (
mov ah,0
/
int 16h
), или
вывести некоторую строку на экран (
mov ah,9
/
int 21h
).
Аппаратные прерывания вызываются самостоятельно процессором (аппарату-
рой компьютера) при возникновении каких-либо событий. При этом процессор
прекращает выполнение текущей программы, сохраняет в стеке регистры
ss
,
sp
и флаги, вызывает соответствующее прерывание, а затем восстанавливает со-
храненные регистры и продолжает выполнение текущей программы. Например:
при нажатии и отпускании какой-либо клавиши пользователем вызывается пре-
рывание ПЗУ
09h
. Или прерывание таймера (также ПЗУ)
1Ch
, вызываемое авто-
матически примерно 18,2 раза в секунду.
Однако процессор не всегда продолжает выполнение текущей программы после
обработки аппаратного прерывания. В некоторых случаях (например, если про-
грамма выполнила деление на ноль) продолжение выполнения программы невоз-
можно. Процессор вызывает соответствующее прерывание, которое выводит на
экран сообщение "Divide overflow" (или подобное ему) и останавливает систему.
Программы могут ненадолго заблокировать вызов аппаратных прерываний
(кроме немаскируемых, типа "деления на ноль") с помощью команды
cli
в момент
выполнения критических участков кода. К таким участкам можно отнести, к при-
меру, смену векторов прерываний напрямую в таблице векторов прерываний или
смену стековых регистров. После выполнения необходимых действий программа
должна снова разрешать вызов аппаратных прерываний при помощи команды
sti
.
Если программист "забыл" разблокировать вызов аппаратных прерываний коман-
дой
sti
после их блокировки
cli
, то пользователь не сможет работать с компьюте-
ром, т. к. аппаратные прерывания (в том числе и
09h
— управление клавиатурой)
будут заблокированы, что фактически приведет к "зависанию" компьютера. Однако
программные прерывания можно вызывать всегда.
В системе Windows используется новое поколение готовых подпрограмм,
которые называются WinAPI. Принцип работы WinAPI очень похож на работу пре-
рываний. Однако исследовать и понять работу прерываний проще, поэтому мы
с них и начнем. Полученные знания понадобятся вам при программировании под
Windows.
10.4. Первый обработчик прерывания
Теперь перейдем непосредственно к обработчику, его код расположен в листин-
ге 10.4 (\010\prog10.asm) (обращайте внимание на описания после точки с запятой).
Часть III. Файловая оболочка, вирус, резидент
102
Листинг 10.4. Обработчик прерывания
CSEG segment
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG
org 100h
Start:
;Переходим на метку инициализации. Нам нужно будет перехватить прерывание 21h,
;а также оставить программу резидентной в памяти
jmp Init
;Ниже идет, собственно, код обработчика прерывания 21h (он будет резидентным).
;После того как программа выйдет, процедура Int_21h_proc останется в памяти
;и будет контролировать функцию 09h прерывания 21h.
;Мы выделим код обработчика полужирным шрифтом.
Int_21h_proc proc
cmp ah,9 ;Проверим: это функция 09h?
je Ok_09
;Если нет, перейдем на оригинальный обработчик прерывания 21h.
;Все. На метку Ok_09 программа уже не вернется
jmp dword ptr cs:[Int_21h_vect]
Ok_09:
push ds ;Сохраним регистры
push dx
push cs ;Адрес строки должен быть в ds:dx
pop ds
;Выводим нашу строку (My_string) вместо той, которую должна была вывести
;программа, вызывающая прерывание 21h
mov dx,offset My_string
pushf ;Эта инструкция здесь необходима...
call dword ptr cs:[Int_21h_vect]
pop dx ;Восстановим использованные регистры
pop ds
iret ;Продолжим работу (выйдем из прерывания)
;Программа, выводящая строку, считает, что на экран было выведено
;ее сообщение. Но на самом деле это не так!
;Переменная для хранения оригинального адреса обработчика 21h
Глава 10. Введение в вирусологию. Обработчик прерываний
103
Int_21h_vect dd ?
My_string db 'Моя строка!$'
int_21h_proc endp
;Со следующей метки нашей программы уже не будет в памяти (это нерезидентная
;часть). Она затрется сразу после выхода (после вызова прерывания 27h)
Init:
;Установим наш обработчик (Int_21h_proc) (адрес нашего обработчика)
;на прерывание 21h. Это позволяет сделать функция 25h прерывания 21h.
;Но прежде нам нужно запомнить оригинальный адрес этого прерывания.
;Для этого используется функция 35h прерывания 21h:
;ah содержит номер функции
mov ah,35h
;al указывает номер прерывания, адрес (или вектор) которого нужно получить
mov al,21h
int 21h
;Теперь в es:bx адрес (вектор) прерывания 21h (es — сегмент, bx — смещение)
;Обратите внимание на форму записи
mov word ptr Int_21h_vect,bx
mov word ptr Int_21h_vect+2,es
;Итак, адрес сохранили. Теперь перехватываем прерывание:
mov ax,2521h
mov dx,offset Int_21h_proc ;ds:dx должны указывать на наш обработчик
;(т. е. Int_21h_proc)
int 21h
;Все! Теперь, если какая-либо программа вызовет 21h, то вначале компьютер
;попадет на наш обработчик (Int_21h_proc). Что осталось? Завершить программу,
;оставив ее резидентной в памяти (чтобы никто не затер наш обработчик.
;Иначе компьютер просто зависнет.).
mov dx,offset Init
int 27h
;Прерывание 27h выходит в DOS (как 20h), при этом оставив нашу программу
;резидентной. dx должен указывать на последний байт, оставшийся в памяти
;(это как раз метка Init). То есть в памяти остается от 0000h до адреса,
;по которому находится метка Init.
CSEG ends
end Start