Драйверы, резидентные программы и WINDOWS
В этом разделе, как и в следующем, мы не будем ничего говорить о защищённом режиме работы процессора. Мы рассмотрим здесь некоторые особенности, которые необходимо учитывать при разработке резидентных программ и драйверов, работающих совместно с WINDOWS.
Очень часто резидентные программы или драйверы перехватывают аппаратное прерывание клавиатуры и отслеживают коды нажимаемых клавиш, выполняя те или иные действия при нажатии заданных комбинаций. Например, драйвер "секретного" диска Norton DISKREET может при нажатии заданной комбинации клавиш блокировать доступ к "секретному" диску, экрану и клавиатуре.
Так как WINDOWS в расширенном режиме использует собственную систему клавиатурного ввода/вывода, основанную на очередях сообщений, а также реализует концепцию виртуальных машин, нажатие активизирующих резидентную программу комбинаций клавиш в неподходящий момент может не привести к желаемому результату и даже стать причиной "зависания" системы.
Есть два возможных решения этой проблемы. Во-первых, можно запретить запуск WINDOWS, если активна резидентная программа или драйвер, не способные работать совместно с WINDOWS. Во-вторых, на время работы WINDOWS можно запретить выполнение резидентной программой или драйвером специфических функций, несовместимых с WINDOWS (например, запретить активизацию резидентной программы при нажатии комбинации клавиш).
Перед запуском WINDOWS и перед её завершением вызываются функции прерывания INT2Fh 1605h и 1606h соответственно. Ваша резидентная программа или драйвер могут подготовить собственные обработчики для этих прерываний и отслеживать моменты запуска WINDOWS и завершения её работы.
Функция 1605h вызывается при запуске WINDOWS:
Регистры при вызове прерывания: AX 1605h ES:BX 0000h:0000h DS:SI 0000h:0000h CX 0000h DX Флаги: Бит 0 = 0, если выполняется инициализация WINDOWS в расширенном режиме; Бит 0 = 1, если выполняется инициализация DOS-экстендера "Microsoft 286 DOS extender" (используется в стандартном режиме работы WINDOWS); Биты 1-15 зарезервированы, их содержимое неопределено.
Регистры перед возвратом из прерывания: CX 0000h, если WINDOWS может продолжать инициализацию; CX <> 0, если запуск WINDOWS недопустим.
Функция 1606h вызывается при завершении WINDOWS расширенном или стандартном режиме:
Регистры при вызове прерывания:
AX 1606h DX Флаги: Бит 0 = 0, если выполняется завершение WINDOWS, работавшей в расширенном режиме; Бит 0 = 1, если выполняется завершение DOS-экстендера "Microsoft 286 DOS extender"; Биты 1-15 зарезервированы, их содержимое не определено.
Обработчик функции 1605h может выполнить необходимые действия, связанные с модификацией алгоритма работы резидентной программы или драйвера, а также при помощи соответствующей установки регистра CX может разрешить или запретить запуск WINDOWS.
Обработчик функции 1606h получает управление при завершении работы WINDOWS и может восстановить прежний алгоритм работы критичной к WINDOWS резидентной программы или драйвера.
Приведённая ниже резидентная программа перехватывает прерывание INT 2Fh и отслеживает фунции 1605h и 1606h, вызавая сообщение и ожидая нажатия на любую клавишу при запуске и завершении работы WINDOWS:
Листинг 22.
Контроль запуска WINDOWS Файл wintsr.asm -----------------------------------------------------------
.MODEL tiny .CODE .STARTUP
jmp begin
old_int2Fh_off dw 0 ; Адрес старого обработчика old_int2Fh_seg dw 0 ; прерывания 2Fh
; Сообщение, которое будет выдано на экран ; при запуске WINDOWS
msg_win db 'WINDOWS Started. Press any key...$' msg_win_off dw offset msg_win
; Сообщение, которое будет выдано на экран ; при завершении WINDOWS
msg_win1 db 'WINDOWS Ended. Press any key...$' msg_winend_off dw offset msg_win1
; Новый обработчик прерывания 2Fh нужен ; для проверки наличия программы в памяти ; при ее запуске для предохранения ; от повторного запуска
new_int2Fh proc far cmp ax,0FF00h jz installed
cmp ax,1605h jz winstart
cmp ax,1606h jz winend jmp dword ptr cs:old_int2Fh_off
winstart: ; запуск WINDOWS
push ax push bx push cx push dx push ds
mov dx,cs:msg_win_off mov ah,9 push cs pop ds int 21h
mov ax,0 int 16h
pop ds pop dx pop cx pop bx pop ax
jmp dword ptr cs:old_int2Fh_off
winend: ; завершение WINDOWS
push ax push bx push cx push dx push ds
mov dx,cs:msg_winend_off mov ah,9 push cs pop ds int 21h
mov ax,0 int 16h
pop ds pop dx pop cx pop bx pop ax
jmp dword ptr cs:old_int2Fh_off
; Если код функции 0FF00h, то возвращаем ; в регистре AX значение 00FFh. Это признак ; того, что программа уже загружена в память
installed: mov ax,00FFh iret
new_int2Fh endp
;==============================
; Точка входа в программу ; В этом месте начинается выполнение программы
begin proc
; Проверяем, не загружена ли уже программа ; в память
mov ax,0FF00h int 2Fh
cmp ax,00FFh jne first_start
mov dx,offset msg_load1 mov ah,9 int 21h
.EXIT
; Первоначальный запуск программы
first_start:
; Запоминаем адрес старого обработчика прерывания 2Fh
mov ax,352Fh int 21h mov cs:old_int2Fh_off,bx mov cs:old_int2Fh_seg,es
push cs pop ds
; Выводим сообщение
mov dx,offset msg_load mov ah,9 int 21h
mov dx,OFFSET new_int2Fh mov ax,252Fh int 21h
; Завершаем программу и оставляем резидентно ; в памяти часть программы, содержащую новые ; обработчики прерываний
mov dx,OFFSET begin int 27h
begin endp
msg_load db 'Резидентная программа WINTSR загружена$' msg_load1 db 'Резидентная программа WINTSR уже загружена$'
end
Следующая резидентная программа работает аналогично, но она запрещает запуск WINDOWS.
Попробуйте, запустив предварительно программу NOWINTSR, запустить WINDOWS и посмотрите, что из этого получится.
Листинг 23. Запрет запуска WINDOWS Файл nowintsr.asm -----------------------------------------------------------
.MODEL tiny .CODE .STARTUP
jmp begin
old_int2Fh_off dw 0 ; Адрес старого обработчика old_int2Fh_seg dw 0 ; прерывания 2Fh
; Сообщение, которое выдаётся при запуске WINDOWS
msg_win db 'NOWINTSR несовместима с WINDOWS. Нажмите любую клавишу...$' msg_win_off dw offset msg_win
; Сообщение, которое выдаётся при завершении WINDOWS
msg_win1 db 10,13,'WINDOWS Ended. Press any key...$' msg_winend_off dw offset msg_win1
; Новый обработчик прерывания 2Fh нужен ; для проверки наличия программы в памяти ; при ее запуске для предохранения ; от повторного запуска
new_int2Fh proc far cmp ax,0FF00h jz installed
cmp ax,1605h jz winstart
cmp ax,1606h jz winend jmp dword ptr cs:old_int2Fh_off
winstart:
push ax push bx push cx push dx push ds
mov dx,cs:msg_win_off mov ah,9 push cs pop ds int 21h
mov ax,0 int 16h
pop ds pop dx pop cx pop bx pop ax
mov cx,0ffh jmp dword ptr cs:old_int2Fh_off
winend:
push ax push bx push cx push dx push ds
mov dx,cs:msg_winend_off mov ah,9 push cs pop ds int 21h
mov ax,0 int 16h
pop ds pop dx pop cx pop bx pop ax
jmp dword ptr cs:old_int2Fh_off
; Если код функции 0FF00h, то возвращаем ; в регистре AX значение 00FFh. Это признак ; того, что программа уже загружена в память
installed: mov ax,00FFh iret
new_int2Fh endp
;==============================
; Точка входа в программу
begin proc
; Проверяем, не загружена ли уже программа ; в память
mov ax,0FF00h int 2Fh
cmp ax,00FFh jne first_start
mov dx,offset msg_load1 mov ah,9 int 21h
.EXIT
; Первоначальный запуск программы
first_start:
; Запоминаем адрес старого обработчика прерывания 2Fh
mov ax,352Fh int 21h mov cs:old_int2Fh_off,bx mov cs:old_int2Fh_seg,es
push cs pop ds
; Выводим сообщение
mov dx,offset msg_load mov ah,9 int 21h
mov dx,OFFSET new_int2Fh mov ax,252Fh int 21h
; Завершаем программу и оставляем резидентно ; в памяти часть программы, содержащую новые ; обработчики прерываний
mov dx,OFFSET begin int 27h
begin endp
msg_load db 'Резидентная программа NOWINTSR загружена$' msg_load1 db 'Резидентная программа NOWINTSR уже загружена$'
end