"Вирусы", "Черви", "Драконы" и резиденты на службе прогресса

Вид материалаДокументы

Содержание


"Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.
Mov ds,dx ; ---+--> ds = 0 ¦
Подобный материал:
1   2   3   4   5   6   7   8   9   ...   16

"Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.


Назад | Далее



гл.4 СОЗДАНИЕ РЕЗИДЕНТНЫХ ПРОГРАММ (практическая реализация)

============================================================

Итак, мы познакомились с механизмом работы прерываний и научились реали-

зовывать вызов прерываний на уровне обычный JMP-ов и CALL-ов и даже не ис-

пользовать при этом таблицу векторов.

Чудесно!

Теперь об создании резидентных программ (именно таковыми являются 'дра-

коны' и 90% всех 'вирусов').

Что такое резидентная программа (в дальнейшем - просто - резидент) ?

Это такая программа, которая находится в оперативной памяти постоянно

(обычные, нерезидентные программы присутствуют в памяти лишь во время их не-

посредственного исполнения; когда их выполнение заканчивается -- они "умира-

ют" - память занятая ими - освобождается . Резидент же может обитать в ОЗУ,

[кстати rezide - по англ. 'обитать'] не будучи в данный момент исполняем, но

в то же время, - готовый к действию).

Вот как распределяется память при выполнении обычных и резидент. прог-

рамм (КРАЙНЕ упрощенно):


-----------------------------------------------------------¬

¦ рис.2 ¦:

L-----------------------------------------------------------

обычная программа:


До загрузки чего-либо:


---------T--------------T----------------------------------------

¦таблица ¦ ¦

¦векторов¦ область DOS ¦ свободная память . . . . . . . . . .

¦прерыв-й¦ ¦

L--------+--------------+----------------------------------------

0:0 ----> старшие адреса


Загрузили и исполняем обычную программу:

¦

-----------------

---------T--------------T--------V----------T--------------------

¦таблица ¦ ¦ TETRIS.EXE ¦

¦векторов¦ область DOS ¦(есть такая глупая ¦ свободная память .

¦прерыв-й¦ ¦ игрушка) ¦

L--------+--------------+-------------------+--------------------

0:0


После того, как прогр-а завершилась:


---------T--------------T----------------------------------------

¦таблица ¦ ¦

¦векторов¦ область DOS ¦ свободная память . . . . . . . . . .

¦прерыв-й¦ ¦ (все вернулось на кругИ своя...)

L--------+--------------+----------------------------------------

0:0

===========================================================================

Теперь - что касается резидентов:


До загрузки чего-либо:


---------T--------------T----------------------------------------

¦таблица ¦ ¦

¦векторов¦ область DOS ¦ свободная память . . . . . . . . . .

¦прерыв-й¦ ¦

L--------+--------------+----------------------------------------

0:0


Загрузили резидент:-------------¬

¦

-----------------

---------T--------------T--------V----------T--------------------

¦таблица ¦ ¦ KEYR23.COM ¦

¦векторов¦ область DOS ¦(драйвер рус. кла- ¦ свободная память .

¦прерыв-й¦ ¦ виатуры) ¦

L--------+--------------+-------------------+--------------------

0:0 ---------------------------

¦

И так- -- до окончания работы на PC! Резидент будет сидеть в ОЗУ и быть

всегда готов вам услужить. Если Вы теперь загрУзите tetris, он попадет вот

куда:


---------T--------------T-------------------T----------T---------

¦таблица ¦ ¦ KEYR23.COM ¦ ¦

¦векторов¦ область DOS ¦(драйвер рус. кла- ¦TETRIS.EXE¦свободная

¦прерыв-й¦ ¦ ¦ виатуры) ¦ ¦память...

L--------+--------------+-¦-----------------+----------+---------

0:0 Lстал резидентом


Только программы типа Volkov Comander могут безболезненно удалять рези-

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

Сделать программу резидентной (постоянно присутствующей в памяти) --

ЭЛЕМЕНТАРНО. Вот один из способов (в дальнейшем мы рассмотрим их все):


(прокоментируем ниже)

-----------------------------------------------------------¬

¦ пример 4 ¦:

L-----------------------------------------------------------


TITLE Это - COM. программа N4 для демонстрации посадки резидента

ASSUME CS:CodeSegment

;---------------------------------------------------------------------------

CodeSegment SEGMENT PARA

ORG(100h)

Start:

MainProcedure PROC NEAR

;

;

;

;

;

MOV AX,0E61h ; напечатать

INT 10h ; символ 'a'

resident_end: ;

;

MOV DX,OFFSET resident_end ; DX<-- адрес последней команды

; ; рез-та+1

INT 27h ; вернуться в DOS

; ; оставив программу резидентной

;

;

;

MainProcedure ENDP

;

CodeSegment ENDS

END Start


Если Вы захотите откомпилировать и запустить вышеданный пример, то лучше

перед этим выйти из Norton Comander-а и запустить Volkov Comander (если у Вас

его еще нет -- настоятельно советуем приобрести !). Volkov Comander позволяет

по нажатию комбинации клавиш Alt/F5 показать карту памяти PC и Вы можете уда-

лить любой резидент, загруженный после Volkov Comander-а.


А теперь -- коментарии к примеру 4:

Чтобы после выполнения программы выйти в DOS и одновременно оставить

прогр-му резидентной всего-то лишь и нужно: ВМЕСТО КОМАНДЫ RET ДАТЬ ПРЕРЫВ-Е

INT 27h и при этом в регистр DX нужно поместить адрес последнего оператора

остающегося резидентно куска+1. Адрес этот = смещению от PSP программы (если

не знаете ничего о PSP, то почитайте хотя бы /1/; или -- чуть подождите, мы

кратко расскажем об этом позднее -- в гл.5). Прерывание INT 27h -- програм-

мное прерыв-е. П/п-ма его обработки изменяет структуру ОЗУ так, что часть па-

мяти теперь навечно закреплена за фрагментом Вашей программы.

Итак, мы оставили в ОЗУ резидентный фрагмент, который печатает букву

'a'. Вопрос -- когда же он будет выполняться? А вот -- никогда! Вернее --

всего один раз - когда мы оставляли резидент в памяти. Но, тем не менее, он

останется в ОЗУ. Он больше никогда не получит управление. Это - настоящий

программный труп, который просто занимает место. Ради прикола запустите эту

программу еще разок. И в ОЗУ останется еще один памятник глупости. Расботая в

Volkov Comander-е, нажав Alt/F5, Вы можете в этом убедиться. Сколько бы раз

эта программа не запускалась - каждый раз она будет отхватывать все новые и

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

больше не будет выполнен.

Но ведь резиденты пишутся не для этого! Они остаются в памяти ДЛЯ того,

чтобы в ОПРЕДЕЛЕННЫЕ МОМЕНТЫ получать управление и выполнять определенные

действия. Когда и каким образом? ПРИ ПОМОЩИ МЕХАНИЗМА ПРЕРЫВАНИЙ!

ПОДРОБНЕЕ:

Что будет если программа заменит в таблице векторов прерываний адрес ис-

ходной п/п-мы обработки прерывания на адрес своей собственной п/п-мы? В этом

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

п/п-ма. Возникнет некий самозванец, который будет обрабатывать прерывание по

своему. Например, -- вы нажали букву "А", а PC напечатал в на экране "О"; Вы

начали печатать документ на принтере, а одновременно его текст запишется в

специальный скрытый файл (пример шпионящего "дракона"); программа TETRIS.EXE

загружена на выполнение (прерывание 21h), а вирус "почуял" это и впридачу к

запуску программы "заразил" ее своей копией.

Прерывания происходят постоянно, и если, окончив свою работу, прогр-ма,

на адрес которой был заменен вектор прерывания, исчезнет из памяти - "умрет"

и при этом не восстановит исходное значение вектора (адрес исходной п/п-мы

обработки прерывания), то PC по-прежнему всякий раз, когда будет генериться

прерывание, будет передавать управление в область, где раньше сидел самозван-

ный обработчик, а теперь остался лишь мусор. И PC зависнет (в лучшем случае).

Зависнет намертво.

Следовательно, если мы решимся перехватить прерывание то существуют

только две возможности:

1. Либо, если мы не будем сажать в память резидент, то, перед тем как

"умрем", отреставрируем адрес исходного обработчика прерываний;

2. Либо мы не будем реставрировать адрес исходного обработчика прерыва-

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

гинальный обработчик резидентно в памяти;

Нас интересует второй вариант, т.к. созданные нами программы будут кон-

тролировать работу PC во время всей DOS-овской сессии, во время выполнения

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

не будет).

