Int_21h_proc endp
Popf
Pop ds
Pushf
Mov dx,offset My_string
Pop ds
Push dx
Je Ok_09
Int_21h_proc proc
pushf ;сохраним в стеке регистр флагов
cmp ah,9 ;проверим: это функция 09h?
popf ;восстановим регистр флагов
;Если нет, прейдем на оригинальный обработчик прерывания 21h. Все, на метку Ok_09 программа уже не вернется
jmp dword ptr cs:[Int_21h_vect]
Ok_09:
push ds ;Сохраним регистры
push cs ;Адрес троки должен быть DS:DX
; Выводим нашу строку (My_string) вместо той, которую должна была вывести программа, вызывающая 21h-е прерывание
call dword ptr cs:[Int_21h_vect] ;Вывели нашу строку вместо той, которую надо было
pop dx ;восстановим использованные регистры
iret ;продолжим работу (выйдем из прерывания)
; Программа, выводящая строку, считает, что на экран было выведено её сообщение. Но на самом деле это не так!
Int_21h_vect dd ? ;переменная для хранения оригинального адреса обработчика 21h
My_string db 'My string!$'
;Со следующей метки нашей программы уже не будет в памяти (это нерезидентная часть). Она затрется сразу после выхода (после вызова прерывания 27h).
Init:
;Установим наш обработчик (Int_21h_proc) (адрес нашего обработчика) на прерывание 21h. Это позволяет сделать функция 25h прерывания 21h. Но прежде нужно запомнить оригинальный адрес этого прерывания. Для этого используется функция 35h прерывания 21h:
mov ah,35h ;AH содержит номер функции
mov al,21h ;AL указывает номер прерывания, адрес (или вектор) которого нужно получить
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
_______________
Как проверить работу программы? В данном случае нам понадобится отладчик, который способен заходить внутрь прерываний. Например, AFD и пр., но не как не CodeView. То, что нам сейчас нужно! Если нет AFD, то его можно взять на сайте: http://www.Kalashnikoff.ru
Затем создайте простейшую программу, которая будет выводить на экран некоторую строку путем вызова 09 функции 21h прерывания. Например, так:
Программа N 02
CSEG segment
assume CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
org 100h
Begin:
mov ah,9
mov dx,offset String
int 21h
int 20h
String db 'My string.$'
end Begin
CSEG ends
Запускаем сперва Программу N 01. После того, как она вернется в DOS (в Norton Commander, DOS Navigator, Far и пр. оболочки), запускайте Программу N 02. Что вы увидите?
Теперь запустите Программу N 02 в отладчике. Заходите смело "внутрь" 21h-ого прерывание. Что вы видите теперь?