Например:
адрес п/п-ы прерывания N0 (реакция на ситуацию "деление на нуль") нахо-
дится в таблице векторов прерываний по адресу 0000:0000--segment и
0000:0002--offset
адрес п/п-ы прерывания N5 (печать экрана) находится в таблице векторов
прерываний по адресу 0000:0014--segment и 0000:0016--offset
Если мы замыслим заменить исходную стандартную п/п-му нашей собственной,
мы должны:
1. Засунуть куда-то эту нашу п/п-му (и чтобы она там действительно была!
И была готова обработать соотв. прер-е).
2. Изменить в таблице векторов адрес исходной стандартной п/п-ы на адрес
нашей.
Отметим, что пихать нашу п/п-му обработки прерыв-я в то-же место, где
сидела исходная (иногда это физически возможно) лучше не надо, хотябы потому,
что исходная нам ЕЩЕ ОЧЕНЬ МОЖЕТ ПРИГОДИТЬСЯ.
ПОДРОБНО опишем механизм того, как PC реализует обработку прерываний:
1. PC прячет в стек регистр флагов.
2. PC прячет в стек содержимое регистра CS.
3. PC прячет в стек значение IP для следующей команды.
4. PC делает JMP Far <сегмент. адрес п/п>:[<офсетн. адрес п/п>] (джамп @@??
на п/п-у обработки прерывания).
ПРИ ЭТОМ все операции 1-4 (PUSHF, PUSH CS, , JMP FAR ) содер-
жатся неявно В ОДНОЙ КОМАНДЕ "INT" вызова прерывания (если прерыв-е реализу-
ется программно).
5. Далее выполняется п/п-ма обработки прерывания.
6. П/п-ма обработки прерывания завершается специальной командой IRET,
которая состоит из следующих действий:
а) помещается из стека слово в регистр IP (и увелич. SP на 2) ---¬
б) помещается из стека слово в регистр CS (и увелич. SP на 2) +----¬
в) помещается из стека слово в регистр флагов (увелич. SP на 2)--- ¦
¦
¦
т.е. это- JMP Far обратно, в программу ¦
пользователя, на команду, следующую за той, ¦
которая осуществила вызвлв прерыв-я ¦
+ восстановление флагов <-----------------------------------
А что такое п/п-ма обработки прерывания? Очень часто - трудно сказать;
считайте, что стандартная п/п-ма обработки прерывания (та - которую PC имеет
изначально) -- своего рода -- черный ящик. И, как в любом черном ящике, здесь
нам доступны, в лучшем случае, лишь ВХОД и ВЫХОД. Пусть, например, мы хотим
напечатать на экране PC один символ. Это легко можно сделать, дав прерывание
номер 10h. При этом будет вызвана п/п-ма печати символа, о структуре которой
мы ничего не знаем. Как и большинство п/п-м, эта п/п-ма имеет входные и вы-
ходные параметры, через которые мы загружаем в нее и получаем обратно инфор-
мацию. ВХОДНЫЕ И ВЫХОДНЫЕ ПАРАМЕТРЫ П/П-МЫ ПОМЕЩАЮТСЯ В РЕГИСТРЫ ПРОЦЕССОРА.
Например, -- мы страстно хотим напечатать на экране символ 'a'. Тогда
нам необходимо сделать следующие действия:
MOV AH,0Eh ;это означает что п/п-ма должна ПЕЧАТАТЬ ОДИН СИМВОЛ------¬
; ¦
MOV AL,61h ;п/п-ма должна печатать КОНКРЕТНЫЙ СИМВОЛ 'a'(его код=61h)¦
;L---T-----------------------------------------------------
; L-загрузка входных параметров
;
INT 10h ;приказ "печатай!" (генерация прерывания N 10h)
а вот выходных параметров это прерывание (вернее сказать- данный способ
его использования) не имеет
А вот как выглядит при этом работа процессора:
-----------------------------------------------------------¬
¦ рис.1 ¦:
L-----------------------------------------------------------
-- НАША (ПОЛЬЗОВАТЕЛЬСКАЯ) ПРОГРММА ------¦--- П/П-ма ОБРАБОТКИ ПРЕРЫВАНИЙ ---
¦
¦
¦
ДЕЙСТВИЯ ПРОЦЕССОРА МНЕМОКОДЫ ¦ МНЕМОКОДЫ ДЕЙСТВИЯ
('на пальцах'): ¦ ПРОЦЕССОРА:
=======>====¬ ¦ г===>=====¬
AH<---0Eh MOV AH,0Eh¦ ¦ ¦.печать..¦ .печать..
AL<---61h MOV AL,61h¦ ¦ ¦.символа.¦ .символа.
PUSHF---------------TT---> INT 10h L===>===-.........¦ .........
PUSH CS ¦¦ г===============<====T¬IRET<--¦------¬--POP IP
PUSH IP ¦¦ L==>=¬ ¦ ¦L=====<=- L+ POP CS
JMP Far по адресу --+- ¦ ¦ ¦ L-POPF
<0:40>:[<0:42>] ¦ ¦
L--T-- L---T- ¦ ¦
¦ ¦ ¦ L-> путь выполнения @@??
¦ ¦ ¦
слово по- L--- слово по
адресу 0:40 адресу 0:42
Покажем, опираясь на описонный выше подробный механизм выполнения преры-
ваний, как реализовать прер-е НЕ пользуясь классической командой INT. Печата-
ем символ 'a' (см. пример 1):
-----------------------------------------------------------¬
¦ пример 1 ¦:
L-----------------------------------------------------------
TITLE Это - COM. программа N1 для демонстрации механизма вызова прерываний
ASSUME CS:CodeSegment
;---------------------------------------------------------------------------
CodeSegment SEGMENT PARA
ORG(100h)
Start:
MainProcedure PROC NEAR
;
;
;
;--запасаем в стеке информацию, необходимую для автоматического-¬
; возврата из п/п-мы обработки прерыв-я ¦
; ; ¦
PUSHF ; ¦
PUSH CS ; ¦
MOV BX,OFFSET after_int ;---¬ ¦
PUSH BX ;---------+-засунуть в стек IP точки возврата¦
; ; из п/п-ы обработки прерывания ¦
;----------------------------------------------------------------
;
MOV AH,0Eh ;входные
MOV AL,61h ; параметры прерыв-я 10h
;
;
XOR DX,DX ; DX = 0 напрямую, как Вы знаете, засунуть
MOV DS,DX ; DS = 0 константу в регистр DS невозможно
;
;
JMP dword ptr DS:[40h] ;длин. джамп по адресу, котор. находится
; ; в ячейках 0000:0040h и 0000:0042h
after_int: ;
RET ;закончить программу и вернуться в DOS
;
;
;
MainProcedure ENDP
;
CodeSegment ENDS
END Start
Пояснение: команда JMP dword ptr DS:[40h] (длинный JMP) осуществляет пе-
реход по адресу, который хранится по адресу DS:[40h]. Это -- адрес п/п-мы об-
работчика прерывания 10h. При этом в стек нужно уронить регистр флагов, ре-
гистры CS и IP. Т.о. для того, чтобы команда IRET, завершающая обработчик
прерывания 10h вернула управление нашей программе (на метку after_int), необ-
ходимо сохранить в стеке определенную информацию (см. пример 1).
-----------T------------------------------------------------------T------------
¦ в случае, если кто-то не знаком с пакетом TASM, то:
L-------------------------------------------------------
А теперь -- создадим загрузочный модуль.
Вы можете при помощи любого текстового редактора перетащить вышепред-
ставленный текст исходника в текстовой файл с расширением .asm, например --
proba.asm
А после этого сделать сначала .OBJ файл:
tasm.exe /l proba.asm
LT-
L---- будет создан файл листинга proba.lst
(там Вам покажут ошибки - если они есть)
а потом - и .COM файл:
tlink.exe /t proba.obj
LT-
L--- будет создан .COM файл
Если Вы не знакомы с пакетом TASM и это - Ваш первый опыт, то можете по-
ка-что пропустить (особо не задумываясь) всякие диррективы, предшествующие и
последующие за текстом основной программы (TITLE ...; ASSUME ...; CodeSegment
SEGMENT PARA; ORG(100h);MainProcedure ENDP; CodeSegment ENDS; END Start ).
Поступайте с ними пока-что чисто механически. (На досуге можете почитать
/1/,/2/,/7/)
Итак, у нас есть готовый COM. модуль, который можно загрузить и выпол-
нить.
И он напечатает литеру 'a'.
Замечательно!
А вот тут пример того как решить ту же задачу не JMP Far-ом, а -- CALL
Far-ом Разница в том, что в этом случае не нужно прятать в стек значения CS и
IP для точки возврата из п/п-мы обработки прерывания (CALL Far оказался "ум-
нее" - он делает это автоматически).
-----------------------------------------------------------¬
¦ пример 2 ¦:
L-----------------------------------------------------------
TITLE Это - COM. программа N2 для демонстрации механизма вызова прерываний
ASSUME CS:CodeSegment
;---------------------------------------------------------------------------
CodeSegment SEGMENT PARA
ORG(100h)
Start:
MainProcedure PROC NEAR
;
;
;
XOR DX,DX ; DX = 0
MOV DS,DX ; DS = 0
;
PUSHF ;запасаем информацию, необходимую для автома-
; ;тического ыозврата из п/п-ы обработки прерыв-я
;
MOV AH,0Eh ;входные
MOV AL,61h ; параметры прерыв-я 10h
;
;
CALL dword ptr DS:[40h] ;длинный вызов п/п-ы обработки прерыв-я 10h
; ;(по адресу 0:40h - адрес п/п-ы обработки)
; ;(два слова)
;
RET ;закончить программу и вернуться в DOS
;
;
;
MainProcedure ENDP
;
CodeSegment ENDS
END Start
И последнее, что мы сделаем в этой главе, - научимся давать вызов преры-
вания, не пользуясь INT-ом и не обращаясь каждый раз к таблице векторов. За-
чем ? Это иногда полезно. Дело в том, что 'вирусы' и 'драконы' очень часто
пользуются прерываниями и при этом должны оставаться необнаруживаемыми. А ес-
ли прерывание дается командой INT, то охранные системы (всевозможные антиви-
русные мониторы) тут же узнаЮт об этом и могут насторожиться. Если мы посто-
янно пользуемся таблицей векторов, то где гарантия, что антивирусные мониторы
межу делом не подложили в нее адрес СВОЕЙ п/п-мы, и, вместо того, чтобы полу-
чить сервис п/п-мы-обработчика прерыв-я, мы попадаем в засаду. А если мы сох-
раним в коде нашей программы адрес п/п-мы исходного обработчика прерыв-я (и в
тот момент будем абсолютно уверены, что имеем дело не с 'оборотнем') то в
дальнейшем сможем смело делать JMP Far или CALL Far без опасения быть обнару-
женными.
Вот готовая программа: (коментарии см. ниже)
-----------------------------------------------------------¬
¦ пример 3 ¦:
L-----------------------------------------------------------
TITLE Это - COM. программа N3 для демонстрации механизма вызова прерываний
ASSUME CS:CodeSegment
;---------------------------------------------------------------------------
CodeSegment SEGMENT PARA
ORG(100h)
Start:
MainProcedure PROC NEAR
;
;
;
JMP over_data ; перепрыгнем через данные
;
;
saved_int10: DD 0 ; данные (хранилище для адреса INT 10h
; ; -- 2 слова)
;
over_data: XOR DX,DX ; DX = 0
MOV DS,DX ; DS = 0
;
;--сохраняем в хранилище адрес исходн. п/п-мы INT 10h (2 слова)-¬
; ; ¦
MOV AX,DS:[10h*4] ;мы указываем лишь порядко-¦
MOV word ptr CS:[saved_int10 ],AX ;вый номер прерывания ¦
MOV AX,DS:[10h*4+2] ; ¦
MOV word ptr CS:[saved_int10+2],AX ; ¦
;----------------------------------------------------------------
;
;--запасаем в стеке информацию, необходимую для автоматического-¬
; возврата из п/п-мы обработки прерыв-я ¦
; ; ¦
PUSHF ; ¦
PUSH CS ; ¦
MOV BX,OFFSET after_int ;---¬ засунуть в стек ¦
PUSH BX ;---+- IP точки возврата ¦
;----------------------------------------------------------------
;
;
MOV AH,0Eh ;входные параметры
MOV AL,61h ; прерыв-я 10h (печатать 'a')
;
JMP dword ptr CS:[saved_int10] ;длин. джамп по адресу, котор. на-
; ; ходится теперь в хранилище
after_int: ; ; saved_int10
;
RET ;закончить программу и вывалиться
; ; в DOS
;
;
MainProcedure ENDP
;
CodeSegment ENDS
END Start
Что здесь нового? Совсем немного. Во-первых мы перетащили адрес
п/п-мы-обработчика прерывания 10h из таблицы векторов в тело нашей прогр-мы.
Во-вторых мы теперь делаем JMP Far не на адрес в таблице векторов (dword ptr
00:[40h]), а на адрес, сохраненный в теле нашей прогр-мы (dword ptr
CS:[saved_int10]). Есть одна громоздкость -- данные (saved_int10: DD 0) дол-
жны определяться перед их использованием (MOV word ptr CS:[saved_int10 ],AX)
и, следовательно, мы должны в начале через них перепрыгнуть. Этот бардак свя-
зан с особенностями TASM-овской компиляции.
Далее
|