ОЧЕНЬ ВАЖНОЕ ДОБАВЛЕНИЕ -- очень часто стандартная п/п-ма обработки пре-

----------------------- рывания все же ДОЛЖНА его обработать, независимо

от того, обрабатывали ли мы его сами. Следовательно, после того, как Вы, пе-

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

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

когда некто с целью наварить баксов перехватывает выгодный подряд на строи-

тельство моста, но сам, не будучи строителем, все же вынужден нанять про-

фессионалов (выдать субподряд).


РЕЗЮМИРУЕМ: вот что нам нужно сделать, чтобы создать активный резидент,

реагирующий на появление определенного прерывания и не грохающий работу PC:

1. Сохранить в своем теле адрес истинного обработчика прерыв-я.

2. Заменить в таблице векторов адрес истинного обработчика прерыв-я на

адрес собственной п/п-мы.

3. Завершить программу, оставив собственную п/п-му обработки прерывания

в памяти PC резидентно.

4. Сконструировать резидентную часть так, чтобы после обработки перехва-

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

Если представить все это наглядно, то получится, что наш резидент вкли-

нивается в некую цепочку осуществляющую реагирование PC на некое прерывание.

Вот какая была цепочка до нас:


-----------------------------------------------------------¬

¦ рис.3 ¦:

