Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf

ВУЗ: Не указан

Категория: Книга

Дисциплина: Программирование

Добавлен: 16.02.2019

Просмотров: 29217

Скачиваний: 1689

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

Глава 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 


background image

 

Часть 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

. Что произойдет, полагаем, до-

гадаться несложно. 

Обработчик  прерывания —  это  процедура,  постоянно  (или  временно)  находя-

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


background image

Глава 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) (обращайте внимание на описания после точки с запятой). 


background image

 

Часть 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 


background image

Глава 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