Реферат: Резидентный обработчик клавиатуры (перехват нажатий клавиш и запись в файл)

После отработки функции DOS 31h программа остается в памяти в пассивном состоянии. Для того, чтобы активизировать резидентную программу, ей надо как-то передать управление и, возможно, парамет­ры. Вызвать к жизни резидентную программу можно разными способа­ми, но наиболее употребительным является механизм аппаратных или программных прерываний. В этом случае в секции инициализации не­обходимо заполнить соответствующий вектор адресом резидентной части программы (точка entry на рис. 2.2). Адрес entry образует вторую точку входа в программу, через которую осуществляется ее активизация. Очевидно, что резидентная секция программы должна заканчиваться командой выхода из прерывания iret.

Поля данных резидентной части программы переместились в начало программы после команды imp. Это довольно естественное место дня резидентных данных, потому что и при первом запуске, и при активизации сюда никогда не будет передано управление. При заполнении в секции инициализации векторов не возникает проблем с перенастройкой регистра DS, так как в программе типа СОМ все регистры указывают на единственный сегмент програм­мы. В секции инициализации предусмотрен, как это обычно делается, вывод на экран сообщения о загрузке программы в память.

После запуска программы она остается в памяти и, активизируясь фактически аппаратными прерываниями от клавиатуры (а более точно – программой BIOS, активизируемой аппаратными прерываниями от клавиатуры).

2.2. Защита резидентной программы от повторной установки

Как правило, в секции инициализации загружаются векторы прерываний, через которые будет активизироваться программа. Последними строками секции инициализации вызывается функция DOS 31h, которая выполняет завершение программы с оставлением в памяти ее резидентной части.

Если программу запустить с клавиатуры повторно, в память будет загружена и останется резидентной ее вторая копия. Это плохо не только потому, что понапрасну расходуется память, более неприятным является вторичный перехват тех же векторов. Если резидентная программа после ее активизации не обращается к старому содержимому перехваченных ею векторов, то вторая копия полностью лишит первую работоспособности, и тогда повторная загрузка приведет только к расходованию памяти. Если, однако, как это обычно и имеет место, резидентная программа в процессе своей работы передаст управление старому обработчику перехваченного ею прерывания, то новая копия резидентной программы, сохранившая в процессе инициализации адрес первой копии в качестве содержимого перехватываемого вектора, будет при каждой активизации вызывать и первую копию. В результате резидентная программа будет фактически выполняться при каждом вызове дважды. Во многих случаях такое повторное выполнение нарушит правильную работу программы. Поэтому обязательным элементом любой резидентной программы является процедура защиты ее от повторной загрузки, или, как говорят, установки.

Наиболее распространенным методом защиты резидентной программы от повторной установки является использование прерывания 2Fh, специально предназначенного для связи с резидентными программами. При вызове этого прерывания в регистре АН задается номер функции (от 00h до FFh), а в регистре AL - номер подфункции (в том же диапазоне). 00h - 7Fh зарезервировано для DOS/Windows 0B8h - 0BFh зарезервировано для сетевых функций 0C0h - 0FFh отводится для программ.

Для того, чтобы резидентная программа могла отозваться на вызов прерывания int 2Fh, в ней должен иметься обработчик этого прерывания. Фактически все резидентные программы, как системные, так и прикладные, имеют такие обработчики, через которые осуществляется не только проверка на повторную установку, но и вообще связь с резидентной программой: смена режима ее работы или получение от неё в транзитную программу каких-то параметров. Задание действия, которое надлежит выполнить обработчику прерывания 2Fh конкретной резидентной программы, осуществляется с помощью номера подфункции, помещаемого перед вызовом прерывания в регистр AL

Таким образом, обработчик прерывания 2Fh резидентной программы должен, прежде всего, проверить номер функции в регистре АН; при обнаружении "своей" функции обработчик анализирует содержимое регистра AL и выполняет затребованные действия, после чего командой iret передаст управление вызвавшей его программе. Если, однако, обработчик обнаружил в регистре АН "чужую" функцию, он должен командой jmp CS:old_2fh передать управление по цепочке тому обработчику, адрес которого был ранее в векторе 2Fh. В результате вызов int 2Fh из любой программы будет проходить по цепочке через все загруженные резидентные программы, пока не достигнет "своей" программы или не вернет управление в вызвавшую программу через обработчик DOS (который, очевидно, всегда будет самым последним в цепочке).

