Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29220
Скачиваний: 1689
Часть III. Файловая оболочка, вирус, резидент
114
Таблица 11.3. Функция
05h
прерывания
10h
:
установить текущую видеостраницу
Вход
Выход
ah = 05h
al
= номер видеостраницы
Ничего
Попробуйте поэкспериментировать с данными прерываниями.
Несколько дополнений:
как правило, все страницы дисплея (кроме нулевой) чистые, если некоторая
программа не заносила в них данные;
при переключении режима дисплея с помощью функции
00h
прерывания
10h
очищается не только нулевая, но и все видеостраницы;
смена видеостраницы происходит мгновенно. Это особенно полезно, если необ-
ходимо вывести на экран сложный и объемный рисунок. При этом можно, на-
пример, на текущую нулевую страницу вывести надпись вида "Подождите не-
много", на первой строить сам рисунок, а затем на нее переключиться.
Создается эффект моментального вывода изображения на экран. Этот прием
раньше часто использовали в старых играх.
* * *
На этом данная глава заканчивается. В главе 12 мы начнем рассматривать пер-
вую резидентную программу.
Глава 12
Повторная загрузка резидента
12.1. Резидент
В данной главе рассмотрим один из вариантов применения резидента. Но сперва
изучим файлы-приложения resid12.asm и test12.asm.
Загрузите файл resid12.com в память, а затем запустите test12.com.
Resid12.com оставляет в памяти процедуру, которая выводит на экран строку
ASCIZ (ASCII-строка, заканчивающаяся символом 0). Причем процедура
Int_10h_proc
перехватывает прерывание
10h
(это прерывание BIOS (ПЗУ)). Нечто похожее рас-
сматривалось в главе 10. В данном случае мы добавляем еще одну функцию (
88h
)
к прерыванию
10h
, которой, естественно, может воспользоваться любая другая
программа, если будет знать, что такая функция существует. Для того чтобы вы-
звать функцию
88h
, нам нужно вызвать прерывание
10h
, а в регистры загрузить не-
обходимые числа (данные). А именно:
ah = 88h — номер нашей функции;
ds:si = адрес строки, которую нужно вывести на экран (ds — сегмент, si — смещение).
Обратите еще раз внимание, что мы в
ds
(сегмент) ничего не загружаем, только
в
si
(смещение). Заметьте, что при старте любого COM-файла сегментные регист-
ры
cs
,
ds
,
es
,
ss
равны нашему единственному сегменту, в который загрузилась
программа. Сегмент может быть любым. Все зависит от того, сколько резидентных
программ уже загружено в память, каков объем ядра ОС и пр.
12.2. Проверка на повторную
загрузку резидента
Первое, что делает программа, — переходит на метку инициализации. При ини-
циализации обычно настраиваются необходимые регистры, перехватываются пре-
рывания и пр. То есть происходит "подготовительный процесс" перед установкой
резидентной программы.
Перво-наперво проверим, загружен ли уже наш резидент в память или нет. Как
это сделать? Так как мы перехватили прерывание, то можем из него сделать "от-
клик". Вызываем прерывание
10h
, заносим в регистр
ax
"магическое число"
8899h
.
Можно любое другое, главное — чтобы не конфликтовало с какой-нибудь функцией
Часть III. Файловая оболочка, вирус, резидент
116
данного прерывания. Понятно, что в
ah
заносится
88h
, а в
al
—
99h
. Функции
88h
у прерывания
10h
не существует (не дошли пока разработчики до этого числа).
Это можно с уверенностью предположить на 99,9%. Так как номер прерывания
10h
еще не задействован, то произойдет немедленный выход из прерывания
10h
(регист-
ры, как правило, не меняются), что легко проверить в отладчике. Следовательно, вы-
звав прерывание
10h
с числом
8899h
в
ax
и получив также ответ
8899h
в том же
ax
,
мы уверены, что наш резидент еще не загружен. На рис. 12.1 изображено окно про-
граммы в состоянии после вызова прерывания
10h
и получения отклика от резидента.
Рис. 12.1. Проверка на присутствие резидента в памяти: отзыв получен!
Теперь о нашем обработчике прерывания
10h
. Если файл resid12.com уже нахо-
дится в памяти, перехватив прерывание
10h
, то, процедура обработки прежде всего
должна проверить, вызывают ли ее с числом
8899h
в
ax
. Если так, то нужно будет
послать какой-нибудь ответ. Какой? В нашем примере мы меняем местами содер-
жимое регистров
ah
и
al
и немедленно выходим из прерывания. Проще говоря,
возвращаемся в нашу программу, которая, в свою очередь, проверяет, вернулось ли
число
9988h
. Здесь важно запомнить, что если нашего резидента нет в памяти, то
вернется
8899h
в
ax
, причем BIOS регистры не изменит, т. к. такой функции просто
не существует. Если же в
ax
вернулось
9988h
, то, значит, наша программа уже за-
гружена. То есть процедура
Int_10h_proc
поменяла местами регистры (а что еще
может это сделать?). Обратите внимание на первые строки процедуры, которые
и меняют местами
ah
и
al
(листинг 12.1).
Листинг 12.1. Часть обработчика прерывания 10h программы resid12.com
...
Int_10h_proc proc
Глава 12. Повторная загрузка резидента
117
pushf ;Сохраним флаги в стеке, т. к. они поменяются...
cmp ax,8899h ;Проверим на повторную загрузку в память
jne Next_test ;Если не проверка, то смотрим дальше...
;Меняем местами ah и al — признак того, что мы в памяти,
;что-то вроде ответного сигнала.
xchg ah,al
popf ;Выровняем стек
iret ;Выйдем из прерывания (вернемся в нашу программу)
;ax при возврате будет равен 9988h!!!
...
Познакомимся с новым оператором
xchg
(табл. 12.1 и листинг 12.2).
Таблица 12.1. Оператор
xchg
Команда
Перевод
Назначение
Процессор
xchg источник, прием-
ник
Exchange
— обменять
Обмен регистров 8086
Листинг 12.2. Использование оператора xchg
...
mov ax,10h
mov bx,15h
xchg ax,bx ;Теперь ax=15h, bx=10h
...
Как вам уже известно, регистр
cs
(от англ. code segemt — сегмент кода) всегда
содержит номер сегмента, в котором находится наша программа, а
ip
(от англ.
instruction pointer — указатель инструкций) — смещение. Допустим, процедура
обработки прерывания
10h
расположена по адресу
0010:0400h
, а наша программа
загрузилась в сегмент
1234h
. Тогда получаем:
;cs:ip = 1234:0100h
...
[1234:0100h] mov ax,8899h
;После выполнения данной инструкции cs:ip=1234:0103h
[1234:0103h] int 10h
;Теперь cs:ip = сегменту/смещению адреса (вектора) прерывания 10h,
;т. е. 0010:0400h
[1234:0105h] mov bx,10
;Работаем дальше после того, как прерывание завершило свою работу...
...
Часть III. Файловая оболочка, вирус, резидент
118
Выход из прерывания осуществляется с помощью команды
iret
, в отличие от
выхода из ближней процедуры —
ret
. Это видно из приведенного выше примера
обработчика прерывания
10h
.
Очень просто все выглядит на практике. Попробуйте запустить наш резидент
(resid12.com) в отладчике. Внимательно следите за состоянием пары регистров
cs:ip
. Вы заметите, что они постоянно меняются. Обратите особое внимание на
значения, которые они принимают при вызове прерываний (для этого нужно зайти
внутрь прерываний; в AFD — <F1>, в CodeView — <F8>) (рис. 12.2).
Рис. 12.2. Резидент посылает отклик
12.3. Команды работы со строками
Рассмотрим некоторые новые инструкции, встречающиеся в нашем резиденте:
stos
,
lods
,
rep
. Это очень мощные и быстрые команды ассемблера. Они предназна-
чены для работы с массивами данных (строки, данные любого типа, числа и пр.).
Инструкция
lods
загружает в регистр
ax
/
al
число, которое находится по адресу,
указанному в регистрах
ds:si
, при этом
si
автоматически увеличивается на едини-
цу или на двойку.
Почему мы написали
ax
/
al
? Дело в том, что эта инструкция имеет две разновид-
ности:
lodsb
и
lodsw
.
Lodsb
(
b
— byte, байт) загружает в
al
, а
lodsw
(
w
— word,
слово) — в
ax
(листинг 12.3, рис. 12.3 и 12.4).
Листинг 12.3. Использование операторов lodsb и lodsw
...
mov si,offset String ;si указывает на начало String, т. е. на '1'
lodsb