L-----------------------------------------------------------


¦

¦ ------¬ ------>выполняется

¦ \ ¦ / +TTTTT+ ¦ п/п-ма

L - * - --------> -+++++++ ¦ обработки

/ ¦ \ ¦+++++++ ¦ (стандартная)

возник ¦¦ ¦ ¦ IRET------------¬

сигнал -- ¦L------ ¦ ¦

-- прерывание Lв таблице ---------T---- ¦

векторов делается ¦

---------------¬ найден адрес переход ¦

V L¬ обработчика по этому ¦

¦ (стандартного) адресу ¦

¦ ¦

L------T------------------------------------------

делается переход обратно - в пользовательскую программу,

в ту точку, перед которой прерыв-е возникло


А вот какая стала цепочка после того, как мы перехватили прерыв-е


¦ г================================¬

¦ ------¬ ¦ ------>выполняется ¦

¦ \ ¦ / +TTTTT+ ¦ ¦ ---НАША---- ¦

L - * - --------> -+++++++ ¦ ¦ п/п-ма обработки ¦

/ ¦ \ ¦+++++++ ¦ ¦ (она резидентна) ¦

возник ¦¦ ¦ ¦ ¦ ¦ ¦

сигнал -- ¦L------ ¦ ¦ L---->------¬ ¦

-- прерывание Lв таблице ---->----T---- ¦ ¦

векторов ¦ делается ¦ ¦

------------¬ найден адрес ¦ переход ¦ ¦

¦ L¬ ---НАШЕГО---- ¦ по этому ¦ ¦

V L¬ обработчика ¦ адресу ¦ ¦

¦ ¦ возвращаем ¦ ¦

¦ ¦ управление ¦ ¦

¦ ¦ стандартно- ----+ ¦

¦ ¦ му обработ- ¦ ¦

¦ наше новое-¦ чику (JMP Far) ¦ ¦

¦ звено L=============================¦==-

¦ ---------<--------

¦ выполняется

¦ п/п-ма

¦ обработки

¦ (стандартная)

¦ IRET-----------------¬

L--------T------------------------------------------

делается переход обратно - в пользовательскую программу,

в ту точку, перед которой прерыв-е возникло


Прочитаем еще раз пп.1-3 РЕЗЮМЕ.

А ведь все это мы уже делали! (правда по отдельности)

Попробуем теперь сделать все вместе.

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

обработчик.

Какое прерывание выбрать? Предложим для простоты прерывание N 5 (печать

экрана). Оно возникает, если Вы нажмете клавишу Print Scrin. Вызываемая при

этом п/п-ма печатает на принтере копию экрана PC. Прерывание N 5 не требует и

не возвращает никаких параметров (чУдно!), и еще его не обязательно возвра-

щать стандартному обработчику; ничего не случится, если Вы его зажмете (весь-

ма редкое свойство). Вы наверное не раз замечали что если Вы нажмете Print

Scrin, а принтер не готов -- раздается гудок, -- это стандартный обработчик

прерывания N 5 'назвал' Вас раздолбаем).

Создадим-ка резидент, который, перехватывая прерывание N 5 , ну ска-

жем,.. выведет на экран 'сердечко' (символ с кодом 3), и после этого возвра-

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

Таким образом, при нажатии Print Scrin сначала будет напечатано 'сердечко'

(работает наш резидентный обработчик), а уже потом раздастся гудок (заработа-

ет стандартный обработчик и обругает Вас за отключенный принтер).

Итак -- вперед!


(подробно прокоментируем ниже)

-----------------------------------------------------------¬

¦ пример 5 ¦:

L-----------------------------------------------------------


TITLE Это - COM. программа N5 для демонстрации посадки резидента

ASSUME CS:CodeSegment

;-----------------------------------------------------------------