Естественно, для коммуникации с резидентной программой должен быть установлен некоторый интерфейс. Обычно при проверке на повторную установку резидентная программа, если она уже находится в памяти, возвращает в регистре AL значение FFh, которое является признаком запрета вторичной загрузки. Иногда для большей надежности идентификации "своей" функции резидентная программа, помимо значения FFh в регистре AL, возвращает еще какие-то обусловленные заранее коды в других регистрах. Часто через дополнительные регистры передастся символьная информация, например, имя программы. В этом случае, если вызвавшая программа с именем DUMP.COM (т.е. вторая копия резидентной программы, выясняющая, можно ли ей остаться резидентной в памяти) получает после вызова int 2Fh в регистре AL значение FFh, а в регистрах СХ и DX символьные коды 'DU' и 'МР', она может быть уверена, что ее первая копия уже находится в памяти. Если же в регистре AL вернулся код FFh, а в регистрах СХ и DX -коды, например, 'ОК' и 'RB', это, скорее всего означает, что закрепленная за нашей программой функция мультиплексного прерывания ухе используется другой резидентной программой. В этом случае стоит сменить функцию, чтобы не возбуждать конфликтных ситуаций.

В резидентную часть следует включить обработчик прерывания 2Fh. Его расположение в пределах текста программы не имеет особого значения; мы поместили его в начале резидентной части. Секция инициализации претерпела большие изменения. Она должна начинаться с вызова прерывания 2Fh с соответствующей функций для проверки на повторную установку. Если первая копия программы уже загружена, текущую программу следует завершить не функцией 3th (завершить и оставить в памяти), а обычной функцией завершения 4Ch. Если же нашей программы в памяти нет, то в секции инициализации, помимо заполнения ее "рабочего" вектора, в данном случае 03h, следует также установить наш обработчик мультиплексного прерывания.

Среди функций мультиплексного прерывания, предназначенных для прикладных программ, мы произвольно выбрали для нашей программы функцию F1h, а для проверки на повторную установку подфункцию 00h. Резидентный обработчик прерывания 2Fh, включенный в нашу программу, проверяет номера функции и подфункции и при обнаружении каких-либо других кодов передает управление следующему обработчику этого прерывания. Если же вызвана функция F1h с подфункцией 00h, обработчик устанавливает в регистре AL значение FFh ("я уже загружен") и возвращает управление в вызвавшую программу командой iret.

Секция инициализации начинается с проверки на повторную установку. После загрузки в регистр АН номера функции (F1h), а в регистр AL - номера подфункции (00h), вызывается прерывание 2Fh. После возврата из прерывания анализируется содержимое регистра AL Если обработчик вернул значение FFh, программа должна завершиться без оставления в памяти. Эти действия выполняются по метке installed. Если возвращено другое значение, инициализация продолжается (для надежности стоило проверить, возвращен ли именно 0). Сохраняется старое содержимое вектора 2Fh, устанавливается наш обработчик этого прерывания, после чего выполняются все действия по установке, предусмотренные в старом варианте программы динамического дампа. При переходе на метку installed на экран выводится сообщение о невозможности повторной установки и выполняется функция завершения 4Сh с кодом возврата 01h. Последнее, конечно, имеет символический характер, поскольку этот код в дальнейшем не анализируется.

2.3. Выгрузка резидентной программы из памяти

Следует заметить, что в DOS отсутствуют средства выгрузки резидентных программ. Единственный предусмотренный для этого механизм - перезагрузка компьютера. Практически, однако, большинство резидентных

программных продуктов имеют встроенные средства выгрузки. Обычно выгрузка резидентной программы осуществляется соответствующей ко­мандой, подаваемой с клавиатуры и воспринимаемой резидентной программой. Для этого резидентная программа должна перехватывать прерывания, поступающие с клавиатуры, и "вылавливать" команды вы­грузки. Другой, боже простой способ заключается в запуске некоторой программы, которая с помощью, например, мультиплексного прерыва­ния 2Fh передает резидентной программе команду выгрузки. Чаще всего в качестве "выгружающей" используют саму резидентную про­грамму, точнее, ее вторую копию, которая, если ее запустить в опреде­ленном режиме, не только не пытается остаться в памяти, но, наоборот, выгружает из памяти свою первую копию.

