Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29253
Скачиваний: 1689
Глава 27
Удаление резидента из памяти
27.1. Обзор последнего резидента
27.1.1. Перехват прерывания 21h
В этой главе приведен последний пример резидентной программы под DOS,
в которой мы рассмотрим принцип удаления резидента из памяти.
Казалось бы: неужели так сложно удалить резидентную программу из памяти?
С одной стороны, ничего сложного в этом нет, с другой — существуют некоторые
сложности:
во-первых, не всегда есть возможность удалить из памяти резидентную про-
грамму;
во-вторых, необходима некоторая подготовка и проверка определенных пара-
метров перед удалением резидента;
и наконец, резидентная часть программы, которая будет удалена, должна пере-
давать по определенному запросу нужную информацию программе, которая ее
удалит.
Все это мы рассмотрим в данной главе. Как обычно, освоить тему вам помогут
файлы-приложения.
Рекомендую вам внимательно ознакомиться с приведенной в данной главе ин-
формацией, т. к. она поможет полностью понять механизм прерываний и принцип
работы резидентных программ. Мы уже немало говорили и о прерываниях, и о ре-
зидентных программах, но, тем не менее, рассмотрим некоторые вещи повторно,
с другого ракурса.
Итак, представим, что никакая резидентная программа не перехватывает преры-
вание
21h
. То есть его адрес (вектор) указывает непосредственно на ядро DOS, как
это показано в табл. 27.1.
Таблица 27.1. Вектор
21h
указывает на ядро DOS
Адрес
Что перехватывает
Куда отправляет после обработки
1234:0000h
Ядро DOS
Никуда (возврат из прерывания)
Глава 27. Удаление резидента из памяти
265
Как вам уже известно, MS-DOS имеет свои обработчики некоторых прерываний
(например,
20h
,
21h
и пр.). Использовать прерывания DOS без загрузки самой ОС
невозможно. При попытке это сделать, компьютер просто зависнет. Например,
в программе загрузочного сектора диска (которая загружается первая, т. е. до за-
грузки DOS) нельзя пользоваться функциями DOS, т. к. они еще недоступны. Но
программе ничего не мешает использовать прерывания BIOS, т. к. они находятся
в области ПЗУ и могут вызываться из любой ОС и в любое время.
Теперь внимательно посмотрите на табл. 27.1. Допустим, что процедура обра-
ботки
21h
-го прерывания MS-DOS загрузилась вместе с ОС по адресу
1234:0000h
.
Никакая иная программа данное прерывание еще не перехватила. Таким образом,
выполняя команду
int 21h
, процессор передаст управление напрямую процедуре
обработки
21h
-го прерывания MS-DOS, т. е. на адрес
1234:0000h
. Данная процеду-
ра, отработав и, например, выведя на экран строку, передаст управление нашей
программе. Это простейшая схема.
Теперь допустим, что пользователь загружает некий резидент с именем progA.com,
который перехватывает прерывание
21h
. В результате мы получим именно то, что
изображено в табл. 27.2.
Таблица 27.2. Наша программа перехватила прерывание
21h
Адрес
Что перехватывает
Куда отправляет после обработки
1234:0000h
Ядро DOS
Никуда (возврат из прерывания)
2345:0000h
progA.com
1234:0000h
(ядро DOS)
Из табл. 27.2 видно, что программа progA.com перехватила прерывание
21h
.
Прерывание как бы стало фильтром на пути к оригинальному обработчику (обра-
ботчику MS-DOS (
1234:0000h
)).
После вызова прерывания
int 21h
первым получит управление обработчик пре-
рывания
21h
программы progA.com. Процедура обработки прерывания
21h
про-
граммы progA.com (в нашем случае она находится по адресу
2345:0000h
), получив
управление, сделает все необходимое, а затем передаст управление ядру DOS на
адрес
1234:0000h
. Ядро DOS выполнит определенные действия, а затем выйдет из
прерывания командой
iret
, передав управление программе, которая вызывала пре-
рывание
21h
.
Отметим важный момент. Допустим, пользователь загружает обычную нерези-
дентную программу, которая работает с прерыванием
21h
. Вызов данного преры-
вания этой программы (
int 21h
) находится по адресу
3456:0100h
. Таким образом,
при выполнении команды
int 21h
в стек будет занесен адрес
3456:0102h
, т. е. ад-
рес следующей команды, а процессор получит адрес прерывания
21h
из таблицы
векторов прерываний и передаст управление по этому адресу, т. е. на
2345:0000h
,
программе progA.com. В свою очередь, progA.com отработает (может, например,
изменить значения каких-либо регистров, как наш резидент) и передаст управление
ядру DOS.
Часть III. Файловая оболочка, вирус, резидент
266
Вот только вопрос: а куда именно передать управление, на какой адрес? Для
этой цели наши резиденты сперва получают текущий адрес обработчика того или
иного прерывания, а затем, когда обработчик отработал, передают управление на
сохраненный адрес, т. е. дальше по цепочке.
Программа progA.com поступит точно так же. Перед установкой обработчика на
свою процедуру она получает адрес прежнего (оригинального) обработчика (в дан-
ном случае — ядра DOS, т. к. никакие резидентные программы не загружены), вы-
звав функцию
35h
прерывания
21h
либо прочитав адрес напрямую из таблицы век-
торов прерываний.
ProgA.com отработает и передаст управление по предварительно сохраненному
адресу на предыдущую процедуру обработки прерывания
21h
.
Управление получит ядро DOS. Выполнит свои действия и вернется. Спрашива-
ется: куда вернется и как? Естественно, с помощью команды
iret
, которая вытас-
кивает из стека предварительно занесенный в него адрес. Какой? Адрес progA.com
или адрес программы, которая была запущена пользователем и инициировала вы-
зов
int 21h
?
Все зависит от того, каким образом программа progA.com, выполнив необходи-
мые команды в обработчике
21h
, передала управление ядру DOS. Как уже не раз
отмечалось в предыдущих главах, существуют два способа передачи управления
прежнему (оригинальному) обработчику:
jmp dword ptr cs:[Int_21h_vect]
pushf
call dword ptr cs:[int_21h_vect]
В первом случае команда
iret
ядра DOS передаст управление непосредственно
программе пользователя, которая вызывала прерывание
21h
командой
int 21h
. Это
и понятно: инструкция
jmp
в стек ничего не кладет. Получается, что перед выпол-
нением команды
jmp
в стеке находится адрес возврата на программу пользователя
(если, конечно, progA.com не нарушает работу стека). Команда
iret
обработчика
21h
ядра DOS вытащит из стека адрес возврата на программу пользователя, которая
будет работать дальше, даже не "подозревая" о том, что некая progA.com "сидит"
в памяти и контролирует вызов прерывания
21h
!
Во втором случае после выполнения
iret
управление получит progA.com. Здесь
также все понятно: команда
call
заносит в стек адрес возврата. Команда
iret
и вы-
тащит его из стека! Зачем мы перед
call
используем
pushf
— вам уже известно...
Итак, после завершения работы оригинального обработчика
21h
(ядра DOS)
программа progA.com снова получила управление. Что теперь будет делать
progA.com? Она может посмотреть, что вернула подпрограмма обработки
21h
и выполнить определенные действия, если необходимо. После этого командой
iret
продолжить работу пользовательской программы. Иными словами,
iret
вытащит
из стека адрес возврата на программу, которая и вызывала изначально прерывание
командой
int 21h
.
Для чего нужно программе progA.com перехватывать
int 21h
? Да по разным
причинам. Например, чтобы контролировать открытие/закрытие файла, чтение/
запись, да и все, что выполняет прерывание
21h
! Некоторые варианты мы с вами
рассматривали в предыдущих главах.
Глава 27. Удаление резидента из памяти
267
Итак, progA.com "повисла" на прерывании
21h
, предварительно сохранив в сво-
ем теле адрес оригинального обработчика, т. е. обработчика ядра DOS, который
в нашем примере расположен по адресу
1234:0000h
. Теперь при выполнении ко-
манды
int 21h
управление получает сперва progA.com, а затем уже ядро DOS.
Теперь мы загружаем еще одну программу progB.com, которая также перехватит
прерывание
21h
. В табл. 27.3 показано, что у нас получится.
Таблица 27.3. Загружена вторая резидентная программа
Адрес обработчика
Кто перехватывает
Куда отправляет после обработки
1234:0000h
Ядро DOS
Никуда (возврат из прерывания)
2345:0000h
progA.com
1234:0000h (
ядро DOS)
3456:0000h
progB.com
2345:0000h (progA.com)
Теперь некая загруженная пользователем программа (например, NC.EXE) вызы-
вает прерывание командой
int 21h
. Что происходит?
Инструкция
int 21h
кладет в стек адрес возврата, достает из таблицы векторов
прерываний адрес прерывания
21h
(
3456:0000h
) и передает ему управление. Про-
грамма progB.com выполняет то, что программист ей "приказал", и передает управ-
ление на адрес прежнего (оригинального) обработчика прерывания
21h
, который
ею был предварительно сохранен (на
2345:0000h
, т. е. progA.com). ProgA.com,
в свою очередь, выполняет необходимые действия и так далее, по цепочке, пока
управление в итоге не получит ядро DOS. Таким образом, на одно прерывание
можно "насаживать" неограниченное число резидентов.
27.1.2. Как удалять загруженный резидент
из памяти?
Загрузим в память Resid27.com — написанную нами программу для практики.
Состояние памяти отображено в табл. 27.4.
Таблица 27.4. Состояние памяти после загрузки нашего резидента
Адрес
Что перехватывает
Куда отправляет после обработки
1234:0000h
Ядро DOS
Никуда (возврат из прерывания)
2345:0000h
Resid27.com
1234:0000h
(ядро DOS)
Сразу возникает несколько вопросов.
Как узнать, по какому адресу в памяти загрузилась программа Resid27.com?
Как узнать адрес обработчика прерывания ядра DOS?
Как удалить программу из памяти?
Все ответы достаточно просты.
Часть III. Файловая оболочка, вирус, резидент
268
Помните, как мы передавали "позывной" нашему резиденту? Если резидент от-
кликался, это означало, что он уже загружен.
Таким же способом можно получить сегмент и смещение процедуры обработки
прерывания
21h
. Сперва проверим на повторную загрузку (
mov ax,9988h
/
int 21h
),
а затем отправим в ответ какое-нибудь число (в нашем примере —
9999h
).
Наш резидент, получив
9999h
в
ax
, моментально выходит из процедуры, пере-
давая в определенных регистрах необходимую информацию, а именно: сегмент
и смещение резидентной части, сегмент и смещение оригинального обработчика
прерывания
21h
(в данном случае — ядра DOS). Этого будет вполне достаточно
для удаления резидента.
Затем, убедившись, что резидент загружен, и получив необходимую информа-
цию для удаления, можно приступать к освобождению памяти.
Делаем буквально следующее:
1. Запрещаем все прерывания командой
cli
.
2. Восстанавливаем адрес оригинального обработчика (т. е. ядра DOS).
3. Освобождаем память функцией
49h
.
4. Разрешаем прерывания командой
sti
.
Все! Резидент удален из памяти!
27.1.3. Случаи, когда резидент
удалить невозможно
Представим такую ситуацию. Вслед за Resid27.com загружается еще одна рези-
дентная программа, которая также перехватывает прерывание
21h
(в нашем приме-
ре это будет progA.com). Тогда получаем то, что показано в табл. 27.5.
Таблица 27.5. Загружена сторонняя программа после нашего резидента
Адрес
Что перехватывает
Куда отправляет после обработки
1234:0000h
Ядро DOS
Никуда (возврат из прерывания)
2345:0000h
Resid27.com
1234:0000h (
ядро DOS)
3456:0000h
progA.com
2345:0000h (Resid27.com)
Возникает вопрос: что произойдет, если мы удалим Resid27.com из памяти?
Процессор, выполнив команду
int 21h
любой программы, передаст управление на
адрес
3456:0000h
, т. е. программе progA.com. Последняя, отработав, передаст управле-
ние на адрес
2345:0000h
. И что же будет находиться по этому адресу, если мы удалили
Resid27.com? Обычный "мусор". Компьютер, скорее всего, просто зависнет.
Возникает еще один вопрос: а можно ли удалить Resid27.com, если после него
загрузился еще один резидент, который также перехватывает прерывание
21h
?
К сожалению, в таком случае наш резидент уже удалить не получится. Чтобы это
сделать, нам нужно вначале удалить progA.com, а затем уже удалять Resid27.com.