CodeSegment SEGMENT PARA

ORG(100h)

Start:

MainProcedure PROC NEAR

;

;

JMP initial ; перепрыгнем через данные

; ; и наш обработчик прер-я 05

; ; на инициализирующую часть

;

;

saved_int05: DD 0 ; данные (хранилище для

; ; адреса стандартного обра-

; ; ботчика прерывания 05 --

; ; -- 2 слова)

;

;-----------наша п/п-а обработки прерывания 05------------------¬

;¦ (она останется резидентной в памяти) ¦

;¦ здесь мы можем приколоться как хотим.. ¦

;¦ ; ¦

int05_treater:PUSH AX ; ¦

; ; ¦

MOV AH,0Eh ;входные параметры прерыв-я 10h:¦

MOV AL,03h ; (печатать 'сердечко' - код 03)

INT 10h ;печатаем 'сердечко' ¦

; ; ¦

POP AX ;PUSH и POP ОЧЕНЬ важны (см. ко-¦

; ; ментарий после примера) ¦

; ; ¦

; ; ¦

JMP dword ptr CS:[saved_int05] ; длин. JMP по адресу, котор. ¦

rezident_end: ;¦ ; находится теперь в хранилище ¦

;¦ ; saved_int05 (возвращаем управ-¦

;¦ ; ление стандартному обработчику¦

;¦ ; прерывания 05) ¦

;L---------------------------------------------------------------

; ;

;-----------инициализирующая часть------------------------------¬

;¦ (здесь мы сажаем резидент, переопределяя адреса) ¦

;¦ ; ¦

initial: XOR DX,DX ; ---¬ ¦

MOV DS,DX ; ---+--> DS = 0 ¦

; ; ¦

MOV AX,DS:[5*4] ;сохраняем в хранилище saved_

MOV word ptr CS:[saved_int05 ],AX ; int05 адрес стандартного ¦

MOV AX,DS:[5*4+2] ; обработчика прерывания 05¦

MOV word ptr CS:[saved_int05+2],AX ; ( OFFSET и SEGMENT ) ¦

; ; ¦

; ; ¦

CLI ;запрещаем прерывания ¦

MOV AX,OFFSET int05_treater ; ¦

MOV word ptr DS:[5*4],AX ;кладем в таблицу векторов ¦

PUSH CS ; адрес нашего обработчика ¦

POP AX ; прерывания 05 ¦

MOV word ptr DS:[5*4+2],AX ; ¦

STI ;разрешаем прерывания ¦

; ; ¦

; ; ¦

MOV DX,OFFSET rezident_end ;DX<--конец резид. части ¦

INT 27h ;закончить программу и ¦

;¦ ; вернуться в DOS, оставив ¦

;¦ ; резидентной п/п-му ¦

;¦ ; int05_treater ¦

;L---------------------------------------------------------------

MainProcedure ENDP

;

CodeSegment ENDS

END Start


коментарии:


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

нять:


-----------------------------------------------------------¬

¦ рис.4 ¦:

L-----------------------------------------------------------


-------------¬

¦JMP initial¦======>=======>====>¬ перепрыгнем через данные

L------------- ¦ и наш обработчик прер-я 05

saved_int05: -----------------------------¬ V на инициализирующую часть

¦данные: (хранилище для ¦ ¦ L---------T----------

¦адреса стандартного обра - ¦ ¦--------------

¦ботчика прерывания 05 -- ¦ ¦

¦-- 2 слова) ¦ ¦

L----------------------------- ¦

V

int05_treater:----------------------------¬ ¦

¦наша п/п-ма обработки ¦ ¦

¦прерывания 05 (она оста- ¦ ¦

¦нется резидентной в памяти) ¦ V

rezident_end:L----------------------------- ¦

¦

г=========<=============<============<=-

¦

¦

L=> -----------------------------¬

initial: ¦инициализирующая часть ¦

¦(здесь мы сажаем резидент, ¦

¦переопределяя адреса ¦

¦ ¦

¦MOV DX,OFFSET rezident_end ¦

¦INT 27h (выход в DOS) ¦

L-----------------------------


Как видно из рисунка, прогр-ма состоит из двух главных частей: той, что

остается в памяти резидентно и не выполняется при запуске самой прогр-мы

(данные + наша п/п-ма обработки прерывания 05), и -- инициализирующей части.

Почему порядок следования этих частей именно такой?

Почему инициализирующая часть не остается в памяти резидентно?

Почему наша п/п-ма обработки прерывания 05 начинается оператором PUSH AX

и заканчивается оператором POP AX?

Что за новые операторы CLI и STI?