Выгрузку резидентной программы из памяти можно осуществить разными способами. Наиболее простой - освободить блоки памяти, за­нимаемые программой (собственно программой и ее окружением) с помощью функции DOS 49h. Другой, более сложный - использовать в выгружающей программе функцию завершения 4Ch, заставив ее завер­шить не саму выгружающую, а резидентную программу, да еще после этого вернуть управление в выгружающую. В любом случае перед осво­бождением памяти необходимо восстановить все векторы прерываний, перехваченные резидентной программой. Следует подчеркнуть, что вос­становление векторов представляет в общем случае значительную и иногда даже неразрешимую проблему. Во-первых, старое содержимое вектора, которое хранится где-то в полях данных резидентной програм­мы, невозможно извлечь "снаружи", из другой программы, так как нет никаких способов определить, где именно его спрятала резидентная программа в процессе инициализации. Поэтому выгрузку резидентной программы легче осуществить из нее самой, чем из другой программы. Во-вторых, даже если выгрузку осуществляет сама резидентная про­грамма, она может правильно восстановить старое содержимое вектора лишь в том случае, если этот вектор не был позже перехвачен другой резидентной программой. Если же это произошло, в таблице векторов находится уже адрес не выгружаемой, а следующей резидентной про­граммы, и если восстановить старое содержимое вектора, эта следующая программа "повиснет", лишившись средств своего запуска. Поэтому на­дежно можно выгрузить только последнюю из загруженных резидент­ных программ.

В нашей программе подфункция 00h прерывания 2Fh служит для проверки на повторную установку, а подфункция 01h - для выгрузки. В секцию инициализации добавлены строки сохранения старого содержимого вектора 09h. Это выполняется точно так же, как и для вектора 2Fh - с помощью функции DOS 35h. Старый вектор сохра­няется в ячейке old_09h, размещаемой в резидентной части программы. Поскольку выгрузка программы выполняется с помощью прерывания 2Fh, текст обработчика этого прерывания усложняется.

Резидентный обработчик прерывания 2Fh прежде всего проверяет номер функции, поступивший в регистре АН, Если этот номер от­личается от F1h, управление передается следующему обработчику по цепочке. Далее анализируется содержимое регистра AL. Если AL=00h, выполняются действия по защите от повторной загрузки. Если AL=01h, осуществляется переход на метку uninstall для выполнения действий по выгрузке программы. При любом другом номере подфункции управле­ние передается следующему обработчику по цепочке.

По метке uninstall осуществляется сохранение используемых далее регистров (что делается скорее для красоты, чем по необходимости) и функцией DOS 25h восстанавливается из ячеек old_09h и old_2Fh ис­ходное содержимое соответствующих векторов. Далее из ячейки со сме­щением 2Ch относительно начала PSP в регистр ES загружается адрес окружения программы. Сегментный адрес освобождаемого блока памя­ти - единственный параметр, требуемый для выполнения функции DOS 49h. Размер освобождаемого блока DOS известен, он хранится в блоке управления памятью (МСВ). Далее освобождается блок памяти с самой программой. Сегментный адрес этого блока (адрес PSP) находится в регистре CS. Наконец, командой iret управление передастся в програм­му, вызвавшую прерывание 2Fh.

Функция 49h оповещает DOS о том, что данный блок памяти сво­боден и может впредь использоваться DOS. Это, однако, не мешает вы­полняться завершающим строкам программы (в данном случае – коман­де iret), поскольку освобождение памяти не разрушает ее содержимого. Наша резидентная программа физически сотрется лишь после того, как в память будет загружена очередная выполняемая программа.

Если программа запускается с клавиатуры с указанием каких-либо параметров (имен файлов, ключей, определяющих режим работы про­граммы и проч.), то DOS, загрузив программу в память, помещает все символы, введенные после имени программы (так называемый хвост команды) в префикс программного сегмента программы, начиная с от­носительного адреса 80h. Хвост команды помещается в PSP во вполне определенном формате. В байт по адресу 80h DOS заносят число сим­волов в хвосте команды (включая пробел, разделяющий на командной строке саму команду и ее хвост). Далее (начиная с байта по адресу 81h) следуют все символы, введенные с клавиатуры до нажатия клавиши <Enter>. Завершается хвост колом возврата каретки (13).

К данным секции инициализации добавилась строка с ожидаемым хвостом команды и байтовый флаг запроса на выгрузку.

Поскольку действия программы при её запуске зависят от того, вве­дена ли команда запуска с параметром или нет, наличие хвоста в PSP анализируется в самом начале секции инициализации. При запуске программы типа СОМ все сегментные регистры указывают на начало PSP. Байт с длиной хвоста (возможно, нулевой) помещается в регистр CL и сравнивается с нулем. Если в нем 0, команда запуска была введена без параметров и инициализация программы продолжается обычным образом. Если хвост имеет ненулевую длину, начинается его анализ.

Обнулением регистра СН длина хвоста "расширяется" на весь ре­гистр СХ, что нужно для организации цикла. Регистр DI настраивается на первый байт хвоста, а регистр SI – на начало поля tail с ожидаемой формой параметра. Регистр AL подготавливается для выполнения ко­манды сканирования строки. Команда scasb сравнивает в цикле байты хвоста с содержимым AL (кодом пробела). Сравнение ведется до тех пор, пока не будет найден первый символ, отличный от пробела. Эта операция необходима из-за того, что оператор при вводе команды вы­грузки может отделить параметр команды от самой команды любым числом пробелов, которые попадут в хвост команды в PSP и помешают анализировать введенный параметр.

Выход из цикла выполнения команды scasb осуществляется, когда команда проанализировала первый после пробела символ. После этого регистр DI указывает на второй символ параметра. Команда dec DI кор­ректирует указатель DI, направляя его на первый значащий символ вве­денного параметра. Далее командой сравнения строк cmpsb осу­ществляется сравнение трех оставшихся символов хвоста. Если символы совпадают с параметром 'off', записанным в программе, устанавливается флаг запроса на выгрузку. Если результат сравнения оказался отрица­тельным, флаг запроса не устанавливается (и, следовательно, непра­вильный параметр просто не воспринимается). В любой случае осу­ществляется переход на продолжение программы, начинающей прове­рять, не установлена ли уже эта программа в памяти. Если программа еще не установлена, введенный параметр не имеет смысла. Инициали­зация осуществляется обычным образом: сохраняются и устанавливают­ся векторы и программа завершается с оставлением в памяти.

При наличии в памяти резидентной копии этой программы осу­ществляется переход на метку installed, где прежде всего проверяется, установлен ли флаг запроса на выгрузку. Если флаг сброшен, выводится сообщение о невозможности повторной загрузки и программа завер­шается с кодом возврата 1. Если флаг запроса установлен, выполняется выгрузка программы, которая заключается в вызове мультиплексного прерывания 2Fh с функцией F1h и подфункцией 01h. Резидентный об­работчик этого прерывания, включенный в состав нашей резидентной программы, отработает эту подфункцию, восстановит векторы и освободит занятые программой блоки памяти. После возврата управления из обработчика в текущую программу будет выведено сообщение об успешной выгрузке и программа будет завер­шена функцией 4Ch с нулевым кодом возврата.

Составленная нами программа не избавлена от недостатков. Так, в ней анализируются всегда только 3 значащих символа хвоста. Таким об­разом, программа будет выгружена и при вводе команды (имя).com onset. Другой недостаток заключается в том, что результат сравне­ния записанного в программе хвоста с введенным с клавиатуры пара­метром будет положительным, только если с клавиатуры введены строчные буквы. Команда (имя) OFF не приведет к выгрузке про­граммы. По-настоящему следовало включить в программу перед анали­зом хвоста преобразование символов параметра в прописные буквы.

2.4. Перехват прерываний

В архитектуре процессоров 80х86 предусмотрены особые случаи, когда процессор прекращает (прерывает) выполнение текущей программы и немедленно передает управление программе-обработчику, специально написанной для обработки подобной ситуации. Такие особые ситуации делятся на два тина: прерывания и исключения, в зависимости от того, вызвало ли эту ситуацию какое-нибудь внешнее устройство или выполняемая процессором команда. Исключения делят­ся далее на три типа: ошибки, ловушки и остановы, в зависимости от того, когда по отношению к вызвавшей их команде они происходят. Ошибки появляются перед выполнением команды, поэтому обработчик такого исключения получит в качестве адреса возврата адрес ошибочной команды (начиная с процессоров 80286). Ловушки происходят сразу после выполнения команды, так что обработчик получает в качестве адреса возврата адрес следующей команды. И наконец, остановы могут возникать в любой момент и вообще не предусматривать средств возврата управления в программу.

К-во Просмотров: 269
Бесплатно скачать Реферат: Резидентный обработчик клавиатуры (перехват нажатий клавиш и запись в файл)