Светлана Сорокина Андрей Тихонов Андрей Щербаков
ПРОГРАММИРОВАНИЕ ДРАЙВЕРОВ И СИСТЕМ БЕЗОПАСНОСТИ Учебное пособие
Москва
Санкт-Петербург Издатель 2003
С. В.
УДК ББК
681.3.06 С65 Ре Московский институт электроники и математики (технический университет), Московское отделение Пензенского электротехнического института
Научная р е д а к ц и я : проф., заведующий кафедрой и дискретная математика" МИФИ, начальник Департамента науки, высоких технологий, образования и культуры аппарата Правительства РФ, академик РАО Подуфалов Н. Д,
С65
Сорокина С. И. и др. Программирование драйверов и систем безопасности: Учеб. посо бие Сорокина С. Тихонов А. Щербаков А. Ю. — СПб.: БХВПетербург, Издатель С. В., 2003. — 256 с: ил. ISBN 5-94157-263-8 ISBN 5-94740-005-7 Учебное пособие содержит оригинальный научный и учебнометодический материал, посвященный созданию систем безопасности для операционных сред Windows NT/2000. Рассматриваются вопросы создания различных драйверов уровня ядра ОС, предназначенных для шифрования трафика и контроля доступа. Учебное пособие используется при организа ции занятий на факультете информационной безопасности МИФИ. Для студентов и аспирантов соответствующих специальностей, разработчиков систем защиты информации, а также сотрудников экспертных организаций УДК 681.3.06 ББК 32.973.26-018
5-94157-263-8 ISBN
(Издатель Молгачева С. В.)
© Сорокина С. Тихонов А. Щербаков А. © Издатель Молгачева С. В., 2002 © Обложка, оборот титула, издательство 2002
2002
Содержание Предисловие Введение
12
Что такое драйвер
15
Типы драйверов и характеристики
15
1.2. С р е д а р а з р а б о т к и
16
1.3. У т и л и т а B U I L D
19
1.4. С и С + + . И н т е г р и р о в а н н а я с р е д а р а з р а б о т к и
21
2. Общая архитектура Windows NT
22
Понятия «пользовательский режим» и «режим ядра» Некоторые понятия з а щ и щ е н н о г о р е ж и м а 2.2. О с н о в н ы е характеристики W i n d o w s N T М о д е л ь модифицированного микроядра
22 22 24 24
2.2.2. Э м у л я ц и я нескольких ОС
25
2.2.3. Н е з а в и с и м о с т ь от архитектуры процессора
26
2.2.4. Объектная модель
26
2.2.5. М н о г о п о т о ч н о с т ь
27
2.2.6. В ы т е с н я ю щ а я многозадачность (preemptive multitasking)
27
2.2.7. Виртуальная память с подкачкой страниц по требованию
28
2.2.8. С и м м е т р и ч н а я мультипроцессорная обработка
29
2.2.9. Интегрированная
29
сети
2.3. Структура Windows NT 2 . 3 . 1 . З а щ и щ е н н ы е подсистемы 2.3.2.1. П о д с и с т е м ы среды 2.3.2.1.1. Подсистема среды Win32 «Родной» API для О С W i n d o w s N T (Native W i n d o w s NT API) 2.3.2.2. Н е о т ъ е м л е м ы е подсистемы
29 31 31 32 33 35
2.3.3. Исполнительная система (The Executive)
35
Д и с п е т ч е р Объектов
39
Система
41
2.3.3.3. Я д р о
42
Слой абстрагирования от оборудования 2.3.4. Система приоритетов
43 43
2.3.4.1. Уровни запросов прерываний (IRQL) П р и о р и т е т ы планирования
44 45
Д и н а м и ч е с к и е приоритеты и п р и о р и т е т ы реального времени
45
2.3.4.2.2. Базовый приоритет. Класс приоритета и относительный приоритет
46
2.3.4.3. Как используются I R Q L
47
2.3.4.3.1. I R Q L P A S S I V E J L E V E L , A P C _ L E V E L и Ограничения, налагаемые на код с уровнем IRQL б о л ь ш и м или р а в н ы м D I S P A T C H _ L E V E L 2.3.4.3.2. D I R Q L s
49 50 52
2.3.4.4. П р е р ы в а н и я и планирование
52
2.3.4.5. Определение текущего уровня I R Q L
53
2.3.5. Архитектура памяти
54
2.3.5.1. Организация памяти в з а щ и щ е н н о м р е ж и м е работы процессора
54
2.3.5.2. Организация системного адресного пространства
57
Т и п ы адресов в NT
58
2.3.5.4. С о в м е с т н о е использование памяти
58
2.3.5.5. Объект Секция
60
2.3.5.6. Таблица о п и с а н и я памяти ( M e m o r y Descriptor List,
60
2.3.5.7. Ф у н к ц и и р а б о т ы с п а м я т ь ю
61
В ы д е л е н и е памяти
62
2.3.5.7.2. Список заранее в ы д е л е н н ы х блоков п а м я т и (Lookaside List)
62
2.3.5.7.3. Пространства устройств
63
2.3.5.7.4. Управление п а м я т ь ю и M D L
и отображение памяти 63
2.3.6. Унифицированная модель драйвера
64
2.3.6.1. Объект-файл ( ф а й л о в ы й объект)
65
2.3.6.2. Объект-драйвер
65
2.3.6.3. Объект-устройство
66
2.3.6.3.1. И м я устройства и символическая связь 2.3.6.4. Взаимосвязь о с н о в н ы х объектов 2.4. У с т а н о в к а , у д а л е н и е , з а п у с к и о с т а н о в к а д р а й в е р а Структура драйвера
67 67 69 70
Точки входа драйвера
70
Контекст исполнения и уровень I R Q L
71
Ограничения, налагаемые на драйвер
72
Точка входа
72
2.4.1.3.1. О п р е д е л е н и е конфигурации аппаратного устройства
73
2.4.1.3.2. Создание объекта-устройства и символической связи
73
Передача д а н н ы х от приложения к драйверу. Асинхронная обработка
74
2.4.1.4.1. В ы п о л н е н и е асинхронного запроса 2.4.2. Пакет запроса
(IRP)
Х а р а к т е р и с т и к и подсистемы ввода/вывода 2.4.2.2. Структура пакета запроса ввода/вывода (IRP)
76 78 78 79
Поля в фиксированной части IRP
80
2.4.2.2.2. Поля в стеке размещения ввода/вывода I R P
81
2.4.2.3. О п и с а н и е буфера д а н н ы х
82
2.4.2.4. Коды функции
84
2.4.2.5. Д и с п е т ч е р с к и е точки входа драйвера
85
2.4.2.5.1. З а п р о с ы чтения и записи и IRP_MJ_WRITE 2.4.2.5.1.1. П р и м е р обработки запросов чтения/записи
86 87
2.4.2.5.2. З а п р о с ы и
87 Задание кода управления вводом/выводом
88
2.4.2.5.2.2. П о л у ч е н и е буфера
90
2.4.2.5.2.3. П р и м е р обработки
92
Многоуровневая модель драйверов 2.4.3.1. Реализация у р о в н е в ы х драйверов О б ъ е д и н е н и е драйверов в стек 2.4.3.1.2. Обработка запросов I R P стеком драйверов П о л у ч е н и е драйвером в ы ш е л е ж а щ е г о уровня уведомления о завершении обработки I R P драйвером н и ж е л е ж а щ е г о уровня 2.4.3.2. Реализация драйверов-фильтров П о д к л ю ч е н и е фильтра к устройству 2.4.4. С е р и а л и з а ц и я
93 93 94 95
97 98 98 100
Задержка обработки запросов I R P и п о с т а н о в к а запросов I R P в очередь 2.4.4.1.1. Системная очередь запросов I R P (System Queuing)
101
Обработка пакетов I R P в функции
103
2.4.4.1.2.
у п р а в л я е м ы е драйвером Ф у н к ц и и управления очередью низкого уровня
104 104
Ф у н к ц и и управления очередью высокого уровня
«Очередь Устройства» (Device Queue)
2.4.4.2. О т м е н а запросов О т м е н а I R P и Системная Очередь 2.4.4.2.2. О т м е н а I R P и очереди, управляемые драйвером 2.4.5. М е х а н и з м ы с и н х р о н и з а ц и и 2.4.5.1. С п и н - б л о к и р о в к и
106 106 107 108 109 109
2.4.5.1.1. Использование о б ы ч н ы х спин-блокировок 2.4.5.1.2. П р о б л е м а взаимоблокировок (deadlocks) 2.4.5.2. Д и с п е т ч е р с к и е объекты 2.4.5.2.1. О ж и д а н и е (захват) диспетчерских объектов 2.4.5.2.2. М ь ю т е к с ы ядра 2.4.5.2.3. С е м а ф о р ы 2.4.5.2.4. С о б ы т и я 2.4.5.2.5. Б ы с т р ы е м ь ю т е к с ы 2.4.5.3. Р е с у р с ы Исполнительной с и с т е м ы 2.4.5.4. О б о б щ е н н а я таблица механизмов с и н х р о н и з а ц и и 2.4.6. Рабочие потоки 2.4.6.1. Необходимость в создании рабочих потоков
116 116
2.4.6.2. С и с т е м н ы е рабочие потоки 2.4.6.3. Создание потоков драйвером Потоки как диспетчерские объекты Введение в обработку прерываний О б ъ е к т ы - прерывания
121 121 124 124
2.4.7.2. О т л о ж е н н ы й вызов процедуры (Deferred Procedure
DPC)
DPC-объекты
126 128
2.4.7.2.2. Активизация и обслуживание D P C
129
2.4.7.2.3. М н о г о ч и с л е н н ы е о б р а щ е н и я к D P C
130
2.4.7.2.4. D P C на многопроцессорных системах
130
Характеристики Объекта D P C
131
В а ж н о с т ь D P C ( D P C Importance) 2.4.7.2.5.2. Целевой процессор д л я D P C ( D P C Target Processor) 2.4.7.2.6.
3. Сетевая архитектура Windows NT
132 133
134
3.1. Соответствие сетевой архитектуры Windows NT модели
134
3.2. С е т е в ы е A P I
138
С т а н д а р т н ы й A P I ввода/вывода Win32 3.2.2. С е т е в ы е A P I Win32 (Wnet и Net)
138 138
API N e t B I O S (Network Basic Input/Output System) 3.2.4. Win32 A P I именованных каналов (named pipes) и почтовых ящиков (mailslots)
140
3.2.5. API W i n d o w s Sockets
142
3.2.6. Средство удаленного вызова процедур (Remote Procedure Call, R P C )
143
3.2.7. Средство динамического обмена д а н н ы м и (Network D y n a m i c Data Exchange, N e t D D E )
144
3.2.8. С е р в и с ы удаленного доступа (Remote Access Services, R A S )
146
3.2.9.
148
(Telephony Application Programming Interface)
3.2.10. И н т е р ф е й с D L C (Data Link Control)
150
3.2.11. Протокол S N M P (Simple Network M a n a g e m e n t Protocol)
151
3.2.12. D C O M
152
3.3. С е т е в ы е п р о г р а м м н ы е к о м п о н е н т ы О С W i n d o w s N T 3.3.1. С е т е в ы е сервисы
152
3.3.2. С е т е в ы е ф а й л о в ы е системы
153
Редиректор 3.3.2.2. Сетевой с е р в е р
153 154
3.3.3. Распределенная файловая система (Distributed File System, D F S )
154
3.3.4. М а р ш р у т и з а т о р многосетевого доступа
155
3.3.5. М н о г о с е т е в о й U N C
156
3.3.6. И н т е р ф е й с TDI и транспортные протоколы
157
3.3.6.1. И н т е р ф е й с TDI
157
3.3.6.2. Транспортные протоколы
158
3.3.6.3. Среда S T R E A M S
160
3.3.7. Среда N D I S и N D I S драйверы
161
3.3.7.1. Среда N D I S
161
3.3.7.2. Т и п ы N D I S драйверов
162
Д р а й в е р ы протоколов верхнего уровня 3.3.7.2.2. Д р а й в е р ы протоколов промежуточного уровня 3.3.7.2.2.1. П р о м е ж у т о ч н ы е драйверы ndistapi.sys и ndiswan.sys 3.3.7.2.3. Д р а й в е р ы сетевых карт 3.3.8. О б о б щ е н н а я схема сетевой архитектуры W i n d o w s NT
162 163 163 164 164
Связь между сетевыми программными компонентами
4. Анализ сетевой архитектуры ОС Windows NT с точки зрения возможностей реализации средств защиты и анализа сетевого трафика
169
Используемые средства построения объединенных сетей и их в л и я н и е на у р о в е н ь р а с п о л о ж е н и я с р е д с т в а з а щ и т ы (согласно модели 4.2. О б ъ е м и н ф о р м а ц и и , п р о х о д я щ е й ч е р е з с р е д с т в о з а щ и т ы
170
4.3. Возможности реализации средств защиты сетевой информации на п о л ь з о в а т е л ь с к о м у р о в н е
171
Реализация з а щ и т ы на уровне приложений и с о б с т в е н н ы х D L L
172
4.3.2. Реализация з а щ и т ы на уровне с и с т е м н ы х D L L , п р е д о с т а в л я ю щ и х п р и л о ж е н и я м р а з л и ч н ы е сетевые и н т е р ф е й с ы
173
4.3.3. Реализация защиты на уровне сетевых сервисов
176
4.3.4. Реализация з а щ и т ы на уровне «родного» API для ОС W i n d o w s NT
J 77
Возможности реализации средств защиты сетевой и н ф о р м а ц и и на уровне ядра Драйверы
фильтры
4.4.2. Реализация з а щ и т ы на уровне драйвера M U P
178 181
4.4.3. Реализация з а щ и т ы на уровне драйверов ф а й л о в ы х систем Реализация защиты на уровне транспортного драйвера 4.4.5. Реализация з а щ и т ы с п о м о щ ь ю перехвата функций N D I S - библиотеки 4.4.6. Реализация защиты на уровне сетевого драйвера промежуточного уровня, п о д д е р ж и в а ю щ е г о интерфейс N D I S 4.4.7. Реализация з а щ и т ы на уровне драйверов сетевых устройств Сравнительный анализ способов реализации защиты
189 190
4.5.1.1. З а в и с и м о с т ь способа реализации средства з а щ и т ы от предъявляемых к нему требований
192
4.6. О с о б е н н о с т и р е а л и з а ц и и
194
Синхронизация
195
4.6.2. Структура
196
4.6.3. З а в е р ш а ю щ и е функции
197
4.6.4. Точки входа промежуточного N D I S - драйвера
198
Точка входа
199
4.6.4.2. Точки входа ProtocolXxx
200
4.6.4.3. Точки входа
201
4.7. П р и м е р реализации драйвера шифрования сетевых пакетов
203
5. Общие вопросы обеспечения безопасности в операционной среде Windows NT/2000
210
Механизм идентификации и аутентификации в ОС Windows NT. Общее описание 5.2. О с н о в н ы е с в е д е н и я о п р о ц е с с е W i n l o g o n и его с о с т о я н и я х 5.3. П р о т о к о л в з а и м о д е й с т в и я п р о ц е с с а W i n l o g o n и библиотеки GINA
5.4. Л о к а л ь н а я а у т е н т и ф и к а ц и я п о л ь з о в а т е л я в W i n d o w s N T
225
5.5. С е т е в а я а у т е н т и ф и к а ц и я п о л ь з о в а т е л я в W i n d o w s N T
230
5.6. О с н о в н ы е п о д х о д ы к с о з д а н и ю и з о л и р о в а н н о й п р о г р а м м н о й среды
230
5.7. М а к е т с и с т е м ы з а щ и т ы о т н е с а н к ц и о н и р о в а н н о г о д о с т у п а 5.7.1. Д р а й в е р контроля доступа Перехват операций открытия, создания и удаления файлов 5.7.1.2. С о б с т в е н н ы й обработчик создания файла и обработчик открытия файла О п р е д е л е н и е и м е н и процесса В ы в о д с о о б щ е н и й на «синий» экран 5.7.1.5. П р о ц е д у р а распределения драйвера контроля доступа 5.7.2. М о д и ф и ц и р о в а н н а я библиотека Gina
Список источников информации
232 232 233 234 235 236 238
242
Предисловие Первая глава книги дает читателю общее представление о драйверах ядра опера ционной системы Windows NT/2000 и средствах их разработки. Вторая глава книги посвящена обзору общей архитектуры Windows NT/2000, здесь рассматриваются ключевые архитектурные особенности, основные понятия ОС, а же основы программирования драйверов ядра для нее. В третьей главе дано подробное описание сетевой архитектуры ОС Windows 2000, рассмотрено ее соответствие модели предоставляемые сетевые сы. Описаны сетевые программные компоненты, их назначения и взаимосвязи. Пред ставлена обобщенная схема сетевой архитектуры. Четвертая гаава посвящена анализу сетевой архитектуры ОС Windows NT/2000 с точ ки зрения предоставляемых возможностей для реализации и встраивания средств защиты сетевой информации на различных уровнях сетевой архитектуры, начиная от невых компонентов пользовательского режима и заканчивая низкоуровневыми компонен тами режима ядра. Представлен сравнительный анализ различных способов реализации защиты и приведены основные факторы, влияющие на выбор конкретного способа реали- I зации средства защиты. J Пятая глава посвящена общим вопросам обеспечения безопасности в ОС Windows NT/2000, в ней рассматриваются особенности штатной процедуры идентификации и аутентификации пользователей, а также вопросы синтеза системы защиты от несанк ционированного доступа. Цель книги - представить читателю основы программирования драйверов ядра для ОС Windows NT/2000 и ознакомить с возможными способами собственных реали заций средств защиты и анализа сетевого трафика.
Введение Операционная система NT и ее следующий представитель Windows 2000, благодаря своим современным принципам построения, защищенности, гибкости, а также встроенной сетевой поддержке и мощным сетевым возможностям, получила широкое распространение. Поэтому встает насущная проблема реализации систем за щиты, которые могли бы встраиваться в ОС Windows NT, расширяя ее возможности и обеспечивая функции защиты сетевой информации. Отметим сразу, что базовая архитектура ядра ОС Windows NT практически не из менилась при переходе к Windows 2000, поэтому почти все, что в этой книге верно как для ОС Windows NT, так и для Windows 2000. В книге помимо базовых основ написания драйверов, являющихся неотъемлемыми компонентами средств защиты информации, представлена общая и сетевая архитектура ОС Windows Описание необходимо для определения предоставляемых возможностей по реализации и встраиванию средств защиты сетевой информации, а так же для сравнения возможных способов реализации защиты и определения наиболее пред почтительных способов. Исследование ОС Windows NT позволяет опреде лить не только то, как и куда можно встроить средство защиты, но и то, как этому средству предоставить возможности со стороны операционной системы, поскольку от этого зависит решение конкретных задач по защите, которые оно сможет реализовать. Знание архитектуры ОС и драйверов применительно к сфере защиты информации необходимо с различных точек зрения: • С точки зрения средств защиты информации, в том числе и нестандартных (не упомянутых в документации к ОС). • С точки зрения преодоления средств защиты. Допустим, нам необходимо разработать средство защиты. Его типовая структура на данный момент выглядит примерно так: Контроль доступа Криптозащита данных
Система выявления нарушителя
локальные сетевые локальные сетевые
Реализация почти всех перечисленных элементов системы защиты для ОС NT воз можна только с применением драйверов: • Защита локальных данных - либо драйвер шифрующей файловой системы, либо драйвер-фильтр для прозрачного шифрования, либо перехват вызовов систем ных сервисов.
•
Защита сетевых данных - драйвер протокола, уровня, собственная сетевая служба, фильтр стандартной сетевой службы (та кие службы реализованы как файловые системы), перехват вызовов системных сервисов. • Выявление нарушителя для всех вышеприведенных вариантов анализ тий, регистрация в журнале, запрос на подтверждение подозрительных действий. Для специализированного оборудования должны быть разработаны драйверы для интеграции в эту схему. В зависимости от конкретной постановки задачи должен быть выбран наиболее подходящий способ ее решения. Одного ответа на все случаи жизни быть не может, так как два основных критерия выбора решения: • критерий максимальной простоты реализации; • критерий максимальной универсальности противоречат друг другу: чем более универсальным может быть вариант реализации, тем сложнее его реализация и наоборот. В качестве примера можно привести задачу реализации защиты сетевого Наиболее универсальным (с точки зрения полноты контролируемого трафика) и, на первый взгляд, простым в этом случае будет являться NDIS-драйвер промежуточного уровня. Однако, по мере усложнения системы и приближения ее к коммерческой лизации, будут возникать более серьезные проблемы. Например, такая система долж на знать форматы всех протоколов в системе, в том числе и тех, которые в данный момент даже не созданы. При выборе способа реализации системы жизненно важным может быть также вопрос документированности этого способа, при этом надо учитывать, что большая часть ОС NT не документирована. Драйверы ядра можно разбить на 2 больших класса: драйверы аппаратных уст ройств и драйверы виртуальных устройств: 1) архитектура NT исключает использование устройства, если для него нет драй вера. Прикладному ПО доступ к аппаратуре запрещен. Поэтому самым очевид ным примером драйверов является драйвер аппаратного устройства, предостав ляющий прикладным программам интерфейс доступа к устройству. В области защиты информации задача написания таких драйверов весьма актуальна. 2) Драйвер виртуального устройства не работает с каким либо специализирован ным аппаратным устройством, однако, предоставляет прикладным программам такие возможности по работе со стандартными ресурсами компьютера и ОС (процессор, память, порты, регистры, служебные структуры ОС), которые без драйвера были бы недоступны. Что конкретно можно сделать с помощью драйвера виртуального устройства? Как будет видно из следующих разделов, системная архитектура NT представляет собой набор модулей, связанных друг с другом стандартными, но далеко не всегда документированными интерфейсами. Благодаря этим интерфейсам можно произво дить как замену стандартных модулей на собственные, так и вставлять новые модули
в «разрыв» связей между старыми. Такое устройство ОС позволяет разрабатывать но вые модули (драйверы) для различных целей: • «прозрачная», то есть невидимая для прикладных программ, обработка данных, например, шифрование и компрессия данных на диске или в компьютерной сети; • расширение набора предоставляемых ОС сервисов; • «прозрачное» сканирование на наличие вирусов; • написание вирусов и закладок; • средства сбора статистики о событиях для различных компонентов системы. Следует иметь в виду, что помимо общих правил разработки и взаимодействия драйверов существуют специальные правила для особых типов драйверов. В качестве примера можно привести драйверы файловой системы FSD и сетевые драйверы ар хитектура NDIS и Необходимо остановиться на состоянии изученности данной области. Руководств по разработке драйверов для ОС Windows NT, на русском языке, а также информации в области ОС Windows NT гораздо меньше, чем для обыч ного программирования. В зарубежной литературе также очень мало книг, посвящен ных данной тематике. Что касается подмножества этой тематики сетевой ры, сетевых интерфейсов и сетевых драйверов, то соответствующей специализиро ванной литературы практически нет. Основным источником информации является документация Microsoft Windows NT Device Driver Kit Version 4.0 и документация Microsoft Platform Software Development Kit. Перечисленные источники дают информацию об общей и сетевой архитектуре, но никак не затрагивают вопросы реализации средств защиты сетевой информации. При чтении этой книги настоятельно рекомендуется иметь под рукой: • справочник Visual • MSDN library; • справочник по командам ассемблера.
1. Что такое драйвер Понять, что такое драйвер, мы попробуем на типовом примере взаимодействия прикладной программы с драйвером. Код прикладной программы исполняется в пользовательском режиме работы про цессора. В этом случае имеется ряд серьезных ограничений, связанных с доступом к памяти, аппаратным обеспечением и привилегированными инструкциями процессо ра. Когда возникает необходимость в преодолении этих ограничений, прикладная про грамма обращается к ядру ОС, код которого исполняется процессором в режиме ядра. Режим ядра лишен всех упомянутых ограничений. Для расширения функциональных возможностей ядра служат драйверы ядра mode drivers). Как они работают? В отличие от прикладной программы, драйвер не является процессом и не имеет потока исполнения. Вместо этого любая функция драйвера выполняется в контексте того потока и процесса, в котором она была вызвана. При этом вызов может происхо дить от прикладной программы или драйвера, либо возникать в результате прерыва ния. В первом случае контекст исполнения драйвера точно известен - это прикладная программа. В третьем случае контекст исполнения поскольку прерывание соответственно, исполнение кода драйвера) может произойти при выполнении лю бой прикладной программы. Во втором случае контекст исполнения может быть как известным, так и случайным - это зависит от контекста исполнения функции вызыва ющего драйвера. Под вызовом драйвера здесь подразумевается не обычный вызов функции, а пере дача так называемого запроса ввода/вывода. Различают несколько классов драйверов: • Драйвер, получающий запросы ввода/вывода из прикладной программы, назы вают драйвером высшего уровня. Если такой драйвер не пользуется услугами других драйверов, он называется монолитным. • получающий запросы от другого называют если он пользуется услугами других драйверов, или драйве ром низшего уровня, если он не пользуется услугами других драйверов.
1.1. Типы драйверов и характеристики В NT существует два типа драйверов: драйверы пользовательского режима и драй веры режима ядра. В дальнейшем, говоря «драйвер», мы будем подразумевать драйвер режима ядра. Такие драйверы являются частью исполнительной системы, а более точ но - элементами диспетчера ввода/вывода (архитектура NT и ее компоненты будут обсуждаться ниже). Как следует из названия, при работе драйвера режима ядра про цессор находится в режиме ядра (RING 0 - см. любой справочник по защищенному режиму работы процессора). Драйвер NT располагается в файле с расширением и имеет стандартный РЕформат (РЕ - Portable Executable).
Драйверы реализованы как самостоятельные модули с четко определенным ин терфейсом взаимодействия с ОС. Все драйверы имеют определенный системой набор стандартных функций драйвера (standard driver routines) и некоторое число них функций, определенных разработчиком. Драйверы режима ядра можно разбить на три типа: • драйверы высшего уровня (highest drivers); • драйверы промежуточного уровня (intermediate drivers); • драйверы низшего уровня (lowest level drivers). Как будет показано ниже, такое разбиение обусловлено многоуровневой моделью драйверов (layered model). Для сохранения общности монолитный драйвер можно включить в эту схему, хотя он не использует многоуровневую архитек туру. В этом случае он будет «гибридом» - драйвером, принадлежащим одновременно к нескольким типам. Например, монолитный драйвер, имеющий интерфейс с прило жением и осуществляющий доступ к оборудованию, будет одновременно драйвером высшего и низшего уровня. Кроме того, в зависимости от назначения драйвера, он может являться каким-либо специализированным драйвером, то есть удовлетворять дополнительному набору тре бований к своей структуре. Можно привести следующие типы специализированных драйверов: • драйверы файловой системы; • сетевые драйверы. Отдельно необходимо упомянуть архитектуру WDM - Windows Driver Model. Эта архитектура позволяет создавать драйверы для Windows 98 и Windows 2000, тимые на уровне двоичного кода. Характеристики драйверов - это совокупность следующих вопросов: 1) Поддержка динамической загрузки и выгрузки (однако могут быть исключения). 2) Необходимость следовать определенным протоколам взаимодействия с систе мой, нарушение которых чаще всего ведет к «синему экрану» (Blue Screen Of Death, BSOD). 3) Возможность «наслоения» драйверов поверх друг друга. В Win2000 эта воз можность возведена в абсолют, хотя монолитные драйвера все еще поддержи ваются. 4) Поскольку драйвера являются частью ядра ОС, они могут сделать с системой все, что угодно, поэтому основная проблема - это закрытость архитектуры ОС.
1.2. Среда разработки В этом разделе мы рассмотрим, какое программное обеспечение необходимо для разработки и отладки драйверов, а также его установку и настройку. Необходимое ПО: операционная система Windows NT или Windows 2000, Service Pack и отладоч ная информация;
2) компилятор; 3) SDK; 4) DDK; 5) средства отладки и вспомогательные средства. Операционная система имеет два варианта поставки: • Checked build (Debug build); • Free build (Retail build). Free build - обычная поставка. Включена полная оптимизация, отсутствуют спе циализированные отладочные возможности. Checked build - специальная поставка для использования разработчиками драйве ров. Оптимизации почти нет, что способствует лучшему пониманию кода при работе под отладчиком. Специализированный отладочный код встроен во многие функции для проверки правильности параметров и перехвата ошибочных ситуаций. Поставля ется только в составе подписки MSDN. В комплекте с ОС нам понадобится отладочная информация (файлы с расширени ем и Она содержит сопоставление адресов внутри конкретного исполняе мого файла с символическими именами функций и переменных и может быть исполь зована отладчиками. Необходимо подчеркнуть, что символьная информация различна для checked и free версий системы. После установки ОС необходимо установить последнюю версию SR Надо помнить, что SP заменяет почти все системные файлы, и поэтому для них необходима новая символьная информация. Для checked и free версий системы требуются отдельные версии Кроме того, ОС и SP могут различаться по поддержке криптоалгоритмов бит), что может влиять на возможность установки Компилятор. Хотя принципиально могут использоваться компиляторы различных производителей, структура заголовочных файлов и переменных окружения, поставля емых Microsoft для создания драйверов оптимизирована для использования компиля тора Microsoft Visual С. Версия компилятора должна быть не ниже однако реально необходимая версия будет зависеть от двух других компонентов - SDK и DDK. MSDN Library. При установке Developer Studio запрашивается установка MSDN Library. Этот продукт предоставляет информацию о разработке ПО на всех поддержи ваемых платформах Microsoft. SDK. В ранних версиях комплект назывался Win32 SDK, сейчас Platform SDK. Это необязательный, но желательный для разработки драйверов компонент. Содержит заголовочные файлы, lib-файлы, документацию и примеры программирования на пользовательском уровне с использованием подсистемы Win32. DDK. Существуют D D K для Windows 95, Windows 98, Windows NT и Windows 2000. DDK должен соответствовать платформе, для которой предполагается создание драйвера, что не обязательно для той, на которой производится создание. Мы будем пользоваться DDK для Windows NT 4.0. DDK содержит заголовочные файлы, доку-
ментацию и примеры написания драйверов, за исключением драйверов файловой сис темы. Kit. Пакет для создания драйверов файловой системы. Поставляется как от дельный от подписки MSDN продукт. Существуют версии для Windows 98, Windows NT 4.0 и Windows 2000. Последние версии включают в себя DDK, но с другим набо ром примеров. Более ранние версии требовали предварительной установки DDK. Между перечисленным набором компонентов имеется взаимосвязь. Первым ставится компилятор. Как уже говорилось, хотя существует возможность использования компиляторов других фирм, SDK и D D K предполагают наличие имен но Visual С, причем в зависимости от времени выхода SDK и подразумеваются различные версии компилятора (при линковке будут указаны библиотеки различных версий этим грешит SDK, либо будут некорректно запускаться командные файлы инициализации переменных окружения - этим грешит DDK). Кроме того, ранние вер сии D D K требовали обязательного наличия установленного SDK. Из возможных про блем стоит указать и то, что при использовании ОС Windows NT Workstation могут не устанавливаться системные переменные окружения. Расположение командных файлов для установки переменных окружения: • VC98\bin\vcvars32.bat; • Mstools\setenv.bat; • Ddk\bin\setenv.bat. При наличии версии DDK, требующей наличия SDK, из файла должны быть исключены строки проверки наличия SDK и запуска файла setenv.bat, а также прописан вызов vcvars32.bat. Средства отладки и вспомогательные средства. Выбор средства отладки - важный момент, который может влиять на набор необ ходимых аппаратных средств. Вместе с продуктами Microsoft поставляются четыре отладчика: 1) KD - консольная программа для отладки драйверов режима ядра, находится в директории bin пакета D D K для N T 4 и Win2000. (i386kd.exe, ia64kd.exe, aiphakd.exe, 2) консольная программа для отладки программ и драйверов пользова тельского режима, находится в директории system32 ОС Windows 2000. 3) CDB - вариант NTSD, содержится в директории bin пакета D D K для Windows 2000. 4) WinDbg - графический отладчик для отладки кода как пользовательского режи ма, так и режима ядра, содержится в директории bin пакета D D K для Win2000 и Platform SDK. Из всех перечисленных вариантов упоминания достоин лишь отладчик WinDbg. Он предоставляет удобный пользовательский интерфейс, однако очень неустойчив в работе, плохо документирован и не имеет поддержки от Microsoft. При использования этого продукта для отладки драйверов необходимы два компьютера - Development Platform и Test Platform. Отладчик доступен для всех поддерживаемых платформ, при
этом возможна отладка. Поддерживается работа на мультипро цессорных системах. Лучшим отладчиком для отладки ОС и драйверов многие разработчики с полным основанием считают фирмы стабильный при работе, хорошо доку ментированный, с поддержкой от фирмы. Отладка осуществляется на том же компью тере, на котором проводилась разработка, однако возможна и удаленная отладка по средством dos-программы Недостатком можно считать пользовательский однако - это дело привычки. Более серьезными недостатками является ог раничение поддержки процессоров только платформой intei, а также отсутствие под держки мультипроцессорных систем (однако система все еще активно развивается). Ниже перечислены средства, которые могут выступать к качестве «наглядного посо бия» при написании драйверов (часть этих средств снабжена исходными текстами): 1) Monitor просмотр трассировочной информации, выводимой драйверами и прикладными программами; 2) Winobj - просмотр пространства имен диспетчера объектов; 3) Handleex - информация о запущенных процессах, всех открытых ими описате лях и подгруженных модулях 4) - просмотр активности файловых систем; 5) - отслеживание обращений к реестру, в том числе на этапе загрузки системы; 6) - отслеживание обращений к последовательным и параллельным пор там; 7) отслеживание запросов TDI; 8) — отслеживание работы системы безопасности.
1.3. Утилита BUILD Для построения драйверов и связанных с ними прикладных программ использует ся утилита BUILD, входящая в состав DDK. Эта утилита позволяет создавать любой тип исполняемого файла, поддерживаемый NT с использованием командной строки. Стандартного (и поддерживаемого Microsoft) способа использования ной Среды Разработки для написания драйвера не существует. (Варианты, иллюстри рующие то, как это можно сделать, мы рассмотрим в следующем разделе.) Возможны два варианта построения драйвера: • Checked - эквивалент Debug build в интегрированной среде; • Free build, также называемый Retail build - эквивалент Release build в интегри рованной среде. Для осуществления конкретного построения необходимо запустить файл setenv.bat с соответствующими параметрами. С целью автоматизации этого процесса при уста новке DDK создаются ярлыки «Checked Build и «Free Build Запуск любого из них вызывает командную оболочку, из которой вызы вать команду build.
Для успешной работы команды build в текущей директории должны находиться два специальных файла: SOURCES и DIRS. Файл SOURCES является аналогом понятия проект. В нем определяется имя со здаваемого модуля, его тип, корневая директория, где будет размещен результат, путь поиска список подключаемых библиотек и список файлов с исходным текстом. Файл DIRS определяет список поддиректорий, которые должны быть обработаны командой прежде чем перейти к текущей директории. Директория, в которой запускается команда build, может содержать либо файл SOURCES, либо файл DIRS, либо оба вместе. Отсутствие файлов является ошибкой. Директория, в которой содержится только файл DIRS, не будет обработана коман дой build, но будут обработаны все поддиректории из файла DIRS. Директория, в которой содержится только файл SOURCES, будет обработана ко мандой build, но ни одна поддиректория обработана не будет. Директория, в которой содержатся оба файла, будет обработана командой build только после обработки всех поддиректорий из файла DIRS. Примеры файлов можно посмотреть в DDK, а полное описание утилиты build и структуры файлов - в документации к Kit (В принципе, можно воспользоваться документацией к DDK, однако там описание менее понятно.) Теперь рассмотрим некоторые отличия checked и free build. Как ясно из названия, это отладочный и окончательный варианты драйвера. Соответственно, они отличают ся режимами оптимизации кода. Для режима checked создается отладочная информа ция в формате dbg, которую можно использовать для отладки в символьном режиме с помощью В режиме checked на этапе компиляции определено имя DBG, кото рое может быть использовано директивами # i f DBG #else #endif для выполнения действий, специфичных для checked или free версий драйвера. Ти пичным примером является обрамление таким способом функции которая выводит трассировочную информацию. Функция работает и в checked, и в free build, однако с помощью такой проверки ее можно исключить из free build. Хорошо известная техника Microsoft по написанию кода - использование макроса ASSERTQ. Этот макрос проверяет условие. Если оно ложно, генерируется прерыва ние. При этом отладчику сообщается имя файла с исходным текстом и номер строки с макросом. Макрос работает только в checked-версии драйвера, установленного на checked-версии ОС. Во всех остальных случаях ничего не происходит. При создании серьезного продукта цикл разработки драйвера выгладит примерно так: 1) написание кода;
2) проверка и отладка отладочной версии драйвера на отладочной версии ОС; 3) проверка и отладка отладочной версии драйвера на рабочей версии ОС; 4) проверка рабочей версии драйвера на рабочей версии ОС; 5) проверка работы на многопроцессорной системе. Особо обратите внимание на последний пункт. Нет никакого другого способа убе диться в корректной работе драйвера на многопроцессорной системе, кроме его тести рования на такой системе, даже при условии корректной работы драйвера на однопро цессорной системе.
1.4. С и С++. Интегрированная среда разработки Необходимо особо отметить, что драйверы предполагается писать на С, а не на Microsoft не поддерживает использование С++ для компонентов ядра. Для этого имеется ряд причин: • отсутствие библиотеки времени исполнения (runtime library), а, следовательно, и определяемых в ней глобальных операторов new и • отсутствие поддержки ситуаций С++; нет поддержки инициализации глобальных экземпляров классов. В принципе, все эти проблемы разрешимы. Не будем останавливаться на описа нии конкретных способов. Об этом вы можете узнать в статье «С++ Runtime Support for the NT DDK», а также из анализа заголовочных файлов в продукте DriverWorks (в особенности файла vdw.h). Как было сказано выше, интегрированная среда Developer Studio не имеет поддер жки для создания драйверов. Драйверы компилируются из командной строки с ис пользованием утилиты BUILD, поставляемой в составе DDK. Реализовать поддержку драйверов из интегрированной среды можно несколькими способами: • реализацией собственного AppWizard (см. AppWizard Programming Reference); • созданием проекта на основе make-файла с вызовом собственного командного файла. Этот файл должен: • произвести настройку переменных окружения с помощью вызова setenv.bat из DDK; • перейти в директорию с исходным текстом и вызвать утилиту build (см. также статью BUILD and Developer Studio» в директории NT Insider). Реализация собственного AppWizard довольно непростая задача, однако, можно воспользоваться готовым из DriverWorks. Последовательность действий такая: выбе рите меню Developer Studio В появившемся окне на закладке Projects выберите NT/WDM (DriverWorks). В появившемся окне Мастера укажите тип драйвера NT и следуйте инструкциям, внося минимальные изменения. По завершении работы мастера удалите все созданные им срр- и h-файлы, и вставьте собственные с- и h-файлы.
2. Общая архитектура Windows NT В этой главе рассматриваются ключевые архитектурные особенности и характе ристики ОС Windows Эти сведения необходимы для получения представления о назначении различных компонентов ОС, их взаимодействии друг с другом, а также для ознакомления с терминологией, используемой в данной Кроме того, знание обшей архитектуры ОС позволяет понять, какие возможности операционной системы могут задействовать средства защиты, расположенные на том или ином уровне архи тектуры Большое внимание в этой главе уделено модели драйвера, его и характе ристикам, а также взаимосвязи с другими драйверами и прикладными программами.
2.1. Понятия «пользовательский режим» и «режим ядра» При обсуждении архитектуры ОС Windows NT постоянно используются понятия «режим пользователя» и «режим ядра», поэтому стоит определить, что это значит. Начнем с обсуждения разницы между пользовательским режимом и режимом ядра (user mode/kernel mode). Пользовательский режим наименее привилегированный режим, поддерживае мый NT; он не имеет прямого доступа к оборудованию и у него ограниченный доступ к памяти. Режим ядра - привилегированный режим. Те части NT, которые исполняются в режиме ядра, такие как драйверы устройств и подсистемы типа Диспетчера Виртуаль ной Памяти, имеют прямой доступ ко всей и памяти. Различия в работе программ пользовательского режима и режима ядра поддержи ваются аппаратными средствами компьютера (а именно процессором). Большинство архитектур обеспечивают, по крайней мере, два аппа ратных уровня привилегий. Аппаратный уровень привилегий процессора определяет возможное множество инструкций, которые может вызывать исполняемый в данный момент процессором код. Хотя понятия «режим пользователя» и «режим ядра» часто используются для описания кода, на самом деле это уровни привилегий, ассоцииро ванные с процессором. Уровень привилегий накладывает три типа ограничений: 1) возможность выполнения привилегированных команд, 2) запрет обращения к данным с более высоким уровнем привилегий, 3) запрет передачи управления коду с уровнем привилегий, не равным уровню привилегий вызывающего кода.
2.1.1. Некоторые понятия защищенного режима Защищенный режим является основным и наиболее естественным режимом рабо ты 32-разрядных процессоров. Этот режим был в полной м е р е реализован в процессо рах серии и с тех пор существенных изменений не претерпел.
Защищенный режим 32-разрядных процессоров реализует поддержку следующих механизмов: • Организация памяти, при которой используются два механизма преобразова ния памяти: сегментация и разбиение на страницы. • Четырехуровневая система защиты пространства памяти и • Переключение задач. Сегмент - это блок пространства памяти определенного назначения, внутри кото рого применяется линейная адресация. Максимальный размер сегмента при рядной адресации составляет 4 Гб байт). Максимальное число таких сегментов равно (8192). Сегмент может иметь произвольную длину в допустимых границах. Каждый сегмент характеризуется 8-байтной структурой данных - дескриптором сегмента, в котором, в числе прочего, указаны: • Права доступа, которые определяют возможность чтения, записи и исполнения сегмента. • Уровень привилегий (относится к четырехуровневой системе защиты). На сегментации основана защита памяти. При этом не допускается: • использовать сегменты не по назначению (нарушение прав доступа); • обращаться к сегменту, не имея достаточных привилегий; • адресоваться к элементам, выходящим за границы сегмента. Страничная организация памяти позволяет использовать большее пространство па мяти. При этом базовым памяти служит блок фиксированного размера 4 Кб. Физический адрес памяти, получаемый на выходе сегментного и страничного пре образования памяти, является 32-разрядным, позволяя адресовать, таким образом, до 4 Гб реально доступной физической памяти. Четырехуровневая система привилегий предназначена для управления использо ванием привилегированных инструкций, а также для защиты пространства памяти и ввода/вывода. Уровни привилегий нумеруются от 0 до 3, нулевой уровень соответствует макси мальным (неограниченным) возможностям доступа и для ядра ОС. Уровень 3 имеет самые ограниченные права и обычно предоставляется прикладным задачам. Систему защиты обычно изображают в виде колец, соответствующих уровням привилегий, а сами уровни привилегий иногда называют кольцами защиты. В зависимости от уровня привилегий осуществляется защита по доступу к приви легированным командам, по доступу к данным с более высоким уровнем привилегий и по передаче управления коду с уровнем привилегий, отличным от текущего. Защищенный режим предоставляет средства переключения задач. Состояние каж дой задачи (значения всех связанных с ней регистров процессора) может быть сохра нено в специальном сегменте состояния задачи. Там же хранится карта разрешения ввода/вывода, указывающая для каждого из 64К адресов портов ввода/вывода возмож ность обращения к нему. ОС NT использует два кольца защиты - 0 и 3, имея соответственно режим работы в 0 кольце - kernel mode, в 3 кольце - user mode.
2.2. Основные характеристики Windows NT ОС NT характеризуется поддержкой механизмов: 1) модель модифицированного микроядра; 2) эмуляция нескольких ОС; 3) независимость от архитектуры процессора; 4) объектная модель; 5) многопоточность; 6) вытесняющая многозадачность; 7) виртуальная память с подкачкой страниц по требованию; 8) мультипроцессорная обработка; 9) интегрированная поддержка сети.
2.2.1. Модель модифицированного микроядра На NT иногда ссылаются как на операционную систему на основе микроядра (microkernel-based operating system). лежащая в основе концепции микроядра, состоит в том, что все компоненты ОС за исключением небольшой основы (собствен но, микроядра) исполняются как процессы пользовательского режима. Базовые ком поненты в микроядре исполняются в привилегированном режиме. Архитектура микроядра обеспечивает в системе возможность конфигурации и ус тойчивость к ошибкам. Поскольку подсистемы ОС типа Диспетчера Памяти исполняются как отдельные программы в архитектуре микроядра, их можно заменить различными реализациями, экспортирующими такой же интерфейс. Если в Диспетчере Памяти происходит ошибка, то, благодаря устойчивости к ошибкам в дизайне микроядра, операционная система может перезапустить его с ми нимальным воздействием на остальную систему. Недостаток чистой архитектуры микроядра - низкая производительность. Любое взаимодействие между компонентами ОС при такой схеме нуждается в межпроцесс ном сообщении с длительными переключениями между задачами. NT использует уникальный подход, известный как модифицированное микрояд ро. Он является промежуточным между чистым микроядром и монолитной структу рой. При этом подходе в пользовательском режиме работают прикладные программы и набор подсистем, относящихся к одному из двух классов подсистемы окружения и неотъемлемые подсистемы. Подсистемы и прикладные программы реализованы как процессы, однако способ создания подсистем и интеграции их с ОС не документиро ван. Подсистемы окружения предоставляют прикладным программам интерфейс про граммирования, специфичный для некоторых ОС (WIN32, OS/2, DOS). Неотъемлемые подсистемы исполняют важные функции ОС. Среди таких подсис тем подсистема безопасности, служба рабочей станции и служба сервера.
При выполнения задач, которые не могут быть выполнены в пользовательском режиме, все подсистемы ОС NT полагаются на системные сервисы, экспортируемые режимом ядра. Эти сервисы известны как «родной» API. Такой API состоит примерно из 250 функций, доступных через модуль Прикладная программа использует интерфейс программирования, предоставляе мый какой-либо одной подсистемой окружения, либо использует напрямую собствен ный интерфейс программирования. В режиме ядра работает Исполнительная система NT (NT Executive). Она, сама по себе, является законченной ОС со своим интерфейсом программирования, как для пользовательского режима, так и для режима ядра. Исполнительная система состоит из набора подсистем, Микроядра и Слоя рагирования от Оборудования (HAL). Подсистемы системы и Мик роядро находятся в едином модуле Слой Абстрагирования от Оборудо вания находится в модуле hal.dll. Все загруженные системой драйверы также являются частью исполнительной системы. Каждый компонент исполнительной системы экспортирует набор функций для использования другими компонентами. Кроме того, каждый компонент исполнитель ной системы, за исключением диспетчера Кэша и Слоя Абстрагирования от Обору реализует набор системных сервисов.
2.2.2. Эмуляция нескольких ОС Подсистемы окружения операционной системы NT реализованы как системы типа клиент/сервер. Как часть процесса компиляции, прикладные программы прикрепля ются на этапе компоновки к API операционной системы, который экспортируют под системы окружения ОС NT Связывание на этапе компоновки подключает ную программу к клиентским DLLs подсистем окружения, которые осуществляют эк спорт API. Например, Win32 программа - это клиент подсистемы окружения Win32, поэтому она связана с клиентскими DLL Win32, включая и user32.dll. Программа связана с клиентской DLL Клиентские DLL выполняют задачи от имени их серверов, но они выполняются, как часть клиентского процесса. Как показано рис. 1, в некоторых случаях пользова тельская DLL может полностью реализовывать API без необходимости обращения к помощи сервера; в других случаях сервер должен помочь. Помощь сервера обычно необходима только когда должна быть модифицирована общая информация, связан ная с подсистемой окружения. Когда пользовательская DLL требует помощи от серве ра, DLL посылает сообщение, известное, как вызов локальной процедуры (LPC) на сервер. Когда сервер завершает указанный запрос и возвращает ответ, DLL может за вершить функцию и возвратить управление клиенту. И пользовательская DLL и сер вер могут использовать «родной» API, когда это необходимо. API подсистем окруже ния дополняют «родной» API специфическими функциональными возможностями или семантикой.
26
2.2.3. Независимость от архитектуры процессора Микроядро и Слой от Оборудования (HAL) изолируют подсис темы Исполнительной Системы от конкретной архитектуры процессора. Другой аспект независимости от архитектуры состоит в том, что правильно напи санный драйвер (общающийся с внешним миром только посредством функций, пре доставляемых различными компонентами исполнительной системы) переносим меж ду всеми поддерживаемыми NT платформами на уровне исходных текстов. Микроядро OS Windows NT обеспечивает единый интерфейс для использования ресурсов, общих для определенной аппаратной платформы, на которой может рабо тать OS. Например, микроядро обеспечивает интерфейсы к обработке и управлению прерываниями, сохранению и восстановлению контекста потоков и мультипроцессор ной синхронизации. HAL обеспечивает поддержку и отвечает за предоставление стандартного интер фейса к ресурсам процессора, которые могут меняться в зависимости от модели ри одного семейства процессоров. Возможность замены слоя HAL обеспечивает всем вышележащим слоям операционной системы независимость от аппаратной архитек туры.
Объектная модель В исполнительной системе объект (object) это отдельный образец статически определенного типа объектов, существующий во время выполнения. Тип объектов, иногда называемый классом включает определенный системой тип данных, объектные сервисы, работающие с образцами этого типа, и набор атрибутов объекта.
Атрибут объекта - это поле данных внутри объекта, частично определяющее его стояние. Объектные сервисы способы манипулирования объектами обычно вают или изменяют атрибуты объектов. Windows NT использует объекты для унификации представления и управления системными ресурсами. Каждый системный ресурс, который могут совместно исполь зовать несколько процессов, такой, как файл, память или физическое устройство, реа лизован как объект и обрабатывается объектными сервисами. Доступ ОС к ресурсам и работа с ними унифицированы. Создание, удаление и ссылка на объект осуществляет ся с использованием описателей (handle) объектов. Контроль использования ресурсов сводится к отслеживанию создания и использования объектов. Для всех объектов кон троль доступа к ним осуществляется одинаково с помощью подсистемы защиты. Два процесса совместно используют объект тогда, когда каждый из них открыл его описа тель. ОС может отслеживать количество описателей, открытых для данного объекта, чтобы определить, действительно ли они все еще используется, и может удалить объек ты, которые более не используются.
2.2.5. Многопоточность Каждая исполняющаяся в NT программа представляется как процесс. Процесс (process) - это программа (статическая последовательность команд и дан ные) и системные ресурсы, необходимые для ее работы. ОС предоставляет каждому процессу адресное пространство, выделенное для программы, и гарантирует, что про грамма каждого процесса будет направляться на выполнение в определенном порядке и в нужное время. Чтобы процесс смог заработать, он должен включать, по крайней мере, один поток исполнения (thread of execution). Поток (thread) - единица исполнения в NT. Поток это сущность внутри процес са, которую ядро направляет на исполнение, он может только одному процессу. Поток состоит из указателя текущей команды, пользовательского стека, сте ка ядра и набора значений регистров. Все потоки процесса имеют одинаковый доступ к его адресному пространству, описателям объектов и другим ресурсам. Потоки реа лизованы как объекты-потоки. Начальный поток возникает при создании процесса, и затем он может создать до полнительные Каждый поток имеет свой собственный приоритет, в соответствии с которым, ОС будет принимать решение о его запуске. При этом принадлежность потока к конкрет ному процессу не учитывается.
2.2.6. Вытесняющая многозадачность (preemptive multitasking) NT позволяет нескольким единицам исполнения - потокам - выполняться одно временно, быстро переключаясь между ними. Такое поведение называется многоза дачностью (multitasking).
Каждому потоку на исполнение выделяется квант времени По истече нии этого времени операционная система насильственно отдаст время процессора дру гому потоку (говорят, что поток будет вытеснен). Такое поведение называется вытес няющей многозадачностью (в отличие от невытесняющей многозадачности, когда по ток сам должен освободить процессор). Необходимо определить еще два термина: диспетчеризация и планирование. Диспетчеризация (dispatching) - механизм переключения с одного потока испол нения на другой. Планирование (sheduling) - механизм определения потока, который должен вы полняться следующим на текущем процессоре. Таким образом, по истечении кванта времени некоторого потока на о с н о в е механизма планирования осуществляется выбор следующего потока для испол нения, а на основе механизма диспетчеризации происходит переключение на этот Каждый поток имеет также приоритет, называемый приоритетом планирования, что подчеркивает его важность при выборе для исполнения очередного потока.
Виртуальная память с подкачкой страниц по требованию Виртуальное адресное пространство (virtual address space) процесса - это набор адресов, которые могут использовать потоки процесса, оно равно четырем гигабайтам байт), два из которых предназначены для использования программой, а другие два зарезервированы для ОС. Во время выполнения потока диспетчер памяти при помощи аппаратных средств транслирует (отображает) виртуальные адреса в физические, по которым данные хранятся на самом деле. Посредством контроля над процессом отображения ОС может гарантировать, что процессы не будут пересекаться друг с другом и не по вредят Когда физической памяти не хватает, диспетчер памяти выгружает часть содержи мого памяти на диск. При обращении потока по виртуальному адресу, соответствую щему переписанным на диск данным, диспетчер памяти снова загружает эти данные с диска память. В Windows NT код ОС располагается в верхней части виртуального адресного про странства, а пользовательский код и данные - в нижней. Можно выгружать всю пользо вательскую память. Код пользовательского режима не может производить запись и чтение системной памяти. Часть системной памяти, называемая н е в ы г р у ж а е м ы м (резидентным) пулом (nonpaged pool), никогда не выгружается на диск и используется для хранения некото рых объектов и других важных структур данных. Другая часть системной памяти, ко торая может быть выгружена на диск, называется выгружаемым (нерезидентным) пу лом (paged pool).
Симметричная мультипроцессорная обработка NT поддерживает только архитектуру с симметричной мультипроцессорной обра боткой - SMP. Системы с симметричной мультипроцессорной обработкой позволяют коду опе рационной системы выполняться на любом свободном процессоре или на всех про цессорах одновременно, причем каждому из процессоров доступна вся память. Чтобы гарантировать правильную работу системы, код таких ОС должен следовать строгим правилам. Windows NT обладает свойствами, которые принципиально важны для муль типроцессорной ОС: • Код ОС может выполняться на любом из доступных процессоров и на несколь ких процессорах одновременно. За исключением кода ядра, которое выполняет планировку потоков и обработку прерываний, весь код ОС может быть вытес нен потоком с более высоким приоритетом. • В одном процессе может быть несколько потоков управления. Потоки позволя ют процессу выполнять разные части его программы на нескольких процессо рах одновременно. • Серверные процессы могут иметь несколько потоков для одновременной обра ботки запросов от нескольких клиентов. Имеются механизмы совместного использования объектов потоками разных про цессов, и гибкие возможности коммуникации между потоками разных процессов, вклю чая совместно используемую память и оптимизированное средство передачи сообще ний.
2.2.9. Интегрированная поддержка сети Windows NT разработана со встроенной сетевой поддержкой и включает широкую поддержку сети, интегрированную с системой и интерфейсом Win32 API. Четырьмя основными типами сетевого программного обеспечения являются сете вые сервисы, сетевые API, протоколы и драйверы сетевых карт, располагающиеся друг под другом, формируя сетевой стек. Windows NT предоставляет хорошо определенные интерфейсы для каждого слоя в стеке, чтобы в дополнение к поставляющемуся с Windows NT множеству различных сетевых интерфейсов API, протоколов и драйверов сетевых карт, пользователи могли расширять сетевые возможности ОС путем разработки собственного сетевого про граммного обеспечения.
Структура Windows NT Всю операционную систему Windows NT можно разделить на следующие части (см. рис. 2): 1) защищенные подсистемы (protected subsystems), работающие в пользовательс ком режиме, тогда как остальная часть ОС исполняется в режиме ядра;
2) исполнительная система (executive); 3) ядро (kernel); 4) слой абстрагирования от оборудования (Hardware Abstraction Layer, HAL).
Защищенные подсистемы Серверы Windows NT называются защищенными подсистемами, так как каждый из них - это отдельный процесс, память которого защищена от других процессов системой виртуальной памяти исполнительной системы NT. Каждая защищенная система обеспечивает интерфейс прикладным программам (API) посредством DLLs клиентской стороны. Когда приложение или другой сервер вызывает некоторую про цедуру API, соответствующая DLL упаковывает параметры функции API в сообще ние и с помощью средства локального вызова процедур (Local Procedure Call, LPC) посылает его серверу, реализующему данную процедуру. Сервер же, выполнив вы зов, посылает ответное сообщение вызывающей программе. Передача сообщений остается невидимой для прикладного программиста. Используя такую процедуру, вызывающая программа никогда не получает прямого доступа к адресному простран ству подсистемы. Надо отметить, что далеко не все функции API реализуются сервером, например, большая часть функций API Win32 оптимизирована в DLL клиентской стороны, и в действительности не обращается к подсистеме Win32. Защищенные подсистемы подразделяются на подсистемы среды (environment subsystems) и неотъемлемые подсистемы (integral subsystems).
Подсистемы среды Подсистема среды это сервер пользовательского режима, реализующий API не которой О С . Самая важная подсистема среды в Windows NT - это подсистема среды Win32 (рассматриваемая ниже), которая предоставляет прикладным программам ин терфейс API 32-разрядной Windows. В Windows NT также имеются подсистемы сре ды: POSIX, OS/2 и виртуальная DOS машина (virtual DOS machine, VDM), эмулирую щая 16-разрядную Windows и MS-DOS. Д а н н ы е подсистемы предоставляют свои API, но используют для получения пользовательского ввода и отображения результатов подсистему Win32, то есть п е р е н а п р а в л я ю т в и д е о в ы в о д своих п р и л о ж е н и й подсистеме Win32 для отобра жения. Говоря о подсистемах окружения, необходимо отметить также следующее. Каж дая прикладная программа (и даже более того - каждый модуль, будь то sys или что-то другое) может относиться только к какой-то одной подсистеме окружения, либо не относиться ни к одной из них. Эта информация прописывается в любом ис полняемом модуле на этапе его компиляции и может быть получена через утилиту «Быстрый просмотр» (Quick View) в пункте «Subsystem» (варианты: The image does not require subsystem, Win32 GUI, Win32 Console,
Подсистема среды Win32 Подсистема среды Win32 делится на серверный процесс (csrss.exe Client/Server Runtime и клиентские DLLs (user32.dll, gdi32.dll, kernel32.dll), которые связаны с Win32 API. API разделен на три кате гории: • Управление окнами (windowing) и передача сообщений (messaging). Эти интер фейсы оконных процедур и процедур сообщений включают, например, такие функции, как CreateWindowQ, SendMessage(), и предоставляются прикладной программе через библиотеку user32.dll. • Рисование (drawing). Например, функции BitBit() и LineTo() являются Win32функциями рисования и предоставляются библиотекой gdi32.dll. • Базовые сервисы. Базовые сервисы включают весь ввод/вывод Win32, управле ние процессами и потоками, управление памятью, синхронизацию и предостав ляются библиотекой Когда вызывает функцию API Win32, управление передается одной из клиентских DLLs подсистемы Win32. Эта DLL может: • Выполнить функцию самостоятельно без обращения к системным сервисам ОС и вернуть управление вызывающей программе. • Послать сообщение для обработки запроса в том случае, если сервер должен участвовать в выполнении заданной функции. Так, функция экспортируемая библиотекой требует взаимодей ствия с который, в свою очередь, вызывает функции «родно го» API. • Вовлечь «родной» интерфейс API для выполнения заданной функции. После дний вариант встречается наиболее часто. В версиях Windows NT ниже 4.0 функции окон (windowing) и рисования (drawing) были расположены в Win32сервере (csrss.exe). Это означало, что, когда приложение использовало такие функции, посылались сообщения указанному серверу. В версии 4.0 эти функ ции были перенесены в компонент режима ядра, называемый Те перь вместо того, чтобы посылать сообщение серверу, клиентская DLL обра щается к этому компоненту ядра, уменьшая затраты на создание сообщений и переключение контекстов потоков различных процессов. Это увеличило про изводительность графического ввода/вывода. Библиотеки gdi32.dll и user32.dll стали вторым «родным» API, но оно менее загадочно, чем первое, так как хо рошо документировано. Функциями библиотеки вызывающими «родной» интерфейс API на прямую, являются функции ввода/вывода, синхронизации и управления памятью. Фак тически, большинство экспортируемых библиотекой функций используют «родной» API напрямую. На рис. 3 иллюстрируется передача управления от Win32приложения, выполнившего вызов CreateFile(), библиотеке затем функции в ntdll.dll и далее режиму ядра, где управление передает-
ся системному сервису, реализующему создание/открытие системных сервисов рассматривается в следующем параграфе.
Подробнее вызов
«Родной» API для ОС Windows NT (Native Windows NT API) «Родной» API для Windows NT является средством, которое реализует контроли руемый вызов системных сервисов, исполняемых в режиме ядра. Так, например, если программа, исполняющаяся в пользовательском режиме, захочет выполнить опера цию зарезервировать или освободить регион в виртуальном адресном пространстве, запустить поток или создать процесс, она должна запросить (есте ственно, не напрямую) один или несколько системных сервисов, расположенных в режиме ядра.
Этот интерфейс API является интерфейсом системных вызовов и не предназнача ется для непосредственного использования пользовательскими программами, кроме того, его документация ограничена. В Windows NT «родной» интерфейс спрятан от прикладных программистов под интерфейсами API более высокого уровня, таких как Win32, OS/2, «Родной» API предоставляется коду пользовательского р е ж и м а библиотекой ntdll.dll. Библиотека ntdll.dll, имеющая точки входа в «родной» API для кода пользова тельского режима, содержит также код загрузки модуля и запуска потока процесса. Однако большинство входов в «родной» API являются заглушками, которые просто передают управление режиму ядра. Это осуществляется путем генерации программ ного исключения, например, ассемблерный код функции NtCreateFileQ в библиотеке ntdll.dll, выглядит следующим образом: 0x00000017 lea i n t 0x2E r e t 0x2C Другие вызовы выглядят почти также. Первая инструкция загружает регистр про цессора индексным номером конкретной функции «родного» API (каждая функция «родного» API имеет уникальный индексный номер). Вторая инструкция загружает в регистр указатель на параметры вызова. Следующая инструкция - команда генерации программного исключения. ОС регистрирует обработчик ловушки для перехвата уп равления, переключения из пользовательского режима в режим ядра и передачи уп равления в фиксированную точку ОС при возникновении прерывания или исключе ния. В случае вызова системного сервиса (на процессорах х86 программное исключе ние для вызова системных сервисов генерируется кодом 0х2Е), этот обработчик ло вушки передает управление диспетчеру системных сервисов. Последняя инструкция забирает параметры из стека вызывающего потока. Диспетчер системных сервисов определяет, является ли корректным индексный номер функции «родного» API. Индексный номер, переданный из пользовательского р е ж и м а , используется для входа в таблицу р а с п р е д е л е н и я системных с е р в и с о в (KeServiceDescriptorTable). Каждый элемент этой таблицы включает указатель на со ответствующий системный сервис и число параметров. Диспетчер системных серви сов берет параметры, переданные в стеке пользовательского режима (указатель стека находится в регистре edx) и помещает их в стек ядра, а затем передает управление для обработки запроса соответствующему системному сервису, который исполняется в режиме ядра и находится в Введенные в Windows NT версии 4.0 интерфейсы API Win32 управления окнами и рисованием управляются тем же диспетчером системных сервисов, но индексные номера указывают на то, что должен использоваться второй массив указателей системных сервисов. Указатели во втором массиве ссылаются на функции в win32k.sys. Большинство системных сервисов должно выполнять проверку параметров, пере данных им из пользовательского режима. Некоторые параметры являются указателя-
ми, а передача неверного указателя в режим ядра без предварительной проверки жет привести к краху системы. Проверка параметров обязательна, но оказалось, что некоторые функции «родного» API (13 системных сервисов из не выпол няют обстоятельную проверку, что приводит в некоторых случаях к падению системы. Microsoft закрыла эти дыры в Service Pack В дальнейшем оказалось, что при тести ровании параметров, соответствующих граничным условиям, вызовы еще 40 функций «родного» API, из которых 25 из вызвали падение системы. Эти дыры были закрыты в SP4. После проверки параметров, системные сервисы обычно вызывают функции, реализуемые компонентами исполнительной системы: диспетчером процессов, дис петчером памяти, диспетчером ввода/вывода и средством локального вызова про цедур. Все функции прикладного уровня, вызывающие это прерывание, сосредоточены в модуле ntdll.dll и и м е ю т в с в о е м н а з в а н и и п р е ф и к с Nt л и б о Zw, н а п р и м е р , Точка входа для двух таких имен одна. Вызов многих функций различных подсистем рано или поздно приведет к вызову соответствую щей функции из ntdll.dll. При этом не все, что есть в ntdll.dll вызывается из подсис темы Win32. Вызов системных сервисов возможен не только из прикладной программы, но и из ядра ОС, то есть из драйверов. Имена соответствующих функций ядра имеют префикс либо Zw, либо Nt (ZwCreateFileQ, Функции с префиксом Zw обраща ются к сервисам посредством прерывания 2Е, тогда как функции с префиксом Nt явля ются собственно точками входа стандартных системных сервисов. Из этого следует, что число функций с префиксом Nt неизменно, а множество этих функций является подмножеством функций с префиксом Zw. путайте функции с префиксами Nt и Zw режима ядра и пользовательского режима. В режиме ядра они находятся в модуле (микроядро), в пользовательском режиме в модуле ntdll.dll («родной» API, вызывают int 2Е).
Неотъемлемые подсистемы Другой тип защищенных подсистем неотъемлемые подсистемы - это серверы, выполняющие важные функции ОС. Примером неотъемлемой подсистемы является подсистема защиты, исполняющаяся в пользовательском режиме и реализующая пра вила контроля доступа, определенные для локального компьютера. Некоторые компо ненты сетевого обеспечения Windows NT также реализованы как защищенные стемы, например, сервис рабочей станции реализует API для доступа и управления сетевым редиректором.
Исполнительная система (The Executive) Надо отметить, что в разных источниках понятие исполнительной системы ин терпретируется по-разному. Например, в документации D D K исполнительная
тема это совокупность компонентов, исполняющихся в ре жиме режиме ядра, и формирующих законченную ОС за исключением пользова тельского интерфейса. В данном случае к компонентам исполнительной системы относятся также само ядро и слой абстрагирования от оборудования. (HAL). В дру гих источниках ядро и H A L рассматриваются как отдельные модули. В этой книге решено было следовать именно этому разделению, хотя бы потому, что H A L не предоставляет системных сервисов, к которым могут обращаться защищенные под системы. Подсистемы Исполнительной Системы NT составляют наиболее существенный слой в режиме ядра, и они исполняют большую часть функций, традиционно связан ных с операционными системами. В Таблице 1 перечислены подсистемы Исполни тельной Системы NT, и рис. 2 показывает их позицию в архитектуре NT. Эти подсис темы имеют разные обязанности и названия, так что Вы могли бы подумать, что они являются различными процессами. Например, когда программа типа Microsoft Word запрашивает обслуживание операционной системы типа распределения памяти, поток управления передается от программы Word в режим ядра через «родной» интерфейс системных сервисов NT Тогда обработчик системного сервиса для распределения па мяти напрямую вызывает соответствующую функцию Виртуальной Па Запрошенное распределение памяти выполняется в контексте процесса Word, который запросил его, то есть нет никакого переключения контекста к другому сис темному процессу. Драйверы Windows NT, включая драйверы устройств, промежуточные драйверы и драйверы файловых систем, после загрузки рассматриваются как часть исполнитель ной системы, а точнее как часть системы ввода/вывода. Все компоненты, кроме диспетчера кэша, предоставляют определенное множество системных сервисов, к которым могут обращаться защищенные подсистемы. И каж дый компонент исполнительной системы реализует множество внутренних процедур, доступных только компонентам исполнительной системы. Префиксы в названиях внутренних процедур соответствуют названиям компонен тов исполнительной системы, обеспечивающих эти процедуры, например: «Ех» для функций, реализуемых компонентом Ex(ecutive) Support - исполнительным модулем, Ps - диспетчером процессов, диспетчером объектов, диспетчером ввода/ вывода, Mm - диспетчером памяти, диспетчером кэша, Se монитором безопас ности. Исполнительная система не исполняется постоянно в собственном процессе, а ра ботает в контексте некоторого существующего процесса, завладевая выполняющимся потоком, когда происходит важное системное событие. Например, когда поток вызы вает системный сервис, в результате чего происходит программное прерывание, или когда внешнее устройство генерирует прерывание, ядро получает управление пото ком, который выполнялся процессором. Оно выполняет соответствующий системный код для обработки события и затем возвращает управление коду, выполнявшемуся пе ред прерыванием.
Исполнительный модуль (executive support) - это особый компонент исполнитель ной системы ОС Windows NT, давший свое имя целой группе модулей операционной системы. Он отвечает за многие разнообразные функции, включая управление очере дями (их блокирование), управление резидентной и нерезидентной системной облас тью памяти, увеличение/уменьшение значения глобальной переменной и др. Этот ком понент обеспечивает также системные рабочие потоки, которые драйверы NT, особен но драйверы файловых систем, используют для выполнения необходимой работы. Таблица
Подсистемы Исполнительной
Подсистема исполнительной системы Диспетчер Объектов (Object Manager) Монитор Безопасности rity Reference Monitor)
Диспетчер Виртуальной Па мяти (Virtual Memory Man ager) Диспетчер Ввода/Вывода Manager) Диспетчер Кэша (Cache Man ager) Средство Вызова Локальных Процедур (Local Procedure Call (LPC) Facility) Диспетчер Конфигурации (Configuration Manager) Диспетчер Процессов (Process Structure) Поддержка среды Win32 (Win32 Support) Диспетчер Plug-and-Play (Plug-and-Play Manager) Диспетчер Электропитания (Power Manager) Исполнительный модуль (Ex ecutive Support)
Системы NT и их предназначение
Предназначение Управляет ресурсами и реализует глобальное пространство имен Реализует модель безопасности NT на основе Идентификаторов Безопасности (SID) и Списков Разграничительного Контроля Доступа (Discre tionary Access Control List - DACL) Определяет адресное пространство процесса и распределяет физическую память Служит интерфейсом между прикладными про граммами и драйверами устройств Реализует глобальный файловый кэш Обеспечивает эффективную межпроцессную коммуникацию Управляет Реестром Экспортирует программные интерфейсы (API) процессов и потоков Реализует обмена сообщениями и рисования (новые для NT 4.0) Уведомляет драйверы устройств о включении или отключении устройства (новые для NT 5.0) Контролирует состояние электропитания компью тера (появился в NT 5.0) Реализует управление очередями, системной об ластью памяти, обеспечивает системные рабочие
Ниже перечислены компоненты исполнительной системы и их области ответствен ности.
Справочный монитор защиты (security reference monitor) отвечает за цию единой политики защиты на локальном компьютере. Оберегает ресурсы ОС, обес печивая защиту объектов и аудит во время выполнения доступа к ним. Справочный монитор защиты использует для реализации единой системной политики безопаснос ти списки контроля доступа (Access Control Lists, ACL), содержащие информацию о том, какие процессы имеют доступ к конкретному объекту и какие действия они могут над ним выполнять, и идентификаторы безопасности (Security Identifiers, SI). Он под держивает уникальный для каждого потока профиль защиты, и проверку полномочий при попытке доступа к объектам. При открытии потоком описателя объекта активизи руется подсистема защиты, сверяя ACL, связанный с объектом, с запрашиваемыми потоком действиями над этим объектом. Другим аспектом справочного монитора за щиты является поддержка имперсонации (impersonation), которая позволяет одному потоку передать другому право использования своих атрибутов защиты. Это наиболее часто используется во время клиент-серверных операций, когда сервер использует ат рибуты защиты клиента. Диспетчер процессов (process manager или process structure) отвечает за созда ние и уничтожение процессов и потоков. Диспетчер процессов взаимодействует с дис петчером объектов для построения объекта-процесса и объекта-потока, а также взаи модействует с диспетчером памяти для выделения виртуального адресного простран ства для процесса. Средство локального вызова процедур (LPC) организует взаимодействие меж ду клиентскими и серверными процессами, расположенными на одном и том же ком пьютере. LPC - это гибкая, оптимизированная версия удаленного вызова процедур (Remote Procedure Call, RPC), средства коммуникации между клиентскими и сервер ными процессами по сети. LPC поддерживает передачу данных между клиентом и сервером посредством использования объектов-портов, которые в качестве атрибутов имеют указатель на очередь сообщений и описатель секции разделяемой памяти. API, необходимый для доступа к LPC, не документирован. Интересно, что запрос RPC между приложениями, исполняющимися на одном компьютере, в действительности будет использовать механизм Диспетчер памяти и диспетчер кэша (memory manager и cache manager). Дис петчер памяти и диспетчер кэша вместе формируют подсистему виртуальной Эта подсистема виртуальной памяти реализует 32-разрядную страничную организа цию памяти. Подсистема виртуальной памяти поддерживает совместное использова ние страниц физической памяти между несколькими процессами. Она поддерживает разделяемый сегмент памяти «только для чтения», а также «чтения-записи». Подсис тема виртуальной памяти отвечает за реализацию механизма кэширования Данные файла могут быть доступны через диспетчера при использова нии стандартных операций чтения и записи в файл, или через диспетчер памяти по средством проецирования данных файла напрямую в виртуальное пространство про цесса. Чтобы гарантировать согласованность между этими двумя методами доступа, диспетчер кэша поддерживает единый глобальный общий Этот единый кэш ис-
пользуется для кэширования, как страниц процесса, так и страниц файла. Диспетчер памяти реализует схему управления памятью, которая предоставляет каждому про цессу 4-гигабайтное собственное виртуальное адресное пространство и защищает его от других процессов. Диспетчер памяти реализует механизм подкачки страниц (paging) перенос страниц физической памяти на диск и обратно. Диспетчер кэша повышает производительность файлового ввода/вывода, сохраняя информацию, считанную с диска последней, в системной памяти. Диспетчер кэша использует средство подкачки страниц диспетчера памяти для автоматической записи информации на диск в фоно вом режиме. Поддержка среды Win32 (Win32 support) включает диспетчера окон (window manager), интерфейс графических устройств (Graphic Device Interface, GDI), драйве ры графических устройств (graphic device drivers). Эти компоненты поддержки среды Win32 были перенесены в режим ядра в версии NT 4.0, а ранее они принадлежали подсистеме среды Win32. Эти средства взаимодействуют между GUI - приложениями и графическими устройствами. Диспетчер конфигурации (configuration manager) определяет тип объект-ключ (key object) и манипулирует этими объектами. Тип объект-ключ представляет элемент реестра Windows NT. Каждый экземпляр типа объект-ключ представляет либо некото рый узел реестра, являющийся частью пути к множеству подключей, либо он содер жит именованные поля с соответствующими значениями.
Диспетчер Объектов Диспетчер Объектов (object manager), который является вероятно наименее извес тной из подсистем Исполнительной Системы NT, является также одним из наиболее важных. Главная роль операционной системы - это управление физическими и логи ческими ресурсами компьютера. Другие подсистемы Исполнительной Системы ис пользуют Диспетчер Объектов, чтобы определять и управлять объектами, которые представляют ресурсы. Диспетчером объектов нельзя манипулировать из пользовательского режима на прямую, а его пространство имен является невидимым. В таблице 2 приведен список объектов, определенных в NT 4.0, и подсистем ис полнительной системы, которые управляют ими. Диспетчер Объектов исполняет обязанности: • Поддержание единого пространства имен для всех именованных объектов сис темы. • Отвечает за создание, удаление и управление именованными и неименованны ми объектами ОС, представляющими системные ресурсы. Обязанности по управлению объектами включают в себя идентификацию и под счет ссылок. Когда прикладная программа открывает ресурс, Диспетчер Объектов или определяет местонахождение связанного с ресурсом объекта, или создает новый объект. Вместо возвращения прикладной программе, которая открыла ресурс, указателя на объект, Диспетчер Объектов возвращает непрозрачный (не имеющий смысла) иденти-
фикатор, называемый дескриптором. Значение дескриптора уникально в рамках при кладной программы, которая открыла ресурс, но не уникально между различными прикладными программами. Таблица управляют
Типы
Тип Объекта Тип Объекта (Object type) Директория (Directory) Символическая Связь (SymbolicLink) Событие (Event) Пара Событий (EventPair) Мутант (Mutant) Таймер (Timer) Семафор (Semaphore) Станция Windows (Windows Station) Рабочий Стол (Desktop) Файл (File)
и подсистемы исполнительной
которые ими
Какой ресурс представляет Объект типа объекта
Подсистема Диспетчер объектов
Пространство имен объектов Пространство имен объектов
Диспетчер объектов Диспетчер объектов
Примитив синхронизации Примитив синхронизации
Исполнительный модуль Исполнительный модуль
Примитив синхронизации Таймерное предупреждение Примитив синхронизации Интерактивный вход в систе му Рабочий Стол Windows Отслеживание открытых файлов Отслеживание завершения
Исполнительный модуль Исполнительный модуль Исполнительный модуль Поддержка среды Win32
Завершение вво Comple tion) Адаптер (Adapter) Ресурс прямого Доступа к Памяти (DMA) Контроллер (Controller) Контроллер DMA Устройство (Device) Логическое или физическое устройство Драйвер Драйвер устройства Ключ (Key) Вход в Порт (Port) Канал связи Секция (Section) Отображение в памяти Процесс (Process) Активный процесс Поток (Thread) Профиль безопасности про Маркер (Token) цесса Профиль (Profile) Измерение производительно сти
Поддержка среды Win32 Диспетчер Диспетчер
Диспетчер Диспетчер Диспетчер Диспетчер Диспетчер конфигурации Средство LPC Диспетчер памяти Диспетчер процессов Диспетчер процессов Диспетчер процессов Ядро
Прикладная программа использует дескриптор, чтобы идентифицировать ресурс в последующих операциях. Когда прикладная программа закончила работу с объек том, она закрывает дескриптор. Диспетчер Объектов использует подсчет ссылок, что бы проследить сколько элементов системы, включая прикладные программы и подси стемы Исполнительной Системы, обращаются к объекту, который представляет ре сурс. Когда счетчик ссылок обнуляется, объект больше не используется как представ ление ресурса, и Диспетчер Объектов удаляет объект (но не обязательно ресурс). Для обеспечения идентификации объектов, Диспетчер Объектов реализует про странство имен NT. Все разделяемые ресурсы в NT имеют имена, располагающиеся в этом пространстве имен. Например, когда программа открывает файл, Диспетчер Объек тов анализирует имя файла для выявления драйвера файловой системы для дис ка, который содержит файл. Точно так же, когда прикладная программа открывает ключ Реестра, Диспетчер Объектов по имени ключа Реестра определяет, что должен быть вызван Диспетчер Конфигурации. Рассмотрим следующий пример: Прикладная программа вызывает функцию Win32 CreateFile() с именем файла При этом происходят следующие действия: 1) Вызов системного сервиса NtCreateFile(). В качестве имени ему будет передано Такой формат имени является «родным» для NT, точнее - это формат имени в пространстве имен Диспетчера Объектов. 2) Диспетчер Объектов начнет последовательно разбирать переданное имя. Первым будет разобран элемент Корень пространства имен содержит объект с таким име нем. Тип объекта В этой директории будет произведен поиск объекта с именем Это - ссылка на имя Даль нейшему разбору будет подвергнуто имя Разбор будет закончен при достижении объекта, не являющегося директорией или сим волической связью. Таким объектом будет имеющий тип «Device». Этому объекту для дальнейшей обработки будет передано имя
Система ввода/вывода Система исполнительной системы - это часть кода ОС, получающая запросы ввода/вывода от процессов пользовательского режима и передающая их, в преобразованном виде, устройствам Между сервисами пользовательс кого режима и аппаратурой располагается несколько отдельных систем ных компонентов, включая законченные файловые системы, многочисленные драйве ры устройств и драйверы сетевых транспортов. Система ввода/вывода управляется пакетами запроса Request Packet, IRP). Каждый запрос представляется в виде пакета IRP во время его перехода от одной компоненты системы к другой. IRP - это структу ра данных, управляющая обработкой операции на каждой стадии ее вы полнения.
В систему входят следующие компоненты: 1) Диспетчер ввода/вывода manager). Реализует средства ввода/вывода, не зависящие от типа устройства, и устанавливает модель для исполнитель ной системы. Диспетчер ввода/вывода осуществляет создание, чтение, запись, установ ку и получение информации, и многие другие операции над файловыми объектами. Диспетчер ввода/вывода реализует асинхронную подсистему основанную на передаче пакетов запроса Request Packet, IRP). Диспетчер ввода/ вывода также отвечает за поддержку и обеспечение операционной среды для драйверов. 2) Файловые системы. Драйверы, принимающие запросы файлового ввода/вы вода и транслирующие их в запросы, привязанные к конкретному Сюда же входят сетевые файловые системы, состоящие из двух компонентов: сетевого реди ректора (network redirector), реализуемого как драйвер файловой системы и передаю щего удаленные запросы ввода/вывода на машины в сети, и сетевого сервера (network server), являющегося обычным драйвером, принимающим и обрабатывающим такие запросы. 3) Сетевые драйверы, которые могут загружаться в ОС и рассматриваться как часть системы 4) Драйверы устройств. Низкоуровневые драйверы, напрямую работающие с оборудованием. Диспетчер manager) определяет порядок, по которому запросы ввода/вывода доставляются драйверам. В обязанности диспетчера входит: 1) Получение запроса на ввод/вывод и создание пакета IRP. 2) Передача IRP соответствующему драйверу. Драйвер, получив IRP, выполняет указанную в нем операцию ввода/вывода, и, либо возвращает его диспетчеру ввода/ вывода для завершения обработки, либо передает другому драйверу для продолжения операции ввода/вывода. 3) Сопровождение IRP по стеку драйверов. 4) Завершение IRP по окончании операции ввода/вывода и возвращение результа тов обработки инициатору запроса ввода/вывода. 5) Также диспетчер ввода/вывода реализует общие процедуры, к которым обра щаются драйверы во время обработки ввода/вывода, и предоставляет системные сер висы, позволяющие защищенным подсистемам реализовать свои API ввода/вывода.
Ядро Ядро ОС Windows NT реагирует на прерывания и исключения, занимается плани рованием потоков, сохранением и восстановлением контекстов потоков, направляет потоки на выполнение, выполняет межпроцессорную синхронизацию, предоставляет набор сервисов, элементарных объектов и интерфейсов, используемых компонентами исполнительной системы. Большая часть ядра зависит от типа процессора. Драйверы NT и компоненты исполнительной системы вызывают внутренние про цедуры, обеспечиваемые ядром, названия которых начинаются с префикса ядро.
Я д р о экспортирует два о с н о в н ы х типа объектов ядра: о б ъ е к т ы - д и с п е т ч е р ы (dispatcher objects) и управляющие объекты (control objects). Объекты ядра отличают ся от объектов исполнительного уровня, создаваемых и управляемых менеджером объектов, и зачастую являются базисом для них. Объект-диспетчер используется для планирования и синхронизации и имеет атри бут, определяющий его состояние - «занят» или «свободен». Объектами-диспетчера ми являются: события, мьютексы, семафоры и таймеры. Управляющие объекты используются для управления системными операциями. Управляющими объектами являются: АРС-объект (Asynchronous Procedure Call), со держащий адрес процедуры асинхронного вызова и указатель на объект-поток, кото рый будет исполнять данный вызов; DPC-объект (Deferred Procedure Call), содержа щий адрес процедуры отложенного вызова; объект-прерывание, отвечающий за уста новление соответствия между определенным вектором прерывания и процедурой об работки прерывания (Interrupt Service Routine, ISR) драйвера устройства.
Слой абстрагирования от оборудования Слой абстрагирования от оборудования (Hardware Abstraction Layer, HAL) являет ся относительно тонким слоем кода, взаимодействующим напрямую с процессором, шинами и другим оборудованием, и отвечает за обеспечение стандартного интерфейса к платформенно-зависимым ресурсам для ядра, диспетчера и драйверов устройств. Вместо того чтобы обращаться к аппаратуре непосредственно исполнительная си стема сохраняет максимальную переносимость, обращаясь к функциям HAL, когда ей нужна платформенно-зависимая информация (некоторый объем кода, который зави сит от конкретной архитектуры, располагается не только в HAL, но и в ядре и в менед жере памяти). Драйверы устройств содержат, конечно же, код, зависящий от устройств, но избегают кода, зависящего от процессора или платформы, вызывая процедуры ядра и HAL. HAL обеспечивает поддержку и отвечает за предоставление стандартного интер фейса к ресурсам процессора, которые могут меняться в зависимости от модели внут ри одного семейства процессоров. Возможность замены слоя HAL обеспечивает всем вышележащим слоям операционной системы независимость от аппаратной архитек туры. Внутренние процедуры, обеспечиваемые слоем абстрагирования от оборудования, начинаются с префикса Hal.
2.3.4. Система приоритетов Windows NT имеет двухуровневую модель приоритетов (см. рис. 4). • Приоритеты высшего уровня (уровни запросов прерываний - Interrupt ReQuest Level - IRQL) управляются аппаратными и программными прерываниями. • Приоритеты низшего уровня (приоритеты планирования) управляются пла нировщиком.
2.3.4.1. Уровни запросов прерываний (IRQL) В любое время исполняющийся код будет иметь определенный уровень IRQL. Этот уровень определяет, что позволено делать коду, применяется ли к коду механизм кван тования времени планировщика и каковы его взаимосвязи с другими потоками испол нения. Наивысшие из уровней IRQL запросов прерываний устройств (Device Interrupt Request Levels - DIRQLs). Это уровни IRQL, соответствующие аппаратным прерываниям. Другие уровни IRQL реализованы программно. Уровень IRQL прерывания контролирует то, когда прерывание может быть обра ботано. Прерывание никогда не будет обработано, пока процессор занят обработкой прерывания более высокого уровня. Уровни IRQL располагаются в порядке убывания от HIGH_LEVEL до Уровни в подмножестве от до A P C _ L E V E L называют п о в ы ш е н н ы м и (elevated DISPATCH_LEVEL и реализованы программно.
Модель приоритетов низшего уровня управляет исполнением потоков, выполняю щихся на уровне P A S S 1 V E L E V E L . Этот уровень контролируется планиров щиком (scheduler) (его также называют диспетчером который планиру ет исполнение потоков (но не процессов). Планировщик планирует исполнение при кладных и системных потоков, используя для наблюдения и контроля исполнения по токов системные часы.
Приоритеты планирования Каждому потоку назначается приоритет планирования. Имеется 32 уровня приори тетов планирования со значениями Низший приоритет планирования со значени ем 0 зарезервирован для потока обнуления страниц (Zero Page который выпол няется в случае, когда больше нечего исполнять. Этот поток является компонентом дис петчера памяти, и его работа состоит в обнулении страниц из списка свободных стра ниц. Когда диспетчер памяти получает запрос на выдачу обнуленной страницы памяти, диспетчер памяти вначале попробует выделить страницу, обнуленную потоком обнуле ния страниц, и только если таких страниц нет, он потратит время на обнуление.
Динамические приоритеты и приоритеты реального времени Приоритеты планирования делятся на две главных группы: • динамические приоритеты (dynamic • приоритеты реального времени (real-time priorities). Динамические приоритеты имеют значения в диапазоне Они названы дина мическими, потому что ОС может динамически изменять приоритет потока в этом диапазоне. Приоритеты реального времени имеют значения в диапазоне ОС не может изменять значение приоритета потока, находящееся в этом диапазоне. Имеется два важных отличия между динамическими приоритетами и приоритета ми реального времени. Поток с приоритетом реального времени может сохранять контроль над процессо ром до тех пор, пока не появится поток с большим или равным значением приоритета. Таким образом, пока выполняется поток реального времени, потоки с меньшим значе нием приоритета никогда не получат шанса исполниться (механизм вытесняющей многозадачности не Такой поток должен сам освободить Однако в любом случае при появлении потока с большим или равным значением приоритета задействуется механизм вытесняющей многозадачности. В случае потоков с динамическими приоритетами, потоки с меньшими приорите тами также не могут получить шанса на исполнение, пока готовы к исполнению пото ки с большими приоритетами. Однако, в ряде случаев планировщик повышает приоритет потоков в диапазоне динамических приоритетов. Это дает возможность рано или поздно выполниться лю бому потоку с приоритетом в этом диапазоне.
К механизму повышения приоритетов применимы следующие утверждения: 1) Система никогда не меняет приоритет потоков из диапазона приоритетов реаль ного времени. 2) Повышение приоритета не может вызвать его переход в диапазон приоритетов реального времени, то есть превысить значение 15. 3) Повышение приоритета операционной системой является временным. Каждый раз, когда поток исчерпывает отведенный ему квант времени, значение его при оритета уменьшается на единицу. Так происходит до достижения значения ба зового приоритета. 4) Операционная система не может снизить приоритет ниже уровня базового при оритета. 5) Повышение приоритета может происходить несколько раз
Базовый приоритет. Класс приоритета и относительный приоритет Ядро NT предоставляет функции для назначения потоку любого из уровня при оритетов (кроме зарезервированного нулевого уровня). Программно назначенное по току значение приоритета называют базовым приоритетом. Подсистема Win32 не позволяет непосредственно назначать потоку базовое значе ние приоритета. Вместо этого используется комбинация двух значений: • Класс приоритета процесса, назначаемый процессу при его создании (дальше мы будем ссылаться на этот термин как на класс приоритета). • Относительное значение приоритета потока внутри класса приоритета процес са (относительный приоритет). На рис. 4 показаны группы взаимосвязанных приоритетов. Максимальный (31) и минимальный (1) из возможных приоритетов определяются в Win32 как T H R E A D _ и соответственно. В таблице 3 приведены все возможные классы приоритетов. По умолчанию, про цесс имеет класс N O R M A L P R I O R I T Y C L A S S , если при вызове функции CreateProcess() не было указано другого. Прикладная программа может получать/из менять класс приоритета процесса с помощью функций Win32-API i Таблица
Классы
приоритетов
Класс приоритета REALTIME PRIORITY CLASS HIGH PRIORITY CLASS A B O V E N O R M A L PRIORITY CLASS N O R M A L PRIORITY CLASS B E L O W N O R M A L PRIORITY CLASS IDLE PRIORITY CLASS
Базовый приоритет Примечание 24 13 Только Win 2000 8 6 Только Win 2000 4
Поток может иметь одно из 7 значений (см. таблицу 4): 5 значений, относительных внутри каждого класса приоритетов, и 2 значения, относительных внутри диапазонов динамического приоритета и приоритетов реального 4.
приоритет
Относительный приоритет THREAD PRIORITY TIME CRITICAL THREAD PRIORITY HIGHEST THREAD PRIORITY ABOVE N O R M A L THREAD PRIORITY N O R M A L T H R E A D PRIORITY BELOW N O R M A L T H R E A D PRIORITY LOWEST THREAD PRIORITY IDLE
15(31) +2 +1 +0 -2 1(16)
Два значения, обозначающие минимальное и максимальное значение приоритета внутри диапазона динамических приоритетов и приоритетов реального времени это и JPRIORITY_ T I M E C R I T I C A L . Для диапазо на динамических приоритетов они обозначают базовые приоритеты 1 и а для диа пазона приоритетов реального времени - 16 и 31 соответственно. Л ю б о й п о т о к всегда с о з д а е т с я с о т н о с и т е л ь н ы м п р и о р и т е т о м Соответствующие значения базового приоритета в зависимос ти от класса приоритета указаны в таблице 3. Относительный приоритет потока может быть получен/изменен с помощью WIN32функций Необходимо отметить, что служебные потоки ОС, производящие операции с мы шью и клавиатурой, а также некоторые файловые операции, работают с приоритетом реального времени. Поэтому использование пользовательскими потоками таких при оритетов может повлиять на корректность функционирования
2.3.4.3. Как используются IRQL NT управляет прерываниями путем отображения уровней прерывания контролле ра прерываний в собственную аппаратно-независимую таблицу уровней прерываний. Осуществляет отображение слой абстрагирования от оборудования (HAL - модуль специально написанный для конкретных контроллеров прерываний, материнских плат, либо наборов микросхем процессоров). В мультипроцессорных системах принять пре рывание может любой процессор, поэтому NT поддерживает независимый IRQL для каждого IRQL процессора представляет уровень прерывания, который маскируется в данный момент процессором и прямо соответствует (аналогичен) пре рываниям, которые маскирует контроллер прерываний CPU. Поскольку уровни IRQL NT не привязаны к какой-либо спецификации оборудования, NT также может отобра жать в свою иерархию приоритетов неаппаратные типы прерываний. Операционная
система использует программные прерывания в основном для запуска операций пла нирования, таких как переключение потоков или обработка завершения ввода/вывода. Когда NT обслуживает аппаратное прерывание, NT устанавливает IRQL процес сора в соответствующее значение таблицы IRQL NT программирует контроллер прерываний так, чтобы он маскировал прерывания с более низким приоритетом, и драйверы устройств (так же как и NT) могут запрашивать IRQL для определения его значения. (Как мы увидим позднее, NT позволяет выполнять некоторые операции только когда IRQL меньше определенных значений.) Размер таблицы IRQL разнится между архитектурами процессоров (Intel, Alpha и др.) для того, чтобы лучше отображать уровни прерываний, предоставляемые контрол лерами прерываний, однако уровни прерываний, которые могут найти интересными разработчики драйверов устройств и разработчики NT, имеют символические имена. Таблица 5 представляет символические имена IRQL и соответствующие им числовые значения в архитектурах Intel и Alpha. Низшие уровни IRQL (от passive_level до используются для синх ронизации программных частей операционной системы. Эти IRQL сконструированы как программные прерывания. Уровни IRQL выше имеют ли они конк ретные мнемонические имена или нет, отражают приоритеты аппаратных прерыва ний. Таким образом, эти аппаратные IRQL часто упоминаются как уровни IRQL Уст ройства (или DIRQL). Конкретные значения, назначенные мнемоническим именам IRQL, изменяются от системы к системе. Взаимоотношения между программными уровнями IRQL от сис темы к системе остаются постоянными; верным также остается положение о том, что программные уровни IRQL имеют более низкий приоритет, чем аппаратные IRQL. Таким образом, IRQL p a s s i v e l e v e l всегда является самым низким уровнем IRQL в системе, всегда выше, чем passive_level, и всегда выше, чем Все эти уровни IRQL всегда ниже, чем самый низкий уровень DIRQL. Таблица
Символические и числовые определения IRQL
Символическое имя Предназначение HIGH LEVEL P O W E R LEVEL LEVEL C L O C K LEVEL PROFILE LEVEL DEVICE LEVEL
APC LEVEL PASSIVE LEVEL
Наивысший уровень прерывания Power event Межпроцессорный сигнал такт системных часов Контроль производительности Обычные прерывания устройств Операции планирования и отложенные вызовы процедур (DPC) Асинхронные вызовы процедур (АРС) Нет прерываний
Уровень Intel 31 30 29 28 27 3-26 2
Уровень Alpha 7 7 6 5 3 3-4 2
1 0
1 0
В отличие от программных IRQL, значения и отношения аппаратных IRQL могут изменяться в зависимости от реализации аппаратной части системы. Например, в ар хитектурах на основе х86, уровень IRQL ниже, чем IRQL кото рый является в свою очередь ниже, чем IRQL Однако, на MIPS системах, IRQL и IRQL имеют то же самое значение, и оба ниже, чем IRQL Уровни IRQL являются главным методом, используемым для расположения по приоритетам действий операционной системы Windows NT. Повышение уровня IRQL позволяет подпрограмме операционной системы как управлять повторной входимостью (реентерабельность) так и гарантировать, что она может продолжать работу без приоритетного прерывания (вытеснения) некоторыми другими действиями. Следую щие разделы описывают, как используются наиболее распространенные уровни IRQL.
IRQL и DISPATCHLEVEL Наименьший приоритет IRQL в таблице 5 Passive Level. Этот уровень является обычным уровнем IRQL, на котором производится работа в операционной системе, как в пользовательском режиме, так и в режиме ядра. Когда процессор находится в этом состоянии, не происходит никакой деятельности по обработке прерываний. Под программа, выполняющаяся на уровне IRQL p a s s i v e l e v e l , может быть подвергнута прерыванию и вытеснению почти всем, чем угодно еще случившемся в системе. Так, потоки, выполняющиеся на IRQL подвергаются вытеснению Диспетче ром (планировщиком) по истечении их кванта времени. Большинство подпрограмм уровня исполнительной системы Windows NT (то есть подпрограммы режима ядра, не принадлежащие Микроядру и HAL) стремятся дер жать уровень IRQL как можно более низким. В большинстве случаев, это приводит к выполнению большинства подпрограмм на уровне IRQL Эта стратегия повышает возможность выполнения действий с высоким уровнем IRQL. Следующие два уровня IRQL выше Passive Level (АРС Level и Dispatch Level) программные уровни прерываний, связанные с планировщиком. Когда система находится на уровне Level, исполняющийся поток не будет получать запросы АРС, которые NT обычно использует для операций завершения вво IRQL используется внутри Windows NT для двух различных дей ствий: • Обработка Отложенных Вызовов Процедур (DPCs); • Выполнение Диспетчера (планировщик NT). DPC обработка обсуждена позже в этой главе, в собственном разделе. Следова тельно, мы ограничимся обсуждением Диспетчера. Диспетчер, является планировщи ком потоков Windows NT. Он отвечает за реализацию алгоритма планирования, кото рый выбирает, какой поток будет выполняться, и осуществляет приоритетное преры вание (выгрузку) в конце кванта времени.
Диспетчер (планировщик) Windows NT получает запросы, чтобы выполнить опе рацию перепланирования на уровне IRQL Когда операционная система решает заменить поток, который выполняется на текущем процессоре, она иногда мо жет вызывать Диспетчер напрямую. Однако, когда система выполняется на уровне IRQL в ы ш е , чем она з а п р а ш и в а е т п р о г р а м м н о е п р е р ы в а н и е у р о в н я Результатом явится запуск на текущем процессоре Диспетчера в следу ющий раз, когда уровень станет наиболее приоритетным для обслужи вания системой. Рассмотрим, например, случай потока, выполняющегося в режиме пользователя. Так как он выполняется в режиме пользователя, этот поток, выполняется на IRQL В то время как поток выполняется, часы периодически генериру ют прерывания, чтобы указать операционной системе прохождение промежутка вре мени. С каждым переданным тиком часов, программа обработки прерывания часов уменьшает остающийся у выполняющегося в данный момент потока квант времени. Когда оставшийся у потока квант уменьшается до нуля, программа обработки преры вания часов генерирует прерывание уровня чтобы запросить запуск Диспетчера и выбор им следующего потока для выполнения. Так как программа обра ботки прерывания часов выполняется на уровне IRQL большем, чем (она выполняется на уровне IRQL на х86 процессорах), обработка са для Диспетчера откладывается. После генерирования прерывания уровня программа обработки прерывания часов заканчивает любую другую работу, которую она должна сделать и управление возвращается Микроядру Затем Микроядро распознает следующее самое высоко приоритетное прерывание, которое находится в режиме ожидания. Каждое прерывание обслуживается по очере д и . Когда д л я о б с л у ж и в а н и я н е о с т а е т с я н и к а к и х п р е р ы в а н и й у р о в н я в ы ш е dispatch_level, выполняется программа обработки прерывания уровня dispatch_level. Эта программа обработки прерывания обрабатывает список DPC (обсуждаемый поз же), и задействует Диспетчер, чтобы выбрать новый поток выполнения. Когда Диспетчер, он обращает внимание, что квант времени теку щего потока был уменьшен до нуля. Затем Диспетчер осуществляет алгоритм плани рования Windows NT, чтобы определить следующий поток, который нужно заплани ровать. Если выбран новый поток (мог бы быть перепланирован предыдущий поток), происходит переключение контекста. Если нет никаких ожидающих вызовов АРС для вновь выбранного потока, код потока будет выполнен, когда система возвратится об ратно к уровню IRQL
Ограничения, налагаемые на код с уровнем IRQL большим или равным DISPATCH_LEVEL IRQL имеет важное значение в Windows NT. Как уже говорилось выше, Диспетчер (планировщик) Windows NT получает запросы, чтобы выполнить
операцию перепланирования, на уровне IRQL dispatchjevel. Этот факт имеет три важ ных следствия: • Любой поток с IRQL не подвержен механизму вания. • Любой поток с IRQL DISPATCH_LEVEL не может использовать никакие функции ожидания Диспетчерских Объектов ядра с отличным от нуля време нем ожидания. • Любой поток с IRQL D I S P A T C H L E V E L не должен приводить к ошибкам отсутствия страниц памяти (что происходит при обращении к участку памяти, находящемуся на выгруженной на диск странице памяти). Иными словами, на таких уровнях IRQL может быть использована только невыгружаемая память (nonpaged организация памяти будет рассмотрена в следующем разделе). Рассмотрим эти пункты более подробно. Так как Диспетчер выполняется на уровне IRQL DISPATCH_LEVEL, любая под программа, которая выполняется на IRQL dispatchjevel или выше, не подчиняется приоритетному прерыванию (выгрузке). Таким образом, когда квант времени потока истекает, если этот поток выполняется в настоящее время на IRQL dispatch_level или выше, он продолжит выполняться, пока не попытается понизить IRQL текущего про цессора ниже dispatchjevel. Это должно быть очевидно, так как исполнение на неко тором уровне IRQL блокирует распознавание других событий, запрошенных на этом же или более низком уровне IRQL. Что может быть менее очевидно, так это то, что, когда код выполняется на уровне IRQL d i s p a t c h l e v e l или выше, он не может ждать никакие диспетчерские объекты (Dispatcher Object - см. раздел «Механизмы которые еще не переве дены в сигнальное состояние (состояние Таким образом, например, код, выполняющийся на уровне IRQL dispatchlevel или выше, не может ожидать установ ки объектов событие или Так происходит потому, что действие освобожде ния процессора (которое происходит, когда поток переходит в режим ожидания собы тия) требует (по крайней мере, концептуально) запуска Диспетчера. Однако, если под программа выполняется на уровне d i s p a t c h l e v e l или выше, прерывание уровня dispatch_level (по которому запускается Диспетчер) будет маскировано и, следовательно, распознано не сразу. В результате происходит возврат обратно к коду, который вызвал операцию ожидания! Еще менее очевидным может быть тот факт, что код, выполняющийся на уровне IRQL d i s p a t c h l e v e l или выше не должен приводить к ошибкам отсутствия страниц (page faults). Это означает, что любой такой код сам должен быть невыгружаемым, и должен обращаться только к невыгружаемым структурам данных. В основном это объяс няется тем, что код, выполняющийся на IRQL dispatchlevel или выше не может ждать освобождения диспетчерского объекта. Таким образом, даже если бы страничный зап рос был обработан, поток с ошибкой отсутствия страницы не мог бы быть приоста новлен, пока необходимая страница читалась с диска.
DIRQLs Все уровни IRQL выше Dispatch Level относятся к аппаратным прерываниям. Ап паратные прерывания периферийных устройств системы (например, дисков, клавиа тур, последовательных портов) отображаются на уровни IRQL в диапазоне Device Level. Из Таблицы 5 вы можете видеть, что на процессорах Intel этот диапазон лежит от 3 до 26, а на машинах Alpha от 3 до Тот факт, что существует такая разница между двумя диапазонами, имеет следствием то, что NT в действительности не располагает обычные прерывания устройств в соответствии с приоритетом. Даже на процессорах Intel, где аппаратные прерывания могут иметь различные значения IRQL, назначение IRQL является случайным. Так как это может быть важным моментом для разработчиков драйвера устройства в некоторых системах, необходимо повторить снова: связь между двумя IRQ, назначенны ми двум определенным устройствам не обязательно сохраняется, когда IRQL назначены этим устройствам. Назначен ли устройству с более важным IRQ более высокий (то есть более важный) уровень IRQL, полностью зависит от HAL. В действительности, в боль шинстве HAL стандартных многопроцессорных систем х86, для систем, которые ис пользуют архитектуры APIC, связь между и IRQL не сохраняется. Уровни IRQL выше Device Level имеют предопределенные связи с определенны ми прерываниями. Profile Level относится к таймеру профилирования ядра (механиз му измерения производительности системы), Clock Level относится к такту систем ных часов, Level относится к сигналам, посылаемым от одного CPU к другому, и Power Level относится к событиям сбоя в питании. NT резервирует, но в настоящий момент не использует IRQL High IRQL всегда определяется как самый высокий уровень IRQL в системе Windows NT. Этот уровень IRQL используется для NMI (Немаскируемого Прерыва ния) и других прерываний очень высокого приоритета. В редких случаях, когда драй вер устройства нуждается в блокировании прерываний на конкретном процессоре на короткий период, драйвер может поднимать IRQL до уровня но такое повы шение уровня IRQL драйвером устройства считается очень решительным шагом, и в Windows NT это почти не требуется. Подъем 1RQL до уровня HIGHJLEVEL большинству драйверов Windows NT же лательно никогда не делать. Блокирование прерываний является широко используе мым методом для достижения синхронизации на других операционных системах (типа DOS или Win9x). Однако, в Windows NT, простое поднятие до IRQL HIGHJLEVEL в целях синхронизации не будет работать на многопроцессорных системах. Код режима ядра выполняет сериализацию, используя спин-блокировки, которые подробно описа ны в разделе синхронизации".
Прерывания и планирование Сведения об IRQL в Таблице 5 показывают, что уровень Dispatch Level связан с операциями планирования. Когда уровень 1RQL соответствует Dispatch Level или выше,
NT маскирует программные прерывания планировщика, что означает, что NT чает планировщик. Фактически, драйверы устройств (и NT) не должны осуществлять операции, требующие немедленного ответа от планировщика, когда процессор нахо дится на уровне IRQL большем или равном Dispatch Level. Это ограничение включает в себя выполнение всего, что может указывать NT, что текущий поток уступает CPU для ожидания осуществления некоторого события, так как эта акция заставит планировщик искать новый поток для исполнения. Другое действие, которое требует вмешательства планировщика, это ошибка от сутствия страницы. Когда поток обращается к виртуальной памяти, ссылающейся на данные в страничном файле, NT обычно блокирует поток до тех пор, пока данные не будут прочитаны. Поэтому на уровне Dispatch Level или выше NT не позволяет доступ к памяти, не заблокированной в физической памяти. Если вы когда либо видели код останова синего экрана EQUAL, вы вероятно были свидетелем эффекта от нарушения драйвером этих правил. Отключение планировщика во время обработки прерывания имеет другой, менее очевидный эффект: NT подсчитывает время, затраченное функциями ISR (процедура ми обработки прерываний) и DPC (вызовами отложенных процедур) на фоне величи ны времени, которое поток был активным к моменту, когда CPU получил прерывание. Например допустим, что Word выполняет операцию проверки правописания, от устройства поступает прерывание, и драйвер устройства имеет DPC, которое отбирает весь квант времени программы Word (а затем еще сколько-то). Когда IRQL процессора упадет ниже уровня Dispatch Level, планировщик может решить переключиться на другой поток другого приложения, чувствительно наказывая Word за обработку пре рывания. Хотя такая практика кажется несправедливой, почти каждый раз, когда NT распре деляет прерывание, оно равномерно распределяется между прикладными программа ми в системе.
2.3.4.5. Определение текущего уровня IRQL Текущий уровень IRQL свой у каждого CPU. Код режима ядра может определить IRQL, в котором он выполняется, посредством вызова функции прототип которой: KeGetCurrentlrql в о з в р а щ а е т IRQL т е к у щ е г о Большинство подпрограмм драйвера устройства вызывается Диспетчером вво да/вывода на определенном архитектурой уровне IRQL. есть разработчик драй вера знает уровень (или уровни) IRQL, на котором будет вызываться данная функ ция. Подпрограммы режима ядра могут изменять IRQL, на котором они выполня ются, вызывая функции KeRaiselrql() и прототипы которых: VOID (IN PKIRQL N e w l r q l , OUT PKIRQL
Где: - значение, до которого должен быть поднят уровень IRQL текущего процессора; Oldlrql - указатель на место, в которое будет помещен IRQL, на котором текущий процессор выполнялся перед тем, как был поднят к Newlrql. VOID (IN Где: - значение, до которого должен быть понижен IRQL текущего про цессора. Так как уровни IRQL являются методом синхронизации, большинство подпрог рамм режима ядра (в особенности драйверы устройств) никогда не должны понижать свой уровень IRQL ниже того, на котором они вызывались. Таким образом, драйверы могут вызывать KeRaiselrql чтобы поднять IRQL до более высокого уровня, и затем вызывать чтобы возвратиться обратно к первоначальному уровню IRQL, на котором они были вызваны (например, из Диспетчера Однако драй вер никогда не должен вызывать функцию чтобы понизить IRQL до уровня, меньшего, чем тот, на котором он был вызван. Такое поведение может привес ти к крайне непредсказуемой работе операционной системы, которая наверняка закон чится полным отказом системы.
2.3.5. Архитектура памяти Организация памяти в защищенном режиме работы процессора Ранее мы кратко рассмотрели работу процессоров серии i386 и выше в защищен ном режиме, использующем организацию памяти, при которой используются два ме ханизма преобразования памяти; • сегментация; • разбиение на страницы. ОС NT в различной мере использует оба этих механизма. Как уже говорилось, в защищенном режиме может быть определено до сегментов. Каждый сегмент может иметь размер до 4 Гб байт). Таким образом, максимальный размер виртуального адресного пространства составляет 64 Тб. Каждый сегмент описывается 8-байтной структурой данных - д е с к р и п т о р о м сег мента. Дескрипторы находятся в специальной таблице дескрипторов (GDT, см. рис. 5). Для указания конкретного сегмента используется 16-битный селектор. Он являет ся индексом внутри таблицы дескрипторов. Младшие 2 бита селектора определяют номер привилегированного режима (DPL - уровень привилегий дескриптора), кото рый может воспользоваться данным селектором для доступа к дескриптору, третий бит определяет локальную/глобальную дескрипторную (отсюда максималь ное число селекторов ОС NT, хотя и использует селекторы, но использует их в минимальной степени. NT реализует плоскую 32-разрядную модель памяти с размером линейного адресного пространства 4 Гб байт). Это сделано следующим образом:
Рис. 5 В NT определено Селектор Hex (bin) 08
23
селекторов, из которых нас будут интересовать всего 4:
Назначение
База
Предел
DPL
Тип
Data32 Code32 Data32
00000000 00000000 00000000 00000000
FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
0 0 3 3
RE RW RE RW
Эти четыре селектора позволяют адресовать все 4Гб линейного адресного простран причем для всех селекторов при фиксированном контексте памяти производится трансляция в одни и те же физические адреса. Разница только в режиме доступа. Первые два селектора имеют и используются драйверами и системными компонентами для доступа к системному коду, данным и стеку. Вторые два селектора используются кодом пользовательского режима для доступа к коду, данным и стеку пользовательского режима. Эти селекторы являются константами для ОС NT. Сегментное преобразование пары селекторхмещение дает 32-битный линейный адрес (лежащий в диапазоне 4 Гб линейного адресного пространства). При этом ли нейный адрес совпадает со значением смещения виртуального адреса. Фактически, при такой организации памяти виртуальный и линейный адреса совпадают. Наличие поля определяющего возможность чтения/записи/исполнения кода в соответствующем сегменте может навести на мысль, что именно на этом уровне про-
изводится защита памяти от нецелевого использования. Например, при работе при кладной программы в пользовательском режиме ее код находится в сегменте с селек тором l b . Для этого сегмента разрешены операции чтения и исполнения. Используя селектор l b , программа не сможет модифицировать свой собственный код. Однако, как уже было сказано, для всех сегментов производится трансляция в одни и те же физические адреса. Поэтому при обращении к данным или стеку (селектор 23) при кладная программа обнаружит свой код по тому же смещению, что и для селектора lb, причем режим доступа к сегменту позволяет производить чтение/запись. (При этом важно помнить: одно и то же смещение в разных адресных пространствах указывает на разную физическую память.) Таким образом, способ использования сегментации в ОС NT не защиту кода от нецелевого использования. Далее задействуется механизм страничной организации памяти и переключения контекста памяти. Каждый контекст памяти (адресное пространство процесса) представляется соб ственной таблицей трансляции линейного адреса (совпадающего с виртуальным) в физический. Каждый элемент таблицы страниц содержит бит, указывающий на возможность доступа к странице из пользовательского режима. При этом все страницы доступны из режима ядра. Кроме того, каждый элемент таблицы страниц содержит бит, указывающий на воз можность записи в соответствующую страницу памяти. Эти два бита используются для управления доступом к страницам памяти и фор мируют следующий набор правил: 1) страница всегда может быть прочитана из режима ядра; 2) на страницу может быть произведена запись из режима ядра, только если уста новлен бит разрешения записи; 3) страница может быть прочитана из пользовательского режима, только если ус тановлен бит доступа к странице из пользовательского режима; 4) на страницу может быть произведена запись из пользовательского режима, если установлены оба бита (разрешение записи и доступ из пользовательского режима); 5) если страница может быть прочитана, она может быть исполнена. Страницы памяти с исполняемым кодом не будут иметь разрешения на запись, если не предпринять никаких дополнительных действий. Поэтому при попытке ис пользования селектора данных для модификации кода будет сгенерирована исключи тельная ситуация. Для упрощения организации памяти, для всех контекстов памяти NT осуществля ет одинаковую трансляцию для некоторого диапазона виртуальных адресов. Таким образом, при переключении контекста памяти, то есть переходе на новую таблицу транс ляции виртуального адреса в физический, некоторая часть элементов этой таблицы останется неизменной. Это нужно для того, чтобы ядро операционной системы (компоненты ОС и драй вера) всегда располагалось по фиксированным виртуальным адресам вне зависимости
от текущего контекста памяти. Таким неизменяемым диапазоном адресов являются верхние 2 Гб памяти. Для защиты кода ОС соответствующие элементы таблицы трансляции ного адреса в физический помечены как недоступные из пользовательского режима. Соответственно, диапазон виртуальных адресов 2-4 Гб называют системным ад ресным пространством (system address space), а диапазон 0-2 Гб - пользовательс ким адресным пространством address space). Во избежание путаницы между терминами адресное пространство процесса и адресное пространство, где это возможно, вместо тер мина адресное пространство процесса мы будем пользоваться термином контекст памяти. При этом возможная путаница вполне допустима, так как с точки зрения приклад ного программиста пользовательское адресное пространство - и есть текущий кон текст памяти.
Организация системного адресного пространства Как уже отмечалось, системное адресное пространство сильно отличается от пользовательского: • Системное адресное пространство одинаково вне зависимости от текущего ста памяти, то есть от содержимого пользовательского адресного пространства. • В системном адресном пространстве имеются диапазоны памяти как выгружа емые на диск, так и не выгружаемые. На рис. 6 показана приблизительная организация системного адресного простран ства для платформы х86.
6
Типы адресов в NT Как мы уже отмечали, линейный и виртуальный адреса в NT совпадают. Здесь и далее мы будем пользоваться термином виртуальный адрес. Виртуальный адрес транслируется в физический адрес. Этот адрес соответствует физической памяти. Кроме этих двух типов адресов существует еще один логический адрес, реали зуемый на уровне HAL. HAL поддерживает гибкую модель для адресации аппаратных устройств. В соот ветствии с этой моделью, устройства подключаются к шинам, из которых име ет свое собственное адресное пространство. Реально эти адреса могут быть как в про странстве портов ввода/вывода, так и в пространстве памяти. Прежде чем может быть произведено обращение к некоторому адресу устройства (посредством функции HAL), адрес должен быть переведен из относительного адреса для шины в некоторый транслированный модулем HAL адрес. Этот транслированный адрес и есть логический адрес. Он имеет смысл только для H A L и не имеет ничего общего с конкретным адресом для работы с оборудованием. Для получения логического адреса из шинного адреса служит функция Полученный адрес будет находиться либо в пространстве портов либо в обычном пространстве памяти. В последнем случае для использования в драйвере полученный логический адрес должен быть преобразован к адресу в невыгружаемой области системного адресного пространства. Это делается посредством вызова функции MmMaploSpace().
Совместное использование памяти Схему организации памяти на рис. 5 можно представить более упрощенно, а имен каталог страниц и соответствующие ему таблицы страниц рассматривать как еди ную таблицу страниц для трансляции виртуального адреса в физический. Каждый кон текст памяти при таком представлении определяется своей таблицей страниц. Из рис. 7 видно, что разные таблицы страниц могут иметь ссылку на одну и ту же физическую страницу Эта возможность позволяет приложениям совместно использовать одну и ту же физическую память (обращаясь при этом к различным вир туальным адресам). Кроме того, как уже говорилось выше, на уровне таблиц страниц производится управление доступом к страницам памяти. При этом к одной и той же физической странице памяти может быть предоставлен различный уровень доступа даже внутри одного адресного пространства (никто не запрещает нескольким записям одной таб лицы страниц указывать на одну физическую страницу). Для совместно используемой памяти может быть задействован механизм CopyЗапись в таблице страниц для такой памяти указывает запрет на модифика цию и задействование Пока два процесса совместно используют та кую память для чтения, ничего не происходит. При попытке записи одним из процес-
сов в такую память генерируется исключение (защита от записи). Диспетчер памяти анализирует исключение, обнаруживает, что для страницы установлен механизм создает новую страницу в физической памяти, копирует в нее первоначаль ную страницу и модифицирует элемент таблицы страниц так, чтобы он указывал на новую страницу, а затем производит первоначальную запись в память. Таким образом, каждый процесс получит свою копию первоначальной страницы каждый со своими изменениями.
Рис. 7 Все исполняемые модули в ОС NT находятся в совместно используемой памяти с задействованием Это означает, например, что при отладке кода DLL, используемой в некотором процессе, при установке точки прерывания (что осуще ствляется записью в код), она будет присутствовать только в этом адресном про странстве. Для постановки точки прерывания на код в DLL так, чтобы она срабатывала вне зависимости от адресного пространства, необходимо предпринять специальные дей ствия: 1) Для данного виртуального адреса нужно получить физический адрес. 2) Для физического адреса создать новую запись в таблице страниц, устанавлива ющих для страницы доступ на запись. Создание записи в таблице страниц озна чает получение нового виртуального адреса.
3) При записи полученному виртуальному адресу будет модифицирована стра ница физической памяти, соответственно это изменение увидится во всех кон текстах памяти, на которые отображена эта страница. Естественно, первые два шага можно сделать только в коде режима ядра, то есть из драйвера.
Объект Секция Диспетчер Памяти в NT экспортирует единственную структуру для контроля дан ных - Объект-Секцию. Подобно другим объектам, Объект-Секция может быть имено ванным, то есть имя будет видимо в пространстве имен Диспетчера Объектов. Секция может быть использована драйвером для отображения участка памяти си стемного адресного пространства (в том числе в пользовательское адресное пространство. В этом случае прикладная программа и драйвер получат в свое распоряжение совместно используемую область памяти, что может быть полезно, на пример, при необходимости передачи большого объема данных от драйвера к прило жению. Секция используется для описания всего, что может быть отображено в память. Например, для каждого отображаемого в память файла создается объект-Секция. При этом, сколько бы не было открытий такого файла, объект-Секция всегда одна. Поскольку все исполняемые файлы загружаются посредством механизма отображения памяти, единственность объекта-секции всегда гарантирует наличие только одной копии тако го файла в При создании секции указывается режим доступа (чтение/запись/исполнение). Этот режим доступа будет влиять на записи в таблице страниц, относящиеся к секции. Функции работы с секциями: • ZwOpenSectionQ; • •
2.3.5.6. Таблица описания памяти (Memory Descriptor List, MDL) Диспетчер памяти использует структуру MDL для описания набора страниц физи ческой памяти, составляющих буфер виртуальной памяти в контексте памяти некото рого процесса. Интерпретация MDL не зависит от контекста памяти, поскольку MDL оперирует со страницами физической памяти. Получив для данного буфера описание в виде MDL, драйвер в дальнейшем может использовать буфер в контексте памяти любого процесса. Для того, чтобы обращаться к такой памяти, необходимо получить для MDL адрес памяти в системном адресном пространстве. Сделать это можно с по мощью функции Кроме того, буфер, описанный с помощью MDL, может быть использован для опе раций DMA. Для этого физический адрес внутри MDL должен быть транслирован в
логический адрес (имеющий смысл только для данного устройства DMA) с помощью функции Интересно отметить следующий момент. предназначен для описания буфе ра данных, непрерывного в виртуальной памяти. Однако страницы физической па мяти, список которых собственно и содержит могут располагаться в памяти произвольным образом. Это дает возможность «собирать» непрерывный в виртуаль ной памяти буфер из различных фрагментов физической памяти без копирования памяти. В основном, мы будем встречаться с при передаче данных в драйвер ством пакетов (которые будут описаны в последующих разделах).
2.3.5.7. Функции работы с памятью Из всего вышеизложенного нужно выделить основные моменты: 1) Все адресное пространство процесса (контекст памяти) делится на две области - системное адресное пространство (верхние 2 Гб) и пользовательское адрес ное пространство (нижние 2 Гб): • Системное адресное пространство всегда одинаково, вне зависимости от текущего контекста памяти. • Пользовательское адресное пространство разное для каждого контекста па мяти. 2) Код режима и режима ядра пользуется для доступа к памяти разными селекторами: • Для одного контекста памяти селекторы адресуют одну и ту же физическую память, но с разными режимами доступа. • Селекторы не имеют ничего общего с контекстом памяти. • По селекторам для ядра можно получить доступ (по крайней мере, для чте ния) ко всему адресному пространству в данном контексте памяти, а по се лекторам для ядра только для пользовательской области памяти. 3) Код в пользовательском адресном пространстве всегда вытесняемый, если не предприняты меры по созданию новой записи в каталоге страниц, указываю щих на невытесняемую память. 4) Код, работающий на уровне IRQL большем или равном D I S P A T C H L E V E L , может использовать только невыгружаемую память (соответственно, сам код должен находиться в невыгружаемой памяти). 5) В ОС существуют функции для выполнения любой работы с памятью: • Выделение/освобождение памяти в выгружаемой/невыгружаемой, кешируемой/некешируемой памяти. • Преобразование адресов памяти (виртуальный в физический, физический в виртуальный). • Проверка доступности памяти. Рассмотрим эти функции более подробно.
Выделение памяти IN IN PVOID IN IN ULONG Где:
PoolType,
IN ULONG принимает следующие значения:
Тип памяти (PoolType)
PagedPool
VOID PVOID VOID
Описание Обычное выделение памяти из Nonpaged Pool. Выделение памяти из Nonpaged Pool будет нено по линии кеша. Используется в специальных случаях драйверами, необходимыми загрузки системы. Обычное выделение памяти из Paged Pool. Выделение памяти из Paged Pool будет выров нено по линии кеша. PVOID ULONG I N PVOID B a s e A d d r e s s ,
IN
ULONG
PVOID
ULONG IN
VOID
PVOID
2.3.5.7.2. Список заранее выделенных блоков памяти (Lookaside List) Во многих случаях, выделение и освобождение временного буфера памяти долж но происходить очень часто, дня уменьшения накладных расходов служит Lookaside List список заранее выделенных блоков памяти фиксированного размера. Первоначально, память выделяется только под небольшой заголовок со служебной информацией. При каждом запросе на выделение памяти проверяется, есть ли в списке свободные блоки. Если их нет они выделяются из выгружаемой или невыгружаемой памяти. Если есть, то помечаются, как используемые и выдаются для использования. VOID IN IN Allocate IN Free IN ULONG
IN Size, IN ULONG Tag, IN Depth VOID E x I n i t i a l i z e P a g e d L o o k a s i d e L i s t IN IN Allocate IN Free OPTIONAL, IN ULONG IN ULONG S i z e , IN ULONG Tag, IN USHORT Depth
VOID IN IN VOID IN IN VOID
Lookaside, PVOID Lookaside, PVOID ExDeleteNPageclLookasideList
VOID
Пространства ввода/вывода и отображение памяти устройств Смотри BOOLEAN IN IN IN OUT PVOID IN IN VOID IN
раздел «Типы адресов в NT». HalTranslateBusAddress ULONG PHYSICAL_ADDRESS OUT PULONG
(IN
ULONG BOOLEAN PVOID
BaseAddress,
ULONG
2.3.5.7.4. Управление памятью и MDL BOOLEAN VOID
PVOID OUT
VirtualAddress
IN IN
PVOID PVOID PMDL PVOID
VOID PMDL IN IN IN
ULONG BOOLEAN BOOLEAN
IN OUT VOID VOID
PMDL PMDL
Унифицированная модель драйвера В исполнительной системе драйвер устройства и файловая система строятся и выг лядят для остальной части ОС одинаково. Более того, именованные каналы и сетевые редиректоры рассматриваются, как файловые системы, и реализованы в виде соответ ствующих драйверов. Каждый драйвер - это автономный компонент, который можно динамически загружать и выгружать из системы в зависимости от потребностей пользо вателя. Унифицированный модульный интерфейс, предоставляемый драйверами, позво ляет диспетчеру ввода/вывода не видеть их структуру или внутренние детали. Драйве ры могут вызывать друг друга через диспетчер ввода/вывода, что обеспечивает неза висимую обработку запроса ввода/вывода на нескольких уровнях. Драйверы являются модульными и могут располагаться слоями один над другим, что позволяет, например, драйверам разных файловых систем использовать для досту па к файлам один и тот же драйвер диска. Послойная модель драйверов позволяет также вставлять в иерархию новые драйверы. Драйвер - это особый тип динамически подключаемой библиотеки. Фактически, это DLL, удовлетворяющая ряду дополнительных требований и имеющая расширение Как и любая DLL, драйвер имеет свою точку входа - функцию, вызываемую при загрузке исполняемого файла в память. Адрес этой точки входа содержится в служеб ной информации в самом модуле. При создании модуля в процессе компиляции на стройки среды разработки предполагают, что имя соответствующей функции будет DriverEntry, хотя оно может быть заменено на любое другое. Момент загрузки драй вера определяется соответствующими данному драйверу настройками в реестре (ключ Start). Этими настройками управляет Service Control Manager (SCM), хотя они могут быть изменены и вручную. Прежде чем перейти к описанию структуры драйвера желательно ознакомиться с такими важными понятиями как объект-файл, и объект-устройство.
Объект-файл (файловый объект) Код пользовательского режима может получить доступ к файлам на диске или все возможным устройствам (физическим, логическим и виртуальным) только через опи сатели файловых объектов, обеспечиваемых менеджером ввода/вывода по запросу от пользовательской программы на файла или устройства. После от крытия/создания виртуального файла, обозначающего любой источник или приемник ввода/вывода (работа с которым идет так, как если бы он был обычным файлом на диске), программы могут осуществлять в этот виртуальный файл, манипу лируя им посредством описателя. Итак, файловый объект это объект, видимый из режима пользователя, который представляет всевозможные открытые источники или приемники ввода/вывода: файл на диске или устройство (физическое, логическое, виртуальное). Физическим устрой ством может быть, например, последовательный порт, физический диск; логическим логический диск; виртуальным - виртуальный сетевой адаптер, именованный канал, Всякий раз, когда некоторый поток открывает файл, создается новый файловый объект с новым набором атрибутов. В любой момент времени сразу несколько файло вых объектов могут быть ассоциированы с одним разделяемым виртуальным файлом, но каждый такой файловый объект имеет уникальный описатель, корректный только в контексте процесса, поток которого инициировал открытие файла. Возможны ситуа ции, когда два процесса имеют разные описатели, ссылающиеся на один и тот же фай ловый объект: 1) когда процесс дублирует описатель файлового объекта для другого процесса; 2) когда дочерний процесс наследует описатель от родительского. Файловые объекты, как и другие объекты, имеют иерархические имена, охраня ются объектной защитой, поддерживают синхронизацию и обрабатываются систем ными сервисами.
2.3.6.2. Объект-драйвер Драйверы скрыты от программ пользовательского режима. Устройства (физичес кие, логические и виртуальные), создаваемые и управляемые драйверами, видны про граммам пользовательского режима как именованные файловые объекты. Как уже от мечалось ранее, код пользовательского режима может получить доступ к устройству только через описатель, возвращаемый менеджером ввода/вывода во время открытия/ создания файлового объекта, представляющего устройство. (В структуре JECT есть указатель на ассоциированный с данным файловым объектом.) Объект-драйвер представляет в системе некоторый драйвер Windows NT и хра нит для диспетчера ввода/вывода адреса стандартных процедур (точки входа), кото рые драйвер может или должен иметь в зависимости от того, является ли он драйвером верхнего или нижнего уровней. Объект-драйвер описывает также, где драйвер загру жен в физическую память и размер драйвера. Объект-драйвер описывается частично документированной структурой данных 3 Зак. 910
Диспетчер определяет объект-драйвер и использует экземпляры этого типа для регистрации и отслеживания информации о загруженных образах драй веров. Этот объект создается менеджером ввода/вывода при загрузке драйвера в сис тему, после чего диспетчер вызывает процедуру инициализации драйвера и передает ей указатель на объект-драйвер. Эта процедура устанавливает стандартные и о п ц и о н а л ь н ы е т о ч к и входа п р о ц е д у р д р а й в е р а , с о д е р ж а щ и е с я в с т р у к т у р е чтобы в дальнейшем диспетчер мог направлять па кеты запроса ввода/вывода IRP соответствующей процедуре. В DriverEntry устанавли ваются также точки входа для других процедур, например для Unload. Во вре мя инициализации также может происходить считывание в поля объектадрайвера из базы данных реестра диспетчера конфигурации. Пакеты IRP, направляемые стандартным процедурам, содержат, кроме всего про чего, указатель на объект устройство, который является устройством назначения для конкретного запроса ввода/вывода. Объект-драйвер является скрытым для кода пользовательского уровня, то есть толь ко определенные компоненты уровня ядра (в том числе и диспетчер знают внутреннюю структуру этого типа объекта и могут получать доступ ко всем данным, содержащимся в объекте, напрямую. Microsoft не гарантирует неизменность недокументированных полей любых сво их структур в последующих версиях ОС. Однако фактически, некоторые недокумен тированные в DDK элементы различных системных структур документированы в дру гом пакете для разработчика Kit.
Объект-устройство Диспетчер ввода/вывода определяет тип объекта объект-устройство, использу емый для представления физического, логического или виртуального устройства, чей драйвер был загружен в систему Формат объекта-устройство определяется частично документированной структурой данных DEVICE_OBJECT. Хотя объект-устройство может быть создан в любое время посредством вызова функции IoCreateDeviceQ, обыч но он создается внутри DriverEntry. Объект-устройство описывает характеристики устройства, такие как требование по выравниванию буферов и местоположение очереди устройства, для хранения по ступающих пакетов запросов ввода/вывода. Процедура инициализации драйвера DriverEntry создает по одному объекту-уст ройству для каждого устройства, управляемого данным драйвером, посредством вы зова процедур, предоставляемых менеджером ввода/вывода. Процедура инициализа ции драйвера должна создавать, по крайней мере, один объект-устройство, некоторые драйверы должны создавать более одного объекта-устройства в зависимости от уров ня, на котором они располагаются в цепочке драйверов. В каждом есть указатель на следующий объект-устройство (либо NULL) и указатель на объект-драйвер, управляющий данным устройством. Благодаря этому диспетчер ввода/вывода может определить, процедуру какого драйвера он должен
вызвать при получении запроса к устройству. Диспетчер ввода/вывода поддерживает также указатель на объект-устройство, созданный драйвером, в объекте-драйвере. Большинство драйверов NT используют структуру данных ука затель на которую хранится в объекте-устройстве, а ее размер и содержимое определя ется при создании объекта-устройства во время инициализации драйвера. Внутренняя структура DeviceExtension определяется разработчиками драйверов и используется для хранения информации о состоянии устройства, некоторого контекста текущего запро са ввода/вывода, для хранения указателей на описатели различных объектов ядра, то есть для хранения любых данных, которые нужны драйверу. Диспетчер выделяет память под DeviceExtension из резидентной системной области памяти. Так как большинство процедур драйверов исполняются в контексте произвольно го потока, то есть потока, оказавшегося текущим на момент вызова процедуры драйве ра, то область DeviceExtension является одним из первых мест, где драйвер хранит необходимые ему данные.
Имя устройства и символическая связь При создании объекта-устройства также может быть указано его имя, которое бу дет видимо в директории «\Device» пространства имен диспетчера объектов. Объек ты- устройства используются в Windows NT как точки входа в пространства имен, не контролируемые менеджером объектов. Если при разборе имени объекта диспетчер объектов встречает объект-устройство, то он вызывает метод разбора, связанный с этим устройством. Если имя не указано, объект-устройство может быть использован только внутри драйвера и недоступен извне. Если быть более точным, такой объект-устройство мож но использовать, если иметь указатель на описывающую его структуру. Именованный объект-устройство доступен для использования через вызов сис темного сервиса NtCreateFile(). Для обращения к именованному объекту-устройству из подсистемы Win32 посред ством функции CreateFileQ должны быть предприняты дополнительные действия. Функция CreateFileQ ищет имя устройства в директории Диспетчера Объектов (NT 4.0 и Win2000), либо «\DosDevices»(NT Поэтому в соответствующей дирек тории, а для большей совместимости это должна быть «\DosDevices», должен быть создан объект символическая связь), указывающий на имя устройства в директории «\Device». Обычно связь создается в самом драйвере, хотя это можно сделать и из п р и к л а д н о й п р о г р а м м ы пользовательского р е ж и м а с п о м о щ ь ю
Взаимосвязь основных объектов Что происходит при успешном открытии (неважно, с помо щью какой функции: CreateFile() или Для описания состояния работы с каждым объектом, открытым с помощью этих функций, создается новый экземпляр о б ъ е к т а - ф а й л а . Именно отсюда название функции - CreateFile.
С точки зрения прикладного уровня ОС функции открытия файла могут приме няться для открытия файлов, устройств, именованных каналов, почтовых слотов и т.п. Однако, с точки зрения ядра ОС, объекта-файла (в смысле файл на жестком диске) не существует, как не существует объектов именованный канал или почтовый слот. В дей ствительности объект-файл будет связан с некоторым объектом-устройством, для ко торого имеет смысл, допустим, понятие «файл» (в смысле файл файловой системы). Соответственно, каждая операция на прикладном уровне будет про изводиться с некоторым объектом-файлом. Причем конкретный объект будет указан не напрямую, а через так называемый описатель (HANDLE), возвращаемый как ре зультат работы функции CreateFile(). Важно отметить, что каждый процесс имеет свою собственную таблицу описате лей, обеспечивая уникальность описателей только в рамках своей таблицы. Это озна чает, например, что для двух процессов А и В описателю со значением 1 будут в общем случае соответствовать два различных объекта-файла (см. рис. 8). Кроме того, ОС поддерживает механизм наследования описателей. В этом случае два разных процесса через два разных описателя будут разделять один и тот же объект-файл. Процсс А
В
1
Object 2
Driver Object
размещения ввода/вывода
DcviccObject
t Device Object
FilcObjcct DcviccObject
t Device Object NULL DcviccExtcnsion
NULL
Extension
8
размещения ввода/вывода
П р а к т и ч е с к и у н и к а л ь н о с т ь описателя только в контексте данного п р о ц е с с а означает невозможность использования описателя драйвером при работе в слу чайном контексте. Описатель обязательно должен быть переведен в указатель на объект посредством вызова функции диспетчера объектов ObReferenceObjectByHandle().
2.4. Установка, драйвера
запуск и остановка
С е й ч а с мы коротко р а с с м о т р и м о п е р а ц и и у с т а н о в к и и у п р а в л е н и я д р а й в е р а м и . Д р а й в е р ы в NT п о д д е р ж и в а ю т д и н а м и ч е с к у ю загрузку и выгрузку. И н ф о р м а ц и я о д р а й в е р е , такая, как его имя, тип, м е с т о н а х о ж д е н и е , с п о с о б загруз ки и д р . н а х о д и т с я в р е е с т р е в к л ю ч е П о д р о б н о обо всех п о д к л ю ч а х , которые м о г у т т а м находиться, вы м о ж е т е узнать в статье « U s i n g The NT Registry for Driver в директории NTInsider, либо в DDK Installation\Configuration Registry. Управлением с е р в и с а м и и д р а й в е р а м и в системе занимается Service Control Manager Он управляет базой данных установленных сервисов и драйве ров, обеспечивает единый способ контроля над ними, а т а к ж е предоставляет API. П о д р о б н у ю и н ф о р м а ц и ю о ф у н к ц и о н и р о в а н и и SCM и п р е д о с т а в л я е м о м им API можно получить в M S D N Library в разделе Platform Services\DLLs, Processes and Threads\Services. Примерная последовательность действий при установке/удалении запуске/ос тановке драйвера следующая: 1) открытие SCM OpenSCManagerQ; 2) получение описателя для вновь созданного или уже существующего драйве ра - CreateService() или 3) запуск\остановка\удаление драйвера - StartService(), StopServiceQ, DeleteService(). Установленный в системе драйвер также может быть запущен/остановлен с по м о щ ь ю команды net start\net stop. Рассмотрим другие способы установки драйверов: • Text Setup. Этот механизм используют драйверы, устанавливаемые при ус тановке ОС. Этот механизм требует создания скрипт-файла txtsetup.oem. Его формат описан в DDK, имеются примеры в \ddk\src\setup. В этом файле про грамме установки NT указывается, какие файлы и куда копировать и какие ключи реестра создавать. • GUI Setup. Драйверы для стандартных устройств, устанавливаемые по окон чании установки О С , используют inf-файлы, формат которых и примеры так же приведены в DDK. • Custom Setup. Прикладная программа, использующая функции SCM.
2.4.1.1. Структура драйвера
Точки входа драйвера При написании любого драйвера необходимо помнить четыре основных момента: 1) возможные точки входа драйвера; 2) контекст, в котором могут быть вызваны точки входа драйвера; 3) последовательность обработки типичных запросов; 4) Уровень IRQL, при котором вызывается точка входа, и, следовательно, ограни чения на использование некоторых функций ОС каждая функция ОС может быть вызвана только при определенных уровнях IRQL (см. описание любой функции в DDK, там всегда указаны эти уровни). Архитектура драйвера Windows NT использует модель точек входа, в которой Дис петчер вызывает специфическую подпрограмму в драйвере, когда тре буется, чтобы драйвер выполнил специфическое действие. В каждую точку входа пе редается определенный набор параметров для драйвера, чтобы дать возможность ему выполнить требуемую функцию. Базовая структура драйвера состоит из набора точек входа, наличие которых обя зательно, плюс некоторое количество точек входа, наличие которых зависит от назна чения драйвера. Далее перечисляются точки входа либо классы точек входа драйвера: 1) DriverEntry. Диспетчер Ввода/вывода вызывает эту функцию драйвера при пер воначальной загрузке драйвера. Внутри этой функции драйверы выполняют инициа лизацию как для так и для любых устройств, которыми они управляют. Эта точка входа требуется для всех NT драйверов. 2) Диспетчерские (Dispatch) точки входа. Точки входа Dispatch драйвера вызы ваются Диспетчером Ввода/вывода, чтобы запросить драйвер инициировать некото рую операцию 3) Unload. Диспетчер Ввода/вывода вызывает эту точку входа, чтобы запросить драйвер подготовиться к немедленному удалению из системы. Только драйверы, кото рые поддерживают выгрузку, должны реализовывать эту точку входа. В случае вызова этой функции, драйвер будет выгружен из системы при выходе из этой функции вне зависимости от результата ее работы. 4) Fast Вместо одной точки входа, на самом деле это набор точек входа. Дис петчер Ввода/вывода или Диспетчер Кэша вызывают некоторую функцию быстрого ввода/вывода (Fast I/O), для инициирования некоторого действия Эти под программы поддерживаются почти исключительно драйверами файловой системы. 5) Управление очередями запросов IRP (сериализация процесс выполнения различных транзакций в нужной последовательности). Два типа очередей: Системная очередь и очереди, управляемые драйвером. Диспетчер Ввода/вывода исполь зует точку входа Startlo только в драйверах, которые используют механизм Системной Очереди (System Queuing). Для таких драйверов, Диспетчер Ввода/вывода вызывает эту точку входа, чтобы начать новый запрос
6) Reinitialize. Диспетчер вызывает эту точку входа, если она была зарегистрирована, чтобы позволить драйверу выполнить вторичную инициализацию. 7) Точка входа процедуры обработки прерывания Interrupt Service Routine). точка входа только если драйвер поддерживает обработку прерывания. Как только драйвер подключен к прерываниям от своего устройства, его ISR будет вызы ваться всякий раз, когда одно из его устройств запрашивает аппаратное прерывание. 8) Точки входа вызовов отложенных процедур (DPC - Deferred Procedure Call). типа DPC: и Драйвер использует эти точки входа, чтобы завершить работу, которая должна быть сделана в результате появления прерывания или другого специального условия. Процедура отложенного вызова выполняет боль шую часть работы по обслуживанию прерывания от устройства, которое не требует высокого уровня прерывания IRQL, ассоциированного с процессором. 9) Диспетчер вызывает эту точку входа в ответ на запрос драйвера на захват одной из спин-блокировок его ISR. 10) AdapterControl. Диспетчер вызывает эту точку входа, чтобы ука зать, что общедоступные DMA-ресурсы драйвера доступны для использования в пере даче данных. Только некоторые драйверы устройств DMA реализуют эту точку входа. 11) Cancel. Драйвер может определять точку входа Cancel для каждого кото рый он содержит во внутренней очереди. Если Диспетчер Ввода/вывода хочет отме нить конкретный IRP, он вызывает подпрограмму Cancel, связанную с этим IRP. 12) IoCompletion. Эту точку входа для каждого IRP может устанавливать драйвер верхнего уровня при многоуровневой организации. Диспетчер вызыва ет эту подпрограмму после того, как все драйверы нижнего уровня завершили IRP. 13) IoTimer. Для драйверов, которые инициализировали и запустили поддержку Диспетчер Ввода/вывода вызывает эту точку входа приблизительно каждую секунду. 14) Эта точка входа вызывается, когда истекает время для зап рошенного драйвером таймера.
2.4.1.1.2. Контекст исполнения и уровень IRQL Говоря о точках входа в драйвер, необходимо отметить контекст, при котором эти точки входа могут быть вызваны. Вначале необходимо определиться с тем, что мы подразумеваем под контекстом исполнения? Контекст исполнения определяется двумя составляющими: • исполняемый в настоящее время поток (контекст планирования потока - thread scheduling context); • контекст памяти процесса, которому принадлежит поток. Текущий контекст исполнения может принадлежать одному из трех • контекст процесса (далее - системный контекст); • контекст конкретного потока и процесса; • контекст случайного потока и процесса (далее - случайный контекст).
Различные точки входа в драйвер могут вызываться в контексте, принадлежащем одному из этих классов. DriverEntry всегда вызывается в системном контексте. Диспетчерские функции для драйверов верхнего уровня (то есть получающих зап рос от прикладных программ) вызываются в контексте инициирующего запрос потока. Диспетчерские функции драйверов остальных уровней, получающие запрос от драйвера верхнего уровня, вызываются в случайном контексте. Все точки входа, связанные с сериализацией запросов ввода/вывода или с обра боткой прерываний и DPC, вызываются в случайном контексте. всегда вызывается на IRQL, равным Диспетчерские точки входа вызываются на IRQL, равным PASSIVEJLEVEL или Вызов отложенных процедур на D I S P A T C H L E V E L обработки прерываний - на одном из DIRQL.
налагаемые на драйвер Драйвер режима ядра не может использовать API пользовательского уровня или стандартные библиотеки времени исполнения языка С. Можно использовать только функции ядра. 2) Драйвер не может осуществлять операции с числами с плавающей точкой. По пытка сделать это может вызвать аварийную остановку системы. Причина - в основе реализации архитектуры ММХ. Не вдаваясь в подробности можно сказать, что в этой архитектуре для обозначения регистров М М Х использованы те же обозначения, что и для использования регистров FPU. Переключение между использованием регистров производимое на пользовательском уровне, невидимо для драйвера. 3) Драйвер не может манипулировать физической памятью напрямую. Однако он может получить виртуальный адрес для любого физического адреса и манипулиро вать 4) Код драйвер не должен долгое время работать на повышенных уровнях IRQL. Другие ограничения можно посмотреть в [Developing Windows NT Device Driver, chapter 5, Driver Последующие разделы будут посвящены описанию различных точек входа драйвера.
Точка входа DriverEntry Диспетчер ввода/вывода вызывает точку входа DriverEntry при загрузке драйвера. В NT может существовать только один экземпляр драйвера, вне зависимости от числа физических устройств, контролируемых им. Таким образом, DriverEntry вызывается только один раз, на уровне IRQL равном P A S S I V E L E V E L в системном контексте. Прототип DriverEntry: IN
Где: DriverObject - указатель на объект-драйвер, соответствующий загружаемому драйверу; - указатель на строку в формате Unicode с именем ключа реес тра, соответствующего загружаемому драйверу. Возвращаемое значение имеет тип NTSTATUS. Если возвращается успешный ста тус завершения, диспетчер немедленно позволяет производить обработ ку запросов к объектам-устройствам, созданным драйвером. Во всех остальных слу чаях драйвер не загружается в и запросы к нему не передаются. В функции обычно происходит: • определение всех других точек входа драйвера (их перечень см. в предыдущем разделе); • определение конфигурации аппаратного устройства; • создание одного или нескольких объектов-устройств. Информация об определении всех других точек входа драйвера будет описана в следующих разделах.
Определение конфигурации аппаратного устройства Довольно обширную тему, связанную с подготовкой драйвера к использованию аппаратного устройства мы пропустим. могут обратиться к [Device Driver Development, chapter 13. Driver Тем не менее, один часто используемый в драйверах физических устройств мо мент, а именно использование реестра - необходимо упомянуть. В соответствии с принятыми соглашениями драйверы хранят настроечные пара метры в ключе реестра или Наиболее простой способ запроса содержимого реестра предоставляет функция Имя ключа реестра содержится во втором параметре функции DriverEntry.
Создание объекта-устройства и символической связи Как уже говорилось, объект-устройство представляет физическое, логическое или виртуальное устройство, которое должно быть использовано в качестве получателя запросов ввода/вывода. Именованный объект-устройство может быть использован либо из прикладного уровня посредством вызова (Nt)CreateFile(), либо из другого драйвера посредством вызова IoGetDeviceObjectPointer(). Создается объект-устройство с помощью вызова функции IoCreateDevice(). NTSTATUS IN ULONG IN IN I N ULONG D e v i c e C h a r a c t e r i s t i c s , I N BOOLEAN E x c l u s i v e , OUT PDEVICE OBJECT
Стоит упомянуть, что при необходимости создания нескольких именованных объек тов-устройств одного типа (параметр DeviceType) стандартные драйверы NT пользу ются определенным соглашением (которое не является обязательным). А именно: к фиксированному имени прибавляется номер устройства, начиная с нуля. СОМО, Это позволяет открывать устройства, про которые заранее не извес тно, существуют они или нет. Для получения следующего номера, для которого еще не с о з д а н о у с т р о й с т в о , д р а й в е р может использовать ф у н к ц и ю IoGetConfiguration Эта функция возвращает указатель на структуру, содержащую число устройств текущего драйвера для каждого обнаруженного типа устройства. Параметр «тип устройства» (DeviceType) может принимать или одно из предопре деленных в ntddk.h значений, начинающихся с либо значение в диа пазоне зарезервированное дня нестандартных устройств. Н е о б х о д и м о о т м е т и т ь , что при о п р е д е л е н и и кода з а п р о с а в в о д а / в ы в о д а к CTBy(Device control code, будет рассмотрен в следующем разделе) следует использовать тот же тип устройства. Параметр «характеристики устройства» (DeviceCharacteristics) является набором флагов и представляет интерес в основном для разработчиков стандартных типов уст ройств. Среди возможных значений флагов Параметр «эксклюзивность устройства» (Exclusive). Когда этот параметр установ лен в TRUE, для устройства может быть создан единственный объект-файл. Как видно из рис. 8, взаимосвязь различных объектов может быть довольно сложной. При этом довольно часто в драйвере необходимо представлять, от какого файлового объекта поступил запрос. Для упрощения ситуации можно разрешить использование только одного файлового объекта, то есть в один момент времени устройство будет использо ваться только из одного места. Как уже говорилось, при открытии устройства из подсистемы Win32 для имени устройства в директории \Device в пространстве имен диспетчера объектов должна быть создана символическая связь в директории или \DosDevices. Это можно сде лать в драйвере с помощью функции либо из прикладной про граммы с помощью
Передача данных от приложения к драйверу. Асинхронная обработка Код пользовательского уровня не может напрямую вызвать код режима ядра. Для этого существуют специальные прерывания. Одним из них является прерывание 2Е вызов системного сервиса. Диспетчер ввода/вывода обрабатывает вызовы системных сервисов специальным образом (см. рис. 9). В своем обработчике системного сервиса он создает специальный запрос ввода/вывода IRP и передает его на обработку некото рому объекту-устройству, после чего работа обработчика может завершиться, но обра ботка IRP при этом может быть не закончена.
Рис. 9 Модель ввода/вывода, предусматривающая завершение функции ввода/вывода до завершения запроса называется асинхронным вводом/выводом. Асинх ронный позволяет программе запросить выполнение операции ввода/вы вода, после чего продолжать выполнение другой операции, пока устройство не закон чит пересылку данных. Система автоматически уведомляет программу о завершении операции ввода/вывода. Модель ввода/вывода, обеспечиваемая Диспетчером ввода/вывода, асинхронна всегда, хотя этот факт может быть скрыт функциями подсистемы окружения. Напри мер, так происходит в случае функции CreateFile(). Эта функция не является асинх ронной, хотя пользуется асинхронной функцией При асинхронном вводе/выводе более поздний по времени запрос может быть закончен до более ранних запросов. Вызов прерывания и переключение процессора в режим ядра не влияют на теку щий контекст памяти. Из этого следует, что при обработке вызова системного сервиса и Диспетчер памяти, и соответствующая точка входа драйвера работают в контексте памяти процессора инициатора запроса ввода/вывода.
В случае, когда устройство обрабатывает запрос сразу при его по ступлении, диспетчеру ввода/вывода не требуется переключение контекста памяти для уведомления процесса о завершении запроса. В случае, когда устройство отложило запрос в очередь запросов, в тот момент, когда подошла очередь запроса на обработку, контекст памяти будет неизвестен - слу чайный контекст памяти. При этом диспетчеру ввода/вывода понадобится механизм уведомления нужного процесса о завершении запроса ввода/вывода. Таким механиз мом является механизм Асинхронного Вызова Процедуры (Asynchronous procedure Вкратце он состоит в том, что прикладная программа предоставляет диспетчеру ввода/вывода адрес функции, которая должна быть вызвана при запроса ввода/вывода. При запросе АРС диспетчер указывает этот адрес и по ток, в котором должна быть вызвана эта функция. АРС запрашивается с помощью ге нерации специального прерывания на уровне IRQL равном A P C L E V E L . Запрос АРС откладывается в очередь АРС и будет выполнен, когда управление получит нужный поток и текущий уровень IRQL будет меньше
Выполнение асинхронного запроса Выполняя асинхронный ввод/вывод, поток пользовательского режима может ис пользовать для синхронизации с моментом завершения операции ввода/вывода: 1) ожидание у описателя файла; 2) ожидание у объекта-события, используемого для каж дого запроса ввода/вывода; 3) асинхронный вызов процедуры (Asynchronous procedure call, АРС) пользовательского режима. Выполнение асинхронного запроса приводится на примере запроса на запись, про ходящего через несколько слоев драйверов. При этом добавляется один или несколько уровней обработки. Вместо повторного использования одного IRP, драйвер верхнего уровня может создать группу ассоциированных IRP, которые будут управлять одним запросом па раллельно. Этот драйвер отслеживает завершение всех ассоциированных IRP, и толь ко потом завершает исходный IRP. 1) Подсистема среды или клиентская DLL вызывает функцию «родного» API NtWriteFile() с описателем файлового объекта. 2) Диспетчер ввода/вывода создает IRP, в котором он сохраняет указатель на фай ловый объект и код функции, указывающий драйверу верхнего уровня тип опера ции. Далее диспетчер ввода/вывода отыскивает драйвер верхнего уровня и передает ему IRP. 3) Драйвер верхнего уровня определяет по информации в соответствующей ему области стека IRP, какую операцию он должен выполнить. Драйвер верхнего уровня может, либо разбить первоначальный запрос на несколько запросов, путем размеще ния новых пакетов IRP (возможно дня нескольких драйверов устройств), либо может повторно использовать первоначальный IRP, заполнив область стека IRP, соответству ющую нижележащему драйверу Затем драйвер передает этот IRP (или несколько ас социированных IRP) посредством вызова диспетчера
4) Вызванный драйвер нижнего уровня (пусть это будет уже драйвер устройства) проверяет свою область стека IRP, чтобы определить какую операцию он должен вы полнить на и в поле статуса операции ввода/вывода в пакете IRP ставит код выполняется». Затем он вызывает функцию реализуе мую менеджером 5) Диспетчер ввода/вывода занято ли уже устройство обработкой дру гого IRP, и если да, то ставит текущий IRP в очередь устройства и возвращает управле ние. Если нет, то диспетчер передает IRP соответствующей процедуре драйвера устройства, которая начинает операцию ввода/вывода на устройстве. Начав операцию на устройстве, драйвер устройства возвращает управление. Заметьте, что асинхронный запрос возвращает управление вызывающей программе немедленно, даже если очередь устройства пуста. Вторая стадия обработки запроса состоит в обслуживании преры вания от устройства: 1) После завершения передачи данных устройство генерирует прерывание для обслуживания. Диспетчер прерываний ядра передает управление процедуре обслужи вания прерываний (Interrupt Service Routine, ISR) драйвера устройства. 2) ISR выполняет минимум работы по сохранению необходимого контекста опе рации. ISR также вызывает соответствующие сервисы диспетчера ввода/вывода для постановки в очередь отложенного вызова процедуры (Deferred Procedure Call, DPC). DPC - асинхронная процедура драйвера устройства, выполняющая завершение требу емой операции при более низком уровне IRQL процессора. Прерывания от устройств имеют высокий IRQL, но IRQL процессора, выполняю щего ISR, остается на этом уровне только на время, необходимое для того, чтобы зап ретить новые прерывания от устройства. После завершения ISR, ядро понижает IRQL процессора до уровня, на котором тот находился до прерывания. 3) Если IRQL процессора понизится ниже уровня планирования потоков и об работки отложенного вызова процедуры (Dispatch level), возникнет прерывание DPC, и диспетчер прерываний передаст управление процедуре D P C драйвера уст ройства. 4) Процедура DPC использует для завершения операции сохраненный в процеду ре ISR контекст операции, запоминает статус операции и выбирает из очереди следующий пакет IRP, запустив тем самым новый запрос Затем устанавливает статус только что завершенной операции в поле статуса IRP и вызывает диспетчер для завершения обработки запроса и удаления IRP. 5) Диспетчер ввода/вывода обнуляет область стека IRP, соответствующую драйве ру устройства, и вызывает завершающую процедуру драйвера верхнего уровня. Эта процедура проверяет поле статуса операции, чтобы определить, нужно ли повторить запрос, или можно освободить этот IRP (если это IRP было размещено этим драйве ром). Драйвер верхнего уровня собирает информацию о статусах операций для всех размещенных им пакетов IRP, чтобы установить общий статус в первоначальном паке те IRP и завершить его.
6) Диспетчер ввода/вывода ставит в очередь АРС-объект (Asynchronous Procedure Call - асинхронный вызов процедуры) для завершения в контексте пото ка - инициатора запроса. (Подсистема должна скопировать некоторые данные из системной области памяти в виртуальное адресное пространство процесса, поток которого инициировал запрос во время исполнения такого пото ка, это достигается путем пересылки АРС-объекта режима ядра в очередь АРС-объектов этого потока.) 7) Когда поток - инициатор запроса, начнет исполняться в следующий раз, то никнет прерывание обработки асинхронного вызова процедуры. Диспетчер прерыва ний передает управление процедуре А Р С режима ядра (процедуре А Р С диспетчера 8) Процедура АРС режима ядра записывает данные в адресное пространство по тока-инициатора запроса, устанавливает описатель файла в состояние устанавливает в очередь на исполнение АРС-объект пользовательского режима (если нужно) и удаляет IRP.
2.4.2. Пакет запроса ввода/вывода (IRP) 2.4.2.1. Характеристики подсистемы ввода/вывода В предыдущем разделе мы рассмотрели схему использования системных серви сов, то есть прохождение запроса от приложения к и обратно. Компонентом ОС, отвечающим за реализацию этой схемы, является Диспетчер вывода. Диспетчер ввода/вывода является компонентом более общей модели - подси стемы ввода/вывода. Подсистема ввода/вывода включает в себя все компоненты, кото рые обеспечивают возможность осуществления ввода/вывода. В число этих компо нент входит Диспетчер ввода/вывода и все драйверы режима ядра. В числе характери стик подсистемы ввода/вывода NT принято выделять следующие: 1) согласованность и высокая структурированность; 2) переносимость между процессорными архитектурами; 3) конфигурируемость; 4) вытесняемость и прерываемость; 5) поддержка многопроцессорности; 6) объектная базированность (но не объектная ориентированность); 7) асинхронность; 8) подсистема ввода/вывода управляется пакетами; 9) подсистема ввода/вывода многоуровневая (послойная модель). Как уже говорилось, подсистема ввода/вывода NT управляется пакетами. При та ком подходе каждый запрос ввода/вывода описывается своим собственным пакетом запроса ввода/вывода Request Packet IRP). При задействовании системного сер виса (например, при запросе на чтение или запись в файл) Диспетчер ввода/вывода обрабатывает этот запрос путем создания пакета IRP, описывающего запрос, и затем передает указатель на этот пакет драйверу для обработки.
2.4.2.2. Структура пакета запроса ввода/вывода (IRP) При осуществлении операции диспетчер создает спе циальный пакет, описывающий эту операцию пакет запроса ввода/вывода (I/O Request Packet, IRP). Как будет показано ниже, обработка такого пакета может проис ходить поэтапно несколькими объектами-устройствами. IRP содержит всю необходимую информацию для полного описания запроса Вво да - вывода Диспетчеру и драйверам устройств. IRP описывается дартной структурой типа показанной на 10. Структура IRP специально разработана для поддержки многоуровневой модели ввода/вывода, при которой запрос ввода/вывода последовательно обрабатывается сте ком из нескольких драйверов. Для обеспечения этого каждый пакет запроса состоит из двух час тей: части и Стека Ввода/вывода. часть IRP содержит информацию относительно той части запроса, которая или не изменяется от драйвера к драйверу, или которую не надо сохранять при передаче IRP от одного драй вера к другому. Стек Ввода/вывода содержит набор Стеков Размещения вода, каждый из которых содержит информацию, специфическую для каждого драй вера, который может обрабатывать запрос. В стеке размещения для каждого устройства, которое должно при нимать участие в обработке пакета, содержатся указатели на объект-устройство, кото рое будет обрабатывать запрос, и на объект-файл, для которого была инициирована операция ввода/вывода (см. рис. 10). Пакеты IRP всегда выделяются из невыгружаемой системной памяти (nonpaged pool), поэтому к ним может осуществляться обращение из функций, работающих на любом уровне IRQL. Как уже говорилось, драйверы подразделяются на три класса по их положению в стеке драйверов: драйверы высшего уровня, драйверы промежуточного уровня и драй веры низшего уровня. Драйвер высшего уровня - это верхний драйвер в стеке драйверов, получающий запросы через Диспетчер ввода/вывода от компонентов прикладного уровня. Драйвер высшего уровня (или, что более правильно, устройство высшего уровня) имеет один или несколько стеков размещения ввода/вывода. Число стеков размещения ввода/вывода устанавливается Диспетчером ввода/вы вода в поле StackSize объекта-устройства. По умолчанию это значение равно При сваивание происходит при создании устройства функцией IoCreateDeviceQ. Если вы создаёте многоуровневый драйвер, вы должны установить StackSize на 1 больше, чем StackSize объекта-устройства, над которым будете размещать свое устройство. В слу чае, если ваше устройство будет использовать больше одного устройства уровнем ниже, поле StackSize этого устройства должно быть на 1 больше максимального значения StackSize из всех устройств уровнем ниже.
Пакет Запроса В/В
Рис. 10
Поля в фиксированной части IRP Как было уже сказано, фиксированная часть IRP содержит информацию, которая или не изменяется от драйвера к драйверу или не должна сохраняться, когда IRP пере дается от одного драйвера к другому Особенно интересными или полезными поля в фиксированной части IRP являются следующие: 1) MdlAddress. Это поле указывает на Таблицу Описания Памяти (MDL), которая описывает буфер запроса, когда драйвер использует Прямой ввода/вывода (Direct представлен далее в этом разделе). 2) Flags. Как подразумевает название, это поле содержит флаги, которые (обычно) о п и с ы в а ю т запрос ввода/вывода. Н а п р и м е р , если в этом поле у с т а н о в л е н флаг это указывает на то, что операция чтения или операция записи, опи санная IRP, есть страничный запрос. Точно так же бит указывает, что запрос должен быть обработан без промежуточной буферизации. Поле Flags обычно представляют интерес только для файловых систем. 3) В связанном (associated) IRP это указатель на глав ный (master) IRP, с которым связан этот запрос. Это поле представляет интерес только Драйверам верхнего уровня, типа драйверов файловых систем.
4) Это место указывает на промежуточный буфер в невыгружаемой памяти, содержащий данных запроса в случае, когда драйвер исполь зует буферизированный ввод/вывод (Buffered I/O). 5) IoStatus. Это Блок Состояния Ввода/вывода, который описывает состояние за вершения обработки IRP. Когда завершен, драйвер помещает в поле IoStatus. Status состояние завершения операции а в поле любую дополнительную информацию, которую нужно передать обратно инициатору запроса Как правило, поле содержит фактическое число байтов, прочитанных или записанных запросом передачи данных. 6) RequestorMode. Это поле указывает режим работы процессора (режим ядра или пользовательский режим), из которого был инициирован запрос 7) и Эти поля используются, если IRP может быть отменен в процессе обработки. Cancel - поле типа BOOLEAN, значение которого устанавливается Диспетчером Установка в TRUE указывает, что была запрошена отмена операции описанная этим IRP. CancelRoutine это указатель на функцию драйвера (точка входа драйвера), вызываемую Диспетчером для того, чтобы драйвер мог корректно отменить IRR Точка входа CancelRoutine вызывается на IRQL Cancellrql является тем уров нем IRQL, к которому драйвер должен возвратиться. Более подробно обработка отме ны запроса будет обсуждаться в разделе, посвященном сериализации. 8) Это поле содержит виртуальный адрес буфера данных инициатора запроса, связанного с запросом если такой буфер имеется. 9) Это поле используется Диспетчером вода для постановки IRP в очередь в случае использования системной очереди (System Queuing). Системная очередь будет обсуждаться в разделе, посвященном сериализации. 10) Tail.Overlay.Thread. Это поле является указателем на управляющий блок по тока инициатора запроса (ETHREAD). 11) TailOverlay.ListEntry. Когда драйвер сам IRP, он может использовать это поле для соединения одного IRP с другим.
Поля в стеке размещения ввода/вывода IRP Каждый Стек размещения в IRP содержит информацию для конк ретного драйвера относительно запроса Стек размещения да определяется структурой IO_STACK_LOCATION. Для определения местонахожде ния текущего Стека Размещения внутри данного IRP, драйвер должен использовать функцию StackLocation(). Единственным параметром при вызове является указатель на IRP. Возвращаемым значением будет указатель на теку щий Стек размещения Когда Диспетчер Ввода/вывода создает IRP и инициализирует его фиксированную часть, он также инициализирует в IRP первый Стек Размещения В него помещается информация, которую нужно пе редать первому драйверу в стеке драйверов, которые будут обрабатывать этот запрос. Поля в Стеке Размещения включают следующее:
1) MajorFunction. Это поле указывает главный код функции ввода/вывода, свя занный с запросом ввода/вывода. Тем самым указывается тип операции ввода/вывода, которая должна быть выполнена. 2) MinorFunction. Это поле указывает второстепенный код функции ввода/выво да, связанный с запросом. При использовании, это поле переопределяет главный фун кциональный код. Второстепенные функции используются почти исключительно се тевыми транспортными драйверами и файловыми системами и игнорируются боль шинством драйверов устройств. 3) Flags. Это поле содержит флаги обработки, определенные для выполняемой функции ввода/вывода. Это поле представляет интерес главным для драйве ров файловых систем. 4) Control. Это поле является набором флагов, которые устанавливаются и читаются Диспетчером указывая, как надо обработать данный пакет Например, в этом поле с помощью обращения драйвера к функции может быть уста новлен бит PENDING, указывающий Диспетчеру Ввода/вывода, что завершение обра ботки пакета отложено на неопределенное время. Точно так же флажки и указывают, когда для этого IRP должна быть вызвана Подпрограмма Завершения Ввода/вывода драйвера. 5) Parameters. Это поле включает несколько подполей, каждое из которых зави сит от главной функции Ввода вывода, которая будет выполняться. 6) DeviceObject. Это поле содержит указатель на объект-устройство, который яв ляется получателем запроса Ввода/вывода. 7) FileObject. Это поле содержит указатель на объект-файл, связанный с запросом После того, как фиксированная часть IRP и первый Стек размещения Ввода/выво да в IRP инициализированы, Диспетчер Ввода/вывода вызывает верхний драйвер в стеке драйверов в его точке входа dispatch, которая соответствует главному функцио нальному коду для запроса. Таким образом, если Диспетчер сформиро вал IRP для описания запроса чтения, он вызовет первый драйвер в стеке драйверов в его диспетчерской точке входа для чтения При этом Диспетчер Вво передает следующие параметры: • указатель на IRP, который был только что сформирован; • указатель на обьект-устройство, который соответствует устройству, для которо го драйвер должен обработать запрос.
Описание буфера данных Описатель для буфера данных инициатора запроса находится в фиксированной части IRP. Для осуществления операции NT предусматривает три раз личных метода передачи буфера данных, принадлежащего инициатору запроса: • Прямой Ввод/вывод (Direct I/O). Буфер находится в виртуальном адресном пространстве инициатора запроса. Для передачи буфера драйверу Диспетчер создает таблицу описания памяти (MDL), описывающую разме щение буфера в физической памяти.
• Буферизированный Ввод/вывод (Buffered Для передачи буфера драйве ру Диспетчер создает в невыгружаемой системной памяти копию первоначального буфера. Драйверу передается указатель на этот новый буфер. Выделенная память будет освобождена Диспетчером при завер шении • «Никакой» Ввод/вывод (Neither I/O). В драйвер передается виртуальный ад рес буфера инициатора запроса. Коды функции и IOCTLs освещены более подробно ниже. Таблица 6. и «никакого»
Прямого
ввода/вывода,
Буферизованного
Буферизованный ввод/вывод (Buffered I/O) Буфер Скопирован во вре менный буфер в не тора запроса выгружаемой сис темной памяти Состояние буфера Буфер блокирован в Буфер не блокирован инициатора за памяти Диспетче проса в процессе ром обработки запро са ввода/вывода Описание буфе содержит указатель ра в IRP содержит виртуаль на M D L ный адрес временно го буфера в систем ной области памяти в области невыгру жаемой памяти (nonpaged pool)
«Никакой» ввод/вывод (Neither I/O) Описывается вир туальным адресом инициатора запроса
Контекст, при котором буфер может быть пользован Уровень IRQL, при котором бу фер может быть использован
Прямой ввод/вывод (Direct I/O) Описывается с по мощью M D L
Буфер не блокиро ван
держит не прове ренный на доступ ность виртуальный адрес буфера ини циатора запроса
Случайный текст
Случайный контекст
Только контекст потока - инициато ра запроса
IRQL
Любой
IRQL
DIS
DIS
Для описания всех запросов чтения и которые посылаются конкретному устройству, после создания объекта-устройства в его поле Flags Диспетчеру ввода/
вывода должен быть указан единственный метод для использования. Однако для дого кода Управления Вводом/выводом Устройства Control Code, поддер живаемого драйвером, может использоваться любой другой метод.
2.4.2.4. Коды функции ввода/вывода NT использует коды функции для определения конкретной опера ции которая будет иметь место для конкретного объекта-файла. Коды функции Windows NT разделены на коды главной и второстепенной фун кции Оба кода находятся в IRP в Стеке Размещения драй вера. Главные функциональные коды определены символами, которые начинаются с Перечислим некоторые из главных кодов функции 1) Этот главный функциональный код соответствует созданию нового объекта-файла, либо при обращении к существующему устройству или файлу, либо при создании нового файла. Этот функциональный код представляет запросы, идущие через функцию Win32 или базовый системный сервис NtCreateFile(). 2) IRP_MJ_CLOSE. Этот главный функциональный код соответствует уничтоже нию предварительно созданного объекта-файла. Этот функциональный код представ ляет запросы, идущие через функцию Win32 CloseHandle() или базовый системный сервис NtClose(). К появлению этого запроса ввода/вывода может привести не каждый вызов CloseHandleQ, так как на соответствующий файловый объект могут ссылаться другие, еще не закрытые описатели. Объект не может быть уничтожен, пока для него есть описатели. Кроме того, для каждого объекта диспетчер объектов ведет подсчет ссылок, и объект не может быть уничтожен, пока его число ссылок не равно нулю. 3) IRP_MJ_READ. Этот главный функциональный код выполняет операцию чте ния для существующего объекта-файла. Этот функциональный код представляет зап р о с ы , и д у щ и е через функцию Win32 ReadFile() или базовый системный сервис NtReadFile(). 4) Этот главный функциональный код выполняет операцию за писи для существующего объекта-файла. Этот функциональный код представляет зап росы, идущие через функцию Win32 WriteFile() или системный сервис NtWriteFile(). 5) Этот главный функциональный код выполняет определенную драйвером функцию для существующего объекта-файла. Этот функци ональный код представляет запросы, идущие через функцию Win32 или базовый системный сервис 6) Этот главный функциональный код выполняет определенную драйвером функцию для существующего объекта-фай ла. Никаких API уровня пользователя, соответствующих этой функции, нет. Эта фун кция используется, когда один драйвер посылает запрос другому драй веру. Законченный список кодов функции представлен в NTDDK.H. Вто ростепенные коды функции в NT определены символами, которые начи-
наются с NT обычно избегает использование второстепенных ных кодов для перезагрузки главной функции для драйверов устройства, приветствуя вместо этого использование Кодов Управления вводом/выводом Control Codes, Поэтому, почти все IRP, полученные драйверами устройства, имеют второ степенный функциональный код (который имеет значение 0x00). Вообще, второстепенные коды функции используются исключительно файловыми системами и сетевыми транспортами. Например, одним из второстепен ных кодов функции ввода/вывода, специфичным для файловой системы, является указывающий, что данные должны быть записаны на том в сжатом формате. Главные и второстепенные коды функции связанные с конкретным IRP, сохранены в полях MajorFunction и текущего Стека Размещения в IRP. На эти поля можно ссылаться, как показано в примере проверки главных и второстепенных функциональных кодов IRP: IoStack if if делать
2.4.2.5. Диспетчерские точки входа драйвера Информация, требуемая для выполнения запроса содержится в раз личных элементах как фиксированной части IRP, так и стека размещения ввода/выво да. Рассмотрим эти элементы. Структура поля Parameters в стеке размещения ввода/ вывода зависит от кода главной и второстепенной функции Нас в основ ном будет интересовать структура поля Parameters для запросов чтения, записи и пользо вательских запросов 1) IRP_MJ_READ. Параметры для этого функционального кода содержат следу ющее: • (ULONG) содержит размер в байтах буфера инициа тора запроса. • Parameters.Read.Key (ULONG) содержит ключевое значение, которое нужно использовать при чтении. Обычно представляет интерес только для драйве ров файловой системы. • (LARGE_INTEGER) содержит смещение (обыч но в файле), с которого должна начаться операция чтения. 2) Параметры для этого функционального кода следующие: • (ULONG) содержит размер в байтах буфера инициа тора запроса.
•
(ULONG) содержит ключевое значение, которое нуж но использовать при записи. Обычно представляет интерес только для драй веров файловой системы. • ByteOffset содержит смещение (обыч но в файле) с которого должна начаться операция записи. 3) IRP_MJ_DEVICE_CONTROL. Параметры для этого функционального кода следующие: • (ULONG) содержит длину в байтах буфера • (ULONG) содержит длину в байтах буфера • Parameters. (ULONG) содержит код управления вводом/выводом, идентифицирующий запрашиваемую функцию управления устройством. Этот управляющий код обычно предварительно определен драй вером с использованием макрокоманды • (PVOID) содержит виртуальный а д р е с б у ф е р а и н и ц и а т о р а з а п р о с а InBuffer (см. ф у н к ц и ю Win32 A P I Адрес обычно используется только тогда, когда использует
Запросы чтения и записи и Метод передачи буфера, используемый в запросах чтения и записи, контролирует ся полем Flags объекта-устройства. После создания объекта-устройства с помощью функции IoCreateDevice() необходимо инициализировать это поле. Поле может иметь установленными несколько флагов, при этом применяются следующие правила: 1) Если установлены флаги D O B U F F E R E D I O или метод пе редачи буфера будет соответственно буферизованным или прямым. 2) Если поле флагов не инициализировано (никакие флаги не установлены), ис пользуется метод передачи буфера Neither («никакой» 3) Одновременная установка флагов и зап рещена и будет являться ошибкой. 4) Установленный полем Flags метод передачи будет использован и запросом чте ния, и запросом записи. Расположение буфера в зависимости от метода его передачи для запросов чтения и записи полностью определяется таблицей 6. Для завершения запроса IRP на чтение/запись, необходимо установить поле равным числу прочитанных/записанных в буфер байт. В случае буферизованного это поле укажет Диспетчеру сколько байт нужно скопировать из промежуточного буфера в невыгружаемой области системного адресного пространства в пользовательский буфер.
Пример обработки запросов чтения/записи Данный пример обработки запросов чтения/записи демонстрирует получение ад реса буфера для чтения/записи и его длины. Такой код вставляется в обработчик дис петчерских функций адреса буфера для случае буферизованного случае
прямого
ввода/вывода
случае длины
буфера
для
stack
Запросы и Как говорилось выше, точка входа драйвера вы зывается при вызове пользовательской программой функции Про тотип этой функции: BOOL HANDLE описатель открытого устройства контрольный DWORD запрашиваемой операции LPVOID а д р е с буфера с о входными данными DWORD размер входного буфера LPVOID а д р е с буфера для приема выходных данных размер выходного буфера DWORD адрес переменной для получения числа реально переданных б а й т о в данных адрес структуры LPOVERLAPPED IpOverlapped для обеспечения
Зачем нужен Когда драйвер поддерживает ределенный тип устройства, такое устройство обычно имеет набор специализиро ванных возможностей, которые могут управляться через драйвер. Эти возможнос ти не могут быть з а д е й с т в о в а н ы и с п о л ь з о в а н и е м с т а н д а р т н ы х кодов ф у н к ц и й и Например, драйвер устройства для лентопротяжного устройства SCSI должен обеспечить ме ханизм, который дает возможность пользователям послать запрос на стирание лен ты. Такие зависящие от устройства запросы описываются, используя главный фун кциональный код Устройство обычно может принимать несколько разнотипных команд. Для драйвера тип команды указывает ся Кодом ввода/вывода (IOCTL), который передается как часть зап роса ввода/вывода. Возвращаясь к функции DeviceIoControl(), код управления указывается во втором параметре этой функции. Код управления ввода/вывода имеет специальный формат, указывающий метод передачи буфера и другую информацию. Этот формат будет рассмотрен в следующем разделе. Путаница может возникнуть при задании буфера для ввода/вывода. Как видно из прототипа функции она может передавать два бу фера (третий и пятый параметры). Несмотря на названия буферов входной и выход ной буфер выходной буфер может быть использован как для передачи данных в драй вер, так и для приема данных из драйвера. Разница будет в используемом методе пере буфера. Использование буферов будет подробно рассмотрено в разделе «Получе ние буфера».
Задание кода управления вводом/выводом (IOCTL) Формат кода управления
31
30
показан на рис.
16 15
14 13
с О
m m о п
DeviceType
Access
с и s t о
12
2
Function
1
0
Method
Формат кода управления вводом/выводом Function, Access - специальный макрос, опре деленный в заголовочных файлах ntddk.h и для задания кода в формате, представленном на рис. Рассмотрим составляющие кода управления ввода/вывода: 1) Поле DeviceType определяет тип объекта-устройства, которому предназначен запрос. Это тот самый тип устройства, который передается функции IoCreateDevice()
создании устройства. Как уже говорилось, существует два диапазона значений типов устройств: 0-32767 зарезервированные значения для стандартных типов уст ройств, 32768-65535 - диапазон значений типов устройств для выбора разработчиком. Следует отметить, что несколько разных устройств могут иметь одинаковое значение типа устройства. Поскольку каждый запрос предназначен конкретному устройству, совпадение типов устройств не приводит к неприятностям. Также необхо димо отметить, что тип устройства в коде управления может не совпа дать с типом устройства объекта-устройства, и это не будет являться ошибкой. 2) Поле Function идентифицирует конкретные действия, которые должно пред принять устройство при получении запроса. Значения поля Function должны быть уни кальны внутри устройства. Как и для типов устройств, существует два диапазона зна чений поля Function: 0-2047 - зарезервированный диапазон значений, и диапазон значений, доступный разработчикам устройств. 3) Поле Method указывает метод передачи буферов данных. Для понимания этого поля вернемся к функции DeviceIoControl(). Функция передает два буфера InBuffer и OutBuffer. Буфер InBuffer передает данные драйверу, буфер OutBuffer может переда вать данные в обоих направлениях (к драйверу и от драйвера). В следующей таблице приведены возможные значения поля Method и методы пе редачи буферов InBuffer и OutBuffer: Используемый метод передачи буфера InBuffer OutBuffer
Значение поля Method
Использо вание OutBuffer
M E T H O D BUFFERED
Буферизованный ввод/вывод (Buffered I/O) Прямой Буферизованный Передача Осуществляется про данных к верка буфера на дос драйверу туп по чтению Буферизованный Прямой ввод/вывод. Приема Осуществляется данных от верка буфера на дос драйвера туп по записи Neither I/O
M E T H O D NEITHER
Местоположение буферов данных в пакете IRP будет рассмотрено в следующем разделе («Получение 4) Поле Access указывает тип доступа, который должен был быть запрошен (и пре доставлен) при открытии объекта-файла, для которого передается данный код Управ ления вводом/выводом. Возможные значения для этого параметра следующие: • FILE_ANY_ACCESS. Это значение указывает, что при вызове CreateFile() мог быть запрошен любой доступ.
• FILE_READ_ACCESS. Это значение указывает, что должен был быть запро шен доступ для чтения. • FILE_WRITE_ACCESS. Это значение указывает, что должен был быть запро шен доступ для записи. Заметим, что и могут быть указаны одновремен но, чтобы указать, что при открытии устройства должен быть предоставлен и доступ на чтение, и доступ на запись. Параметр Access требуется потому, что управления ввода/вывода, по своей сути не есть операция чтения или записи. Прежде, чем будет разрешена запрашивае мая операция Диспетчер должен знать, какой режим дос тупа нужно проверить в таблице описателей. 2.4.2.5.2.2. П о л у ч е н и е б у ф е р а При использовании буферизованного метода, Диспетчер ввода/вывода выделя ет в системной невыгружаемой памяти промежуточный буфер, размер которого равен максимальному из размеров буферов InBuffer и OutBuffer. Если при запросе был опре делен InBuffer и его длина не нулевая, содержание InBuffer копируется в промежуточ ный буфер. В любом случае, адрес промежуточного буфера помещается в IRP в поле Затем IRP, содержащий запрос, передается драйверу. Данные, в промежуточном буфере, могут читаться и перезаписываться драйвером. Затем драйвер размещает в промежуточном буфере данные, которые нуж но вернуть в OutBuffer. При завершении запроса если OutBuffer был определен при запросе и его длина не нулевая, Диспетчер ввода/вывода копирует из промежу точного буфера в OutBuffer столько байтов, сколько было указано в поле После этого, как и при любом буферизированном запросе Вво Диспетчер ввода/вывода освобождает промежуточный буфер. П р и и с п о л ь з о в а н и и методов M E T H O D I N D I R E C T и DIRECT, буфер InBuffer, если он определен в запросе и его длина не нулевая, обрабатывается в точности так же, как и при буферизованном вводе/выводе. В этом случае выделяется промежуточный буфер, в него копируется InBuffer, указа тель на промежуточный буфер в IRP в поле Associatedlrp.SystemBuffer. Буфер OutBuffer, если он определен в запросе и его длина не нуле вая, обрабатывается в соответствии с прямым вводом/выводом. В этом случае адрес проверяется на возможность доступа (запись или чтение), производится закрепление физических страниц в памяти, и создается таблица описания памяти MDL, описываю щая OutBuffer. Указатель на MDL передается в поле При метода оба буфера передаются в со ответствии с методом Neither. есть, не производится проверка доступности памяти, не выделяются промежуточные буфера и не создаются MDL. В пакете IRP передаются виртуальные адреса буферов в пространстве памяти инициатора запроса да. А д р е с буфера OutBuffer передается в ф и к с и р о в а н н о й части IRP в поле
>UserBuffer, адрес буфера InBuffer передается в стеке размещения ввода/вывода в поле >Parameters.DeviceControl.Type3InputBuffer. Положение буферов показано в таблице 7. Таблица 7
InBuffer Метод передачи
METHOD BUFFERED
METHOD_IN_ M E T H O D DIRECT DIRECT
Buffered
Buffered
Buffered
METHOD NEITHER Виртуальный адрес инициато ра запроса
Если су Адрес промежуточного буфера в фиксиро В стеке разме в поле ществует, ванной части щения вво то да/вывода вир располо туальный адрес жен инициатора за проса в Parame ters. Control. Type3InputBuffer
Out Buffer
Длина
Длина в байтах в поле Length в текущем стеке размещения
Метод передачи
Buffered I/O
Если су ществует, то где располо жен
MDL, адрес в Адрес про межуточного буфера в фиксирован ной части IRP в поле
Direct
Direct
Виртуальный адрес инициато ра запроса
MDL, адрес в
Виртуальный адрес инициато ра запроса в
dress
uffer Длина
Длина в байтах в поле в текущем стеке раз мещения ввода/вывода.
Для
завершения запроса IRP необходимо установить поле равным числу прочитанных/записанных в буфер байт. В слу чае буферизованного это поле укажет Диспетчеру сколько
байт нужно скопировать из промежуточного буфера в невыгружаемой области систем ного адресного пространства в пользовательский буфер. Пример обработки Пример получения адресов и длин буферов в диспетчерской функции драйвера, о б р а б а т ы в а ю щ е й ф у н к ц и о н а л ь н ы е коды и stack switch case case
case switch case InLength
case доступен
только
доступен
для
для
чтения
InLength
OutLength break; case
InLength OutBuffer OutLength break; case
чтения/записи
InLength OutBuffer OutLength
2.4.3. Многоуровневая модель драйверов Ранее в качестве одной из характеристик подсистемы ввода/вывода упоминалась ее Что это такое? NT позволяет выстраивать драйверы в соответствии с некоторой функциональной иерархией. При этом, например, одни драйверы имеют своей единственной целью об мен данными с некоторым физическим устройством. Что это за данные и что с ними делать, такие драйвера не знают. Другие драйвера выполняют обработку данных, но не знают в точности, как эти данные получены и как будут отправлены. Такая концеп ция разделения полномочий драйверов носит название многоуровневой (или ной) модели драйверов (layered driver model), а сами драйвера - уровневыми драй верами (layered drivers). В NT 4.0 концепция многоуровневых драйверов занимает важное место, но ее ис пользование не является обязательным требованием. В Win2000 все драйвера, считающиеся родными, будут уровневыми (для того, чтобы драйвер считался родным для он должен как минимум поддерживать управ ление питанием, а для этого он должен быть уровневым). Большинство драйверов, которые в N T 4 мы считали монолитными, в будут по своей сути уровневыми. Будем выделять следующие типы драйверов: 1) драйверы, представляющие некоторый уровень в многоуровневой архитектуре. Далее именно эти драйверы мы будем называть уровневыми драйверами; 2) драйверы-фильтры; 3) драйверы файловой системы (File System Driver, FSD); 4) мини-драйверы (mini-driver). Для каждого типа драйверов существует свой протокол реализации многоуровне вой структуры. Мы рассмотрим только уровневые драйверы и драйверы-фильтры.
Реализация уровневых драйверов Стек драйверов обычно создается самими драйверами. Корректное создание стека зависит от правильной последовательности и момента загрузки каждого драйвера из стека. Первыми должны грузиться драйвера самого нижнего уровня и т.д. При рассмотрении организации стека драйверов необходимо понимание трех мо ментов • объединение драйверов в стек; • обработка запросов стеком; освобождение драйверов стека.
Объединение драйверов в стек Для объединения драйверов в стек обычно используется функция IoGetDeviceObject Pointer(). Функция вызывается драйвером вышележащего уровня для получения ука зателя на объект-устройство драйвера нижележащего уровня по его имени. имеет следующий прототип: NTSTATUS IN IN ACCESS_MASK OUT FileObject, OUT Где: ObjectName Имя требуемого Объекта-устройства; DesiredAccess - Требуемый доступ к указанному Объекту-устройству; FileObject Указатель на Объект-файл, который будет использоваться для обраще ния к устройству; DeviceObject Указатель на Объект-устройство с именем ObjectName. Функция IoGetDeviceObjectPointer() принимает имя Объекта-устройства, и воз вращает указатель на Объект-устройство с этим именем. Функция работает, посы лая запрос CREATE на названное устройство. Этот запрос будет неудачным, если никакого устройства по имени ObjectName не существует, или вызывающая про грамма не может предоставить доступ, указанный в параметре DesiredAccess. Если запрос CREATE успешен, создается Объект-файл, что увеличивает счетчик ссылок Объекта-устройства, с которым связан Объект-файл. Затем Диспетчер ввода/выво да искусственно увеличивает счетчик ссылок на Объект-файл на единицу, и посы лает на устройство запрос CLOSE. В результате всего этого процесса, Объект-уст ройство (чей указатель возвращен в DeviceObject) не может быть удален, пока не обнулится счетчик ссылок соответствующего ему Объекта-файла. Таким образом, Объект-устройство нижнего уровня не может быть удален, в то время как драйвер вышележащего уровня имеет указатель на него. Выделим из всего вышесказанного, что функция предусматривает в качестве выходного параметра указатель на Объект-файл специально для того, чтобы при выгрузке стека драйверов освободить устройство нижележащего уровня. Это дол жно быть сделано в функции Unload драйвера вышележащего уровня с помощью вызова функции ObDereferenceObject( FileObject П о с л е п о л у ч е н и я указателя н а о б ъ е к т - у с т р о й с т в о д р а й в е р а н и ж е л е ж а щ е г о уровня, драйвер в ы ш е л е ж а щ е г о уровня должен установить корректное значение полей Characteristics, StackSize, Flags и AlignmentRequirement своего объекта-ус тройства. Поля Characteristics, Flags и AlignmentRequirement объектов-устройств всех драйверов в стеке д о л ж н ы совпадать, а значение поля StackSize в ы ш е л е ж а щего устройства д о л ж н о быть на 1 больше значения этого поля у н и ж е л е ж а щ е г о устройства.
Обработка запросов IRP стеком драйверов Запрос ввода/вывода приходит в виде пакета IRP самому верхнему драйверу в сте ке драйверов (драйверу верхнего уровня). При этом возможны следующие варианты обработки IRP: Обработка IRP полностью в драйвере верхнего уровня. 2) После выполнения своей части обработки IRP драйвер отправляет первоначаль ный пакет IRP драйверу нижележащего уровня. 3) После выполнения своей части обработки IRP драйвер создает один или не сколько новых пакетов IRP и отправляет их драйверу нижележащего уровня. 4) После выполнения своей части обработки IRP драйвер создает один или не сколько новых пакетов IRP, ассоциированных с первоначальным пакетом IRP, и отправляет их драйверу нижележащего уровня. Все варианты, кроме последнего, возможны при обработке пакета IRP драйвером любого уровня. Последний вариант - создание ассоциированных пакетов IRP - возмо жен только при обработке IRP драйвером верхнего уровня. Самостоятельная обработка IRP драйвером. Если драйвер может завершить обработку пакета IRP самостоятельно, он так и должен сделать. При этом обработка может быть завершена либо сразу при поступлении запроса ввода/вывода, либо после постановки запроса ввода/вывода в очередь. Примером немедленного завершения обработки пакета IRP драйвером может слу жить случай обнаружения некорректного параметра в IRP. Передача первоначального пакета IRP драйверу нижележащего уровня. Если драйвер не может самостоятельно обработать пакет IRP, он может передать его ниже лежащему драйверу. Для этого необходимо заполнить параметры в IRP в стеке разме щения ввода/вывода, относящегося к нижележащему драйверу. Указатель на стек раз мещения ввода/вывода нижележащего драйвера возвращается функцией IoGetNext После заполнения необходимых параметров IRP передается драй веру нижележащего уровня с помощью вызова функции IoCallDriver(). Прототип этой функции: NTSTATUS IoCallDriver
(IN
IN OUT
Где: DeviceObject - указатель на объект-устройство, принадлежащий драйверу ниже лежащего уровня в стеке драйверов, которому должен быть послан запрос; IRP - указатель на IRP, который будет послан драйверу нижележащего уровня. При вызове функции Диспетчер ввода/вывода напрямую вызывает соответствующую диспетчерскую функцию требуемого драйвера. Это означает, что IoCallDriver() завершится только после завершения соответствующей диспетчерской функции. Создание новых пакетов IRP для передачи драйверу нижележащего уровня. Вариантом передачи IRP драйверу нижележащего уровня является создание одного или нескольких новых пакетов IRP и передача этих IRP драйверу нижележащего уров-
ня. Пакет IRP может быть создан драйвером различными способами, наиболее общим из которых является вызов функции
(IN
StackSize, IN BOOLEAN
Где: StackSize - Число Стеков размещения требуемых в IRP; ChargeQuota - Указывает, должна ли квота текущего процесса быть загружена. возвращает частично инициализированный пакет IRP. По умолчанию, IoAllocatelrpO предполагает, что вызванный драйвер не хочет иметь собственный размещения Драйвер, распределяющий может факультативно просить IoAllocatelrpO создать IRP с достаточным количеством Стеков Размещения которые он мог иметь один. В случае, драйвер жен вызвать (макрокоманда в NTDDK.H), чтобы вить Стек Размещения. Драйвер может затем вызвать функцию Location(). Причина, по которой драйверу может потребоваться свой собственный Стек мещения Ввода /вывода состоит в том, что драйвер может использовать стек для дачи информации к подпрограмме завершения. Подпрограмма Завершения (Completion Routine) обсуждается позже в этом разделе. Как только IRP создан, драйвер может вызывать что бы получить указатель на Стек размещения для драйвера нижележащего уровня в ке драйверов. Затем драйвер заполняет параметры для Стека Если для запроса требуется буфер данных, драйвер должен или установить или как того требует используемый нижележащим драйвером способ пере дачи буферов в пакете IRP. IRP посылается нижележащему драйверу с помощью фун кции IoCallDriver(), как описано выше. Создание новых ассоциированных пакетов 1RP для передачи драйверу лежащего уровня. Немного другой подход к созданию нового пакета IRP для переда чи драйверу нижележащего уровня состоит в создании ассоциированного пакета IRP с помощью функции PIRP (IN PIRP IN CCHAR Где: - Число Стеков размещения требуемых в IRP; Masterlrp - Указатель на IRP, с которым должен быть связан создаваемый пакет позволяет создавать IRP, которые с некоторым IRP. Драйвер, который вызывает должен вручную инициализировать поле Irp.IrpCount главного IRP счетчиком ассоциированных с IRP, которые созданы до вызова IoMakeAssociatedlrp. Ассоциированные IRP являются особым видом пакетов IRP, при завершении которых значение поля в главном IRP уменьшается. Когда значение поля IrpCount в главном IRP становится равным нулю, Диспетчер автоматически завершает главный IRP. Ассоциированные пакеты IRP могут создаваться только драйвером высшего уров ня. Драйвер высшего уровня, использующий ассоциированные пакеты IRP, может вернуть управление диспетчеру ввода/вывода после вызова IoCallDriver() для каж-
дого из ассоциированных IRP и вызова для главного IRP. Если такой драйвер устанавливает процедуру завершения для созданного им ассоцииро ванного IRP (с помощью вызова описанного ниже), Дис петчер не завершит автоматически главный В этом случае проце дура завершения должна напрямую завершить главный IRP посредством вызова
2.4.3.1.2.1. Получение драйвером вышележащего уровня уведомления о завершении обработки IRP драйвером нижележащего уровня В некоторых случаях драйвер вышележащего уровня может захотеть получить уведомление о завершении обработки переданного им запроса ввода/вывода драйве ром нижележащего Notification). Это может быть сделано с помо щью вызова функции IoSetCompletionRoutine() перед передачей пакета IRP драйверу нижележащего уровня любым указанным выше способом. VOID IN INPVOID IN BOOLEAN IN BOOLEAN IN BOOLEAN Irp - Указатель на IRP, при завершении которого вызывается точка входа CompletionRoutine; CompletionRoutine - Указатель на точку входа драйвера, вызываемую при завер шении IRP; Context - определенное драйвером значение, которое нужно передать как третий параметр для точки входа CompletionRoutine; InvokeOnSuccess, InvokeOnError, - Параметры, которые, указыва ют должна ли точка входа CompletionRoutine быть вызвана при завершении IRP с ука занным состоянием. В качестве второго параметра в вызове передается адрес точки входа драйвера, которая должна быть вызвана при завершении указанного в пер вом параметре пакета IRP. Прототип функции точки входа драйвера: NTSTATUS IN PIRP IN PVOID Где: DeviceObject Указатель на объект-устройство, которому предназначался за кончившийся пакет IRP; IRP - Указатель на закончившийся пакет IRP; Context - Определенное драйвером значение контекста, переданное, когда была вызвана функция При вызове IoSetCompletionRoutine() указатель на функцию завершения сохраня ется в 1RP в следующем после текущего Стеке Размещения ввода/вывода (то есть в 4 Зак.
Стеке Размещения нижележащего драйвера). Из этого следуют два важ ных вывода: • Если драйвер установит функцию завершения для некоторого и завершит этот IRP, функция завершения не будет вызвана. • Драйвер низшего уровня (либо монолитный драйвер) не может устанавливать функции завершения, так как, во-первых, это бессмысленно (см. предыдущий вывод), а во-вторых, это ошибка, так как следующего (за стека разме щения для таких драйверов не существует. Функция завершения вызывается при том же уровне IRQL, при котором нижеле жащим драйвером была вызвана функция завершения IRP IoComplete Это может быть любой уровень IRQL м е н ь ш и й либо равный I R Q L _ DISPATCH_LEVEL. Если драйвер вышележащего уровня создавал новые пакеты IRP для передачи драй веру нижележащего уровня, он обязан использовать функцию завершения для этих IRP, причем параметры должны быть установлены в TRUE. В этих случаях функция завершения должна освободить создан ные драйвером IRP с помощью функции и завершить первоначальный па кет IRP. Требования к реализации функции завершения достаточно сложные. Эти требова ния можно найти в [Developing Windows NT Device Drivers, pages
2.4.3.2. Реализация драйверов-фильтров Диспетчер предоставляет возможность одному драйверу «подклю чить» один из своих объектов-устройств к объекту-устройству другого драйвера. Ре зультатом будет перенаправление пакетов IRP, предназначавшихся некоторому объек ту-устройству, на объект-устройство драйвера-фильтра. Драйвер-фильтр может про смотреть, модифицировать, завершить или передать полученный IRP первоначально му драйверу. могут быть подключены к любому драйверу в стеке драйверов.
Подключение фильтра к устройству Имеется пара различных способов подключения драйвера-фильтра к другому драй веру. Первый способ вначале получить указатель на объект-устройство, к которому необходимо подключиться, с помощью функции (см. раз дел «Объединение драйверов в стек и освобождение драйверов Подключение драйвера-фильтра к найденному устройству производится с помощью вызова функ ции IN
IN TargetDevice) Где: SourceDevice - Указатель на первоначальный Объект-устройство, к которому будем прикреплять фильтр;
- Указатель на Объект-устройство фильтра. Каждый объект-устройство имеет поле AttachedDevice, которое указывает на объект-устройство первого драйвера-фильтра, который был прикреплен к этому объек ту-устройству. Если поле AttachedDevice объекта-устройства нулевое, нет никаких прикрепленных устройств. Если поле AttachedDevice не нулевое, оно указывает на объект-устройство драйвера-фильтра. IoAttachDeviceToDeviceStack() находит конец списка AttachedDevice для объекта-устройства, указанного параметром TargetDevice, и устанавливает в поле AttachedDevice этого конечного объекта-устройства указатель на объект-устройство Возвращаемым значением функции IoAttachDeviceToDeviceStack() является ука затель на объект-устройство, к которому был прикреплен объект-устройство драйвера фильтра. Драйвер-фильтр может использовать этот указатель, чтобы передавать зап росы первоначальному устройству. Хотя этот указатель обычно указывает на то устройство, что и SourceDevice, он может быть указателем на другой драйвер-фильтр, прикрепленный к устройству SourceDevice. Другим способом прикрепления фильтра к устройству является вызов функции IoAttachDevice(). Эта функция просто объединяет функциональные возможности, обес печиваемые функциями и IoAttachDeviceToDeviceStack(). NTSTATUS I o A t t a c h D e v i c e IN OUT Где: SourceDevice - Указатель на Объект-устройство драйвера Фильтра; TargetDevice - строка Unicode с именем устройства, к которому будет прикрепле но устройство AttachedDevice Указатель на объект-устройство, к которому было прикреплено устройство Эффект подсоединения фильтра заключается в том, что при запросе на откры тие некоторого устройства (например, через для этого устройства про сматривается список присоединенных устройств. Если он пуст, как обычно, от крывается запрошенное устройство. Если он не пуст, открывается последнее ройство в списке. После этого все запросы ввода/вывода направляются именно этому устройству. Из сказанного следует, что для успешного подключения фильтра к некоторому ус тройству в стеке, подключение нужно делать до того момента, когда вышележащий драйвер вызовет функцию и, тем самым, пошлет запрос на открытие нижележащего устройства. Выгрузка Для отсоединения устройства служит функция IoDetachDevice. VOID I o D e t a c h D e v i c e (IN OUT Соответственно, функция выгрузки драйвера-фильтра DriverUnload должна делать примерно следующее:
•
С помощью вызова уменьшить счетчик ссылок на объектфайл, полученный в результате вызова • Для каждого объекта-устройства, принадлежащего выгружаемому драйверуфильтру, вызвать IoDetachDevice() и IoDeleteDevice().
Сериализация Сериализация - это процесс выполнения различных операций в нужной последо вательности. Функция драйвера для обработки запроса может быть вызвана из различных потоков различных процессов. Неэксклюзивное устройство может быть одновременно открыто несколькими прикладными программами, каждая из которых может послать один и тот же запрос в одно и то же время. Нам необходимо, чтобы обработка каждого такого запроса началась как можно быстрее, но не раньше, чем завершится обработка предыдущего запроса. Если устройство эксклюзивное и вся обработка завершается в диспетчерской фун кции, сериализация обеспечивается операционной системой. Диспетчерские функции работают в контексте памяти потока-инициатора запроса следователь но, пока не завершится диспетчерская функция, код прикладной программы не полу чит управления и не инициирует очередной запрос Однако даже эксклюзивное устройство может не суметь обработать запрос ввода/ вывода в диспетчерской функции сразу при его поступлении. Если драйвер будет ждать в диспетчерской функции возможности обработать и завершить запрос, прикладная программа - инициатор запроса будет «висеть» - ее код не выполняется. Более того, если такой драйвер является уровневым и не является драйвером верхнего уровня, его диспетчерские функции будут вызываться в случайном контексте потока. «Завесив» свою диспетчерскую функцию, драйвер «завесит» произвольный поток произвольной прикладной программы! Чтобы такого не происходило, диспетчерская функция долж на завершиться как можно скорее. При этом диспетчеру необходимо ука зать, что соответствующий запрос не был завершен, однако это не ошиб ка, и этот запрос ввода/вывода будет завершен когда-то позже. При этом возникают • как указать, что запрос не завершен; • где хранить незавершенные пакеты • как быть, если запрос ввода/вывода не завершен, а инициировавшее запрос при ложение закрылось. З а в е р ш е н и е запроса ввода/вывода. Успешное завершение запроса в диспетчерской функции показано в следующем листинге:
return
STATUS
(Irp, SUCCESS;
При неудачном завершении обработки запроса ввода/вывода в поле Status устанавливается код ошибки (значение, оно же возвращается оператором return.
2.4.4.1. Задержка обработки запросов IRP и постановка запросов IRP в очередь Когда немедленное завершение запроса ввода/вывода в диспетчерской функции невозможно, драйвер должен указать Диспетчеру ввода/вывода, что обработка запро са продолжается. Для этого возвращаемым значением диспетчерской функции долж но быть значение STATUSJPENDING. Перед этим в самом пакете IRP в текущем стеке размещения ввода/вывода должен быть установлен флаг S L P E N D I N G R E T U R N E D , помечающий запрос как неоконченный. Для этого служит функция VOID PIRP Установка этого флага указывает, что IRP будет освобожден драйвером с помо щью вызова когда-то потом. Для постановки пакета IRP в очередь диспетчерская функция должна: 1) пометить IRP как неоконченный; 2) установить функцию отмены IRP; 3) использовать один из нижеприведенных способов для постановки IRP в оче редь; 4) завершиться со статусом STATUSJPENDING. NT предусматривает два способа организации очередей пакетов IRP: Использование диспетчера ввода/вывода для постановки запросов в управляе мую им очередь (Системная Очередь). • Создание и управление очередью самостоятельно (Очереди, управляемые драй вером).
2.4.4.1.1. Системная очередь запросов IRP (System Queuing) Простейший способ, с помощью которого драйвер может организовать очередь IRP использовать Системную Очередь. Для этого драйвер предоставляет диспетчер скую точку входа При получении пакета IRP, ко торый необходимо поставить в Системную Очередь, такой пакет необходимо поме тить как отложенный с помощью вызова а затем вызвать функцию VOID IN P I R P IN IN PDRIVER_CANCEL Где: DeviceObject - Указатель на устройство, которому направлен запрос ввода/ вывода; - Указатель на пакет IRP, описывающий запрос ввода/вывода;
Key - Необязательный указатель на определяющее позицию в очереди IRP, в которую будет вставлен пакет IRP. Если указатель NULL, IRP помещается в конец очереди; - Необязательный указатель на функцию точку входа драйвера, которая будет вызвана при отмене данного запроса диспетчером ввода/ вывода. Вызов этой функции ставит пакет IRP для данного устройства в Системную Оче редь. При этом, если в момент вызова функция Startlo не выполняется, происходит выборка из очереди очередного IRP. При выборке очередного пакета IRP из очереди, если очередь не пуста, для оче редного IRP будет вызвана функция Startlo. Функция имеет такой же прототип, как и все диспетчерские функции, но вызывается в случайном контексте потока на уровне IRQL D I S P A T C H L E V E L : IN PIRP Структура функции Startlo практически такая же, как у диспетчерской функции, не использующей организацию очередей, за двумя важными исключениями: • Startlo вызывается в случайном контексте потока на уровне IRQL LEVEL. Поэтому необходимо: а) соблюдать все ограничения для уровня IRQL D I S P A T C H L E V E L ; б) не использовать метод передачи буфера Neither, так как такой метод передачи предполагает знание контекста памяти процесса, из кото рого был инициирован запрос (см. раздел «Описание Буфера Дан • Какой бы ни был результат обработки пакета IRP, после завершения его обра ботки Startlo обязана вызвать функцию для указания дис петчеру необходимость выбора из системной очереди очередного пакета IRP (то есть вызвать для него Startlo) сразу после завершения Startlo для текущего пакета. Прототип функции VOID IN BOOLEAN Где: DeviceObject - Указатель на устройство, для которого необходимо выбрать следующий пакет из системной очереди IRP; Cancelable - Указывает, может ли выбираемый из очереди пакет IRP быть отменен в процессе обработки. Если при вызове была указана ненулевая функ ция отмены, параметр Cancelable обязан быть равным TRUE. Вместо IoStartNextPacket() может быть использована функция IoStartNext PacketByKey(): VOID IN BOOLEAN IN ULONG В этом случае из очереди будет выбран очередной пакет IRP с заданным значени ем Key (это значение могло быть установлено при помещении пакета в очередь при вызове
При использовании системной очереди диспетчер ввода/вывода отслеживает, ког да данный объект-устройство свободен или занят обработкой очередного Это осу ществляется с помощью специального объекта ядра Очередь Устройства (Device Queue Object, тип структура помещенного внутрь объекта-устройства в поле DeviceQueue. Поле DeviceQueue.Busy устанавливается диспетчером ввода/вы вода равным TRUE непосредственно перед вызовом функции и FALSE, если вызвана функция а системная очередь не содержит паке тов IRP для данного устройства. При обработке очередного IRP указатель на него на ходится в объекте-устройстве в поле
Обработка пакетов IRP в функции Startlo Как должна происходить обработка пакетов из системной очереди? Как уже го ворилось, Startlo работает на уровне IRQL Пока выполняется эта функция, ни один поток с более низким значением IRQL не может получить уп равление (если в системе один процессор). Следовательно, новые запросы ввода/ вывода от прикладных программ попасть в очередь не могут (потоки не выполняют ся). Если завершение очередного пакета ввода/вывода всегда происходит в функции Startlo, системная очередь всегда содержит не более одного пакета ввода/вывода. Если пакет ввода/вывода не может быть обработан в тот момент, когда он попал в функцию Startlo, функция просто должна завершиться, не завершая запрос ввода/ вывода и не вызывая IoStartNextPacketQ. В этом случае устройство остается «заня тым». Поле pDeviceObject->DeviceQueue.Busy все еще T R U E , а в поле pDeviceObject->CurrentIrp находится указатель на этот пакет IRP. Такой пакет может быть обработан, например, при поступлении прерывания от аппаратного устройства (или при возникновении другого ожидаемого события). Функция, которая завершит обра ботку такого пакета, обязана вызвать IoStartNextPacketQ, чтобы инициировать вы борку очередного пакета из системной очереди. Заметим, что пока устройство оста ется «занятым», функция Startlo для обработки пакетов из системной очереди не может быть вызвана. Несмотря на простоту использования системной очереди, имеется существенное ограничение. Оно состоит в том, что очередь одна на все типы запросов ввода/вывода (чтение, запись, управление устройством). В каждый конкретный момент обрабатыва ется только какой-то один пакет IRP. Могут быть ситуации, когда такое ограничение неприемлемо. Классическим при мером является драйвер полнодуплексного устройства, которое одновременно позво ляет как отправлять, так и получать данные. В этом случае необходимо начать обра ботку следующего запроса чтения при завершении текущего запроса чтения, и следу ющего запроса записи при завершении текущего запроса записи. При этом важно по нимать, что в этом случае одновременно (то есть в контекстах разных потоков) могут выполняться один запрос чтения и один запрос записи. Необходимы две очереди: одна - для запросов чтения, другая - для запросов записи.
Очереди,
драйвером
Вместо использования системной очереди, можно предусмотреть свой собствен ный механизм организации очереди. Это можно сделать двумя способами: • использовать для создания очереди функции управления очередью низкого уровня; • использовать функции управления очередью высокого уровня. Этот способ очень редко используется и является промежуточным между использованием систем ной очереди и функций управления очередью низкого уровня. Используя очереди, управляемые драйвером, драйвер получает полный контроль над очередью. Например, драйвер может организовать обработку трех запросов записи и двух запросов чтения при условии, что в данный момент не выпол няется запрос управления устройством. Как и в случае использования системной очереди, при получении пакета IRP, кото рый необходимо поставить в очередь, такой пакет необходимо пометить как отложен ный с помощью вызова Затем используется любой способ поме щения указателя на пакет IRP в очередь.
2.4.4Л.2Л. Функции управления очередью низкого уровня Для организации очереди с помощью функций низкого уровня используется стан дартная структура L I S T E N T R Y .
struct _LIST_ENTRY struct struct _LIST_ENTRY
Указатель на следующий списка Blink; Указатель на предыдущий элемент списка
*PLIST_ENTRY; Очередь, как это видно из определения структуры, двунаправленная. В структуре DeviceExtension обычно создается экземпляр структуры LIST_ENTRY, представляющей голову очереди, который затем инициализируется с помощью функ ции InitializeListHead(). После этого можно добавлять или удалять записи в очередь. Для этого используются функции Пакеты IRP добавляются в очередь в диспетчерской функции, скорее всего рабо тающей на уровне IRQL равном PASSIVE_LEVEL. При этом выниматься из очереди они могут функциями, работающими на любом уровне IRQL, причем функции, выби рающие IRP из очереди, могут вытеснять функции, помещающие IRP в очередь. Воз никает проблема синхронизации. Если ее не решить, незаконченная операция поме щения IRP в очередь может быть прервана операцией выборки IRP из очереди, что приведет к появлению синего экрана. Синхронизация доступа к разделяемому ресурсу производится с помощью спинблокировки (механизмы синхронизации будут рассмотрены в следующем разделе). Поскольку операции добавления и удаления записей в очередь на уровнях IRQL P A S S I V E L E V E L и D I S P A T C H L E V E L очень распространены, для их безопасного
осуществления предусмотрена специальная функция: Для ис пользования этой функции должна быть создана и инициализирована спин-блокиров ка. Создается она обычно там же, где и голова очереди (обычно в DeviceExtension), и инициализируется после инициализации головы очереди. Например: struct
функции
э т о г о можно д о б а в л я т ь PLIST_ENTRY p O l d H e a d
и
удалять
записи
Как видно из определения структуры она не содержит полей для хранения собственно данных (например, указателя на пакет IRP). Поэтому распрост раненный способ использования структуры - включение ее экземпляра в состав более общей структуры. Для организации очереди пакетов IRP, в каждом пакете IRP в поле lay.ListEntry содержится экземпляр структуры При этом встает воп рос, как, зная указатель на структуру получить указатель на структуру в состав которой входит Для этого DDK предоставляет специаль ный макрос #define field) \ (PCHAR) ( a d d r e s s ) (type Где: Address - Известный адрес некоторого поля структуры, адрес которой необхо димо получить; - Тип структуры, адрес которой необходимо получить; Имя поля внутри искомой структуры, адрес этого поля передан в параметре address. Применительно к IRP, мы должны будем написать что-то вроде: PListEntry (pDeviceExtension->ListLock) -
обработка
IRP
106 Организация очереди пакетов IRP показана на рис.
Рис. 12
Функции управления очередью высокого уровня - «Очередь Устройства» (Device Queue) Драйвер создает дополнительные Очереди Устройства с помощью выделения па мяти из невыгружаемой памяти под дополнительные объекты-Очереди Устройства и эти объекты с помощью функции Добавление пакетов IRP в эти очереди производится с помощью функции или а выборка пакетов из о ч е р е д и или Для организации очереди пакетов IRP используется структура типа KDEVICE_ указатель на которую содержится в пакете IRP в поле
2.4.4.2. Отмена запросов ввода/вывода Всякий раз, когда запрос удерживается драйвером в течение продол жительного отрезка драйвер должен быть готов к отмене данного запроса. В случае закрытия потока диспетчер пытается отменить все запросы вво этим потоком и еще не завершенные. Пока все такие запро сы не будут завершены устройством, ему не придет запрос CLOSE, и, следовательно, не освободится объект-файл, а впоследствии и само уст ройство (драйвер никогда не получит запрос DriverUnload). Для обеспечения отмены запроса ввода/вывода в пакете представляющем та кой запрос, должен быть указан адрес диспетчерской точки входа драйвера, собствен но отменяющей запрос. Для этого служит функция IoSetCancelRoutine().
L
(IN P I R P I r p ,
CancelRoutine) Где функция CancelRoutineQ имеет такой же прототип, как и все диспетчерские функции. VOID IN P I R P I r p ) Она вызывается на уровне IRQL DISPATCH_LEVEL в случайном контексте пото ка, однако перед ее вызовом происходит захват специальной системной спин-блоки ровки. До тех пор, пока системная спин-блокировка не будет освобождена, функция CancelRoutineQ работает на уровне IRQL Уровень IRQL, на кото р ы й нужно п е р е й т и п о с л е о с в о б о ж д е н и я б л о к и р о в к и у к а з ы в а е т с я при вызове (см. ниже). Принцип реализации функции следующий: • Если указанный пакет IRP не может быть отменен или не принадлежит драйве ру, то надо освободить системную спин-блокировку и завершить работу функ ции. • В противном случае: удалить пакет IRP из любых очередей, в которых он присутствует; 2) установить функцию отмены IRP в NULL с помощью IoSetCancelRoutineQ; 3) освободить системную спин-блокировку; 4) установить в IRP поле равном 0, а поле равном 5) завершить IRP с помощью Системная спин-блокировка отмены IRP освобождается с помощью IoRelease VOID Где: ния
- уровень IRQL, на который система должна вернуться после Это значение хранится в IRP в поле Cancellrql.
2.4.4.2.1. Отмена IRP и Системная Очередь Пример функции отмены IRP драйвера, использующего системную очередь, пока зан в следующем листинге. Необходимо отметить, что для удаления IRP из системной очереди используется функция так, как это показано в листинге. VOID C a n c e l (IN IN PIRP I r p )
if
Обрабатывается ли (Irp
отменяемый
запрос
в
данный
Освободить системную спин-блокировку и у к а з а т ь диспетчеру н а ч а т ь о б р а б о т к у следующего Отмена IRP - в к о н ц е функции
else Отменяемый IRP н а х о д и т с я Удалить е г о из очереди
Отменить
в
IRP
(Irp,
Отмена IRP и очереди, управляемые драйвером VOID
IN
PIRP devExt; oldlrql; обнулить
указатель
на
Освободить системную к а к можно б ы с т р е е
функцию
отмены
спин-блокировку
devExt DeviceObj Захватить спин-блокировку у д а л и т ь IRP и о с в о б о д и т ь спин-блокировку
доступа
Отменить IRP Irp->IoStatus Status 0;
к
PIRP
Irp)
Механизмы синхронизации В операционной системе с вытесняющей да еще поддержива ющей несколько процессоров, остро встает задача синхронизации доступа к совмест но используемым ресурсам компьютера, будь то аппаратное устройство или структура в памяти.
Спин-блокировки Спин-блокировка простейший механизм синхронизации. Спин-блокировка мо жет быть захвачена, и освобождена. Если спин-блокировка была захвачена, последу ющая попытка захватить любым потоком приведет к бесконечному циклу с попыткой захвата спин-блокировки (состояние потока Цикл за кончится только тогда, когда прежний владелец спин-блокировки освободит ее. пользование спин-блокировок безопасно на мультипроцессорных платформах, то есть гарантируется, что, даже если ее запрашивают одновременно два потока на двух цессорах, захватит ее только один из потоков. Спин-блокировки предназначены для защиты данных, доступ к которым про изводится на различных, в том числе повышенных уровнях IRQL. Теперь вим такую ситуацию: код, работающий на уровне IRQL LEVEL захва тил спин-блокировку для последующего безопасного изменения некоторых дан н ы х . П о с л е э т о г о код б ы л п р е р в а н к о д о м с б о л е е в ы с о к и м у р о в н е м I R Q L который попытался захватить ту же спин-блокировку, и, как следует из описания спин-блокировки, вошел в бесконечный цикл ожидания осво бождения блокировки. Этот цикл никогда не закончится, так как код, который ватил спин-блокировку и должен ее освободить, имеет более низкий уровень IRQL и никогда не получит шанса Чтобы такая ситуация не возникла, не обходим механизм, не позволяющий коду с некоторым уровнем IRQL прерывать код с более низким уровнем IRQL в тот момент когда код с более низким уровнем IRQL владеет спин-блокировкой. Таким механизмом является повышение текуще го уровня IRQL в момент захвата спин-блокировки до некоторого уровня IRQL, ассоциированного со спин-блокировкой, и восстановление старого уровня IRQL в момент ее освобождения. Из сказанного следует, что код, р а б о т а ю щ и й на повы шенном уровне IRQL, не имеет права обращаться к ресурсу, защищенному спинблокировкой, если уровень IRQL спин-блокировки ниже уровня IRQL производя щего доступ к ресурсу кода. При попытке таким кодом захватить спин-блокировку его уровень IRQL будет понижен до уровня IRQL спин-блокировки, что приведет к непредсказуемым последствиям. В NT имеется два вида спин-блокировок: • Обычные спин-блокировки, особым случаем которых являются спин-блокировки отмены запроса используемые при организации очередей запро сов (см. раздел «Отмена запросов • Спин-блокировки синхронизации прерываний.
С обычными спин-блокировками связан IRQL DISPATCHJLEVEL, то есть: • все их захвата должны производиться на уровне IRQL, меньшим или равным DISPATCHJLEVEL; • в случае захвата спин-блокировки текущий уровень IRQL поднимается до уровня DISPATCHJLEVEL. спин-блокировками синхронизации прерываний связан один из уровней DIRQL. Использование обычных спин-блокировок будет описано ниже (за исключением спинблокировок отмены запросов которые были описаны в предыдущем раз деле). Использование спин-блокировок синхронизации прерываний будет описано в разделе, посвященном обработке прерываний.
2.4.5.1.1. Использование обычных спин-блокировок 1) VOID SpinLock); Эта функция инициа лизирует объект ядра Память под спин-блокировку уже должна быть выделена в невыгружаемой памяти. 2) VOID PKSPINJLOCK SpinLock, OUT PKIRQL Oldlrql); Эта функция захватывает спин-блокировку. Функция не вернет управление до успеха захвата блокировки. При завершении функции уровень IRQL повышается до уровня DISPATCHJLEVEL. Во втором параметре возвращается уровень IRQL, который был до захвата блокировки (он должен быть 3) VOID KeReleaseSpinLock(IN PKSPINJLOCK OUT PKIRQL Newlrql); Эта функция освобождает спин-блокировку и устанавливает уровень IRQL в значение п а р а м е т р а N e w l r q l . Это д о л ж н о быть т о з н а ч е н и е , к о т о р о е в е р н у л а ф у н к ц и я KeAcquireSpinLock() в параметре Oldlrql. 4) VOID KeAcquireLockAtDpcLevel(IN SpinLock); Эта оптимизи рованная функция захватывает спин-блокировку кодом, уже работающем на уровне IRQL DISPATCHJLEVEL. В этом случае изменение уровня IRQL не требуется. На однопроцессорной платформе эта функция вообще ничего не делает, так как синхро низация обеспечивается самой архитектурой IRQL. 5) VOID KeReleaseLockFromDpcLevel(IN PKSPINJLOCK SpinLock); Эта функция освобождает спин-блокировку кодом, захватившим блокировку с помощью функции KeAcquireLockAtDpcLevel(). На однопроцессорной платформе эта функция ничего не делает. Пример использования обычных спин-блокировок: struct
spinlock
NTSTATUS
произвести защищенных
обработку данных, спин-блокировкой
Проблема взаимоблокировок (deadlocks) Если поток попробует захватить спин-блокировку повторно, он войдет в бесконеч ный цикл ожидания - «повиснет». Такая же ситуация возникнет, если два потока ис пользуют две спин-блокировки. Поток 1 захватывает блокировку 1, одновременно с этим поток 2 захватывает блокировку 2. Затем поток 1 пробует захватить блокировку 2, а поток 2 - блокировку 1. Оба потока «виснут». Эту ситуацию можно распростра нить на произвольное число потоков, она широко известна и носит название взаимо блокировки (deadlocks). Решение этой проблемы очень простое. Все блокировки, которые могут захваты ваться одновременно, помещаются в список в порядке убывания частоты использо вания. При необходимости захвата блокировок они должны быть захвачены в том порядке, в котором они указаны в списке. Таким образом, мы создали иерархию блокировок.
Диспетчерские объекты Спин-блокировки абсолютно необходимы в случаях, когда требуется синхрониза ция кода, работающего на повышенных уровнях IRQL. Но основное правило в NT работать на повышенных уровнях IRQL в течение как можно более короткого времени значит, использование спин-блокировок следует по возможности избегать. Диспетчер ские объекты NT - это набор механизмов синхронизации, рассчитанных на примене ние в основном для уровня IRQL Базой для любого диспетчерского объекта является структура DISPAT(определена в ntddk.h). Общим свойством любого диспетчерского объекта является то, что в каждый момент времени такой объект находится в одном из двух состояний - сигнальном или несигнальном, а также то, что поток, ожидающий захвата диспетчерского объекта, блокирован и помещен в список ожидания, находя щийся в структуре Блокирование потока означает его особое состояние, при котором он не занимает время процессора. Блокированный поток не будет поставлен планировщиком в оче-
редь на исполнение до тех пор, пока не будет выведен из состояния блокирования. Это фундаментально отличает ожидание освобождения любого диспетчерского объекта от попытки захвата В последнем случае, как было сказано, поток, ватывающий спин-блокировку, «крутится» в бесконечном цикле до момента успешно го захвата (отсюда и название спин-блокировка - блокировка вращения в бесконечном цикле). Единственным отличием одного диспетчерского объекта от другого является пра вило, в соответствии с которым меняется состояние объекта (переход в сигнальное или несигнальное состояние). В таблице 8 перечислены диспетчерские объекты и мо менты изменения их состояния. Таблица Тип Объекта
Счетчик захватов стано вится ненулевым Установка события в сиг нальное состояние
Результат для ожидаю щих Освобождается один из ожидающих потоков Освобождается некоторое число ожидающих потоков Освобождается один из ожидающих потоков
Установка события в сиг нальное состояние Наступило время или истек интервал
Освобождаются все ожи дающие Освобождается один из ожидающих потоков
Поток
Наступило время или истек интервал Завершился последний по ток процесса Поток завершился
Файл
Завершена операция вво
Освобождаются дающие Освобождаются дающие Освобождаются дающие Освобождаются дающие
Мьютекс Семафор (Semaphore) Событие синхрониза ции (Synchronization events) Событие оповещения (Notification event) Таймер синхрониза ции (Synchronization timer) Таймер оповещения (Notification timer) Процесс
Переход в сигнальное состояние Освобождение мьютекса
все ожи все ожи все ожи все ожи
Диспетчерские объекты управляются Диспетчером объектов. Как и все объек ты Д и с п е т ч е р а объектов, они могут иметь имена в пространстве имен Диспетче ра объектов. С п о м о щ ь ю этого имени р а з л и ч н ы е д р а й в е р а и п р и к л а д н ы е п р о г р а м м ы могут обращаться к соответствующему объекту. Кроме того, каждый про цесс имеет таблицу описателей, связанных с конкретным объектом. Как уже го в о р и л о с ь , описатель в таблице описателей уникален и имеет с м ы с л только в кон тексте конкретного процесса. Однако Д и с п е т ч е р объектов предоставляет функ-
цию которая дает в о з м о ж н о с т ь получения указа теля на объект по его описателю. Эту функцию, как следует из вышесказанного, можно использовать только в контексте известного процесса (для которого со здавался описатель), а полученный указатель на объект уже можно использовать в случайном контексте. Ч т о б ы такой объект впоследствии мог быть удален, по о к о н ч а н и и его и с п о л ь з о в а н и я д о л ж н а б ы т ь в ы з в а н а ф у н к ц и я O b D e r e f e r e n c e
Ожидание (захват) диспетчерских объектов Как уже было сказано, каждый диспетчерский объект всегда находится в одном из двух состояний сигнальном (свободном) или несигнальном (занятым). Термины «сво бодный» и «занятый» довольно вольные, поэтому лучше использовать термины сиг нальный и несигнальный. Для ожидания момента перехода объекта из несигнального в сигнальное состояние служат специальные функции ожидания: KeWaitFor и Важной особенностью этих функций слу жит то, что в качестве одного из их параметров указывается интервал времени, в тече ние которого необходимо ждать. Если указан нулевой интервал времени (но не NULL вызов функции ожида ния не блокирует поток. В этом случае она работает как функция проверки состояния диспетчерского объекта, и может быть вызвана на уровне IRQL меньшем или равном DISPATCH_LEVEL. Если интервал времени не указан (NULL в качестве параметра), или указан ненулевой интервал времени, функции ожидания можно вызывать на уровне IRQL строго м е н ь ш е м D I S P A T C H L E V E L . В противном случае будет сделана попытка блокирования потока, но, как мы говорили р а н ь ш е , механизм диспетчеризации на уровнях IRQL м е н ь ш и х или равных D I S P A T C H L E V E L не работает. П е р е к л ю чение контекста потока не сможет произойти, и функция ожидания завершит ра боту, как будто ожидаемый объект находится в сигнальном состо янии. Отличие функции KeWaitForMultipleObjects() от в том, что она может ожидать перехода в сигнальное состояние сразу всех указанных в ней диспетчерских объектов, либо любого одного из них.
Мьютексы ядра Слово Мьютекс Mutually Exclusive) означает взаимоисключение, то есть мьютекс обеспечивает нескольким потокам взаимоисключающий доступ к совместно используемому ресурсу. Вначале отметим, что кроме мьютексов ядра, есть еще быстрые мьютексы, являю щиеся объектами исполнительной системы и не являющиеся диспетчерскими объек тами. Мьютексы ядра обычно называют просто мьютексами. Мьютексы это диспетчерские объекты, эквиваленты спин-блокировок. Двумя важными отличиями мьютексов от спин-блокировок являются:
• Захват является уникальным в рамках конкретного контекста потока. Поток, в контексте которого произошел захват мьютекса, является его владельцем, и может впоследствии захватывать вер, захвативший мьютекс в конкретном контексте потока, обязан освобо дить его в том же потока, нарушение этого правила приведет к появлению «синего экрана». • Для мьютексов предусмотрен механизм исключения взаимоблокировок. з а к л ю ч а е т с я в т о м , ч т о при и н и ц и а л и з а ц и и м ь ю т е к с а ф у н к ц и е й KeInitializeMutex() указывается уровень (level) мьютекса. Если потоку тре буется захватить несколько мьютексов одновременно, он должен делать это в порядке возрастания значения level. Функции работы с мьютексами ядра: 1) VOID Mutex, IN ULONG Level); Эта функция инициализирует мьютекс. Память под мьютекс уже должна быть выделена. После инициализации мьютекс находится в сигнальном состоянии. 2) LONG PKMUTEX Mutex, IN BOOLEAN Wait); Эта функ ция освобождает мьютекс, с указанием того, последует ли сразу после этого вызов функции ожидания мьютекса. Если параметр Wait равен TRUE, сразу за вызовом KeReleaseMutex() должен следовать вызов одной из функций ожида ния В этом случае гарантируется, что пара функций освобож дение мьютекса и ожидание - будет выполнена как одна операция, без воз можного в противном случае переключения контекста потока. Возвращаемым значением будет 0, если мьютекс был освобожден, то есть переведен из несиг нального состояния в сигнальное. В противном случае возвращается ненуле вое значение. 3) LONG KeReadStateMutex(IN PKMUTEX Mutex); Эта функция возвращает со стояние мьютекса - сигнальное или несигнальное.
Семафоры Семафоры являются более гибкой формой мьютексов. В отличие от мьютексов, программа имеет контроль над тем, сколько потоков одновременно могут захватывать семафор. Семафор инициализируется с помощью функции KeInitializeSemaphore(): VOID IN PKSEMAPHORE S e m a p h o r e , IN LONG IN LONG Где: Count - начальное значение, присвоенное семафору, определяющее число свобод ных в данный момент ресурсов. Если семафор находится в несигнальном состоянии (свободных ресурсов нет), если - в сигнальном;
Limit - максимальное значение, которое может достигать Count (максимальное число свободных ресурсов). Функция KeReleaseSemaphore() увеличивает счетчик семафора Count на указан ное в параметре функции значение, то есть освобождает указанное число ресурсов. Если при этом значение Count превышает значение Limit, значение Count не изменяет ся и генерируется исключение При вызове функции ожидания счетчик семафора уменьшается на 1 для каждого разблокированного потока (число свободных ресурсов уменьшается). Когда он дости гает значения 0, семафор переходит в несигнальное состояние (свободных ресурсов Использование семафора не зависит от контекста потока или процесса в том смыс ле, что занять ресурс семафора может один поток, а освободить его другой, но драй вер не должен использовать семафоры в случайном контексте потока, так как в этом случае будет заблокирован случайный поток, не имеющий к драйверу никакого отно шения. Семафоры следует использовать в ситуациях, когда драйвер создал собствен ные системные потоки.
События позволяют проводить синхронизацию исполнения различных потоков, то есть один или несколько потоков могут ожидать перевода события в сиг нальное состояние другим потоком. При этом события могут быть двух видов: • События, при переводе которых в сигнальное состояние будет разблокирован только один поток, после чего событие автоматически переходит в несигналь ное состояние. Такие события носят название события синхронизации (synchronization events). • События, при переводе которых в сигнальное состояние будут разблокированы все ожидающие их потоки. Событие должно быть переведено в несигнальное состояние вручную. Такие события носят название оповещающих (notification event). Функции работы с событиями: 1) инициализирует событие. Память под событие уже должна быть выделена. При инициализации указывается тип синхронизация или оповеще ние, а также начальное состояние сигнальное или несигнальное. Имя события задать нельзя. Функция может быть использована в случайном контексте памяти на уровне IRQL 2) IoCreateNotificationEvent(), IoCreateSynchronizationEvent() создают новое или открывает с у щ е с т в у ю щ е е событие с заданным именем. Если объект с таким именем существует, он открывается, если не существует, то создается. Имя со бытия обычно указывается в директории диспетчера объектов И м е н н о в этой д и р е к т о р и и содержатся имена с о б ы т и й , создаваемых или открываемых
Функция возвращает как указатель на так и его описатель в таб лице описателя текущего процесса. Для уничтожения объекта необходимо исполь зовать ф у н к ц и ю Z w C l o s e ( ) с описателем в качестве параметра. Описатель дол жен быть использован в контексте того процесса, в котором он был получен на уровне IRQL 3) KeClearEventQ и сбрасывают указанное событие в несигналь ное состояние. Отличие между функциями в том, что KeResetEvent() возвращает со стояние события до сброса. Функции могут быть вызваны на уровне IRQL меньшем или равном 4) KeSetEvent() переводит событие в сигнальное состояние получает предыду щее состояние. Одним из параметров является логическая переменная, указывающая, будет ли за вызовом KeSetEventQ немедленно следовать вызов функции ожидания. Если параметр TRUE, то гарантируется, что вызов этих двух функций будет выполнен как одна В случае событий оповещения сброс события в несигнальное состояние должен быть сделан вручную. Обычно это делает тот же код, который перевел событие в сиг нальное состояние. Следующий код корректно уведомляет все блокированные потоки о наступлении ожидаемого ими события:
2.4.5.2.5. Быстрые мьютексы Быстрый мьютекс являются урезанным вариантом мьютекса, который не мо жет быть р е к у р с и в н о захвачен. Поскольку быстрый мьютекс не является диспет черским объектом, он не может использоваться функцией KeWaitForSingleObjectQ или В м е с т о этого н у ж н о и с п о л ь з о в а т ь ф у н к ц и ю E x A c q u i r e F a s t M u t e x ( ) . Э к в и в а л е н т а б ы с т р ы м м ь ю т е к с а м на пользовательском у р о в н е нет, поэтому они могут использоваться только для с и н х р о н и з а ц и и кода режима ядра. Функции работы с быстрыми мьютексами: 1) VOID ExInitializeFastMutex(IN PFAST_MUTEX FastMutex); 2) VOID ExAcquireFastMutex(IN PFASTJVIUTEX FastMutex); 3) BOOLEAN PFASTJVIUTEX FastMutex); 4) VOID ExReleaseFastMutex(IN FastMutex); 5) VOID ExAcquireFastMutexUnsafe(IN PFASTJVIUTEX FastMutex); VOID ExReleaseFastMutexUnsafe (IN PFASTJVIUTEX FastMutex).
Р е с у р с ы Исполнительной системы Ресурсы являются вариантом быстрого мьютекса. Ресурсы не являются диспет черскими объектами, поэтому они не могут иметь имя и использоваться в функции
или
Ресурсы предоставляют две
формы захвата: • Эксклюзивный в этом случае ресурс ведет себя как обычный мьютекс - поток, который попытается захватить такой ресурс для эксклюзивного или совместно го использования, будет блокирован. • Совместно используемый в этом случае ресурс может быть одновременно зах вачен для совместного использования любым числом потоков. Ресурсы идеально подходят для защиты структур данных, которые могут одновре менно читаться несколькими потоками, но должны модифицироваться в каждый мо мент времени только одним потоком. Для работы с ресурсами существуют функции запроса эксклюзивного доступа, неэксклюзивного доступа и преобразования уже полученного неэксклюзивного дос тупа в эксклюзивный и, наоборот, без промежуточных операций освобождения ресур са и запроса нового режима доступа. Все функции должны вызываться на уровне IRQL меньшем DISPATCH_LEVEL. Функции работы с ресурсами: 1) NTSTATUS ExInitializeResourceLite(IN PERESOURCE 2) ExReinitializeResourceLite(IN PERESOURCE 3) B O O L E A N ExAcquireResourceExclusiveLite(IN PERESOURCE BOOLEAN 4) BOOLEAN ExTryToAcquireResourceExclusiveLite(IN PERESOURCE Resource); 5) BOOLEAN PERESOURCE BOOLEAN Wait); 6) 7) 8) 9) 10) 12) 13) 14)
B O O L E A N ExAcquireSharedStarveExclusive(IN PERESOURCE BOOLEAN Wait); BOOLEAN ExAcquireSharedWaitForExclusive(IN PERESOURCE BOOLEAN Wait); VOID PERESOURCE Resource); BOOLEAN PERESOURCE Resource); USHORT PERESOURCE Resource); ULONG ExGetExclusiveWaiterCount(IN PERESOURCE Resource); ULONG ExGetSharedWaiterCount(IN PERESOURCE Resource); NTSTATUS ExDeleteResourceLite(IN PERESOURCE Resource); VOID PERESOURCE Resource; IN
2.4.5.4. Обобщенная таблица механизмов синхронизации В таблице 9 представлены механизмы синхронизации и особенности использова ния каждого из них.
Таблица 9 Уровень IRQL, на котором может работать
Объект синхронизации
Уровень IRQL, на котором будет ра ботать запросив ший синхрониза синхронизацию цию поток при освобождении объекта синхрони Запрос Запрос или его пе без блокирования с блокированием реходе в сигналь потока потока. ное состояние
Стандартная спинDISPATCH_LEVEL блокировка (Stan dard Spin Lock) Спин-блокировка для ISR, определен DIRQL DIRQL ная по умолчанию ISR Spin Lock) Спин-блокировка для синхронизации Specified DIRQL Specified DIRQL с ISR (ISR Synchro nize Spin Lock) <DISPATCH LEVEL Мьютекс (Mutex) Семафор (Sema <=DISPATCH_LEVEL phore) Событие синхрони <=DISPATCH_LEVEL зации (Synchroniza tion Event) Событие уведомле <=DISPATCH_LEVEL ния (Notification Event) Таймер синхрониза ции (Synchronization Timer) Таймер уведомле <=DISPATCH_LEVEL ния (Notification Timer) <=DISPATCH_LEVEL <DISPATCH LEVEL Процесс (Process) Поток (Thread) Файл (File) DISPATCH LEVEL Ресурсы (Resources)
Рабочие потоки 2.4.6.1. Необходимость в создании рабочих потоков Любой исполняемый код, как и код драйвера, работает в контексте некоторого потока. Мы пока не обсуждали способы, с помощью которых драйвер может создать собственный поток, поэтому предполагается, что поток, в котором выполняется код драйвера, принадлежит некоторой прикладной программе. Это означает, что при кладная программа создала такой поток для выполнения своего кода, а не кода наше го драйвера. Если код драйвера производит длительную обработку, либо драйвер использует механизм синхронизации с ожиданием освобождения некоторого ресур са, код прикладной программы, для выполнения которого и создавался поток, не выполняется. Если этот поток единственный в прикладном процессе, то прикладная программа «висит». Если описанная ситуация имеет место в диспетчерской функции драйвера верхне го уровня, мы «всего лишь» «подвесили» прикладную программу, непосредственно взаимодействующую с драйвером. В этом случае прикладная программа знает о такой возможности, и может поместить операции взаимодействия с драйвером (чтение, за пись, отправка кодов управления) в отдельный поток. В этом случае драйвер может не беспокоиться о прикладной программе. Однако, такая ситуация довольно редка. Очень часто код драйвера работает в контексте случайного потока, то есть любого произ вольного потока в системе. Такой поток ничего не знает о нашем драйвере и вышеопи санная ситуация неприемлема. В этом случае драйвер должен создать свой собствен ный поток, в котором и производить длительную обработку, либо ожидание освобож дения ресурсов. Возможна другая ситуация, требующая обязательного создания потоков, когда драй веру необходимо выполнить операции на уровне IRQL меньшем а код д р а й в е р а работает на п о в ы ш е н н ы х уровнях I R Q L , б о л ь ш и х или р а в н ы х
2.4.6.2. Системные рабочие потоки В процессе системной инициализации NT создает несколько потоков в процессе System. Эти потоки служат исключительно для выполнения работы, затребованной другими потоками. Такие потоки наиболее удобны в случаях, когда потоку с повышен ным уровнем IRQL требуется выполнить работу на уровне IRQL PASSIVE_LEVEL. В принципе, можно создать новый поток, однако создание нового потока и его планирование планировщиком является более ресурсоемким, чем использование су ществующего потока. Большинство стандартных компонент ОС, таких как компо ненты файловой системы, используют для своих нужд готовые системные рабочие Имеются ситуации, при которых использование системных рабочих потоков не приемлемо в силу их организации. Такими ситуациями являются необходимость в дли-
тельной (несколько сотен микросекунд) обработке внутри потока, либо длительное ожидание освобождения ресурса или наступления события. В этих ситуациях драйвер должен создавать свой собственный поток. Как организованы системные рабочие потоки? Как уже было сказано, в процессе системной инициализации NT создает несколько системных рабочих потоков. Число этих потоков фиксировано. Для всех потоков существует единая очередь, из которой поток выбирает адрес функции драйвера, которая должна быть выполнена в данном потоке. Такая функция называется рабочим элементом Функция выпол няется в потоке до своего завершения, после чего поток выбирает из очереди следую щий рабочий элемент. Если очередь пуста, поток блокируется до появления в очереди очередного рабочего элемента. Существует три типа системных рабочих потоков: Delayed (замедленные), Critical (критические) и (сверхкритические). Все типы потоков создаются на уров не IRQL Для каждого типа потоков будут различны: • число потоков данного типа; • базовый приоритет планирования потока, относящегося к данному типу; • очередь рабочих элементов. Число потоков каждого типа зависит от объема памяти и типа ОС. В таблице 10 указано число потоков и базовый приоритет планирования для ОС Win2000 Professional и Server. Таблица
Число
Системных Рабочих Потоков
Объем системной памяти Тип рабочего потока 12-19 MB MB Delayed 3 3 3
Critical
3
HyperCritical
1
Professional: 3 Server: 6 1
Professional: 5 Server: 10 1
Базовый планирования Значение в диапазо не динамических приоритетов Значение в диапазо не приоритетов ре ального времени Не документирован
Следует отметить, что использование единственного потока типа HyperCritical не документировано. ОС использует этот поток для выполнения функции - чистильщи ка, которая освобождает потоки при их завершении. При постановке рабочего элемента в очередь указывается тип потока, которому предназначен рабочий элемент. Для работы с системными рабочими потоками существует два набора функций функции с префиксом и функции с префиксом Функции с префиксом ис пользовались в ОС NT 4.0 и более ранних версиях, и в считаются устаревши ми. В любом случае, вначале драйвер должен инициализировать рабочий элемент с
помощью функций ExInitializeWorkItem() или поместить чий элемент в очередь с помощью функций ExQueueWorkItem() или а при запуске функции, указанной в рабочем элементе, эта функция обязана освобо дить занимаемые рабочим элементом ресурсы с помощью функций ExFreePool() или IoFreeWorkItem().
Создание потоков драйвером В случае, использование системных рабочих потоков невозможно, драйвер должен создать свой собственный поток. Для создания нового потока используется функция PsCreateSystemThread(). В качестве одного из параметров функция имеет описатель процесса, в контексте которого нужно создать поток. Чтобы правильно ис пользовать описатель, код драйвера должен выполняться в контексте процесса, табли ца описателей которого содержит описатель процесса, в контексте которого мы хотим создать поток. Если описатель процесса не указан (значение NULL), новый поток бу дет создан в контексте процесса System. Д л я у н и ч т о ж е н и я потока и з д р а й в е р а и с п о л ь з у е т с я ф у н к ц и я P s T e r m i n a t e Эта функция должна быть вызвана из самого уничтожаемого потока, так как она уничтожает текущий поток и не позволяет указать поток, который нужно уничтожить. Вновь созданный поток будет работать на уровне IRQL P A S S I V E L E V E L и иметь базовое значение приоритета планирования равным 8 (динамический диапа зон приоритетов, базовое значение для класса N O R M A L ) . После создания код по может изменить базовое значение приоритета планирования на любое значе в диапазоне динамических приоритетов либо приоритетов реального времени. Это делается с п о м о щ ь ю функции Отметим, что это не по в ы ш е н и е уровня приоритета планирования, после которого уровень приоритета постепенно снизится до базового значения, а именно установка нового базового значения приоритета. Код потока может не только изменить значение приоритета планирования при уров не IRQL но и повысить уровень IRQL. Для этого служит функция KeRaiseIrql(). Работа потока на повышенном уровне IRQL должна быть завершена как можно скорее, после чего должно быть восстановлено первоначальное значение IRQL с помощью функции KeLowerIrql(). Использование функции KeRaiseIrql() для пони жения IRQL и функции KeLowerIrql() для повышения IRQL не допускается, так как это приведет к возникновению синего экрана.
2.4.6.4. Потоки как диспетчерские объекты Как говорилось в разделе, посвященном механизмам синхронизации, поток явля ется диспетчерским объектом, который переходит в сигнальное состояние при своем завершении. Следующий пример демонстрирует способ синхронизации с помощью объекта-потока.
status NULL, 0, NULL,
if ( s t a t u s
STATUS_SUCCESS) ошибки
else status THREAD_ALL_ACCESS, NULL,
if
(status ошибки
Функция VOID
(PVOID
код
Context)
потока потока
ожидающая
работы
status Executive,
П р о к о м м е н т и р у е м этот п р и м е р . При создании потока с п о м о щ ь ю функции P s C r e a t e S y s t e m T h r e a d ( ) в о з в р а щ а е т с я о п и с а т е л ь потока в контексте процесса, в котором поток б ы л создан. В а ж н о п о н и м а т ь , что это может быть с о в е р ш е н н о не тот п р о ц е с с , в контексте которого была вызвана функция PsCreateSystem T h r e a d ( ) , В этом с л у ч а е мы не м о ж е м н а п р я м у ю в о с п о л ь з о в а т ь с я ф у н к ц и е й ObReference для п о л у ч е н и я указателя на объект-поток по его описателю. С у щ е с т в у е т п р о с т е й ш и й с п о с о б р е ш е н и я этой п р о б л е м ы , о с н о в а н н ы й на том ф а к т е , что ф у н к ц и я - точка входа в д р а й в е р DriverEntry, всегда вызывается в контексте потока System. Вызов функции следует про изводить из DriverEntry, и при этом указывать создавать поток в контексте про ц е с с а S y s t e m . П о л у ч и в о п и с а т е л ь с о з д а н н о г о потока, м о ж н о п о л у ч и т ь указа т е л ь на о б ъ е к т - п о т о к с п о м о щ ь ю и в дальней ш е м пользоваться этим указателем в контексте л ю б о г о п р о ц е с с а и потока, При з а в е р ш е н и и и с п о л ь з о в а н и я объекта-потока надо о б я з а т е л ь н о о с в о б о д и т ь его с помощью вызова Все вышесказанное иллюстрируется р и с . 13. Если д р а й в е р у все же необходимо по к а к и м - л и б о п р и ч и н а м создать поток в контексте п р о ц е с с а , отличного от п р о ц е с с а System, л и б о создать поток в кон т е к с т е п р о ц е с с а S y s t e m , находясь в контексте д р у г о г о потока, ему н у ж н о ка ким-то образом попасть в контекст памяти п р о ц е с с а , в т а б л и ц е о п и с а т е л е й ко т о р о г о х р а н и т с я и н ф о р м а ц и я о н у ж н о м п р о ц е с с е . Для этого с л у ж и т недокумен тированная функция П о с л е н е о б х о д и м о й обработки необхо д и м о в о с с т а н о в и т ь п р е д ы д у щ е е состояние с п о м о щ ь ю вызова В ы ш е с к а з а н н о е о т н о с и л о с ь только к ОС W i n d o w s NT 4.0. В ОС п о я в и л а с ь с п е ц и а л ь н а я таблица о п и с а т е л е й , н а з ы в а е м а я т а б л и ц е й о п и с а т е л е й ядра (kernel handle которая может б ы т ь д о с т у п н а с п о м о щ ь ю экспортиру емого имени Таблица д о с т у п н а только из р е ж и м а ядра, при этом у всех описателей с т а р ш и й бит у с т а н о в л е н в 1, так что значения всех о п и с а т е л е й п р е в ы ш а ю т 0 x 8 0 0 0 0 0 0 0 . Как быть с у н и к а л ь н о с т ь ю о п и с а т е л е й для каждого на м о м е н т н а п и с а н и я д а н н о й книги н е я с н о и н у ж д а е т с я в исследовании.
Рис. 13
2.4.7. Введение в обработку прерываний Одной из основных обязанностей NT является сопряжение компьютера с его пе риферийными устройствами. Подобно почти всем современным операционным сис темам, NT может динамически объединять программы драйверов устройств для уп равления устройствами. Драйверы устройств обычно используют сигналы прерыва ний для связи с контролируемыми ими устройствами. Когда устройство завершает указанную драйвером операцию, или когда устройство имеет новые данные для драй вера, устройство генерирует сигнал прерывания. В зависимости от состояния CPU, либо функция драйвера немедленно обслуживает прерывание, либо CPU помещает прерывание в очередь для обслуживания позднее.
Объекты
прерывания
Драйверам устройств необходим способ сообщения NT, что они хотят, чтобы ис полнялась определенная функция, когда процессор получает прерывание, относящее ся к их устройствам. Для этого драйверы устройств с помощью Диспетчера ввода/ вывода регистрируют функцию обработки прерывания (Interrupt Service Routine, ISR) посредством вызова функции Параметры, передаваемые в
описывают все свойства ISR драйвера, включая ее адрес, прерыва ние, к которому подключена ISR и то, могут ли другие устройства исполь зовать это же прерывание. Функция IoConnectlnterrupt инициализирует объект-прерывание (Interrupt Object), д л я т о г о ч т о б ы х р а н и т ь и н ф о р м а ц и ю о п р е р ы в а н и и и п о д к л ю ч е н н о й ISR. IoConnectlnterrupt программирует также аппаратуру прерываний для того, чтобы ука зывать код, который IoConnectlnterrupt поместила в объект-прерывание. Таким обра зом, когда CPU получит прерывание, управление немедленно перейдет к коду в объекте-прерывание. Этот код вызовет вспомогательную функцию обслуживания прерыва ния, которая повысит уровень IRQL процессора, вызовет соответ ствующую ISR, и понизит IRQL до предыдущего значения. KiInterruptDispatch также получает спин-блокировку, индивидуальную для прерывания, и удерживает ее, пока выполняется ISR (см. раздел «Механизмы Спин-блокировка гаран тирует, что ISR не будет одновременно исполняться более чем на одном процессоре, а это может привести к печальным последствиям. В NT, ISR обычно не делает ничего, кроме чтения минимального количества ин формации из прерывающего устройства и подтверждения устройству того факта, что драйвер «увидел» прерывание. В других операционных системах ISR часто няют дополнительные обязанности, такие, как полная обработка прерывания путем чтения больших буферов данных, или записи больших буферов данных в устрой ство. Однако, одна из задач NT минимизировать время, проводимое на повышен ных уровнях IRQL, поэтому NT откладывает большую часть обслуживания преры вания до момента уменьшения уровня IRQL. Процедуры ISR запрашивают отложен ный вызов процедур (Deferred Procedure DPC) для информирования Дис петчера ВВОДА/ВЫВОДА о том, что у них имеется работа для исполнения на ниж нем уровне IRQL. DPC еще одна функция в драйвере, которую вызовет Диспетчер после завершения ISR; D P C осуществляет почти все взаимодействие с устройством. Рис. 14 описывает типичный ход обслуживания прерывания NT. Контроллер уст ройства генерирует сигнал прерывания на шине процессора, который обрабатывает контроллер прерываний процессора. Этот сигнал служит причиной для CPU для вы полнения кода в объекте-прерывании, зарегистрированном для прерывания. Этот код, в свою очередь, вызывает вспомогательную функцию Dispatch. KiInterruptDispatch вызывает ISR драйвера, которая запрашивает NT также имеет механизм обработки прерываний, не зарегистрированных драйве рами устройств. В процессе инициализации системы NT программирует контроллер прерываний так, чтобы указывать на функции ISR по умолчанию. Функции ISR по умолчанию осуществляют специальную обработку, когда система генерирует ожидае мые прерывания. Например, ISR ошибки отсутствия страницы должна выполнить об работку в ситуации, при которой программа ссылается на виртуальную память, кото рая не имеет выделенного пространства в физической памяти компьютера. Такая си туация может возникнуть когда программы взаимодействуют с файловой системой для
получения данных из файла подкачки или исполняемого или когда программы ссылаются на недействительный NT программирует незарегистрированные так, чтобы они указывали на обработчики ISR, которые распознают, что система сгенерировала неразрешенное прерывание. Почти все эти ISR высвечивают синий экран смерти SOD - blue screen of death) для уведомления системного адми нистратора о том, что произошло неразрешенное прерывание.
Таблица обработчиков прерываний процессора
О
Контроллер периферийного устройства
Контроллер процессора N
ISR драйвера Адрес Спинблокировка
Повышение IRQL
Чтение из
Захват спин-блокировки Опознание прерывания
входа
Освобождение спин-блокировки Понижение IRQL
Запрос
14
Отложенный вызов процедуры (Deferred Procedure Call, DPC) Вдобавок к использованию для работы Диспетчера (планировщика) NT, IRQL d i s p a t c h l e v e l также используется для обработки Отложенных Вызовов Процедур (DPC). Вызовы DPC - обратные вызовы подпрограмм, которые будут выполнены на IRQL dispatchlevel. Вызовы DPC обычно запрашиваются с более высоких уровней IRQL, для осуществления расширенной, не критической по времени обработки. Давайте рассмотрим пару примеров того, когда используются Драйверы ус тройств Windows NT выполняют очень небольшую обработку внутри своих подпрог-
рамм обслуживания Вместо этого, когда устройство прерывается (на уров не DIRQL) и его драйвер определяет, что требуется сложная обработка, драйвер шивает Запрос DPC приводит к обратному вызову определенной функции драй вера на уровне IRQL dispatchlevel для выполнения оставшейся части требуемой об работки. Выполняя эту обработку на IRQL dispatch_level, драйвер проводит меньшее количество времени на уровне DIRQL, и, следовательно, уменьшает время задержки прерывания для всех других устройств в системе. На рис. изображена типовая последовательность событий. iSR драйвера
Контрольный блок процессора
Чтение из устройства
Опознание прерывания
Запрос
15 Вначале ISR запрашивает DPC и NT помещает объект D P C в очередь целевого процессора. В зависимости от приоритета DPC и длины очереди DPC, NT генерирует программное прерывание DPC сразу же или спустя некоторое время. Когда процессор очищает очередь DPC, объект DPC покидает очередь и управление передается в его функцию DPC, завершающую обработку прерывания путем чтения данных из устрой ства или записи данных в устройство, сгенерировавшего прерывание. Другое распространенное использование DPC подпрограммы таймера. Драйвер может запросить выполнение конкретной функции для уведомления об истечении оп ределенного периода времени (это делается путем использования функции Программа обработки прерывания часов следит за прохождением вре мени, и, по истечении определенного периода времени, запрашивает D P C для под программы, определенной драйвером. Использование DPC для таймерного уведомле ния позволяет программе обработки прерывания часов возвращаться быстро, но все же приводить к вызову указанной процедуры без чрезмерной задержки.
DPC-объекты Вызов DPC описывается Объектом DPC, Определение Объекта DPC изведено в ntddk.h и показано на рис. 16.
про
Рис. 16. Объект DPC Объект DPC может быть выделен драйвером из любого невыгружаемого простран ства (типа невыгружаемого пула). Объекты DPC инициализируются, используя функ цию KeInitializeDpc(), прототип которой: VOID (IN PKDPC D p c , IN IN PVOID D e f e r r e d C o n t e x t ) Где: Dp с ~ Указатель на DPC объект, который надо инициализировать; - указатель на функцию, по которому должен быть сделан отло женный вызов на уровне IRQL DISPATCHJLEVEL. Прототип функции DeferredRoutine следующий: IN PKDPC D p c , I N PVOID D e f e r r e d C o n t e x t , I N PVOID I N PVOID S y s t e m A r g u m e n t 2 Где: DeferredContext значение для передачи к DeferredRoutine в качестве параметра, вместе с указателем на объект DPC и двумя дополнительными параметрами. Запрос на выполнение конкретной подпрограммы DPC делается путем помеще ния объекта описывающего эту подпрограмму DPC, в Очередь DPC заданного CPU, и последующим (обычно) запросом программного прерывания уровня IRQL
Имеется одной Очереди DPC на процессор. CPU, к которому объект DPC поставлен в очередь, является обычно текущим процессором, на котором выдан запрос (на прерывание). Как выбирается процессор для конкретного DPC, обсуждает ся позже, в разделе Объекта Объект DPC ставится в очередь с помощью функцию прототип которой: VOID (IN PKDPC I N PVOID IN PVOID Где: Dpc - Указывает на объект DPC, который нужно поставить в очередь; SystemArgument2 произвольные значения, которые нужно пе редать функции как 3 и 4 параметры соответственно, наряду с указа телем на объект DPC и параметром определенным при инициализа ции Объекта
Активизация и обслуживание DPC Происхождение программного прерывания уровня Dispatch_level распознается тогда, когда это прерывание становится наивысшим по уровню IRQL событием, ожи д а ю щ е м обработки на этом процессоре. Таким образом, после вызова функции обычно в следующий раз, когда процессор готов возвратиться на уровень IRQL ниже dispatch_level, вместо этого он вернется на IRQL и попытается обработать содержимое Очереди Как отмечено ранее в этой главе, IRQL D I S P A T C H L E V E L используется как для диспетчеризации ,так и для обработки Очереди DPC. В NT 4.0, когда обработано пре рывание уровня сначала обслуживается вся очередь DPC, и затем вызывается Диспетчер для планирования выполнения следующего потока. Это разум но, потому что обработка, сделанная подпрограммой DPC, могла изменить состояние базы данных планирования потоков, например, делая работоспособным ожидающий Очередь DPC обслуживается Микроядром. Каждый раз, когда обслуживается Оче редь DPC, обрабатываются все элементы Очереди DPC для текущего процессора. По одному за раз, М и к р о я д р о удаляет Объект D P C из начала очереди и вызывает указанную в объекте. Микроядро передает в качестве параметров для функции указатель на Объект DPC, содержимое полей DeferredContext, и Объекта DPC. Поскольку Очередь DPC обслуживается на IRQL dispatchjevel, подпрограммы DPC вызываются на IRQL dispatchlevel. Поскольку Очередь DPC обслуживается всякий раз, когда IRQL dispatchjevel является самым высокоприоритетным IRQL для обслу живания (например, сразу после того, как отработала программа обработки прерыва ния и перед возвращением к прерванному потоку функции DPC рабо тают в контексте произвольного потока (arbitrary thread context). Под контекстом про извольного потока мы подразумеваем, что DPC выполняется в процессе и потоке, 5 Зак.
торые могут вообще не иметь никакого отношения к запросу, который обрабатывает (Контекст выполнения описан более подробно в разделе Мо дель Подпрограмма DPC завершает обработку и возвращается, По возвращении из под программы DPC, Микроядро пытается выбрать другой Объект DPC из Очереди DPC и обрабатывать его, Когда очередь DPC пуста, обработка DPC заканчивается, Микрояд ро переходит к вызову Диспетчера (планировщика).
Многочисленные обращения к DPC Каждый DPC описан конкретным Объектом В всякий раз, когда вызывается функция и выясняется, что переданный ей Объект DPC уже находится в той же самой Очереди DPC, функция просто воз вращается (не выполняя никаких действий). Таким образом, всякий раз, когда Объект DPC уже находится в Очереди DPC, любые последующие попытки постановки в оче редь того же самого Объекта DPC, осуществляемые до удаления Объекта DPC из оче реди, игнорируются. Это имеет смысл, так как Объект DPC может физически быть включен только в одну Очередь DPC одновременно, Может возникнуть очевидный вопрос; Что произойдет, когда сделан запрос поста новки Объекта DPC в очередь, но система уже выполняет подпрограмму DPC, указан ную этим Объектом DPC (на этом же или другом процессоре)? Ответ на этот вопрос может быть найден при внимательном чтении предыдущего раздела. Когда Микрояд ро обслуживает Очередь DPC, оно удаляет Объект DPC из головы очереди, и только потом вызывает подпрограмму DPC, указанную Объектом Таким образом, когда подпрограмма DPC вызвана, Объект DPC уже удален из Очереди DPC процессора, Поэтому, когда сделан запрос на постановку Объекта DPC в очередь и система нахо дится внутри подпрограммы DPC, заданной в этом Объекте DPC, DPC ставится в оче редь как обычно.
2.4.7.2.4. DPC на многопроцессорных системах Вопреки тому, что утверждалось в некоторых других источниках, и, как должно быть очевидно из предшествующего обсуждения, одна и та же подпрограмма DPC может выполняться на нескольких процессорах одновременно. Нет абсолютно ника кого блокирования со стороны Микроядра, чтобы предотвратить это, Рассмотрим случай драйвера устройства, который в одно и то же время имеет не сколько запросов, ожидающих обработки. Устройство драйвера прерывается на Про цессоре 0, выполняется программа обработки прерывания драйвера и запрашивает DPC для завершения обработки прерывания. Это стандартный путь, которому следуют драй веры в Windows NT. Когда завершается программа обработки прерывания, и система готова возвратиться к прерванному потоку пользователя, уровень IRQL процессора О понижается от DIRQL, на котором выполнялась до IRQL dispatchlevel. В резуль тате, Микроядро обслуживает Очередь DPC, удаляя Объект DPC драйвера и вызывая
указанную в нем подпрограмму DPC. На Процессоре 0 теперь выполняется подпрог рамма DPC драйвера. Сразу после вызова подпрограммы DPC драйвера, устройство генерирует преры вание еще раз. Однако на этот раз, по причинам, известным только аппаратуре, преры вание обслуживается на Процессоре Снова, программа обработки прерывания вера запрашивает DPC. И, снова, когда программа обработки прерывания закончится, система (Процессор 1) готова возвратиться к прерванному потоку пользователя. При этом IRQL процессора 1 понижается до уровня IRQL dispatchjevel, и Микроядро об служивает Очередь Делая так (и по-прежнему выполняясь на Процессоре 1), микроядро удаляет Объект DPC драйвера, и вызывает подпрограмму DPC драйвера. Подпрограмма DPC драйвера теперь выполняется на Процессоре Предполагая, что DPC драйвера еще не завершила выполнение на Процессоре О, замеим, что та же самая подпрограмма DPC теперь выполняется параллельно на обоих роцессорах. Этот пример подчеркивает важность использования в драйверах надлежащего на бора механизмов многопроцессорной синхронизации. В в DPC должны использоваться спин-блокировки для сериализации доступа к любым струк турам данных, к которым нужно обратиться как к единому целому, при условии, что конструкция драйвера такая, что одновременно может произойти несколько вызовов DPC.
Характеристики Объекта DPC Объекты DPC имеют две характеристики, которые влияют на путь, которым они обрабатываются. Этими характеристиками являются поля Importance и Number.
2.4.7.2.5.1. Важность DPC (DPC Importance) Каждый Объект DPC имеет важность, которая хранится в поле Importance Объекта DPC. Значения для этого поля перечислены в ntddk.h под именами и Это значение DPC Объекта влияет на место в Очереди DPC, куда помещается Объект DPC при постановке в очередь, а также то, будет ли иметь место прерывание уровня IRQL dispatchjevel при постановке Объекта DPC в очередь. Функция KeInitializeDpc() инициализирует Объекты DPC с важностью Mediumlmportance. Значение важности объекта DPC может быть установлено, исполь зуя функцию прототип которой:
VOID В
(IN PKDPC Dpc, Importance)
Где: Dpc - Указатель на объект DPC, в котором быть установлено поле Importance; Importance - значение важности для установки в Объекте D P C . Объекты DPC с Mediumlmportance или Lowlmportance помещаются в конец Оче реди DPC. Объекты DPC с Highlmportance ставятся в начало Очереди
Важность Объектов DPC также влияет на то, будет ли при помещении Объекта DPC в очередь сгенерировано программное прерывание уровня dispatchlevel. Когда Объект DPC с или ставится в очередь текущего процессора, всегда генерируется прерывание dispatchlevel. Прерывание генерируется для DPC или для тех DPC, которые предназначены для отличного от текущего процессора, согласно сложному (и недокументированному) алгоритму планирования. В таблице перечислены ситуации, инициирующие освобождение очереди объек тов Большинству драйверов устройства никогда не понадобится устанавливать важ ность своих Объектов В редких случаях, когда задержка между запросом DPC и выполнением DPC чрезмерна, и разработчик драйвера не в состоянии решить устра нить эту задержку другим способом, Вы можете попытаться установить DPC Объекта в Highlmportance. Однако обычно драйверы устройств в Windows NT не изменяют свое значение DPC со значения по умолчанию Mediumlmportance. Таблица
11.
Приоритет DPC Низкий
Средний Высокий
инициирующие
очереди DPC
DPC выполняются на том же DPC выполняются на другом процессоре, что и ISR процессоре Размер очереди DPC превышает Размер очереди DPC превышает максимум, частота появления за максимум или система простаи просов DPC меньше минимальной, вает (выполняется поток idle) или система простаивает Размер очереди DPC превышает Всегда максимум или система простаи вает (выполняется поток idle) Всегда Всегда
2.4.7.2.5.2. Целевой процессор для DPC (DPC Target Processor) В дополнение к важности, каждый DPC Объект имеет целевой процессор (target processor). Это значение хранится в поле Number Объекта Целевой процессор показывает, ограничено ли выполнение DPC заданным процессором в системе, и, если да, то каким процессором. По умолчанию, Kelnitialize Dpc() не определяет целевой процессор. Следовательно, по умолчанию, процедуры DPC будут работать на процес соре, на котором они запрошены (то есть, DPC будет вызван на процессоре, на котором была вызвана подпрограмма DPC может быть ограничено выполнением на указанном процессоре, используя функцию KeSetTargetProcessorDpc(), прототип которой: VOID PKDPC Dpc, IN CCHAR
Где: Dpc - Указывает на объект DPC, для которого должен быть установлен целевой процессор; Number отсчитываемый от нуля номер процессора, на котором должен быть полнен С. Подобно важности DPC, целевой процессор DPC почти никогда не устанавливает ся драйвером устройства. Заданное по умолчанию значение, которое служит для вы полнения DPC на текущем процессоре, почти всегда желательно. Когда для Объекта DPC установлен конкретный целевой процессор, такой Объект DPC будет всегда ставиться в Очередь DPC указанного процессора. Таким образом, например, даже когда вызывается на процессоре 0, Объект DPC с установленным в качестве целевого процессора Процессором 1 будет вставлен в Оче редь DPC Процессора 1.
Как уже было сказано ранее в этой главе, наиболее часто DPC используются для завершения Программы Обработки Прерывания Для того, чтобы упростить драй верам устройств запросы DPC для завершения ISR из их функций ISR, Диспетчер вво определяет специальный DPC, который может использоваться для этой цели. Этот DPC называется DpcForlsr. Диспетчер вставляет Объект DPC в каждый Объект Устройство, котоый он создает. Этот внедренный Объект DPC инициализируется драйвером устрой ства, обычно при первой загрузке драйвера, посредством вызова функции oInitializeDpcRequest(). IoInitializeDpcRequest() принимает на входе указатель на Объект Устройство, в который внедрен Объект DPC, указатель на функцию драйвера для вызова, и значение онтекста для передачи этой функции. IoInitializeDpcRequest(), в свою очередь, вызыает KeInitializeDpc(), чтобы инициализировать внедренный Объект DPC, передавая указатель на функцию драйвера как параметр DeferredRoutine, и значение контекста как параметр DeferredContext. Чтобы запросить DPC из ISR, драйвер просто вызывает IoRequestDpcQ, передавая у к а з а т е л ь на О б ъ е к т У с т р о й с т в о . I o R e q u e s t D p c ( ) , в с в о ю о ч е р е д ь , в ы з ы в а е т для Объекта DPC, внедренного в Объект-Устройство. Поскольку все драйверы устройства имеют Объекты-Устройства, и все драйверы, которые используют прерывания, также используют DPC, использование механизма DpcForlsr Диспетчера ввода/вывода очень удобно. Фактически, большинство драйве ров устройств в Windows NT никогда напрямую не вызывают функции KeInitializeDpc() и л и K e I n s e r t Q u e u e D p c ( ) , а в м е с т о этого в ы з ы в а ю т I o I n i t i a l i z e D p c R e q u e s t ( ) и IoRequestDpcQ.
3. Сетевая архитектура Windows NT Эта глава посвящена рассмотрению сетевой архитектуры ОС Windows Рас сматривается соответствие сетевой архитектуры модели описаны сетевые интер фейсы, сетевые компоненты, их предназначение и взаимосвязи. Обзор сетевой архи тектуры позволит определить, через какие сетевые компоненты и в какой последова тельности проходят данные приложений перед тем, как попасть в сеть. Windows NT содержит механизмы динамической загрузки и выгрузки встроен ной сетевой поддержки, те же самые механизмы могут использоваться для загрузки и выгрузки сетевого программного обеспечения других После ин сталляции сетевое программное обеспечение становится неотъемлемой частью опе рационной системы, например, сетевые файловые системы, драйверы сетевых про токолов и драйверы сетевых устройств являются расширением подсистемы ввода/ вывода исполнительной системы. Таким образом, в Windows NT сетевое программ ное обеспечение может задействовать внутренние интерфейсы, предоставляемые компонентами исполнительной системы, ядром и HAL, тем самым, увеличивая свою производительность. Дополнительная информация о встроенных сетевых компонентах может быть по лучена при непосредственном анализе реестра Windows NT, а точнее его ключа \ System \ \ Services, где, помимо прочих сервисов ОС, хранится информация о сетевых компонентах: сетевых сервисах и сете вых драйверах. По значениям соответствующих подключей можно получить инфор мацию о зависимостях между сетевыми компонентами и их привязках друг к другу. Также можно анализировались таблицы импорта и экспорта различных системных сетевых DLL и сетевых сервисов.
3.1. Соответствие сетевой архитектуры Windows NT модели OSI Чтобы помочь производителям в стандартизации и интегрировании их сетевого программного обеспечения, Международная организация по стандартизации (ISO) определила программную модель пересылки сообщений между компьютерами мо дель соединения открытых систем (Open System reference model, OSI). В модели определены семь уровней программного обеспечения. Сетевой запрос должен спуститься до самого нижнего уровня на клиентской ма шине, затем он передается по физическому носителю и вновь поднимается на сервер ной машине до уровня, который его поймет и обработает. Каждый уровень иерархии считает, что он общается с аналогичным уровнем на другой машине и использует не который стандартный протокол. Набор протоколов, в соответствии с которыми запрос проходит вниз по уровням и обратно, называется стеком протоколов. На рис. 17 представлен общий вид сетевых компонентов Windows NT, а также их соответствие уровням модели Модель далеко не всегда точно соответствует
коммерческим сетевым программным продуктам, реальные программные модули ко торых объединяют несколько уровней модели или разбивают один уровень на сколько.
Рис.17. Соответствие сетевой архитектуры ОС Windows NT модели OSI
Задача каждого уровня состоит в том, чтобы предоставить обслуживание верхним уровням, абстрагируясь от того, как реализовано это обслуживание на нижних уров нях. Далее приводится краткое описание этих уровней: 1) Физический Физический уровень является самым нижним уровнем в модели Задачи этого уровня связаны с отправкой и получением неструктуриро ванного потока бит по физической среде передачи. Он характеризует электрический/ оптический, механический и функциональный интерфейс к физической среде. В Windows NT физический уровень реализуется сетевой картой, ее трансивером, и сре дой передачи данных, к которой она присоединена. Для сетевых компонентов, кото рые используют последовательный порт, физический уровень может также включать низкоуровневое сетевое программное обеспечение, которое определяет, как поток пос ледовательных бит делится на пакеты данных. 2) Канальный Пересылает низкоуровневые кадры данных, ожидает под тверждения их получения и повторяет передачу потерянных кадров. В официальной терминологии модели группа бит, посылаемая канальным уров нем, называется physical layer service data unit (единица данных, обслуживаемая физи ческим уровнем), на практике эту группу бит называют фреймом. Канальный уровень делится на два подуровня: подуровень управления логичес кой связью (Logical Link Control, LLC) и подуровень управления доступом к среде (Media Access Control, MAC). LLC управляет взаимодействием с верхним сетевым уровнем и обеспечивает безошибочную передачу фреймов от одного узла к другому. Этот уровень отвечает за установление и завершение логической связи, контроль пос ледовательности фреймов, подтверждение приема фрейма и повтор передачи неподт вержденных фреймов. В сетевой архитектуре Windows NT функции подуровня LLC реализуются в транспортных драйверах (transport driver). M A C подуровень управляет доступом к среде передачи, проверяет ошибки во фрей мах, распознает адреса полученных фреймов. Подуровень MAC обеспечивает стан дартные интерфейсы для сетей Ethernet (со случайным коллективным доступом с кон тролем несущей и обнаружением конфликтов, Carrier Sense Multiple Access with Collision Detection, CSMA/CD), для сетей, где используется передача маркера на общей шине (ArcNet) и в кольце (Token Ring). В сетевой архитектуре Windows NT функции поду ровня MAC реализуются в сетевой карте. 3) Сетевой уровень. Сетевой уровень контролирует операции подсети. Он ре шает, какой физический путь должны пройти данные в зависимости от условий передачи, приоритетов сервиса и других факторов. Он отвечает за маршрутиза цию, контроль интенсивности трафика, фрагментацию пакетов, распознавание ад ресов и межсетевой обмен. Это самый низкий из уровней, понимающих тополо гию сети, то есть физическую конфигурацию машин в ней, тип физических соеди нений между ними и ограничения пропускной способности, длины используемых кабелей и т.д. Сетевой уровень имеет дело с пакетами, которые могут быть меньше или больше чем фрейм. Если пакет больше, чем фрейм, то сетевой уровень разбивает его на не-
сколько фреймов, и собирает их при получении. Если пакет меньше фрейма, то сете вой уровень объединяет несколько пакетов во фрейм, и разбивает этот фрейм на паке ты при получении. В модели Windows NT сетевой и транспортный уровни реализуются в транспорт ных драйверах, иначе называемых драйверами протоколов верхнего уровня (protocol driver). Windows NT поставляется со следующими транспортными драйверами: NetBEUI, AppleTalk и 4) уровень. Транспортный уровень имеет дело с сообщениями, которые могут быть больше или меньше, чем пакеты. Транспортный уровень гаранти рует безошибочную доставку сообщений, в нужной последовательности, без потерь и повторов. Если стек протоколов не включает подуровень LLC, и если сетевой уровень нена дежный и поддерживает передачу дейтаграмм (например, уровень IP в стеке TCP/IP или IPX в NWLink), то транспортный уровень должен включать контроль последова тельности пакетов и их подтверждение, а также повторную передачу неподтвержден ных пакетов. Другой пример: так как транспортный драйвер протокола NetBEUI вклю чает функции подуровня LLC, то ему необходимо реализовать лишь минимум функ ций транспортного уровня. 5) Сеансовый уровень. Управляет соединением между взаимодействующими при ложениями на разных компьютерах, включая синхронизацию высокого уровня, и кон троль за тем, какое из приложений «говорит», а какое «слушает». Он знает имена ком пьютеров в сети. Интерфейсы NetBIOS и WinSockets являются примерами типичного программно го обеспечения сеансового уровня. 6) Уровень представления. Предоставляет сервисы, используемые приложения ми, такие как шифрование, сжатие, или преобразование символов (PC ASCII в EBCDIC). Отвечает за форматирование в том числе решает, должны ли строки заканчи ваться парой символов «возврат каретки/перевод строки» или только симво лом «возврат каретки» (CR). Примером программного модуля, реализующего уровень представления, является XDR (External Data Representation) под управлением средства удаленного вызова цедур RPC (Remote Procedure Call). 7) Прикладной уровень. Обрабатывает передачу данных между двумя сетевыми приложениями, включая проверку прав доступа, идентификацию взаимодействующих машин и инициирование передачи данных. Прикладной уровень обрабатывает запросы от приложений, например, обращение к базе данных или доставку почты. Этот уровень доступен приложениям напрямую. RPC является примером реализации прикладного уровня. Множество известных про токолов объединяют прикладной уровень и уровень представления, например: имено ванные каналы и FTP (File Transfer Protocol). Клиенты используют именованные кана лы, например, для взаимодействия с Microsoft SQL Server.
3.2. Сетевые API Windows NT реализует несколько сетевых API для обеспечения поддержки вых приложений и совместимости с промышленными стандартами. Важно иметь в виду, что решение о том, какое API должно использовать приложение, зависит от ха рактеристик Таких характеристик как, например, поддерживает ли API надежное или двунаправленное взаимодействие, над каким протоколом располагается API, пе реносимость API на другие Windows-платформы, на которых должно исполняться приложение. Итак, Windows NT предоставляет несколько способов получения досту па к сети, то есть несколько механизмов межпроцессных коммуникаций, часть кото рых описана ниже.
3.2.1. Стандартный API ввода/вывода Win32 Приложения получают доступ к удаленным файлам, используя стандартные Win32функции ввода/вывода (открытия, закрытия, чтения, записи и т. п). Соответствующие запросы проходят по сети только в том случае, когда файл или устройство находятся на удаленной машине. Как правило, это означает, что имя файла является именем UNC (Uniform Naming Convention, то есть начинается с символов или начинается с бук вы, указывающей на диск на удаленной машине. Функции API ввода/вывода Win32, большая часть которых оптимизирована в DLL клиентской стороны, реализуются пу тем вызова системных сервисов системы ввода/вывода NT, в результате чего диспет чер ввода/вывода посылает пакеты IRP сначала драйверу а затем соответству ющему редиректору, взаимодействующему с драйвером файловой системы сервера на сервере.
3.2.2. Сетевые API Win32 (Wnet и Net) Windows NT обеспечивает множество независимых от сети WNet-функций, кото рые позволяют работать через провайдеров разных сетей. Функции этого интерфейса полезны таким приложениям, как File Manager, кото рые выполняют соединение с удаленными файловыми системами и их просмотр. Фун кции WNet можно использовать для просмотра файловых систем Microsoft и других файловых систем по сетям LAN Manager, NetWare, VINES и т.д. Вызовы интерфейса API WNet, реализованного в виде DLL, проходят через сетевой компонент, называе мый сервисом рабочей станции. Этот сервис - серверный процесс, похожий на защи щенную подсистему. Сервис рабочей станции является, в сущности, надстройкой пользовательского режима для редиректора. Он поддерживает API WNet, предостав ляет функции конфигурации редиректора и содержит код пользовательского режима для получения статистики редиректора. Когда приложение вызывает функции API WNet, этот вызов в начале поступает сервису рабочей станции, а затем уже к диспетчеру и далее к редиректору.
Net-функции, предоставляемые сетевым интерфейсом Net, поддерживаются в «се тевой ОС» Microsoft LAN Manager, основанной на OS/2. В Windows NT большая часть сетевой функциональности является встроенной, поэтому некоторые из пер воначальных Net-функций уже не поддерживаются. В Windows NT множество Netфункций, предоставляемых библиотекой netapi32.dll, дополняет встроенную сете вую функциональность, не обеспечиваемую другими сетевыми интерфейсами. На пример, Net-функции позволяют приложениям взаимодействовать с сервисом рабо чей станции, сервисом сервера, сервисом оповещений, сервисом передачи сообще ний, репликатором и т.д. Если существует базовая функция или WNet-функция, требуемая приложению, то лучше использовать ее, чем эквивалентную Net-функцию по двум причинам. Во-пер вых, WNet-функции являются независимыми от сети, в то время как Net-функции ра ботают только в сетях Microsoft Windows. Во-вторых, некоторые из Net-функций мо гут быть замещены функциями базовых сетевых интерфейсов или и не поддерживаться в будущих версиях Windows NT.
3.2.3. API NetBIOS (Network Basic Input/Output System) Это прикладной программный интерфейс, позволяющий обмениваться запроса ми ввода/вывода с удаленным компьютером. Этот интерфейс используется для со здания приложении для локальных Microsoft LAN Manager, IBM LAN Server, Microsoft MS-Net или операционной среды OS/2. Этот API обеспечивает обратную совместимость для программ MS-DOS, 16-разрядной Windows и OS/2, которые пе р е д а ю т д а н н ы е по сети. Имеется т а к ж е новая 3 2 - р а з р я д н а я версия. И н т е р ф е й с NetBIOS используется для посылки запросов, имеющих структуру формата SMB, на другой компьютер. NetBIOS полагается на соглашение об именах, согласно которому компьютерам и сетевым сервисам назначаются имена, называемые NetBIOS-именами. Сетевой сервис W I N S (Windows Internet Name service) поддерживает отображение между NetBIOS-именами и адресами протокола TCP/IP. NetBIOS-функции, как и экспортируются приложениям библиотекой Netapi32.dll (см. 18). Эта библиотека открывает описатель драйвера уровня ядра, называемого эмулятором NetBIOS (драйвер файловой системы NetBIOS), и вызывает функцию по команде приложения. Эмулятор NetBIOS транслирует команды, выданные приложениями, в которые посылаются рам протоколов. Если приложение хочет использовать N e t B I O S поверх протокола TCP/IP, эмулятор NetBIOS требует присутствия драйвера NetBT.sys. Этот драйвер отвечает за поддержку семантики NetBIOS, присущей протоколу NetBEUI, а не токолу TCP/IP. NetBIOS полагается на передачу NetBEUI-сообщений и средство решения имен NetBIOS, поэтому драйвер NetBT реализует их в вершине протокола TCP/IP. Подобно этому, драйвер реализует семантику NetBIOS поверх протокола IPX/SPX.
Рис. 18. Интерфейс NetBIOS
Win32 API именованных каналов (named pipes) и почтовых ящиков (mailslots) Именованные каналы обеспечивают надежное двунаправленное взаимодействие между двумя процессами, независимо от того, является ли принимающая сторона кальной или удаленной. Почтовые ящики выполняют аналогичные функции, однако, вместо обеспечения канала «один к одному» между принимающей и передающей сто ронами, они предоставляют ненадежную однонаправленную передачу данных типа «один ко многим» и «многие к одному». Почтовые ящики полезны для посылки широ ковещательных сообщений произвольному числу процессов. Именованные каналы предоставляют абстрактный и удобный сетевой интерфейс. Вместо того чтобы иметь дело с маршрутизацией и пересылкой данных, программист, использующий именованные каналы, может просто открыть канал и поместить в него данные. Пользователь именованного канала открывает его и считывает данные. Пере-
дача данных между компьютерами выполняется автоматически и один вызов имено ванного канала эквивалентен нескольким операциям на уровне транспорта. Все функции именованных каналов и почтовых ящиков реализованы в DLL клиент ской части подсистемы Win32 (см. рис. 19). Однако имена, задаваемые приложениями, использующими именованные каналы и почтовые ящики, определяют системное пространство имен, управляемое драйвером файловой системы именован ных каналов (Named Pipes File System, и драйвером файловой системы почто вых ящиков (Mail Slots File System, Драйвер файловой системы именован ных каналов создает объект-устройство, называемое и символичес кую ссылку к этому объекту, называемую \??\Pipe, а драйвер файловой системы почто вых ящиков создает объект-устройство, называемое \Device\Mailslot, и символическую ссылку \??\Mailslot, указывающую на этот объект. Имена, передаваемые функции CreateFile вида и имеют префикс преобразуемый в \??\, так что имена разрешаются через символическую ссылку к объекту-устройству.
Рис. 19. Интерфейс именованных каналов и почтовых ящиков Позже мы обсудим, как драйвер файловой системы редиректора вовлекается, ког да разрешаются имена, определяющие именованные и почтовые ящики на уда ленной системе. Однако, когда именованные каналы и почтовые ящики создаются сер вером, или открываются клиентом, вовлекается соответствующий драйвер файловой системы на машине, где располагается именованный канал или почтовый ящик. Обработка запроса к локальному именованному каналу или прием запроса к нему от удаленного компьютера выполняется файловой системой именованных каналов. Если же запрос к именованному каналу происходит по имени UNC, то обработка выполня ется так же, как и для других запросов к удаленным файлам: запрос перехватывается драйвером mup.sys и направляется соответствующему редиректору.
3.2.5. API Windows Sockets Этот API реализует и 32-разрядные - стандартный сетевой интерфейс, используемый UNIX. Winsock поддерживает надежное, ориентированное на соедине ние, а также ненадежное, не ориентированное на соединение взаимодействия. Прикладной интерфейс Windows Sockets состоит из DLL Ws2_32.dll, которая обес печивает приложениям доступ к (см. рис. 20). вызыва ет сервисы пространства имен и провайдеров сервисов транспорта для выполнения операций по разрешению имен и передачи сообщений. Библиотека Msafd.dll действу ет как провайдер сервисов транспорта. Msafd.dll использует для Winsock (которые являются специфичными для протоколов), для взаимодействия с драйверами протоколов уровня ядра. Например, Wshtcpip.dll является помощником для протокола TCP/IP, а Wshnetbs.dll - для протокола NetBEUI. Библиотека Mswsock.dll реализует расширенную функциональность Microsoft Winsock. Mswsock.dll Ws2_32.dll Msafd.dll Wshtcpip.dll Ntdll.dll
сервиса Winsock
режим Режим ядра драйвер файловой системы AFD
TDI
NetBEUI
TCP/IP
20. Интерфейс WinSockets Провайдер сетевого транспорта Msafd.dll использует сервисы драйвера файловой системы AFD.sys (Ancillary Function driver, AFD) для реализации функций сокетов. A F D является и исполняет сетевые операции с такие как по сылка и получение сообщений, путем отправки запросов TDI IRP драйверам протоко-
лов. AFD не запрограммирован для использования определенных драйверов протоко лов, поэтому Msafd.dll информирует AFD об имени протокола, используемого опреде ленным так чтобы AFD мог открыть объект-устройство, представляющее этот протокол.
3.2.6. Средство (Remote Procedure
вызова процедур RPC)
Средство удаленного вызова процедур позволяет создавать распределенные при ложения, вызывающие реализованные как локально, так и на удаленных ком пьютерах. RPC предоставляет модель работы с сетью, ориентированную на процеду ры, а не на транспорты, что позволяет упростить разработку распределенных прило жений. Библиотека RPC и компилятор (Microsoft Interface Definition Language, MIDL - язык описания интерфейса фирмы позволяют програм мистам с легкостью создавать распределенные приложения. Библиотека RPC периода выполнения включает процедуры для управления по сети, а также разновидность RPC, называемою локальным Локальный RPC может быть использован для взаимодействия между двумя процессами, располагаю щимися на одной и той же машине, для этого библиотека RPC периода выполнения использует средство LPC в режиме ядра в качестве локального сетевого API. Когда RPC базируется на нелокальных механизмах взаимодействия, библиотека RPC исполь зует такие интерфейсы как Winsock и именованные каналы (см. рис.
Заглушка Интерфейс доступа к транспорту
Winsock
Named Pipes
DLL, предостав
DLL, предостав
клиенту транспорт Winsock
клиенту транспорт именованных каналов
LPC
2 1 . Интерфейс RPC Для регистрации и поиска имен RPC-приложения связываются с клиентской биб лиотекой сервиса имен Эта DLL взаимодействует с подсистемой
RPCSS (RPCSS.exe для WinNT, RPCSS.dll для Win2000), стартующей автоматически при загрузке ОС, и реализованной как Сервис Rpcss сам является RPCприложением, которое взаимодействует с такими же приложениями на других систе мах для выполнения поиска имен и регистрации. Сервис Rpcss реализует некоторые RPC и OLE сервисы, включая динамическое назначение и разрешение адреса серверного процесса и разрешение DCOM-объектов (Distributed Component Object Model). Также при загрузке ОС автоматически стартует и исполняется в собственном процессе сервис locator.exe - провайдер сервиса имен для Сервис имен сервис, отображающий имена в объекты и хранящий пары имя/объект в базе данных. Например, сервис имен RPC отображает логи ческие имена в описатели соединений (структуры данных, представляющие логичес кие соединения между клиентом и сервером), чтобы клиентское приложение могло обращаться по этому логическому имени, а не использовать последовательность про токолов и сетевые адреса. Библиотека RPC периода выполнения использует для взаимодействия с транспор тным протоколом единый интерфейс доступа к транспорту RPC (RPC transport provider Этот интерфейс служит прослойкой между средством RPC и транс портом и предоставляется различными библиотеками. Например: 1) библиотека клиентского приложения, предоставляющая транспорт именованных каналов; 2) библиотека серверного приложения, предоставляющая транспорт именованных каналов; 3) библиотека клиентского приложения, предоставляющая транспорт TCP/IP; 4) библиотека серверного приложения, предоставляющая транспорт TCP/IP. Существуют и другие библиотеки для серверных и клиентских приложений, пре доставляющие транспорты NetBIOS, Winsock и т.д. Средство RPC может работать поверх большого количества транспортов, однако при работе по сети LAN Manager средство RPC использует в качестве сетевого транс порта именованные каналы. Большинство сетевых сервисов Windows NT являются RPC-приложениями, и могут вызываться как локальными процессами, так и процессами на удаленных машинах.
Средство динамического обмена данными (Network Dynamic Data Exchange, NetDDE) Network DDE используется для установления и поддержания сетевых соедине ний, необходимых для динамического обмена данными между приложениями, выпол няющимися на разных компьютерах в сети. Для реализации динамического обмена данными, приложениям необходимо использовать библиотеки nddeapi.dll (см. рис. 22)
или ddeml.dll (DDE Management Library - библиотека управления динамическим об меном данными).
Рис. 22 Приложения могут, либо использовать протокол D D E (подключая библиотеку nddeapi.dll), являющийся множеством правил передачи определенных DDE-сообще ний, либо могут использовать библиотеку ddeml.dll. Ddeml.dll обеспечивает интер фейс, который облегчает задачу добавления возможностей динамического обмена дан ными Вместо отправления, получения и обработки непосред ственно DDE-сообщений по протоколу DDE-приложения используют функции, пре доставляемые библиотекой ddeml.dll для управления между клиентским и серверным приложениями. Эта библиотека также делает возможным серверным приложениям регистрировать имена поддерживаемых ими сервисов. При использующие протокол основанный на передаче сообщений, полнос тью совместимы с приложениями, использующими ddeml.dll, но из-за большого числа преимуществ библиотеки ddeml.dll, новым приложениям предпочтительнее использо вать эту библиотеку, чем передачу DDE-сообщений. Во время загрузки компьютера автоматически запускается приложение (Network DDE Agent), которое служит для обнаружения локальной DDE-активности, после чего, этот агент стартует приложения, необходимые для динамического обмена данными. DDE-взаимодейтствия контролируются посредством DDE-окна,
ванного с одним из приложений, обеспечивающих DDE Windows NT DDE Server). Это приложение взаимодействует со всеми и прило жениями, использующими DDE. Но Nddeagnt не может обнаружить попытку удаленного Преж де чем удаленный клиент сможет успешно соединиться с серверным приложением, на серверном компьютере должен стартовать сервис В качестве транспорта механизм DDE использует средство RPC с транспортом именованных каналов.
Сервисы удаленного доступа (Remote Access Services, RAS) Сервер Windows NT поддерживает удаленный доступ (RAS) для создания времен ных соединений с системами, которые не находятся в локальной сети, обычно это со единения через телефонные линии. Сервер Windows NT включает встроенную под держку для модемов, устройств Х.25 и ISDN модемов. Один сервер Windows NT мо жет поддерживать одновременно до 255 удаленных соединений. Новый протокол РРТР (Point-to-Point Tunneling Protocol), заявленный в марте 1996, позволяет пользователям в любой сети на базе протокола TCP/IP для достижения своих защищенных сетей с удаленного компьютера. Функции сервиса RAS позволяют устанавливать удаленные соеди нения. Удаленные соединения могут использовать протокол SLIP (Serial Line Protocol), или, более предпочтительный протокол РРР (Point-to-Point Protocol). сом RAS обеспечиваются протоколы РРР аутентификации (PAP, CHAP) н протоколы конфигурирования сети NBFCP, LCP). Когда РРР уже уста новлено, приложения могут использовать стандартные сетевые интерфейсы, такие как Windows Sockets, NetBIOS, Named Pipes, RPC и взаимодействовать протоколам: NetBEUI, TCP/IP или инкапсулированным в РРР. TCP/IP на основе РРР ис пользуется наиболее часто для связи мобильных пользователей с Intranet. на клиентском узле, реализующие удаленный доступ к сервер ному узлу, могут выполнять задачи по обеспечению RAS двумя способами. Первый способ основан на вызове отображающих окна сервиса обеспечиваемые системой для интерфейса с пользователем. Эти функции предостав ляются библиотекой rasdlg.dll и позволяют легко отображать диалого вые окна, знакомые пользователю, чтобы тот мог выполнять RAS-задачн, такие как установление, завершение и наблюдение за удаленными соединениями, или работать с записной книжкой, содержащей телефонные номера. Второй способ основан на использовании непосредственных дозвона, предоставляемых RAS-приложениям библиотекой rasapi32.dll (см. рис. 23). Клиентс кое может использовать функцию RasDial() для установления соединения с RAS-сервером. Функция RasDial() начинает операцию которая затем будет выполняться программным компонентом, называемым менеджером удаленных соединений (Remote Access Connection Manager). удаленных соединений
является сервисом пользовательского уровня, управляющим деталями установления соединения с удаленным сервером и разделяющим процесс с другим сер висом - менеджером автодозвона (Remote Access Autodial Manager). Диспетчер уда ленных соединений стартует автоматически, когда приложение загружает библиотеку rasapi32.dll. j
приложение DLL RAS API (rasapi32.dll)
RAS DLL API администрирования сервера RAS (rassapi.dll)
API WinSockets, NetBIOS, именованных каналов, RPC с транспортами TCP/IP, NetBEUI,
т RAS сервер
т Менеджер удаленных соединений и менеджер дозвона (rasman.exe) DLL согласования с | TAPI DLL Telephony API
DLLs протоколов PPP аутентификации (raschap.dll, и протоколов конфигурирования сети
т К Рис. 23. RAS В процесс rasman.exe для согласования с интерфейсом TAPI (Telephony Application P r o g r a m m i n g Interface) з а г р у ж а е т с я библиотека rastapi.dll ( R e m o t e Access TAPI
Compliance Layer), которая и обращается к функциям интерфейса TAPI, предоставля емым библиотекой tapi32.dll Windows Telephony API Client DLL). На удаленном серверном компьютере исполняется процесс (Remote Access Server Windows NT обеспечивает поддержку админист рирования приложениями сторонних производителей посредством ис пользования библиотеки rassapi.dll (Remote Access Admin APIs
3.2.9. TAPI (Telephony Application Programming Interface) Интерфейс TAPI множество функций, позволяющих запрограммировать устрой ства, передающие данные по телефонным линиям, не зависящим от конкретного уст ройства образом, предоставляя пользователям возможность взаимодействия по теле фону. TAPI поддерживает передачу речи и данных, обеспечивает множество типов соединений и управление вызовами. TAPI позволяет приложениям использовать все возможности телефона, например, поддержку конференций и передачу звуковой по чты. В Windows NT подсистема обеспечения взаимодействия по телефону состоит из следующих компонентов (см. рис. 24): 1) (telephony service), реализующий TAPI функции; 2) библиотеки динамической загрузки tapi.dll и tapi32.dll, взаимодействующие по средством механизма RPC с 3) один или несколько провайдеров т е л е ф о н н ы х сервисов (telephony service providers), загружаемых TAPI-сервером. Приложения полагаются на существование провайдеров телефонных сервисов, которые предоставляют интерфейс (Telephony Service Provider Interface). Про вайдеры телефонных сервисов могут быть разработаны для различных технологий, включая: 1) POTS (Plain Old Telephone Service) для передачи голоса и данных в анало говом формате по абонентской линии, а далее везде в цифровом виде; 2) ISDN (Integrated Services Digital Network) для передачи в цифровом виде; 3) Т1/Е1 для цифровой пере дачи со скоростью Mbps; 4) Switched 56 и т. д. Провайдер сервиса является библиотекой динамической загрузки (иногда в его состав входит драйвер устройства), реализующей зависящие от устройства действия для выполнения телефонных операций на оборудовании, таком как телефон, модем, факс или ISDN карта. Таким образом, провайдер во взаимодействии с библиотеками интерфейса TAPI, предохраняет приложения от деталей, зависящих от типа сервиса и технологий коммуникаций по телефонным сетям. Приложения вызывают функции только из библиотек tapi.dll (16-разрядные при ложения) и tapi32.dll (32-разрядные приложения), они никогда не вызывают функции провайдеров напрямую. Библиотека tapi.dll является тонкой прослойкой, которая ото бражает 16-разрядные адреса в 32-разрядные, и передает запросы tapi32.dll. Библио тека tapi32.dll проверяет параметры функции и направляет запрос на выполнение сер-
вису а когда нужно загружает в процесс библиотеки, обеспечивающие интерфейс с пользователем (Telephony service provider user interface DLLs), в том случае, если провайдер реализует какие-либо элементы интерфейса с пользователем.
Рис. 24. TAPI
Tapisrv.exe, и с п о л н я ю щ и й с я в собственном п р о ц е с с е , является ядром TAPI. Tapisrv.exe обрабатывает вызов и передает запрос соответствующему провайдеру сервиса. Получив запрос от tapisrv.exe, провайдер сервиса должен реализовать ин т е р ф е й с TSPI. Все п р о в а й д е р ы т е л е ф о н н ы х сервисов и с п о л н я ю т с я в п р о ц е с с е и могут создавать необходимые для них в контексте этого про цесса. D L L п р о в а й д е р а может использовать л ю б ы е с и с т е м н ы е ф у н к ц и и , в к л ю ч а я CreateFile() и для работы с драйверами оборудования независи мых поставщиков, а также с драйверами стандартных устройств, как последо вательные и параллельные Они также могут обращаться к сетевым интер фейсам, таким как RPC, Windows Sockets, Named Pipes для клиент-серверного взаи модействия. В Windows NT есть встроенный провайдер телефонных сервисов юнимодема unimdm.tsp (Unimodem Service Provider), взаимодействующий с драйвером модема и предоставляющий сервисы TAPI с большинством типов А также встроенный провайдер сервисов компонента интерфейса TAPI уровня ядра kmddsp.tsp (TAPI kernel-mode service provider). предоставляет интерфейс провайдера телефонных сервисов (TSPI) и передает преобразованные пользователь ские запросы драйверу который и является компонентом интерфейса TAPI уровня ядра. TAPI также включает провайдеров сервиса среды (Media Service Providers, MSP), которые дают улучшенный, определяемый средой контроль над этой средой. Библиотека MSP, загруженная приложением, имеет соответствующий TSP в Пользователь может инсталлировать любое количество провайдеров телефонных сервисов, если только они не будут обращаться к одному и тому же устройству одно временно. Пользователь ассоциирует устройство с провайдером сервиса при инстал ляции. Некоторые провайдеры способны обращаться к нескольким устройствам. В неко торых случаях пользователю может понадобиться инсталлировать драйвер устройства вместе с провайдером.
3.2.10. Интерфейс DLC (Data Link Control) В Windows NT приложения могут использовать интерфейс DLC, обращаясь к биб лиотеке dlcapi.dll. Интерфейс DLC это немаршрутизируемый протокол, используе мый для взаимодействия между компьютером с ОС Windows и мейнфреймами IBM или принтерами, присоединенными непосредственно к сети. Интерфейс DLC представляет собой множество команд, которые сохраняются в структуре ССВ (Command Control Block) и передаются драйверу транспорта (Драйвер транспорта DLC в своей верхней части не предоставляет
Протокол SNMP (Simple Network Management Protocol) Стандартный протокол Internet для обмена управляющей информацией между кон солями управления, такими как HP Novell N M S , IBM Sun Net Manager, и набором управляемых устройств, такими как маршрутизаторы, мосты, кон центраторы. S N M P использует р а с п р е д е л е н н у ю архитектуру, с о с т о я щ у ю из менеджеров (консолей управления) и агентов. SNMP-агентами являются приложения, отвеча ю щ и е за отслеживание и передачу информации об устройстве по запросу SNMPдиспетчера, а также за обновление локальной у п р а в л я ю щ е й информации на ос нове сообщений, переданных от SNMP-диспетчера. Агент также оповещает заре гистрированных менеджеров о происхождении значительных событий или осо бых с и т у а ц и й . S N M P - д и с п е т ч е р - это п р и л о ж е н и е , г е н е р и р у ю щ е е з а п р о с ы на получение и у с т а н о в к у п а р а м е т р о в к SNMP-агенту, и п о л у ч а ю щ е е оповещения от SNMP-агентов. В ОС Windows NT SNMP-агент реализован в SNMP-сервисе Это глав ный агент, получающий SNMP-запросы и передающий их соответствующей библио теке DLL расширения агента (extension agent DLL). В Windows NT имеется множе ство D L L s , р а с ш и р я ю щ и х в о з м о ж н о с т и главного S N M P - а г е н т а , н а п р и м е р : 1) dhcpmib.dll - DLL расширения агента, реализующая DHCP MIB и инсталлируемая только на D H C P серверах; 2) - DLL расширения агента, реализующая MIB ресурсов хоста; 3) - DLL расширения агента, реализующая MIB-II; 4) lmmib2.dll D L L расширения агента, реализующая L A N Manager MIB-II; 5) winsmib.dll - DLL расширения агента, реализующая W I N S M I B , инсталлируемая только на WINS серверах. Management Information Base (MIB) описывает множество управляемых объектов, имеющих уникальные идентификаторы. SNMP-диспетчер может манипулировать объектами в требуемой базе MIB на определенном компьютере, если на этом компью тере главный SNMP-агент имеет библиотеку DLL расширения агента, поддерживаю щую требуемую MIB. D L L s расширения агента для MIB-II, L A N Manager MIB-II и Host Resources MIB инсталлируются вместе с сервисом SNMP (главным агентом). DLLs расшире ния агента для других инсталлируются при инсталляции соответствующих им сервисов. Во время запуска некоторого сервиса, SNMP-агент загружает все со ответствующие этому сервису DLLs расширения агента, перечисленные в реестре Windows NT. SNMP-диспетчер обычно является консольным приложением третьих производи телей. Windows NT включает библиотеки, поддерживающие SNMP-менеджеров. Кон соль управления формирует SNMP-сообщение, используя библиотеки (Microsoft SNMP Management API DLL), (Microsoft WinSNMP Manager API DLL), (SNMP Utility Library). Библиотека функций
используется как консолями управления, так и библиотеками расширения агента. сервис, получающий и передающий их SNMP-диспетчеру, а - сервис Программные компоненты, отвечающие за передачу используют интерфейс API Windows Sockets.
3.2.12. DCOM COM API фирмы позволяет приложениям состоять из разных компонен тов, где каждый компонент является заменяемым модулем. СОМэкспортируют ориентированный на объекты интерфейс к методам для мани пулирования данными внутри объекта. Так как СОМ-объекты предоставляют хорошо определенные интерфейсы, разработчики могут реализовывать новые объекты для расширения существующих интерфейсов и для динамического добавления приложе ниям новой поддержки. расширяет позволяя компонентам приложения располагаться на раз ных компьютерах. Что позволяет приложениям не беспокоиться о том, что один СОМобъект располагается на локальном компьютере, а другой в сети. DCOM, таким обра зом, обеспечивает прозрачность расположения, что облегчает разработку распреде ленных приложений. DCOM не является самостоятельным API, а полагается на RPC для выполнения своей работы.
3.3. Сетевые программные компоненты ОС Windows NT 3.3.1. Сетевые сервисы Сетевые сервисы это процессы, похожие на защищенные подсистемы. В от личие от з а щ и щ е н н ы х подсистем, сетевые сервисы, имеющие собственные API, обычно используют RPC, а не LPC для обмена сообщениями со своими клиентами. Применение R P C делает сервисы доступными процессам, как на локальной, так и на удаленной машине. За загрузку и запуск сервисов отвечает компонент, называе мый контроллером сервисов (service controller). Он также предоставляет средства загрузки и выгрузки драйверов. Примерами сетевых сервисов являются: 1) сервис рабочей станции в ы п о л н я ю щ и й администрирование встро енного редиректора; 2) сервис оповещений (alerter), выполняющий рассылку опо вещений п о д к л ю ч е н н ы м пользователям, н а п р и м е р , при переполнении жесткого диска; 3) сервис сообщений выполняющий прием сообщений от дру гих систем, например, уведомлений о том, что завершено выполнение задания на печать. Почти все сетевые сервисы, такие как сервис рабочей станции, сервис сервера (LanMan Server), сервис оповещений, сервис сообщений, обозреватель сети (Computer
Browser), D H C P - к л и е н т ( D H C P client) содержатся в одном ЕХЕ-файле с именем Остальные исполняются в собственных процессах, например, сервис WINS (Windows Internet Name Service) это сервис динамической регистрации и разреше ния имен, отображающий компьютеров в IP-адреса. Сервис системы доменных имен DNS (Domain Name System), преобразующий DNS-имена в IP-адреса. А также ранее упомянутые при рассмотрении сетевых API сервисы: and D i s t r i b u t e с е р в и с ы ) , locator.exe ( п р о в а й д е р с е р в и с а имен для nddeagnt.exe (Network DDE агент), tapisrv.exe (сервер, обеспечивающий телефонные сервисы). В Win2000 появился сервис репликации файлов (File Replication Service, FSR), глав ная задача которого состоит в репликации содержимого директории контроллера до мена \SYSVOL, в которой контроллер домена хранит скрипты входа в домен и группо вую политику. В дополнение FSR может быть использован для репликации разделяе мых файлов распределенной файловой системы (Distributed File System) между систе мами. FSR реализован как который использует RPC с аутенти фикацией и шифрованием для взаимодействия между своими же экземплярам, испол няющимися на других компьютерах.
3.3.2. Сетевые файловые системы Сетевые файловые системы позволяют пользователям разделять свои локальные диски с другими пользователями в локальной или глобальной сети. Любая сетевая файловая система состоит из двух компонентов: сетевого редиректора на клиентской стороне и сетевого сервера на узле с разделяемым диском.
3.3.2.1. Редиректор Сетевой редиректор является компонентом уровня ядра, предоставляющий ин терфейс файловой системы локальным пользователям, для этого он принимает зап росы ввода/вывода для удаленных файлов и устройств, пересылает их сетевому серверу на удаленном узле, получает данные с удаленного компьютера и предос тавляет их локальному пользователю. Реализован в виде драйвера файловой сис Сетевой редиректор использует для взаимодействия с сервером протокол SMB (про токол прикладного уровня, унаследованный от MS-NET), и поэтому он может рабо тать с существующими серверами MS-NET и LAN Manager, обеспечивая Windows NT доступ к системам MS-DOS, Windows и OS/2. Но для обмена данными между систе мами Windows NT, базовый протокол SMB был расширен, чтобы поддерживать рас пространенные операции NT. Редиректор вызывает интерфейс драйвера транспорта TDI для передачи пакета SMB различным транспортным драйверам. Редиректор должен открыть для связи с удаленной машиной канал, называемый виртуальным контуром, и затем послать пакет SMB через этот контур. Редиректор поддерживает по одному виртуальному контуру для каждого сетевого сервера, с которым связана операционная система
Windows NT, и мультиплексирует все запросы к данному серверу в один контур. Встроенный редиректор Windows NT может сосуществовать с редиректорами дру гих сетей. Встроенный редиректор и другие редиректоры создают объект-устройство в про странстве имен диспетчера объектов во время своей загрузки и инициализации. Ког да WNet или другой API обращается к диспетчеру объектов для открытия ресурса, расположенного в сети, диспетчер объектов, пройдя по дереву объектов обнару жив объект-устройство редиректора (точку входа в удаленную файловую систему), вызывает метод разбора диспетчера ввода/вывода, который передает оставшуюся часть имени ресурса соответствующему редиректору, который находит удаленный ресурс.
Сетевой сервер Этот компонент должен отвечать на удаленные запросы, полученные от реди ректора на клиентской стороне, взаимодействуя с локальной файловой системой или принтером. Сетевой сервер реализован в виде драйвера: в одних источниках утверждается, что сервер является драйвером файловой системы (согласно реест ру тип этого компонента также драйвер файловой системы), а в других - обычным драйвером. Сетевой сервер обеспечивает совместимость с существующими протоколами SMB MS-NET и LAN Manager, и обрабатывает запросы, приходящие не только от систем Windows NT, но также и от других систем, на которых работает программное обеспе чение LAN Manager. Так как сервер находится в исполнительной системе, то он может непосредственно вызывать диспетчер кэша для оптимизации передачи данных. Если требуемых данных в кэше не оказывается, то сервер создает собственные пакеты зап роса IRP и посылает их непосредственно драйверам локальных файло вых систем NTFS, FAT и HPFS. В году Microsoft представила на рассмотрение организации IETF специфика цию сетевого протокола Common Internet File System 1.0, являющимся после дним расширением и улучшением протокола SMB.
3.3.3. Распределенная файловая система (Distributed File System, DFS) В случае сетевых файловых систем, несмотря на то, что весь механизм транспор тировки данных скрыт от пользователя файловой системы, он все равно знает, какие данные хранятся локально, а какие были получены с удаленного серверного узла. Рас пределенные файловые системы предлагают единое глобальное пространство имен файлов для пользователя и полностью скрывают действительное физическое располо жение файлов от пользователя файловой системы. DFS является сервисом, который располагается выше сервиса рабочей станции, чтобы соединить вместе разделяемые файлы в одно пространство имен.
Распределенные файловые системы имеют сходную архитектуру с сетевыми фай ловыми системами. Они также имеют клиентскую часть, исполняемую на клиентском узле, и серверную часть, исполняемую на удаленном узле. Клиентская и серверная части могут одновременно исполняться на любом узле, участвующем в реализации распределенной файловой системы. Корень пространства имен DFS должен быть разделяемым файлом, на Windows 2000 Server. Серверная часть реализации DFS состоит из и драйвера Dfs.sys. DFS-сервис отвечает за экспортирование управления DFSтопологией и за поддержание DFS-топологии либо в реестре, либо в Active Directory. Получив запрос от клиента, DFS-драйвер выполняет просмотр топологии и направля ет клиента в систему, где находится запрашиваемый им файл.
Маршрутизатор многосетевого доступа Так как в систему могут быть загружены дополнительные редиректоры для досту па к сетям других типов, то существует компонент, который решает, какой редиректор вызвать для обработки запроса на удаленный ввод/вывод. Маршрутизатор многосетевого доступа (Multiple Provider Router, MPR) - это биб лиотека DLL, предоставляющая приложениям интерфейс API WNet, и определяющая к какой сети следует обратиться, когда приложение использует этот интерфейс для просмотра удаленной файловой системы. Когда приложение вызывает некоторою фун кцию WNet, этот вызов попадает непосредственно в DLL маршрутизатора многосете вого доступа, который принимает вызов и определяет, через какой из сетевого доступа (сетевых провайдеров) можно осуществить доступ к данному ре сурсу. MPR позволяет приложениям взаимодействовать стандартным образом с сколькими редиректорами, установленными в системе. Компонент сетевого доступа (сетевой провайдер) является программным моду лем (DLL), разработанным для работы в тесной кооперации с сетевым редиректором. Провайдер - это как бы надстройка над редиректором в виде DLL, которая позволяет компьютеру взаимодействовать с конкретной сетью. В состав программного обеспе чения Windows NT входят: провайдер для сетей на базе Windows NT, провайдер шлю за (и клиента) для NetWare. Компонент сетевого доступа позволяет Windows NT выступать в качестве клиента некоторого удаленного сервера. Среди операций, выполняемых, например, встроен ным компонентом сетевого доступа WNet, можно назвать установление и разрыв сете вого соединения, удаленную печать и передачу данных по сети. Кроме DLL встроен ного компонента сетевого доступа и встроенного редиректора в этих операциях при нимает участие сервис рабочей станции. От других изготовителей сетей требуется предоставить только DLL и редиректор. MPR определяет два множества функций. Одно множество - независящий от сети интерфейс API WNet, предоставляемый MPR всем Win32 приложениям, желающим использовать сервисы сетевых редиректоров (посредством сетевых провайдеров). Этот
интерфейс позволяет сетевым приложениям запрашивать в стандартной форме вы полнения редиректором некоторой общей функциональности, без необходимости раз работки специфичного кода для этого редиректора. Другое множество - интерфейс сетевого доступа, предоставляемый всеми сетевыми провайдерами маршрутизатору многосетевого доступа.
3.3.5. Многосетевой UNC Это еще один компонент, который решает, какой редиректор вызвать для обработ ки запроса на удаленный ввод/вывод, если в систему загружены дополнительные ре директоры для доступа к сетям других типов. Многосетевой UNC (Multiple UNC Provider, MUP) - драйвер, определяющий, к какой сети следует обратиться, когда приложение использует стандартный Win32 API ввода/вывода для открытия удаленных файлов. Этот сетевой компонент обраба тывает запросы к файлам или устройствам, содержащим имя UNC (это имя, начина ющееся с символов которые указывают, что данный ресурс находится в сети). Драйвер подобно MPR, определяет, какой локальный редиректор распозна ет удаленный ресурс. Драйвер mup.sys активизируется, когда приложение пытается открыть удален ный файл или устройство, задавая имя UNC. Получив подобный запрос, подсисте ма Win32 заменяет символы \\ строкой \DosDevices\UNC и передает запрос испол нительной системе. Объект \DosDevices\UNC является символической связью на объект-устройство создаваемый драйвером mup.sys. Драйвер mup.sys является довольно простым драйвером и реализует процедуры распределения open. П о с л е получения запроса на открытие, драйвер mup.sys посылает особые контрольные пакеты каждому редиректору, зарегистрированному драйвером mup.sys. Этим запросом mup.sys спрашивает редиректор, может ли тот распознать оставшуюся часть имени UNC. Если да, то редиректор должен информировать драй вер mup.sys о числе символов в строке имени U N C , которые он распознает как у н и к а л ь н ы й идентификатор удаленного ресурса. Драйвер mup.sys кэширует эту часть строки имени, и в последствии посылает имена, н а ч и н а ю щ и е с я с данной подстроки сразу этому редиректору. Для того чтобы редиректор мог взаимодействовать с драйвером mup.sys, редирек тор должен зарегистрировать себя для драйвера mup.sys во время инициализации, и в дальнейшем отвечать на запросы о распознавании имени, выдаваемые драйвером mup.sys. Редиректор, который первым зарегистрировал себя для драйвера mup.sys, имеет больший приоритет. Этот приоритет определяет, какой редиректор будет обрабаты вать запрос, в том случае, если более одного редиректора распознает имя удаленного ресурса. После того, как какой-нибудь редиректор распознает имя удаленного ресурса, mup.sys подставляет в начало строки имени удаленного ресурса имя объекта-устрой ства редиректора и помещает эту строку в файловый объект, соответствующий этому
ресурсу. С этого момента, запросы к этому удаленному ресурсу идут к редиректору напрямую, и больше не вовлекается.
Интерфейс TDI и транспортные протоколы Интерфейс TDI Windows NT предоставляет для взаимосвязи между транспортными драйверами и уровня ядра, такими как эмуляторы сетевых интерфейсов, редиректо ры, серверы, единый программный интерфейс, называемый интерфейсом тных драйверов (Transport Driver Interface, TDI). Изначально этот интерфейс планировалось использовать в пользовательском ре жиме работы ОС. В окончательном варианте интерфейс стал работать в режиме ядра, однако все еще может быть использован напрямую из пользовательского уровня. TDI предоставляется верхней частью любого транспортного стека протоколов Windows NT. TDI обеспечивает независимость от используемых ими транспортов. TDI поддерживает передачу как с установлением постоянного сеанса (виртуальная цепь), так и без него (датаграммы). Требование, чтобы все драйверы транспорта предоставляли единый общий интер фейс TDI, облегчает задачу их разработки, а также разработки TDI-клиентов, потому что только этот интерфейс необходимо запрограммировать. Так как TDI является относительно новым сетевым интерфейсом, и большинство приложений разработаны для использования других существующих стандартных ин терфейсов, таких как NetBIOS и WinSockets, то Windows NT включает эмуляторы для этих двух популярных интерфейсов. Части уровня ядра этих эмуляторов, являющиеся драйверами файловых систем, отображают родные функции с соответствующими па раметрами в одну или несколько а затем вызывают соответствующие драйверы транспортов через Интерфейс TDI включает следующее: Множество стандартных диспетчерских точек входа, обеспечиваемых транспор тным драйвером, к которому может обратиться с запросом ввода/вывода. 2) Множество процедур обратного вызова, экспортируемых любым и регистрируемых транспортным драйвером, для получения TDI-клиентом уведомле ния о произошедшем сетевом событии. 3) Параметры, структуры, IOCTLs и правила, ассоциированные с процедурами транспорта и его клиента. 4) Множество функций вида TdiXxx, которые транспорт и его клиент могут вызы вать для взаимодействия друг с другом. 5) Множество макросов и функций вида которые TDI-клиент может использовать, чтобы обеспечить принятие запросов нижележащим транспортом. Программная модель TDI очень похожа на модель Winsocket. реали зуют следующие шаги для установления соединения с удаленным сервером:
1)
формирует пакет TDI IRP типа address open для размещения адре са. возвращает файловый объект, известный как объект-адрес, пред ставляющий адрес. Этот шаг эквивалентен использованию функции bind в 2) TDI-клиент размещает и формирует пакет TDI IRP типа connection open, и TDIтранспорт возвращает файловый объект, известный как объект-соединение, представ л я ю щ и й с о е д и н е н и е . Этот шаг э к в и в а л е н т е н и с п о л ь з о в а н и ю ф у н к ц и и socket в Winsocket. 3) TDI-клиент ассоциирует объект-соединение с объектом-адресом с помощью пакета TDI IRP типа associate 4) TDI-клиент, принимающий удаленное соединение, выпускает TDI IRP пакет типа listen, определяющий число соединений, поддерживаемых для объекта-соединения, и затем выпускает пакет TDI IRP типа accept, который завершается, когда удаленная система установит соединение. Эта операция эквивалентна использованию функций listen и accept в Winsocket. 5) TDI-клиент, который хочет установить соединение с удаленным сервером, вы пускает TDI IRP пакет типа connect, определяя объект-соединение, который TDI-транс порт завершает, когда установится соединение. Выпуск TDI IRP пакета типа connect эквивалентно использованию функции connect в Winsocket.
Транспортные протоколы Microsoft предоставляет следующие транспорты: 1) NetBEUI и NetBEUI F r a m e (NBF) Транспортный протокол NetBEUI (Network Basic Input/Output System Extended User Interface) является расширением канального уровня пользовательского интерфейса NetBIOS. Он является основным транспортным протоколом, используемым Windows NT для локальных сетей, и обеспечивающим вза имодействие с LAN Manager, LAN Server и MS-Net. Протокол NetBEUI обеспечивает наивысшую скорость работы, но из-за ряда при сущих ему недостатков, таких как невозможность маршрутизации и сильная зашум ленность в большой сети, NetBEUI можно эффективно использовать только в неболь ших локальных сетях (IBM разработала протокол NetBEUI для локальных сетей, со держащих порядка 20 - 200 рабочих станций). Так как NetBEUI не маршрутизируе мый, то он не позволяет создавать глобальные сети, объединяя несколько локальных сетей. Сети, основанные на протоколе NetBEUI, легко реализуются, но их трудно рас ширять, так как протокол NetBEUI не маршрутизируемый. Протокол NetBEUI F r a m e (NBF) был создан на основе NetBEUI для преодоления некоторых его недостатков. В частности, NBF преодолевает предел количества систем 254, существующий в NetBEUI. 2) Транспорт T C P / I P (Transmission Control Protocol/Internet Protocol, TCP/IP). Он был разработан для поддержки сети Министерства Обороны С Ш А - ARPANET (Advanced Research Projects Agency's network), предшествующей Internet, и предназна чен для соединения разнородных систем через глобальные сети. Протокол TCP/IP
широко распространен в сетях UNIX и позволяет Windows NT взаимодействовать с различными сервисами на UNIX - машинах. Этот протокол быстро был адаптирован и для локальных сетей. В отличие от NetBEUI, который является запатентованным протоколом IBM и Microsoft, TCP/IP является общедоступным. Internet Engineering Task Force (IETF) координирует шения и расширения протокола TCP/IP через механизм, известный как Requests for Comments (RFCs). TCP/IP является надежным транспортом и очень гибким прото колом. TCP/IP можно использовать в локальных сетях небольшого масштаба, кото рые в дальнейшем можно легко расширить для вовлечения сотен и тысяч пользо вателей. Протокол TCP/IP требует больше знаний для его использования, так как каждая машина должна иметь уникальный IP адрес и маску подсети. Такие средства как DHCP, WINS доступны в ОС Windows NT для облегчения задачи администрирования сети. Самое важное преимущество протокола TCP/IP перед NetBEUI то, что TCP/IP - марш рутизируемый протокол. Структура IP адресов специально разработана для эффектив ной маршрутизации. Транспорт TCP/IP поддерживает множество компонент, реализующих сессионный уровень модели таких как NetBIOS, Winsock, RPC. TCP/IP также является наибо лее часто используемым протоколом при выполнении удаленного доступа. В Windows 2000 существуют встроенные драйверы, интегрированные с драйве ром протокола TCP/IP, которые расширяют базовые сетевые характеристики протоко ла TCP/IP, используя с ним закрытый интерфейс. Например, NAT-компонента (Network Address Translation) состоит из драйвера NAT, взаимодействующего со стеком TCP/IP, а также редактора, используемого админист ратором для задания адресов. Другой пример реализация (Internet Protocol security), включающая драйвер интегрированный с драйвером протокола IP. Агент политики получает информацию о конфигурации IPSec из Active Directory и передает информацию о фильтрации драйверу IPSec, а установки о безопасности мо дулю Internet Key Exchange. ожидает запрос от драйвера IPSec и организу ет переговоры, возвращая результат обратно драйверу 3) (IPX/SPX). Протокол Internetwork Packet Exchange/Sequenced Packet Exchange используется в ОС NetWare фирмы Novell. Реализация этого про токола фирмой Microsoft называется NWLink. базируется на протоколе Xerox Network System (XNS), разработанном Xerox Corp. Протокол XNS, который больше не используется, определял коммуникационные уровни от физического до прикладного. Novell использовала часть этого стека, а именно два компонента: (Internet Datagram Protocol) and SPP (Sequenced Packet Protocol). IPX базируется на IDP, SPX (nwlnkspx.sys) - на SPP. - высокопроизводительный протокол для локальных сетей, и легче реа лизуется и чем TCP/IP. Как и TCP/IP является маршрути зируемым, и, следовательно, может использоваться для реализации глобальных сетей.
В W i n d o w s N T с у щ е с т в у е т т а к ж е в с т р о е н н ы й протокол N W L i n k N e t B I O S который предоставляет возможность пересылки Novell тов между NetWare-сервером, исполняющим Novell NetBIOS, и Windows NT компью тером, или между двумя Windows NT компьютерами. 4) AppleTalk. Семейство сетевых протоколов AppleTalk было первоначально раз работано для компьютеров Macintosh, однако уже вторая версия этого сетевого про дукта позволяет взаимодействовать различным персональным компьютерам. В Windows NT он используется сервисами для Macintosh, которые позволяют пользователям Macintosh совместно использовать файлы формата хранящиеся в папках Windows NT сервера, и использовать принтеры, присоединенные к Windows NT серверу. 5) D a t a L i n k Control (DLC). DLC - протокол фирмы IBM, используется как ком понент архитектуры SNA (System Network Architecture) для связи с мейнфреймами. В некоторых локальных сетях DLC используется для связи с принтерами, присоединен ными непосредственно к локальной сети, а не к серверу или рабочей станции. Некото рые лазерные принтеры Hewlett-Packard предлагают по выбору интерфейс Это «сырой» протокол в том смысле, что никакие сетевые API не могут его использовать приложения, которые захотят его использовать должны взаимодействовать напрямую с драйвером транспорта обычно реализуют все протоколы, связанные с их основным про токолом. Например, драйвер TCP/IP (tcpip.sys) реализует TCP, IP, ARP, TDI-транспорты обычно создают объекты-устройства, реализующие конкрет ный протокол так, чтобы клиенты могли открыть объект-файл, представляющий про токол, и выдать сетевой запрос ввода/вывода этому протоколу, используя создает три объекта-устройства, которые представляют различные протоколы: \Device\Tcp, \Device\Udp, \Device\Ip.
Среда STREAMS STREAMS, основанная на UNIX STREAMS, является средой для существующих S-совместимых транспортных драйверов, которая обеспечивает переноси мость этим драйверам в ОС Windows NT из других ОС с незначительными модифика циями или вовсе без изменений. Один или несколько драйверов, расположенных друг над другом, окружаются сверху и снизу средой Streams и формируют провайдера сете вого транспорта. Среда STREAMS обеспечивает: • Интерфейс (Transport Provider Interface) - интерфейс верхней части транс портного стека. STREAMS транслирует этот интерфейс в «родной» интерфейс транспорта Windows NT - TDI. • Интерфейс (Data Link Provider Interface) - интерфейс нижней части транс портного стека. STREAMS транслирует этот интерфейс в «родной» для Windows NT интерфейс канального уровня - NDIS 3.0. Дополнительные функции для облегчения переноса, включая функции использо вания таймера ОС UNIX.
Среда NDIS и NDIS драйверы Среда NDIS В г Microsoft и 3Com совместно разработали спецификацию интерфейса имодействия между подуровнем M A C канального уровня модели и драйверами протоколов, располагающихся на вышележащих уровнях. Стандарт получил название Network Driver Interface Specification (NDIS спецификация интерфейса сетевых драй веров). Он играет ключевую роль в сетевой архитектуре NT и служит для отделения логики работы драйвера сетевой карты от особенностей реализации различных сете вых протоколов. С тех NDIS превратилась в целую семью сетевых стандартов, и сейчас эта семья включает стандарты для драйверов сетевых карт локальных и глобальных сетей (LAN NIC driver и WAN NIC driver), и стандарты для промежуточных драйверов (intermediate driver), располагающихся между драйверами протоколов и драйверами сетевых карт. Вместо того чтобы писать несколько транспортно-зависимых драйверов, произво дители сетевого оборудования реализуют интерфейс NDIS на самом верхнем уровне одного драйвера сетевой карты. Это позволяет любому драйверу протокола работать с данной сетевой картой, вызывая функции этого интерфейса. Таким образом, пользова тель может работать и по протоколу TCP/IP, и по протоколу при помощи одной сетевой карты и одного драйвера этой сетевой карты. Библиотека NDIS является драйвером, реализующим взаимодействие между понентами, которые обращаются к ней, такими как: драйверы транспортов, NDIS драй веры промежуточного уровня, драйвер драйвер ndistapi.sys, NDIS драйве ры сетевых карт локальных и глобальных сетей. Интерфейс, реализуемый NDIS биб лиотекой, является «call and интерфейсом (асинхронным интерфейсом). Драй веры вызывают функции, расположенные в NDIS библиотеке, a NDIS библиотека мо жет в свою очередь вызывать функции из других драйверов, чтобы выполнить опера цию. Пакеты запроса (IRP) не используются в NDIS спецификации. Так как NDIS драйверы существуют в среде, созданной NDIS библиотекой, то струк тура и интерфейс, используемые этими драйверами, определяются NDIS библиоте кой. Это позволяет NDIS драйверам быть переносимыми между различными операци онными системами, поддерживающими данную спецификацию. Цели NDIS библиотеки следующие: • Изолировать NDIS драйверы сетевых карт и промежуточные драйверы от опе рационной системы. NDIS библиотека экспортирует множество стандартных функций и макросов, которые скрывают детали операционной системы, и ис пользуются всеми NDIS драйверами. NDIS драйверы могут вызывать только те функции, которые обеспечивает NDIS библиотека. Например, создание тайме ра для целей синхронизации, или создание связанного списка осуществляется NDIS. Требование к разработчикам применять вызовы NDIS функций 6
910
позволяет сделать код переносимым между операционными системами фирмы Microsoft, которые поддерживают NDIS. • Выполнять как можно больше общих операций. Одной из основных целей NDIS библиотеки является исключение кодов общих функций из NDIS драйверов. Это означает, что большинству NDIS драйверов необходимо реализовать только не большое число функций, необходимых для выполнения операций, специфичес ких для оборудования. NDIS библиотека поддерживает информацию о статусе операции, указатели на функции интерфейсов верхнего и нижнего уровней, па раметры этих функций, и многие другие системные значения для драйверов протоколов и драйверов сетевых карт. • Обеспечивать добавление заголовков к данным пользователя без необходимос ти их перекопирования, во время передачи данных по стеку сетевых драйверов. Во время перемещения сетевого сообщения по стеку драйверов сверху вниз, на каждом уровне к первоначальному сообщению может быть добавлен свой заго ловок (реже хвост). NDIS спецификация делает возможным добавление подоб ных структур спереди или сзади без необходимости перекопирования первона чального сообщения. Эта возможность построения сетевого сообщения без пе рекопирований данных позволяет достигать высокой производительности при передаче данных. получает от и транслирует их в вызовы функций Для передачи данных между па кеты IRP не используются. Вместо них используются, например, опи санные в разделе «Особенности реализации NDIS-драйверов».
Типы NDIS драйверов Спецификация NDIS определяет три типа сетевых драйверов: • драйверы протоколов верхнего уровня (upper level protocol drivers); • драйверы протоколов промежуточного уровня (intermediate protocol drivers); • драйверы сетевых карт (NIC drivers), сюда входят драйверы сетевых карт ло кальных сетей (NDIS LAN NIC drivers) и драйверы сетевых карт гло бальных сетей (NDIS WAN Miniport NIC drivers).
Драйверы протоколов верхнего уровня Драйвер протокола верхнего уровня в своей верхней части предоставляет TDIинтерфейс или, возможно, другой интерфейс, необходимый пользователям сети. По добные драйверы выделяют ресурсы для пакетов, копируют данные приложения в па кеты и передают их драйверам более низкого уровня посредством вызова NDIS. В своей нижней части этот тип драйверов обеспечивает интерфейс для получения паке тов от нижележащего драйвера и передает полученные данные или при ложению, для которого они предназначены. К драйверам протоколов верхнего уровня относится и драйвер транспорта, реали зующий стек сетевых протоколов, такой как IPX/SPX или TCP/IP. Транспортный драй-
вер Windows NT реализует в своей верхней части и использует NDIS библиотеку для взаимодействия с драйверами сетевых карт в своей нижней части.
Драйверы протоколов промежуточного уровня NDIS драйверы протоколов промежуточного уровня взаимодействуют сверху с драйверами протоколов верхнего уровня (такими как драйверы транспортов), а снизу с NDIS драйверами сетевых карт локальных и глобальных сетей. Ниже NDIS драйвера промежуточного уровня может быть драйвер, не поддерживающий интерфейс NDIS. NDIS драйверы промежуточного уровня могут служить различным целям, включая фильтрацию и шифрование пакетов, реализацию специализированных протоколов и тому подобные функции. Для драйвера протокола верхнего уровня промежуточный драйвер выглядит как драйвер виртуальной сетевой карты. Для драйвера реальной сетевой карты - как вер протокола. Промежуточные драйверы могут выстраиваться в цепочку, хотя такое расположение может оказывать негативное влияние на производительность системы. Типичный случай разработки и применения промежуточного драйвера чтобы вы полнять преобразование пакетов для среды передачи, неизвестной драйверу порта, но поддерживаемой драйвером сетевой карты. Например, может ся преобразование между протоколом локальной сети и ATM протоколом. NDIS драйвер промежуточного уровня обычно экспортирует ции на своем верхнем уровне и ProtocolXxx функции на своем нижнем уровне. Реже промежуточный драйвер может экспортировать MiniportXxx функции на своем верх нем уровне, а в своей нижней части предоставлять закрытый интерфейс посредством использования пакетов IRP, нижнему драйверу, не являющемуся N D I S драйвером. Например, промежуточный драйвер может управлять сетевыми запросами вода для устройства, соединенного с последовательным портом.
Промежуточные драйверы ndistapi.sys и Часть интерфейса TAPI располагается в режиме ядра и реализуется драйвером ndistapi.sys, который взаимодействует с драйвером ndiswan.sys. Драйвер ndiswan.sys NDIS драйвер промежуточного уровня, обеспечивающий РРР форматирование, тификацию, сжатие и шифрование для драйверов сетевых карт глобальных сетей. Он преобразует пакет формата получаемый от драйвера транспорта, в пакет формата и передает этот переформатированный пакет нижеле жащим драйверам сетевых карт глобальных сетей. Ndiswan.sys предоставляет интерфейс стандарта 802.3 верхним драйверам прото колов и выступает в роли драйвера протокола для нижележащих драйверов сетевых карт глобальных сетей. Ndiswan.sys имеет закрытый интерфейс с драйвером ndistapi.sys для динамической установки и обрыва TAPI связей. Ndiswan.sys преобразует формат отправляемого пакета из LAN в РРР. Большая часть разбивки фреймов, специфичной для среды передачи, должна выполняться в драйвере сетевой карты глобальной сети.
Драйверы сетевых карт NDIS LAN Miniport NIC драйверы поддерживают сетевые карты локальных сетей. NDIS WAN Miniport NIC драйверы имеют дополнительные требования и предоставляют приложениям доступ к глобальным сетям, таким как, ISDN, Frame Relay, Switched 56. Драйверы сетевых карт в своей нижней части взаимодействуют с оборудованием, а в верхней части предоставляют интерфейс, позволяющий верхним уровням посы лать пакеты в сеть, сбрасывать и останавливать сетевую карту, а также осуществлять опрос и установку параметров драйвера сетевой карты. Существует два типа NDIS драйверов сетевых карт: • Miniport NIC драйвер выполняет специфичные для конкретной сетевой карты операции, включая отправление и получение данных. Операции общие для всех драйверов сетевых карт, такие как синхронизация, обычно обеспечиваются NDIS. Miniport драйвер не вызывает напрямую обслуживание операционной системы, он взаимодействует с операционной системой через NDIS. • Full NIC legasy драйверы в настоящее время устарели и используются для со вместимости, они выполняют одновременно специфичные для сетевой карты операции, а также всю работу по синхронизации, обслуживанию очередей, обыч но выполняемые NDIS. Full NIC драйвер, например, поддерживает свою соб ственную информацию о привязках к вышележащим драйверам для индикации полученных данных. Miniport драйвер, напротив, не хранит информацию о при вязках, он просто передает пакеты наверх интерфейсу NDIS, а тот уже гаранти рует, что пакеты будут переданы соответствующим драйверам Поставщики ISDN, Switched 56, и т.п. должны поставлять драйверы сетевых карт для своих адаптеров. Драйверы сетевых карт глобальных сетей взаимодействуют с драйвером ndiswan.sys посредством использования библиотеки N D I S . Драйвер ndiswan.sys обеспечивает наиболее общие сервисы, такие как сжатие данных, зашиф рование, простое РРР форматирование. Драйверы сетевых карт глобальных сетей дол жны только специфические черты конкретной среды передачи. Если драйвер сетевой карты глобальной сети управляет сетевой картой, которая сама реа лизует сжатие, РРР-форматирование и шифрование, то он может указать на это во вре мя своей инициализации драйверу ndiswan.sys, чтобы тот не выполнял эти Ndiswan.sys привязывается к одному или нескольким драйверам сетевых карт гло б а л ь н ы х сетей, и один или несколько д р а й в е р о в протоколов п р и в я з ы в а ю т с я к ndiswan.sys. Драйверы протоколов имеют по одной привязке к драйверу ndiswan.sys, даже если они передают данные через адаптеры разных глобальных сетей. Это эконо мит память и упрощает реализацию драйвера сетевой карты глобальной сети. А также упрощает драйверы протоколов, так как они должны реализовывать только одну связь с глобальной сетью. С точки зрения драйвера сетевой карты глобальной сети у каждо го адаптера имеется только одна привязка к вышележащим драйверам протоколов, даже если несколько драйверов протоколов передают данные через этот адаптер. В Windows NT есть встроенный асинхронный WAN miniport NIC драйвер, исполь зующий драйвер последовательного порта для связи по модему.
Рис. 25
Связь компонентами
сетевыми программными
В Windows NT различные сетевые программные компоненты связаны в опреде ленной логической иерархии. Например, каждый драйвер транспорта может быть свя зан с одним или более сетевым адаптером через соответствующие драйверы сетевых карт. Сетевые компоненты более высокого уровня, такие как редиректор, могут быть связаны с одним или несколькими драйверами транспорта. При установке сетевых компонентов информация о них записывается в реестр Windows NT, который описывает порядок, в котором сетевые компоненты должны заг ружаться, и как они должны быть связаны друг с другом. Приложением, которое уп равляет установкой и связыванием сетевых компонентов, является Network Control Panel Application (NCPA). При установке сетевого компонента NCPA ищет специальный файл с расшире нием в разделе, определенном пользователем. Если устанавливаемый сетевой драйвер не поставляется вместе с Windows NT, то разработчик этого драйвера дол жен поставлять файл содержащий сценарий и правила установки драй вера. После компиляции и исполнения файла NCPA проводит анализ того, как данная сетевая компонента должна быть связана с другими компонентами, исполь зуя правила RawRules и NetRules. RawRules - правила по умолчанию, определяе мые системой, и хранимые в реестре в ключе MACHINE \ SOFTWARE \ Microsoft \ Ncpa \ \ RawRules. Правила NetRules - пра вила, определяемые разработчиками, и специфичные для данной компоненты, они с о д е р ж а т с я в только что созданном при у с т а н о в к е сетевой компоненты ключе \ SOFTWARE \ Microsoft \ <имя сетевой компоненты> \ CurrentVersion \ NetRules. В конце этого процесса NCPA создаст (или обновит) ключ M A C H I N E \ SYSTEM \ CurrentControlSet \ Services \ <имя сетевой компоненты> \ Linkage. NCPA включает следующие поля в этот ключ: • Bind. Список имен устройств сетевых компонент, с которыми данная сетевая компонента будет связана. • Export. Список имен объектов-устройств, которые будут добавлены в простран ство имен объектов Windows NT, чтобы сделать возможным доступ к этой ком поненте. Этот список содержит по одному имени объекта-устройства для каж дой компоненты, с которой данная компонента будет связана внизу. • Route. Содержит список строк, где каждая строка показывает точный путь в стеке сетевых компонент. После того как NCPA создаст все возможные связи, пользователь может, исполь зуя диалоговое окно Bindings в NCPA, посмотреть результирующие привязки и, если
блокировать (или разрешить) некоторые привязки. Это вызовет удаление (или восстановление) соответствующих значений в поле Bind подключа Linkage выб ранной сетевой компоненты. При загрузке ОС контроллер сервисов использует информацию в реестре для заг рузки сетевых компонентов согласно иерархии связей. Во время инициализации сете вой компонент может к каким компонентам более низкого уровня ему нужно привязаться, прочитав из ключа реестра Linkage значения поля
4. Анализ сетевой архитектуры ОС Windows NT с точки зрения возможностей реализации средств защиты и анализа сетевого трафика В этой главе будут проанализированы возможности реализации средств защиты сетевой информации на различных уровнях сетевой архитектуры Windows NT, ная от уровня приложений и кончая уровнем драйверов устройств. Анализ будет про водиться со следующих позиций: существует ли возможность встраивания средства защиты на том или ином уровне сетевой архитектуры, сложность конкретной реализа ции с точки зрения ее недокументированности, прозрачность защиты, какие данные и в каком объеме будут контролироваться конкретным средством защиты, какое обслу живание со стороны ОС ему доступно и какие возможности предоставлены операци онной системой средству защиты. В проведенном анализе не будет рассматриваться вопрос, может ли конкретный способ защиты быть подвержен воздействию со сторо ны злоумышленника. Не на всех уровнях сетевой архитектуры операционная система Windows NT пре доставляет возможность встраивания дополнительных программных модулей, кото рые могли бы контролировать вызовы сетевых программных компонент этого уровня и реализовывать функции защиты. Для реализации защиты на этих уровнях приходит ся использовать недокументированные методы, к которым относятся реализации за щиты на уровне системных сетевых DLL, сетевых сервисов и «родного» API. Эти ме тоды представлены в общем виде в этой главе, для их разработки использовался про граммный продукт фирмы - средство отладки, работающее ниже уровня ядра операционной системы и позволяющее отлаживать такие компоненты ядра, как драйверы. С помощью Softlce удалось исследовать некоторые важные структуры данных. Вначале будут рассмотрены основные факторы, влияющие на выбор конкретного способа реализации системы защиты.
4.1. Используемые средства построения объединенных сетей и их влияние на уровень расположения средства защиты (согласно модели В качестве средства наращивания и интеграции сетей может использоваться: 1) Повторитель или концентратор. Устройство, которое передает электрические сигналы из одного кабеля в другой без маршрутизации или фильтрации пакетов и слу-
жит для наращивания однотипных сетей. В терминах повторитель представляет собой промежуточное устройство физического уровня. 2) Мост. Устройство, соединяющее две или несколько физических сетей. Мосты могут фильтровать пакеты, то есть передавать в другие сегменты или сети только часть трафика на основе информации канального уровня (MAC - адреса). В терминологии мост является промежуточной системой на уровне канала передачи данных. 3) Маршрутизатор. Функционирует на сетевом уровне эталонной модели и служит для организации связи между сетями с одинаковыми сетевыми протоколами. Маршрутизатор принимает решение о передаче пакетов на основе различных крите риев, основанных на информации сетевого уровня. Для пакетов их адреса там оптимальным образом маршрутизатор использует протоколы на пример 4) Шлюз. Обеспечивает взаимодействие систем и сетей, которые не совместимые протоколы. Предназначен для сопряжения локальных сетей с сетями других архитектур типа SNA, DNA, Работает на самом верхнем уровне стека протоколов. Проблем с использованием средства построения объединенных сетей не возник нет, если это средство реализует межсетевое взаимодействие, используя уровни моде ли расположенные ниже уровня, на котором реализован модуль защиты. Если же средство построения объединенных сетей реализует межсетевое взаимодействие, ис пользуя уровни модели расположенные как ниже, так и выше уровня реализации модуля защиты, то необходимо рассмотреть следующее условие - можно ли в это сред ство встроить модуль защиты: • Если можно, то после встраивания модуля защиты, средство построения объе диненных сетей не повлияет на работу сети, в которой используется данный модуль защиты. • Если нельзя, тогда модуль защиты должен обладать функциональностью тех уровней, которые участвовали в переформировании пакетов в средстве межсе тевого взаимодействия. Для иллюстрации этого случая рассмотрим пример реализации модуля, осуществ ляющего шифрование сетевых данных. Если модуль защиты располагается ниже сете вого уровня, то надо предусмотреть случай фрагментации пакета. То есть, если сред ство построения объединенных сетей разобьет зашифрованный пакет, принятый от системы-отправителя, использующего этот модуль защиты, то модуль защиты в систе ме-получателе должен уметь собрать фрагментированный пакет (реализовать функ ции сетевого уровня) и выполнить его расшифрование.
Объем средство защиты
проходящей через
От расположения средства защиты относительно иерархии сетевых компонентов, зависит количество проходящих через это средство данных, и, следовательно, возмож-
ность контроля и защиты этих данных. Чем выше уровень реализации средства ты, тем меньше объем данных, проходящих через него (смотри рис. 25 и 26, где пред ставлена обобщенная сетевая архитектура ОС Windows NT). Для иллюстрации этого принципа рассмотрим следующий пример. Если разрабо тан драйвер транспорта с функциями шифрования, то надо учитывать, что процессам зашифрования и расшифрования будут подвергаться только те данные, для которых драйверы файловых систем выбирают именно этот транспорт. В данном случае можно повлиять на объем проходящих данных через собственный драйвер транспорта, уда лив из системы все другие транспорты законными методами, используя, например, NCPA (Network Control Panel Application). Но, к сожалению, не во всех случаях возможно такое простое изменение конфигу рации ОС. Например, если защита осуществляется на уровне API Windows Sockets, то удалить API NetBIOS законными методами не удастся, а простое удаление библиотеки динамической загрузки, реализующей интерфейс NetBIOS, приведет к невозможнос ти работы приложений, использующих это API. Поэтому наиболее естественным, и не требующим дополнительного конфигури рования способом защиты относительно этого фактора является защита на уровне драй веров сетевых карт (или на уровне промежуточного драйвера, привязанного снизу к драйверам сетевых карт). Но тогда появятся проблемы относительно реализации в этих драйверах функциональности, не свойственной уровню модели на котором они располагаются. К сожалению, эти два фактора: объем проходящих данных и необходимость реа лизации дополнительной функциональности, вступают в противоречие в сетевой ар хитектуре операционной системе Windows NT из-за ее послойной модели.
Возможности реализации средств защиты сетевой информации на пользовательском уровне Рассмотрим особенности реализации средств защиты на различных уровнях сете вой архитектуры ОС Windows NT. Под реализацией защиты на каком-либо уровне понимается следующее: можно ли с помощью разработки компоненты этого уровня осуществить функции защиты сетевой информации, и возможно ли реализовать сред ство защиты, которое, воздействуя каким-либо образом на уже существующие компо ненты данного уровня, сможет осуществлять функции защиты. К функциям защиты сетевых ресурсов, прежде всего, относятся: • Шифрование сетевого трафика. • Пассивный мониторинг сетевых пакетов, либо, на более высоком уровне, пас сивный мониторинг доступа к сетевым системным DLL и сетевым сервисам.
•
Активный мониторинг сетевых пакетов, то есть мониторинг и фильтрация не желательных пакетов (на более высоком уровне мониторинг и фильтрация запросов к сетевым системным DLL и сетевым сервисам). Комбинируя перечисленные функции, можно получить любой класс системы бе зопасности - от простого шифрования данных приложения перед их отправкой в сеть, до брандмауэра с возможностями поддержки виртуальных соединений.
Реализация защиты на уровне приложений и собственных DLL Разработка приложений или DLL с функциями защиты - это самая тривиальная и обеспечивающая наименьшую гибкость реализация. Защита на уровне приложения не является прозрачной, в этом случае пользователь должен сам решать, нужно ли защи щать его данные перед отправкой в сеть. При запуске приложения создается новый процесс, адресное пространство кото рого защищено от других процессов. Поэтому приложение может защищать только свои Для того чтобы данные других приложений могли обрабатываться приложением, реализующим защиту этих данных, должны использоваться специальные механизмы межпроцессной коммуникации: копирование данных из одного адресного простран ства в другое или создание области совместно используемой памяти, видимой из обо их адресных пространств. Следовательно, приложение, данные которого необходимо защитить, должно быть разработано для взаимодействия с приложением, защищаю щим его данные, с использованием этих механизмов. Но оно не обязано знать о суще ствовании какой-либо системы защиты. Реализация защиты на уровне собственной DLL также не обеспечивает прозрач ной защиты. Для того чтобы защиту данных приложений выполняла некоторая биб лиотека DLL, необходимо, чтобы приложения были разработаны так, чтобы вызывать функции из этой DLL. После того, как исполняемый код DLL спроецируется на адрес ное пространство вызывающего процесса (при помощи неявной компоновки при заг рузке приложения, или явной - в период выполнения), код и данные DLL просто ста новятся частью процесса приложения. Уровень приложений и DLL не имеет всех возможностей по реализа ции функций защиты сетевых ресурсов. Интерфейсы API, предоставляемые операци онной системой приложениям и DLLs, не позволяют им выполнять пассивный или активный мониторинг сетевых пакетов или контролировать доступ к сетевым систем ным DLL и сетевым сервисам. Единственное, что они могут это зашифровывать собственные данные перед отправкой их в сеть и расшифровывать после получения их из сети для предоставления пользователю. Только лишь во взаимодействии с соб ственным драйвером приложение может стать мощным средством защиты, причем в некоторых случаях приложению приходится использовать недокументированные ме тоды.
4.3.2. Реализация защиты на уровне системных предоставляющих приложениям различные сетевые интерфейсы К этим DLLs относятся, в первую очередь, следующие библиотеки DLLs сетевых (Net Win32 API DLL), предоставляющая интерфейс NetBIOS и Netфункции. 2) (Windows Socket 2.0 32-Bit DLL), (Microsoft Windows Sockets 2.0 Service Provider), wsock32.dll (Windows Socket 32-Bit DLL), являющиеся DLLs ин терфейса Windows Sockets. 3) (Multiple Provider Router DLL), предоставляющая сетевой интерфейс Win32 (WNet), и реализующая функции маршрутизатора многосетевого доступа. 4) (Windows NT BASE API Client DLL) DLL API Win32, эта библиотека предоставляет стандартные функции, такие как функции открытия, закрытия, чтения, записи и т.п. Если файл, именованный канал, почтовый ящик, устройство, к которым обращены запросы в этих функциях, находятся на удаленной ма шине, то эти запросы пройдут по сети. 5) Wininet.dll (Internet Extensions for Win32), реализующая функции передачи HTTP запросов, навигации файлов по протоколу FTP, функции удаленного доступа в Internet, URL (Uniform Resource и т.п. 6) Rpcrt4.dll (Remote Procedure Call Runtime), реализующая вызовы удаленных процедур. 7) Rasapi32.dll (Remote Access API), обеспечивающая приложениям удаленный доступ. 8) Nddeapi.dll (Network DDE Share Management APIs) и ddeml.dll (DDE management library), реализующие динамический обмен данными по сети. 9) Tapi32.dll (Microsoft Windows Telephony API Client DLL), предоставляющая приложениям телефонные сервисы. 10) Dlcapi.dll (DLC APIs), обеспечивающая взаимодействие по протоколу 11) (Microsoft WinSNMP Manager API DLL), (Microsoft SNMP Management API DLL), (SNMP utilities DLL), используемые SNMPПрежде чем перейти к рассмотрению возможностей реализации защиты на уровне этих DLL, ниже будет представлен процесс загрузки ЕХЕ-файла. При запуске ЕХЕфайла загрузчик операционной системы выполняет следующие действия: 1) Отыскивает ЕХЕ-файл, создает новый объект-процесс, создает адресное про странство нового процесса размером 4 Гб. Резервирует регион адресного простран ства - такой, чтобы в него поместился заданный ЕХЕ-файл. Желаемое расположение этого региона указывается внутри самого ЕХЕ-файла. По умолчанию базовый адрес ЕХЕ-файла - 0x00400000 (но при компоновке это значение может быть изменено). И
|
отмечает, что физическая память, увязанная с зарезервированным регионом, - ЕХЕфайл на диске. 2) Затем загрузчик просматривает таблицу импорта, содержащуюся в этом файле, и пытается найти и спроецировать на адресное пространство нового процесса все не обходимые DLLs. Желаемое расположение региона адресного пространства, куда бу дет спроецирована конкретная DLL, указывается внутри самого DLL-файла, по умол чанию базовый адрес DLL-файла Но у всех стандартных системных DLL модулей (в том числе и вышеперечисленных), входящих в комплект поставки Windows NT, разные базовые адреса. Затем загрузчик отмечает, что физическая па мять, увязанная с зарезервированным регионом, DLL-файл 3) Затем загрузчик сохраняет адреса импортируемых идентификаторов (функций и переменных), используемых ЕХЕ-файлом, в особой таблице адресов импорта (Import Address Table). Всякий раз, когда приложение ссылается на один из таких идентифика торов, сгенерированный компилятором код выбирает его адрес из таблицы и предос тавляет необходимую связь. Приведенная выше схема загрузки и исполнения функций DLL-файла позволяет реализовать защиту на уровне системных сетевых DLLs следующими способами: • П е р в ы й с п о с о б связан с использованием таблиц адресов импорта (Import Address Table). Для того чтобы найти эту таблицу, нужно сначала найти по сигнатуре «.idata» в таблице секций, располагающейся после заголовка фай ла, описание секции Секция содержит информацию о функциях и данных, импортируемых приложением из DLLs. В описании этой секции есть поле, содержащее виртуальный адрес, по которому загрузчик отобразил сек цию Содержимое этой секции начинается с массива структур, по одной структуре, содержащей пять элементов, на каждую DLL. Первый элемент каж дой структуры указывает на таблицу имен функций, четвертый является ука зателем на имя DLL, а пятый указывает на таблицу адресов функций. Найдя по таблице имен функций имя нужной функции, можно узнать ее адрес в со ответствующем элементе таблицы адресов функций, и затем заменить этот адрес на адрес собственного обработчика. Собственный обработчик может рас полагаться в собственной DLL. После завершения необходимой обработки нужно перейти по сохраненному первоначальному адресу импортируемой фун кции. Для того чтобы собственная DLL загружалась в адресное пространство любого процесса GUI-приложения, нужно в реестре в ключе HKEY_ Software\ \ Windows\APPINIT_DLLS включить имя этой DLL. В этом способе нужно менять таблицы адресов импорта во всех процессах при ложений, использующих системные DLLs. Но обычно процессов в системе не так уж много (несколько десятков). Можно разработать приложение, которое будет получать описатели объектов-процессов, соответствующих защищаемым приложениям, а затем осуществлять чтение и запись в их виртуальные адрес ные пространства, с помощью функций и
Секция имеет атрибут «для чтения и записи», поэтому запись в нее воз можна. • Второй способ - создание собственной DLL, в которой необходимо заменить только часть функций, непосредственно через которые проходят данные пользо вателя. Имена этих новых функций должны быть сохранены. При этом новая D L L д о л ж н а иметь то же имя. Или же в ключе р е е с т р а MACHINE \ \ \ Control \ Session Manager \ должна быть строка: «имя заменяемой DLL без расширения» «имя собственной DLL с расширением dll», тогда система загружает вместо первоначальной DLL собственную библиотеку DLL из каталога, указанного в параметре реестра Так как все системные DLL имеют определенные базовые адреса, по которым они проецируются в адресное пространство процесса приложения, то преобра зованная DLL не должна перекрывать базовый адрес другой системной DLL. • Третий способ основан на изменении части кода функций (необязательно на чальной) вышеперечисленных DLL так, чтобы передать управление (с помо щью команд call или собственному коду, реализующему необходимые фун кции по защите. Этот собственный код может содержаться в собственной DLL. Это изменение может происходить до загрузки DLL (исправляя DLL-файл во время загрузки ОС с помощью приложения-сервиса), или же можно ровать функции DLL после того, как DLL загружена. Этот способ основан на том, что все выполняемые приложения, импортирующие функции некоторой DLL, совместно используют один и тот же код этой DLL, загруженный в опера тивную память. И если страницы, содержащие код DLL, изменить, то это изме нение коснется всех приложений. Для реализации этого способа нужно разработать собственное приложение (или сервис), которое будет подгружать системные DLLs в свое адресное пространство. В этом приложении нужно получить адрес начала кода требуемой функции (например, с помощью функции Затем в том случае, если коман да перехода будет находить не в самом начале, вычислить адрес кода, который будет перезаписан командой перехода, и по этому адресу осуществить запись инструкции JMP или CALL на адрес собственного обработчика, при этом надо сохранить переза писываемые инструкции. Но запись в секцию кода не так-то просто осуществить, так как эта секция являет ся исполняемой и предназначена для чтения. Один из способов решения этой пробле мы состоит в отображении физического адреса, соответствующего виртуальному ад ресу региона с кодом на другой регион, в который запись разрешена, и затем осуще ствить в него запись нужных инструкций. (Получится, что двум разным регионам вир туального пространства соответствует одна и та же физическая память). Но подобные махинации можно проводить только в драйвере, следовательно, для реализации защи ты потребуется разработка драйвера. Возможно, что можно обойтись и без драйвера, воспользовавшись тем, что физическая память в ОС Windows NT представляется объек-
том-секцией, содержащимся в пространстве имен диспетчера объектов под именем Приложение может спроецировать физическую память в свое адресное пространство и затем манипулировать ее содержимым. Несмотря на то, что вышеперечисленные способы реализации защиты на уров не системных DLLs, кажутся довольно надуманными и неосуществимыми, подоб ные программные продукты все же существуют, например SPY product series, реализует технологию перехвата вызовов любых DLLs, в том числе и сис темных.
Реализация защиты на уровне сетевых сервисов Почти все сетевые сервисы, такие как сервис рабочей станции Workstation), сервис сервера (LanMan Server), сервис оповещений (alerter), сервис сообщений (messenger), обозреватель сети (Computer Browser), DHCP client содержатся в одном ЕХЕ-файле с именем services. Остальные исполняются в собственных процессах, на пример, wins.exe (WINS SERVER), dns.exe (Domain N a m e System (DNS) Server), (RPC and Distribute C O M Services), locator.exe (RPC locator), nddeagnt.exe (Network DDE Agent), tapisrv.exe (Microsoft Windows Telephony Server), rassrv.exe (Remote Access Server Supervisor). Сетевой сервис — это серверный Некоторые сетевые сервисы выполня ются в фоновом режиме, в то время как другие предоставляют API через соответству ющие DLLs клиентской стороны, например: 1) tapi32.dll (Microsoft Windows Telephony API Client DLL) предоставляет приложениям взаимодействие с tapisrv.exe; 2) библио тека rassapi.dll (Remote Access Admin APIs может использоваться приложениями для конфигурирования RAS сервера (rassrv.exe); 3) библиотека netapi32.dll (Net Win32 API DLL) используется приложениями для взаимодействия с browser, workstation, server, alerter, messenger, replicator; и т.д. Сервисы, имеющие собственные API, для обмена данными со своими клиентами обычно используют механизм RPC. При этом для реализации RPC-запросов между процессами, исполняющимися на одной машине, в действительности используется механизм Если клиентский процесс и процесс сетевого сервиса не находятся на одном ком пьютере, то контролировать передачу данных между этими процессами можно с по мощью вмешательства в исполнение функций библиотеки Этот метод был подробно описан выше. Чтобы контролировать передачу данных между клиентским процессом и процес сом сетевого сервиса, исполняющимися на одной машине, нужно вмешаться в меха низм В общих чертах он выглядит следующим образом: после, того как клиентс кий запрос к сервису перехватится заглушкой в DLL клиентского процесса, эта заг лушка упакует параметры вызова, необходимые процедуре сервиса. Затем LPC обес печит механизм передачи данных клиентского процесса процессу сетевого сервиса с помощью разделяемой памяти или с помощью посылки сообщения в очередь сообще-
ний. Затем после того как процедура сервиса выполнит необходимую обработку, LPC возвратит результаты обработки обратно клиентскому процессу с помощью тех же механизмов. LPC при своей работе создает и использует объекты-порты (port object), которые в качестве атрибутов имеют описатель секции (описатель совместно используемой па мяти) и указатель на очередь сообщений. Объект-порт ассоциируется с серверным процессом и обеспечивает выполнение сервисов для клиентского процесса. Объектпорт как бы представляет канал для передачи сообщений между клиентским и сервер ным процессами. Серверный процесс определяет для связи с собой именованный объект-порт (порт соединения), а когда клиентский процесс свяжется с этим имено ванным портом, серверный процесс создаст два неименованных объекта-порта (порты связи) для взаимодействия. API, необходимое для получения доступа к сервисам LPC, не документировано. Скорее всего, заглушка в клиентской DLL получает обслуживание LPC через преры вание int 2Е с п о м о щ ь ю вызова ф у н к ц и й NtAccept ConnectPort, NtCompleteConnectPort, NtCreatePort, Port, NtReplyWaitReplyPort, NtRequestPort, NtConnectPort, NtReplyPort, NtRequest и т.п., предоставляемых библиотекой ntdll.dll. В результате чего обра ботчик прерывания 2Е (диспетчер системных сервисов) использует таблицу распреде ления системных сервисов (KeServiceDescriptorTable) для вызова соответствующих сервисов LPC из Следовательно, для перехвата обращений к сервису LPC, можно воспользоваться методом перехвата обращений к системным сервисам, кото рый будет рассмотрен ниже. Тем самым будет получен доступ к данным, передавае мым от клиентских процессов серверным процессам, в том числе и процессам сете вых сервисов.
Реализация защиты на уровне «родного» API для ОС Windows NT Один из способов обеспечения прозрачной защиты связан с заменой механизма предоставления функций «родного» API. Как уже рассматривалось в главе «Общая архитектура Windows NT», системные сервисы могут быть предоставлены коду пользо вательского режима посредством использования библиотеки ntdll.dll. В ntdll.dll вызов системных сервисов происходит с помощью программного прерывания int 2Е. После чего обработчик прерывания для вызова соответствующего системного сервиса ис пользует таблицу распределения системных сервисов (KeServiceDescriptorTable), экс портируемую ntoskrnl.exe (ядро и исполнительная система). Структура этой таблицы следующая; 1) указатель на массив, содержащий 4-х байтовые адреса точек входа системных сервисов в ntoskrnl.exe (4 байта); 2) 00000000 (4 байта); 3) количество системных сервисов (4 байта);
4) указатель на массив, содержащий однобайтовые значения, определяющие сколь ко байт надо извлечь из стека режима пользователя, содержащего параметры функции (4 байта). Зная индексный номер функции, реализующей системный сервис, можно по таб лице определить адрес ее начала и заменить его на адрес начала собственного обра ботчика режима ядра. После исполнения собственного обработчика необходимо пере дать управление по старому адресу, чтобы дать возможность стандартному обработ чику выполнить запрошенные действия. Так как эта таблица находится в системной области памяти, то такая замена может выполняться только кодом режима ядра (например, драйвером). Примером подобного средства защиты может служить программный продукт for Windows NT, выполняющий перехват системных сервисов, реализующих обращение к реестру Windows NT.
Возможности реализации средств защиты сетевой информации на уровне ядра С этого момента начинается анализ сетевых компонент, исполняющихся в приви легированном режиме - режиме ядра, и являющихся драйверами. Прежде чем перехо дить к дальнейшему рассмотрению того, как и куда сетевая архитектура Windows NT позволяет встраивать модули защиты на уровне ядра, необходимо вспомнить меха низм драйверов-фильтров (см. раздел «Реализация драйверов-фильтров»), который может использоваться в качестве средства защиты на любом уровне в стеке драйверов.
Драйверы
фильтры
Система ввода/вывода Windows NT является расширяемой. Один из методов рас ширения возможностей системы ввода/вывода разработка и применение драйверовДрайвер-фильтр - это промежуточный драйвер, перехватывающий запросы, пред назначенные некоторым программным модулям (например, драйверу файловой систе мы или драйверу диска). Перехватив запрос, драйвер-фильтр имеет возможность либо расширения, либо замещения функциональности, обеспечиваемой первоначальным получателем запроса. Драйвер-фильтр может, либо использовать сервисы драйвера, которому изначально предназначался запрос, либо использовать сервисы других про граммных модулей уровня ядра для обеспечения дополнительной функциональности. Драйверы-фильтры могут встраиваться в любое место в стеке драйверов, их может быть несколько, в том числе расположенных подряд. Примером использования может послужить реализация прозрач ного зашифрования/расшифрования данных, хранимых на диске или в сети. Ни одна из поставляемых с ОС Windows NT файловых систем (FASTFAT, NTFS, CDFS, LAN Manager Redirector) не обеспечивает поддержки прозрачного зашифрования данных
перед записью их на диск (или отправкой в сеть) и расшифрования данных перед пре доставлением их авторизированному пользователю. Но для того, чтобы выполнять шифрование, необязательно реализовать свой собственный драйвер файловой систе мы или специальный драйвер диска. Достаточно разработать драйвер-фильтр, распо лагающийся либо выше драйвера файловой системы (в этом случае драйвер-фильтр сможет обработать запрос перед тем, как драйвер файловой системы получит возмож ность его увидеть), либо ниже драйвера файловой системы (этот случай позволяет драйверу-фильтру выполнить любые требуемые операции после того, как драйвер файловой системы завершит свою задачу, и перед тем, как запрос будет получен драй вером диска (или сетевым также может выполнять автоматическое обнаружение в реальном времени сигнатур вирусов в файлах (в том числе и файлах, полученных из сети). Фундаментальные шаги в разработке драйверов-фильтров: • Присоединение к нужному объекту-устройству. Диспетчер вклю чает возможность присоединения к объекту-устройству созданному некото рым драйвером (1), объекта-устройства (2), созданного другим драйвером(2). В результате такого присоединения пакеты IRP, направленные драйверу ассо циированному с объектом-устройством (1), будут перенаправляться драйверу (2), ассоциированному с присоединенным объектом-устройством (2). Этот «при соединенный» драйвер (2) и является драйв Каждый объект-устройство имеет поле, называемое AttachedDevice и содержащее указатель на объект-устройство, созданное драйвером-фильтром, который первым при соединил свое устройство. Если это поле содержит NULL, значит нет присоединен ных устройств. В процессе присоединения нового объекта-устройства, процедура при соединения пройдет по связанному списку присоединенных устройств до конца и вы полнит присоединение нового объекта-устройства к последнему объекту-устройству в этом списке. В результате, любой запрос на предназначающийся некоторому объекту-устройству, будет перенаправляться последнему (в его списке при соединенных устройств) объекту-устройству. То есть последнему драйверу-фильтру, ассоциированному с последним объектом-устройством. Аналогично, при вызове фун кции, которая по имени объекта-устройства возвращает его указатель, будет возвра щен указатель на объект-устройство, являющийся последним в списке присоединен ных устройств. Но важно заметить, что диспетчер ввода/вывода проходит до конца списка присо единенных устройств не во всех случаях. Например, никакого перенаправления не будет, если будет вызвана функция Следовательно, чтобы обращение к драй веру назначения не миновало драйвер-фильтр, этот драйвер-фильтр должен присоеди нить свой объект-устройство к объекту-устройству драйвера-назначения, прежде, чем вышележащий драйвер вызовет процедуру определения указателя нижележащего объек та-устройства по его имени (это делается во время запроса на открытие), а затем будет использовать этот указатель (в частности при вызове функции
• Драйвер-фильтр может исследовать, модифицировать, завершать, или переда вать дальше полученный пакет запроса драйверу назначения. Для того чтобы передать драйверу назначения полученный запрос, драйвер-фильтр должен со здать собственный пакет IRP. • Драйв должен реализовать завершающие процедуры для завершения обработки IRP, после того, как IRP завершит драйвер назначения. • Отсоединение от объекта-устройства назначения. Ниже рассмотрены основные принципы, которых необходимо придерживаться при разработке драйвера-фильтра, а также недостатки, из-за которых применение драйве ров-фильтров становится нежелательным: 1) Разработчик драйв должен четко знать, как работает драйвер, со здавший устройство, к которому будет присоединяться его драйвер. Драйверфильтр должен уметь обрабатывать все получаемые им запросы, адресованные на самом деле первоначальному драйверу, к которому он присоединился. Несмотря на то, что все драйверы должны отвечать на стандартное множество запросов, выдаваемых менеджером ввода/вывода, в реальности, в случае разработки собственного драйвера-фильтра, нужно знать очень тонкие взаимосвязи, которые про являются в зависимости от того, на каком уровне иерархии находится драйв Возможны случаи, когда драйвер одной файловой системы запрашивает сервисы у драйвера другой файловой системы, или когда промежуточный драйвер запрашивает сервисы вышележащего драйвера файловой системы. Например, в случае фильтрации всех запросов, предназначенных некоторому ло гическому тому, управляемому драйвером файловой системы (NTFS), необходимо понимать всевозможные пути вовлечения драйвера NTFS в обработку запроса, так как во всех этих случаях будут вовлекаться процедуры распределения драйвера-филь тра. Процедуры обработки запроса на чтения/запись драйвера файловой системы могут вовлекаться несколькими путями, например: из потока режима ядра, вызвав шего системные сервисы ZwReadFile(), из потока пользовательского режима, вызвавшего системные сервисы NtReadFile(), NtWriteFileQ; из диспетчера памяти из-за обращения к отсутствующей странице спроецированного файла; из дис петчера кэша в результате асинхронного сброса буферов диспетчера кэша на диск; и так далее. В некоторых ситуациях может быть приемлемым то, что драйвер фильтр отправ ляет запрос ввода/вывода на асинхронное выполнение, но в других ситуациях (напри мер, при обслуживании обращения к отсутствующей странице) драйвер-фильтр не должен этого делать, иначе это приведет к взаимной блокировке или зависанию. В случае, когда драйвер-фильтр привязывается к объекту-устройству нижнего уров ня (например, к устройству, представляющему физический диск), то возникают дру гие проблемы, с которыми сталкивается подобный драйвер-фильтр. Например, пакеты IRP, посланные драйверу диска, могут быть чаще всего ассоциированными пакетами, созданными драйвером файловой системы, и поэтому драйвер-фильтр не должен пы-
таться в свою очередь создавать ассоциированные пакеты. Обычно от драйверов дис ков ожидают, что они будут выполнять операции асинхронно, поэтому драйвер-фильтр должен удовлетворять этим ожиданиям. 2) Драйвер-фильтр должен корректно работать с другими возможными драйве рами-фильтрами, присоединенными до или после него. Никогда нельзя пола гаться на то, что запрос, выпущенный драйвером-фильтром, пойдет напря мую нужному драйверу, так как он может быть перехвачен другим драйверомфильтром. 3) должен знать, к чему он привязывается. Например, может по лучиться так, что когда драйвер-фильтр попытается привязаться к объекту-уст ройству, представляющему логический том файловой системы, он в действи тельности привяжется к объекту-устройству, представляющему физический диск, если том еще не был вмонтирован. 4) Драйвер-фильтр должен избегать поддержки ненужных ссылок. Если по небреж ности драйвер-фильтр поддерживает лишнюю ссылку на объект-устройство назначения, то, возможно, что это будет препятствовать всем дальнейшим зап росам на открытие и выполнение операций над этим устройством. 5) должен следить за контекстом потока, в котором выполняется его процедура распределения. В первую очередь это касается запросов вывода к драйверам файловых систем, так как эти запросы должны передавать ся в контексте потока, инициировавшего запрос, поэтому если драйвер-фильтр вызвал переключение контекста, например, послав запрос на исполнение рабо чему потоку, то дальнейшая обработка этого запроса драйвером файловой сис темы приведет к непредсказуемым последствиям. На всех уровнях, которые будут обсуждаться в дальнейшем, возможны реализация и применение драйверов-фильтров.
4.4.2. Реализация защиты на уровне драйвера MUP Как уже отмечалось ранее, драйвер mup.sys вовлекается при попытке достижения удаленных разделяемых ресурсов, используя UNC имена. Этот компонент во взаимо действии с сетевыми редиректорами отвечает за интеграцию пространства имен уда ленных файловых систем в локальное пространство имен. Этот драйвер является про стейшим драйвером файловой системы. После запроса на создание/открытие файла по имени UNC драйвер mup.sys уже не вовлекается в обработку дальнейших запросов к этому файлу. То есть через драйвер mup.sys данные пользователя не проходят, но этот драйвер играет важную роль при открытии файлов и устройств по имени В связи с этим стоит рассмотреть воз можность регистрации и аудита открытия файлов и устройств с помощью драйверафильтра, присоединенного к драйверу mup.sys. Так как драйвер mup.sys, является до вольно простым драйвером, то реализация драйвера-фильтра в данном случае допус тима.
Реализация защиты на уровне драйверов файловых систем Драйвер файловой системы обеспечивает пользователям механизм хранения и получения информации, реализуя возможности по созданию, модификации, удалению, разделению файлов и идентификации файлов по их символическим/логическим име нам. В дополнение к этим функциям сетевые файловые системы обеспечивают в той или иной степени прозрачность местоположения файлов в сети и мобильность фай лов. Файловые системы бывают разных типов, например: локальные файловые систе мы (FASTFAT, NTFS); сетевые и распределенные файловые системы (редиректор сервер); псевдофайловые системы, предоставляющие пользователю интерфейс, подоб ный интерфейсу файловых систем, но реализующие особую функциональность, от личную от традиционных задач хранения и предоставления данных с диска, к относятся файловые системы, реализующие механизм межпроцессной коммуникации система именованных каналов и система почтовых В принципе, ОС Windows NT предоставляет стандартный механизм для перехвата всех запросов к драйверу файловой системы - это описанный выше ме ханизм использования драйверов - фильтров. Для реализации этого механизма можно воспользоваться книгой автора Rajeev Negar «Windows NT File System Internals: A Guide», которая посвящена разработке драйверов файловых систем, и зат рагивает вопрос реализации и драйверов-фильтров. Основным недостатком, мешающим реализации защиты на уровне драйверов фай ловых систем, в том числе и реализации драйверов-фильтров, является закрытость и недокументированность интерфейса с этими драйверами. Microsoft неохотно поддер живает разработку драйверов файловых систем. В MSDN разработка драйверов фай ловых систем не документирована. И все же в году Microsoft выпустила докумен тацию WinNT Kit, необходимую для разработки драйверов файловых систем, и содержащую требуемые заголовочные файлы для успешной компиляции драйверов файловых систем с помощью компилятора Microsoft Visual С++, а также примеры про стейших файловых систем. Но это средство разработки дорого стоит (1000$). Из-за этого многие запросы к драйверам файловых систем остаются загадкой, и поэтому применение драйверов-фильтров, в данном случае, скорее всего, приведет к некоррек тной работе стека драйверов. В дополнение к вышеперечисленным трудностям надо учитывать и то, что многие части сетевых служб (почему-то имея тип драйверов файловых систем) на самом деле не предоставляют интерфейса, характерного драйверам файловых систем, что выяс няется из анализа реализуемых ими процедур распределения. Согласно реестру Windows NT почти все части сетевых служб, исполняющихся в режиме ядра, являются драйверами файловых систем, а именно: встроенный сетевой редиректор и сервер файловая система именованных каналов (npfs.sys), ф а й л о в а я с и с т е м а п о ч т о в ы х ящиков эмулятор интерфейса NetBIOS
(netbios.sys). Только эмулятор интерфейса WinSock (afd.sys) имеет тип стандартного драйвера. (Все эти вышеперечисленные драйверы являются и предос тавляют в своей нижней части интерфейс Но далеко не все из этих драйверов действительно реализуют в своей верхней части интерфейс драйвера файловой системы. Например, драйвер srv.sys, который вечает на запросы, посылаемые редиректором с удаленной машины, и взаимодейству ет с локальной файловой системой, не нуждается в предоставлении интерфейса фай ловой системы. Аналогично, драйвер netbios.sys не предоставляет интерфейс файло вой системы, так как он является частью эмулятора сетевого интерфейса NetBIOS и взаимодействует с соответствующей DLL для преобразования «родных» функций это го интерфейса, вызываемых приложениями, в для вызова драйверов транспортов. Драйвер netbios.sys имеет только одну процедуру распределения для па кетов всего четырех типов IRP_MJ_CREATE, CONTROL, в то время как драйвер файловой системы должен обрабатывать гораздо большее число специфичных для него типов пакетов. Возмож но, что это обстоятельство облегчит разработку к нему. Чего нельзя сказать, о драйверах srv.sys и afd.sys, которые регистрируют одну процедуру распреде ления для всевозможных типов пакетов IRP, поэтому потребуется более детальное исследование того, какие из этих пакетов они действительно обрабатывают, а для ка ких просто имеют заглушки. Архитектура Windows NT позволяет инсталлировать собственные драйверы фай ловых систем, это сделано специально для того, чтобы ОС Windows NT обладала спо собностью подключения к разнообразным сетям. Поэтому возможна разработка соб ственного редиректора, который сможет заменить встроенный и реализовывать функ ции защиты (при этом надо выгрузить встроенный редиректор и сервер). Если разрабатывается собственный редиректор, который будет единственным в системе, то нужно также разработать и DLL сетевого провайдера (DLL компонента сетевого доступа), позволив тем самым приложениям сетевой интерфейс API Win32 (WNet) для запроса сервисов от этого редиректора. Если редиректор вы полняется одновременно со встроенным, то соответствующую ему библиотеку DLL провайдера можно не реализовывать, тогда запросы по интерфейсу API WNet через собственный редиректор не пойдут, а пойдут только запросы по стандартному интер фейсу API ввода/вывода через драйвер mup.sys. Чтобы зарегистрировать DLL сетевого провайдера для MPR (предоставляющего API WNet), нужно изменить реестр. Порядок, в котором вовлекаются DLL провайде ров, зависит от порядка, в котором перечислены эти провайдеры в реестре. MPR изу чает содержимое следующего ключа реестра, чтобы определить какие DLL провайде ров существуют в системе и порядок, в котором они будут вовлекаться: der\order. В этом ключе содержится список имен провайдеров. Каждое имя соответствует некоторому сервису в ключе
провайдера», который должен создаваться при инсталляции сетево го провайдера. В книге автора Rajeev Negar «Windows NT File System Internals: A Guide» описаны функции, которые могут быть реализованы в DLL провайдера. Из них только одна функция, позволяющая пользователям узнать возможности сети, является обяза тельной. DLL сетевого провайдера вовлекает сетевой редиректор в обработку запроса, ис пользуя у п р а в л я ю щ и е з а п р о с ы к ф а й л о в о й с и с т е м е ( F S C T L ) ч е р е з ф у н к ц и ю Эти запросы передаются менеджером редиректору в пакете IRP со значением кода основной функции Ниже рассматриваются основные отличительные особенности драйверов файло вых систем: • Первой особенностью драйверов файловых систем является то, что они нахо дятся в вершине стека драйверов, и поэтому гарантированно вызываются в контексте потока, инициировавшего запрос. Эта особенность позволяет драй верам файловых систем манипулировать данными из адресного пространства инициатора запроса, а также осуществлять «быстрый» (fast I/O), при котором диспетчер вызывает драйвер файловой системы без использования пакетов запроса IRP, а с помощью определенной функции с параметрами, необходимыми для выполнения запроса драйвером файловой Учитывать эту особенность (заключающуюся в том, что запрос ввода/вывода дол жен передаваться драйверу файловой системы в контексте потока - инициатора запро са) необходимо также при разработке драйвера-фильтра, присоединяемого к драйверу файловой системы. • Следующая особенность драйвера файловой системы заключается в тесном вза имодействии с подсистемой виртуальной памяти, то есть с менеджером памяти и менеджером кеша. Для иллюстрации этого взаимодействия рассмотрим, уп рощенно, «быстрый» запрос на чтение данных от диспетчера ввода/вывода к драйверу файловой системы. По этому запросу драйвер файловой системы вы зывает диспетчер кэша, который в свою очередь копирует требуемые данные из системного кэша (если все необходимые данные там присутствуют) в пользова тельский буфер. Если же не все требуемые данные присутствуют в кеше, то возникает ошибка страницы, в результате чего вовлекается диспетчер памяти для загрузки в память данных из файла на диске. Для этого он строит пакет запроса ввода/вывода IRP для получения данных с диска и передает этот IRP драйверу файловой системы. В результате данные будут прочитаны с диска в системный кэш, после чего диспетчер кеша скопирует данные из системного кэша в пользовательский буфер. Файловые системы отвечают за обеспечение поддержки механизма кеширования данных и механизма файлов, проецируемых в память. Подсистема виртуальной памя ти зависит от файловой системы, обеспечивающей поддержку страничных файлов, а
файловая система в свою очередь зависит от подсистемы виртуальной памяти, щающей память, требуемую для выполнения запросов к файловой системе. • Кроме описанных выше взаимосвязей с подсистемой виртуальной памяти драй вер файловой системы очень тесно (в отличие, например, от драйвера физичес кого устройства) взаимодействует с менеджером ввода/вывода и менеджером объектов, так как каждая файловая система в Windows NT отвечает за реализа цию своей части системного пространства имен. При открытии файла диспет чер ввода/вывода вызывает диспетчер объектов, который просматривает свое пространство имен, до тех пор, пока не попадает в объект-устройство. После чего разрешением оставшейся части имени файла занимается соответствующий драйвер файловой системы в тесной взаимосвязи с менеджером ввода/вывода. Несмотря на желание разработчиков ОС Windows NT поддерживать четкое раз граничение между файловой системой и остальной частью ОС, постепенно эта грани ца размывалась, и все более и более скрытая функциональность проникала в систему. Тесное взаимодействие драйвера файловой системы с различными компонентами ис полнительной системы; менеджеров ввода/вывода, менеджером объектов, менедже ром памяти и менеджером кэша, естественно, отражается на сложности разработки собственного драйвера файловой системы. Примером реализации фильтра к драйверам файловой системы может служить программа разработанная Mark Russinovich и Bruce Cogswell.
Реализация защиты на уровне транспортного драйвера Транспортные драйверы являются, фактически, стандартными драйверами про межуточного уровня и реализуют в своей верхней части стандартный интерфейс, со ответствующий TDI спецификации. Этот интерфейс в основном базируется на полу чении и обработке пакетов содержащих различные значения контрольных кодов определяемых TDI спецификаци ей. В своей нижней части TDI драйвер взаимодействует с NDIS библиотекой. Разработка драйверов транспорта хорошо документирована в Windows NT DDK в главе Network drivers, в части Intermediate NDIS drivers and TDI drivers. Имеется при мер транспортного драйвера в DDK\src\network\tdi. Можно также реализовать драйвер-фильтр, который будет присоединяться к объек там-устройствам, создаваемым драйвером транспорта. Например, к объектам-устрой ствам \Device\Tcp и \Device\Udp, создаваемым драйвером транспорта TCP/IP. В связи с тем, что драйвер транспорта должен предоставлять единый, хорошо до кументированный интерфейс, облегчается задача его разработки. Вместе с тем, TDIинтерфейс является расширяемым интерфейсом, так как: • TDI предоставляет возможность расширения множества операций, позволяю щих взаимодействовать с определенным транспортом, в том числе и операций, связанных с управлением и конфигурацией.
•
TDI предоставляет гибкую схему адресации, позволяющую определять и ис пользовать множество форматов адресов. • TDI определяет механизм, с помощью которого драйвер транспорта может сиг нализировать клиенту об интересующих клиента событиях, произошедших в сети (без предварительного запроса от клиента). Это происходит с помощью вызова драйвером транспорта функций, указатели иа которые передаются TDIклиентом драйверу транспорта в самом начале сетевых операций в пакете C O N T R O L с контрольным кодом EVENT_HANDLER, и регистрируются драйвером транспорта. TDI-клиент даже может использовать подобный механизм функций вызова в качестве альтернативы обычным пакетам запросов к транспорту с контрольными кодами Когда драйвер транспорта вызывает подобную функцию, он может передать в качестве параметров некоторое ограниченное количе ство данных. При этом, если существует драйвер-фильтр, присоединенный к драйверу транспорта, то он не получит возможности проконтролировать эти данные. Подобное свойство расширения возможностей взаимодействия клиента с оп ределенным драйвером транспорта приводит к частичной закрытости интерфейса между ними. В результате чего сильно затрудняется разработка драйвера-фильтра, присоединяемого к транспортному драйверу, как и в случае с драйверами файло вых систем. В то же время сохраняется возможность разработки собственного драйвера транс порта с использованием документации DDK, который будет реализовать требуемые функции защиты.
Реализация защиты с помощью перехвата функций NDIS - библиотеки Этот метод является трудоемким, совершенно не документированным, но в то же время чрезвычайно эффективным. Реализация этого метода позволяет создать как бы оболочку над самой библиоте кой NDIS. Этот метод аналогичен методу перехвата системных сервисов. В данном случае заменяются адреса необходимых функций библиотеки NDIS, в результате чего можно получить контроль над всеми сетевыми операциями в системе. Рассмотрим упрощенно, какие вызываются во время загрузки се тевых драйверов. Сначала инициализируется драйвер сетевой карты, при этом для него вызывается функция предупреждающая NDIS о том, что ини циализируется новый минипорт (драйвер сетевой карты). Затем вызывается функция которая регистрирует точки входа минипорта для Д а л е е в ы з ы в а е т с я т о ч к а в х о д а в д р а й в е р е с е т е в о й карты которая готовит реальную или виртуальную сетевую карту для вы полнения сетевых операций ввода/вывода, запрашивает все аппаратные ресурсы, не-
обходимые сетевой карте, и размещает ресурсы, необходимые драйверу для выполне ния сетевых операций Затем начинают инициализироваться драйверы протоколов TCP/IP, NETBEUI, SPX и т.д. Для каждого из них вызывается функция NdisRegisterProtocol, которая реги стрирует точки входа драйвера и имя протокола для библиотеки NDIS. После инициализации драйвера протокола и драйвера сетевой карты вызывается функция которая устанавливает связь между вызвавшим эту функ цию протоколом и определенным нижележащим драйвером сетевой карты или проме жуточным драйвером. А затем уже начинается обмен пакетами между драйвером про токола и драйвером сетевой карты. Из приведенного выше процесса инициализации сетевых драйверов видно, что, перехватив вызовы таких функций как NdisRegisterProtocol, NdisOpenAdapter, можно отследить загрузку сетевого драйве ра, и установить адреса своих обработчиков для точек входа (например, MiniportQuery Information, MiniportSend, MiniportSendPackets, MiniportTransferData и т.д.) и для точек входа P r o t o c o l X x x - п р о ц е д у р (например, ProtocolBindAdapter, ProtocolReceive, Protocol ReceiveComplete, Complete, ProtocolTransferDataComplete, ProtocolReceivePacket, ProtocolUnbindAdapter, ProtocolUnload и т.д.), а затем контролировать все сетевые операции ввода/вывода, проходящие через него. Важно, чтобы ваш драйвер, который должен заменить адреса в таб лице экспорта библиотеки NDIS на адреса собственных обработчиков, загружался сразу после загрузки драйвера Ndis.sys. Иначе, к моменту загрузки вашего драйвера может уже произойти инициализация некоторых сетевых драйверов.
4.4.6. Реализация защиты на уровне сетевого драйвера промежуточного уровня, поддерживающего интерфейс NDIS Разработка NDIS драйверов промежуточного уровня - это один из хорошо до кументированных механизмов расширения возможностей системы ввода/вывода ОС Windows NT. Библиотека NDIS является очень мощным средством, используе мым для разработки драйверов сетевых карт глобальных и локальных сетей и про межуточных драйверов. С использованием библиотеки N D I S можно реализовать промежуточные драйверы, служащие самым разным целям, включая фильтрацию и ш и ф р о в а н и е пакетов. П р о м е ж у т о ч н ы е д р а й в е р ы х о р о ш о д о к у м е н т и р о в а н ы в Windows NT D D K в главе Network drivers, в части Intermediate NDIS drivers and TDI drivers. Имеется пример промежуточного драйвера packet в DDK\src\ network\packet\driver. Типичные варианты реализации промежуточного драйвера рассмотрены на рис. 27.
Рис. 27 В первых двух случаях промежуточный драйвер выглядит для драйвера та как драйвер виртуальной сетевой карты. Используя механизм привязок можно все транспорты привязать снизу к требуемому промежуточному драйверу, который таким образом получит возможность контролировать все пакеты, идущие в/из сети. Это по зволит ему реализовать функции защиты типа: преобразования форматов пакетов, пре образования пользовательских данных в пакетах, фильтрации пакетов, регистрации пакетов, контроля содержимого пакетов и т.п. Во втором случае NDIS драйвер промежуточного уровня будет взаимодействовать посредством использования пакетов IRP с драйвером устройства, не являющимся се тевой картой (следовательно, этот драйвер устройства не поддерживается интерфей сом NDIS, им может быть, например, драйвер последовательного устройства). Подоб ные NDIS драйверы промежуточного уровня поставляются с Windows NT, например взаимодействующий с драйвером последовательного порта. Поэтому необходимость в их реализации возникает только в случае разработки собственного сетевого устройства (но тогда придется разрабатывать и драйвер этого устройства, куда можно встроить функции защиты), или в случае, когда необходимо полностью заме нить встроенный драйвер (но в этом случае лучше реализовать драйвер-фильтр, при соединенный к драйверу устройства). Третий случай соответствует случаю реализации драйвера транспорта, рассмот ренного выше.
Во всех этих случаях промежуточный драйвер может реализовать в свой верхней части закрытый интерфейс, посредством использования пакетов IRP с вышележащим драйвером или приложением. (Для подобного взаимодействия вышележащий драйвер или приложение должны открыть устройство, созданное промежуточным драйвером.) Этот механизм используется в сетевых анализаторах, являющихся программными ком плексами, состоящими из приложения и драйвера. В DDK подробно описано, какие функции интерфейсов верхнего и нижнего уров ней промежуточный драйвер должен регистрировать обязательно, а какие по желанию разработчика. Благодаря таким четко оговоренным правилам взаимодействия посред ством использования библиотеки NDIS, встраивание промежуточного драйвера меж ду драйвером транспорта, поддерживающим интерфейс NDIS в своей нижней части, и NDIS драйвером сетевой карты не приводит к нарушению работы стека сетевых драй веров. Промежуточный драйвер, расположенный над драйвером сетевой карты, факти чески эквивалентен драйверу-фильтру, присоединенному к драйверу физического ус тройства. Только последний встраивается в цепь драйверов, взаимодействующих по средством пакетов IRP. Надо отметить, что драйвер ndiswan.sys сам по себе является промежуточным драйвером, поставляемым Microsoft, и участвующим в поддержке взаимодействия по глобальным сетям типа ISDN, FR, Switched 56. Драйвер ndiswan.sys во взаимо действии с ndistapi.sys может служить провайдером сервисов TAPI. Для того чтобы контролировать трафик на уровне этих драйверов, необязательно заменять их соб ственными аналогами со встроенными функциями защиты, можно создать проме жуточный драйвер, реализующий функции защиты, и расположить его выше или ниже этих драйверов. Промежуточные драйверы являются неотъемлемой частью многих систем защи ты сетевых ресурсов, реализующих пассивный мониторинг сетевых пакетов. Приме ром такой системы безопасности может служить программный анализатор сетевого трафика Microsoft Network Monitor. Также подобные драйверы являются частью мно гих систем безопасности, выполняющих фильтрацию и шифрование сетевого трафи ка, например, Guardian Windows NT Firewall.
4.4.7. Реализация защиты на уровне драйверов сетевых устройств Сюда входят драйверы сетевых карт, драйверы устройств, которым могут направ ляться сетевые запросы ввода/вывода (например, драйвер последовательного порта). Реализация защиты на этом уровне зависит от того, разработан ли драйвер устройства в соответствии со стандартом NDIS (то есть это NDIS драйвер сетевой карты локаль ной или глобальной сети), или же это драйвер устройства, управляемый пакетами IRP. В первом случае лучше всего перейти к реализации промежуточного драйвера, а во втором к реализации драйвера-фильтра. Также на этом уровне возможна реализация
собственного драйвера устройства со встроенными функциями защиты, так как разра ботка подобных драйверов хорошо документирована в Windows NT DDK. Разработка собственных драйверов сетевых карт глобальных и локальных сетей, поддерживаемых интерфейсом NDIS, хорошо документирована в DDK в главе Network drivers, в части Miniport NIC и имеется ряд примеров в DDK\src\network. Если же сетевое устройство не является сетевой картой, то можно воспользоваться общими рекомендациями по разработке драйверов устройств в главе Kernel-mode drivers. В случае разработки драйверов-фильтров, присоединенных к драйверам устройств, не являющимся сетевыми картами, необходимо знать все типы пакетов IRP и все воз можные управляющие коды в пакетах CONTROL, используемые для взаимодействия вышележащих драйверов с драйвером устройства.
4.5. Сравнительный анализ способов реализации защиты В табл. дается сравнительный анализ реализаций средств защиты относитель но следующих факторов: 1) Объем контролируемых данных. Этот фактор определяет, какие данные прохо дят через средство защиты, и, следовательно, могут им контролироваться и обрабаты ваться. 2) Сложность реализации. Определяет сложность реализации средства защиты, как с точки зрения ее недокументированности, так и сложности отладки. Этот фактор оценивается по 3-х бальной шкале: высокая, средняя, низкая. Низкая сложность озна чает, что при разработке средства защиты используются хорошо документированные интерфейсы и доступные средства отладки. Средняя - означает, что используются до кументированные интерфейсы, но отладка средства защиты уже не столь проста, в связи с тем, что данное средство исполняется в режиме ядра, а, следовательно, боль шинство ошибок в нем приводят к падению ОС, и, кроме того, при отладке приходится иметь дело с ассемблерным кодом. Высокая сложность означает, что к проблемам с отладкой прибавляется еще частичное или полное отсутствие документации для раз работки средства защиты. 3) Возможность встраивания защиты. Этот фактор определяет, предоставляет ли операционная система возможность встраивания средства защиты и расширения своей функциональности на данном уровне. 4) Прозрачность защиты означает, должен ли пользователь предпринять какиелибо действия для того, чтобы защитить свои данные с помощью средства защиты. 5) предоставляемые ОС коду средства защиты. В зависимости от того, исполняется ли средство защиты в режиме пользователя, или в ном режиме - режиме ядра, ему предоставляются различные возможности. В режиме ядра разрешено выполнение всех команд процессора и доступна системная область памяти и оборудование, тогда как в пользовательском режиме некоторые команды зап рещены, а системные области памяти недоступны.
Таблица
11.
Сравнительный
анализ реализаций
Реализация Объем контролируе защиты на мых данных уровне:
ния Собствен ной DLL Системной DLL Сетевого сервиса
API Драйвера файловой системы Транс портного драйвера
Драйвера Ndis.sys Промежу точного драйвера
Драйвера сетевого устройства
Слож ность реализа ции Низкая
Только данные само го приложения Данные приложений, Низкая использующих эту DLL Данные приложений, Высокая использующих сис темную DLL Зависит от сетевого Высокая сервиса Данные всех прило жений Данные приложений, использующих соот ветствующее сетевое API Данные приложений, использующих транспорт прило жений, взаимодейст вующих с др. транс порта напрямую Данные всех прило жений Данные приложений, использующих транспорты, привя занные снизу к этому промежуточному драйверу приложе ний, взаимодейст вующих с промежу точным драйвером напрямую Данные всех прило жений
средств защиты
Возмож ность встраивания защиты Да
Прозрач ность сти, предос защиты тавляемые
Да
-
Нет
Нет
-
Минималь ные Минималь ные Зависит от способа реализации Зависит от способа реализации Макси мальные Макси мальные
Высокая
Нет
Высокая
Да
Высокая
Да
Макси мальные
Высокая
Нет
Средняя
Да
Макси мальные Макси мальные
Средняя
Да
Макси мальные
Средства защиты уровня ядра могут использовать внутренние интерфейсы для взаимодействия с компонентами исполнительной системы. Модуль исполня ющийся в режиме ядра, может повышать или понижать текущий IRQL процессора, маскируя тем самым прерывания с равными или меньшими IRQL. Модуль защиты, исполняющийся в режиме пользователя, не может получить дос тупа к системным данным иначе, как, вызвав предоставляемые операционной систе мой функции, которые в свою очередь вызывают системные сервисы, осуществив пред варительную проверку параметров вызова. Следовательно, относительно этого фактора логично оценивать средства защиты по 2-х бальной системе: если средство защиты реализуется как компонент уровня ядра, то ему будут предоставлены максимальные возможности со стороны если сред ство защиты реализуется как компонент уровня пользователя, то Если для реализации защиты на уровне системной DLL или сетевого сервиса был разработан компонент, исполняющийся в режиме ядра, то это средство защиты также может использовать максимальные возможности, предоставляемые ОС. Для реализа ции защиты на уровне «родного» API в любом случае должен использоваться компо нент уровня ядра.
4.5.1.1. Зависимость способа реализации средства защиты от предъявляемых к нему требований Выбор конкретного способа реализации системы защиты зависит от первоначаль но предъявляемых к этой системе требований. В табл. перечислены наиболее часто возникающие требования к средству защи ты и рекомендуемый уровень его реализации в соответствии с табл. Из приведенного выше анализа сетевой архитектуры и сравнительной характери стики различных способов реализации средств защиты сетевой информации следует, что реализации на уровне промежуточных драйверов и драйверов сетевых устройств являются наиболее предпочтительными. Даже с учетом фактора реализации дополни тельной функциональности, несвойственной данному уровню относительно модели то есть, по сути дела, разработки некоторых функций драйвера транспорта. Разработка этих драйверов хорошо документирована, и ОС Windows NT предос тавляет возможность их внедрения в стек сетевых драйверов без необходимости мо дификации уже существующих компонентов. Они могут взаимодействовать с компо нентами исполнительной системы, ядром и HAL, получая тем самым максимальные возможности для реализации функций защиты. Эти драйверы способны контролиро вать весь сетевой трафик, через них проходят данные всех приложений, исполняю щихся в системе. В результате исследования сетевой архитектуры можно сделать следующие выводы: • Не на всех уровнях сетевой архитектуры операционная система Windows NT позволяет расширять свои возможности и, в частности, возможности по реали зации защиты сетевой информации.
• Даже там, где сетевая архитектура предоставляет возможность расширения своей функциональности, Microsoft не всегда документирует эту возможность. Это в первую очередь относится к драйверам файловых систем, разработка которых до недавнего времени вообще не была документирована, а сейчас соответству ющая документация предоставляется отдельно от MSDN и по достаточно высо кой цене. • Наибольшие возможности по расширению своей функциональности ОС пре доставляет на уровне промежуточных драйверов, располагающихся между драйверами транспортов и драйверами сетевых карт. Но в этом случае может возникнуть проблема реализации дополнительной функциональности в драй вере для корректной своей работы в сети. Эта проблема возникает в результа те того, что ОС позволяет безболезненно встраивать собственные модули за щиты только на довольно низком уровне относительно модели Поэтому средству защиты иногда требуется реализовывать функциональность, уже и м е ю щ у ю с я у сетевых компонентов более высокого уровня (относительно модели Таблица 12 Требования к системе безопасности Необходимо реализовать программу, обес печивающую безопасный обмен данными по компьютерной сети Необходимо обеспечить безопасный обмен данными по сети между приложениями, использующими конкретное сетевое API или контроль доступа к сетевому API
Рекомендуемый уровень реализа ции средства Реализация на уровне приложения и DLL
Реализация на уровне системной DLL, предоставляющей этот API, или на уровне "родного" API, или на уровне соответствующего драй вера файловой системы Необходимо обеспечить контроль доступа к Реализация на уровне сетевого сер виса (которая сводится к реализации сетевым сервисам защиты на уровне системной DLL или API) или реа лизация на уровне сетевых драйве ров для контроля адресов Необходимо обеспечить безопасный обмен Реализация на уровне драйвера данными по сети между приложениями, или драйвера транспорта использующими конкретный транспортный или промежуточного драйвера или протокол, или анализ трафика по этому про драйвера устройства токолу Необходимо обеспечить безопасный обмен Реализация на уровне драйвера данными по сети между любыми приложе ndis.sys или промежуточного драй вера или драйвера устройства ниями, или анализ всего трафика в сети
4.6. Особенности реализации NDIS-драйверов Среда NDIS и ранее уже описывались в разделе «Среда NDIS и NDIS драйверы». Хотя по требованиям переносимости должен пользо ваться только функциями, экспортируемыми средой NDIS, в реальных приложениях ничто не запрещает использовать функции ядра той ОС, под которую реализуется этот драйвер. Переносимость и поддержка многопроцессорности. Драйвер должен быть на писан на причем необходимый формат результирующего файла может быть сгенерирован только компиляторами фирмы Microsoft. должен избегать лю бой зависимости от типов данных, чей размер различается между платформами. Драй вер не должен вызывать никаких функций стандартных библиотек С, кроме функций обеспечиваемых NDIS. В режиме ядра не разрешены никакие операции с плавающей В многопроцессорной среде процессоры одновременно выполняют несколько ма шинных инструкций. И поэтому драйвер должен синхронизировать доступ к общим структурам данных, чтобы в то время, когда одна функция драйвера обрабатывала об щие данные, другая функция (или та же самая), исполняемая на другом процессоре, не попыталась изменить общие данные в тот же момент времени. Чтобы исключить эту проблему драйверы используют спин-блокировки. Уровни IRQL драйвера. Любая функция драйвера, вызы ваемая NDIS, исполняется с уровнем приоритета, определяемым операционной систе мой, и варьирующимся между LEVEL DIRQL. На пример, функция инициализации минипорта, функции останова, сброса и закрытия обычно исполняются с приоритетом PASSIVE_ LEVEL. Все другие функции NDISдрайвера исполняются на уровне IRQL меньшем или равном Про межуточный NDIS-драйвер или драйвер протокола никогда не исполняются на уровне DIRQL. Уровень приоритета потока драйвера влияет на то, какие функции NDIS библиоте ки он может вызывать. Некоторые функции могут вызываться только, если уровень приоритета другие если или ниже. Выгружаемый и уничтожаемый код драйвера. Функции драйвера, которые все гда выполняются с уровнем приоритета могут быть помечены как pageable (выгружаемые), используя макрос FUNCTION. При раз работке драйверов поощряется помечать код как выгружаемый где это возможно, ос вобождая тем самым системную область памяти для кода, который должен быть рези дентным. Функция драйвера, которая исполняется с приоритетом может быть помечена как выгружаемая, если она сама не вызывает или ее не вызывает любая функция, которая исполняется с приоритетом большим или равным (Например, если она не вызывает функцию, которая захватывает спин-блокировку, так как ее захват вызывает повышение уровня приоритета захваты вающего потока до уровня D I S P A T C H L E V E L ) .
Функция DriverEntry промежуточного драйвера должна быть определена как фун кция используя макрос Предполагается, что код, помеченный этим макросом, исполняется только один раз во время инициализа ции системы, в результате чего он присутствует в оперативной памяти только в это время. После того как функция DriverEntry вернет управление, код, помеченный как уничтожается из оперативной памяти. вызовов функций. Среда NDIS обеспечивает сериализацию вы зовов точек входа но при этом существует исключение. Начиная с некоторых версий ОС, появилось понятие десериализованного где по тре бованиям производительности полнодуплексного обмена данными, реализация сери ализации передачи пакетов протоколам возложена на разработчика минипорта.
Синхронизация NDIS предоставляет механизм спин-блокировок, который может быть использо ван, чтобы синхронизировать доступ к общим ресурсам между потоками, имеющими одинаковый приоритет. Если два потока исполняются с различными приоритетами, то NDIS обеспечивает механизм временного приоритета потока с более низ ким приоритетом. Любая функция драйвера, разделяющая ресурсы с процедурой ISR этого же драйвера, должна иметь возможность повышения своего IRQL до DIRQL, этот механизм обеспечивается NDIS. Также NDIS предоставляет механизм извещения, необходимый в том случае, если исполнение потока зависит от появления вне этого потока. Например, драйвер может нуждаться в механизме предупреждения об истечении какого-то периода време ни, чтобы он мог проверить свои устройства. Или драйвер сетевой карты может выпол нять периодический опрос сетевой карты. Этот механизм обеспечивают таймеры. (Spin Lock). Поток должен сначала захватить спин-блокиров ку, прежде чем пытаться получить доступ к защищаемым ресурсам. Спин-блокировка удерживает все потоки, кроме одного потока-владельца спин-блокировкой, от исполь зования ресурсов. Другие потоки ждут освобождения спин-блокировки. Сетевые драй веры должны минимизировать время захвата спин-блокировки. Функции для работы со спин-блокировками: VOID N d i s A l l o c a t e S p i n L o c k (IN VOID N d i s A c q u i r e S p i n L o c k ( I N VOID N d i s R e l e a s e S p i n L o c k ( I N VOID N d i s F r e e S p i n L o c k (IN Таймер. Драйвер создает таймер и ассоциирует с ним функцию, которая вызыва ется, когда истекает определенный период времени. Таймер может быть одноразовым (сработать один раз) или периодическим. Периодические таймеры срабатывают до тех пор, пока не будут сброшены. Таймеры создаются и инициализируются путем вызова функции NdisMInitialize Timer, и устанавливаются с помощью вызова NdisMSetTimer, или (если это периода-
ч е с к и й т а й м е р ) с п о м о щ ь ю вызова ф у н к ц и и Timer. Ф у н к ц и я сбрасывает таймер. С о б ы т и я (Events). События обеспечивают механизм синхронизации исполнения потоков. Один поток инициализирует событие с помощью вызова NdisInitializeEvent. Другой поток (или тот же самый), исполняемый с приоритетом PASSIVE_LEVEL, может вызвать функцию NdisWaitEvent, чтобы усыпиться до момента возникновения собы тия, либо до истечения определенного им промежутка времени (что произойдет быст рее). Событие устанавливается и сбрасывается с помощью функций NdisSetEvent и NdisResetEvent соответственно. ч
4.6.2. Структура Драйвер протокола выделяет ресурсы под заполняет их данными и посылает вниз следующему Драйверы сетевых карт выделяют ресур сы под NDIS-пакеты, чтобы поместить в них полученные из сети данные и передать наверх соответствующим драйверам. Иногда драйвер протокола размещает кет и передает его драйверу сетевой карты с требованием, чтобы драйвер сетевой кар ты скопировал полученные из сети данные в этот пакет. NDIS обеспечивает функции, размещающие и обрабатывающие структуры, из которых состоит Описатель является структурой содержащей, поми мо прочих, следующие поля (см. рис. 28): 1) закрытые области данных для драйвера минипорта и драйвера протокола; 2) флаги; 3) число физических страниц, содержащих пакет; 4) полную длину пакета; 5) указатели на описатели первого и последнего буфера пакета. Описатель буфера является структурой N D I S _ B U F F E R (в действительности, определяется как тип содержащей, помимо всего прочего, сле дующее: 1) начальный виртуальный адрес буфера; 2) смещение буфера относительно страницы; 3) длину буфера в байтах; 4) указатель на описатель следующего буфера (или NULL, если его нет). Основные функции для работы с и 1) NdisAllocatePacketPool размещает и инициализирует пространство для пула описателей пакетов; 2) NdisAllocatePacket размещает и инициализирует описатель пакета; 3) NdisReinitializePacket - удаляет все присоединенные буфера из пакета и иници ализирует его для повторного использования; 4) 5) - возвращает информацию о пакете; NdisFreePacket;
7) возвращает указатель, с помощью которого затем можно разместить описатели буферов, вызвав 8) описатель буфера; 9) NdisChainBufferAtBack (NdisChainBufferAtFront) - присоединяет описатель фера в хвост (в начало) цепи описателей буферов, присоединенных к пакету; 10) 12) буферов пакета; 13)
- возвращает указатель на буфер первый в цепи
Рис. 28. Структура
и
Завершающие функции Множество функций интерфейса верхнего уровня драйвера сетевой карты и фун кций интерфейса нижнего уровня драйвера протокола разработаны для поддержки асинхронных операций. Чтобы не тратить бесполезно такты процессора, ожидая в цикле завершения какой-нибудь длительной операции или сигнала от устройства,
сетевые драйверы полагаются на возможность управления большинством операций асинхронно. Асинхронный ввод/вывод поддерживается с помощью использования щих функций. Когда драйвер протокола вызывает NDIS, чтобы отправить пакет (что приводит в результате к вызову функции MiniportSend в драйвере сетевой карты), драй вер сетевой карты может попытаться завершить эту передачу немедленно и вернуть в качестве результата соответствующее значение статуса операции. Для синхронных операций вероятным значением статуса могут быть N D I S S T A T U S S U C C E S S (если операция завершилась успешно) и или FAILURE в случае неудачи. А может, тут же вернуть управление со значением статуса операции N D I S S T A T U S P E N D I N G . Но операция пакета в сеть может потребовать некоторого времени для ее завершения. За это время драйвер сетевой карты (или NDIS) поставит пакет в и будет ожидать до тех пор, пока сетевая карта не просигнализирует результат опера ции отправки. Функция MiniportSend драйвера сетевой карты может управлять этой операцией отправки асинхронно, путем возвращения значения статуса PENDING. Когда драйвер сетевой карты завершит отправку, он вызовет завершаю щую функцию передав в эту функцию указатель на отосланный Эта информация передается драйверу протокола, сигнализируя заверше ние операции отправки. Большинство операций драйвера, которые могут потребовать продолжительного времени для их завершения, поддерживают асинхронное выполнение с простой за в е р ш а ю щ е й ф у н к ц и е й . Такие ф у н к ц и и и м е ю т имена вида Наряду с функциями отправления и получения, завершающие функции доступны для установки и опроса параметров конфигурации, сброса устройств, индикации ста туса.
Точки входа промежуточного NDIS- драйвера Промежуточный NDIS-драйвер обычно экспортирует функции MiniportXxx на сво ем верхнем уровне и функции ProtocolXxx на своем нижнем уровне (см. рис. 29). Реже промежуточный драйвер может экспортировать MiniportXxx функции на своем верхнем уровне, а в своей нижней части предоставлять частный закрытый интерфейс нижележащему драйверу, не являющемуся Например, промежуточный драйвер может управлять сетевыми запросами ввода/вывода для устройства, соединенного с портом. Подобный промежуточный драйвер будет экспортировать ряд функций MiniportXxx, чтобы взаимодействовать с NDIS в своей верхней части, а в своей нижней части использовать стандартные па кеты запроса ввода/вывода (IRP), чтобы взаимодействовать с драйвером последова тельного устройства. Если реализуемый промежуточный драйвер должен поддерживать ориентиро ванный на соединение (Connection-Oriented) интерфейс нижнего уровня, либо под-
держивать работу с ND1SWAN, то необходимо реализовать как ряд дополнитель ных точек входа, так и изменить некоторые из описанных в этом разделе точек входа.
Сетевая карта Рис. 29. Расположение промежуточных
Точка входа DriverEntry Точка входа DriverEntry промежуточного драйвера должна по крайней мере: 1) в ы з в а т ь и сохранить возвращенный описатель Ndis 2) вызвать функцию чтобы зарегистрировать свои точки входа 3) вызвать функцию NdisRegisterProtocol, чтобы зарегистрировать свои точки вхо да (если драйвер привязывается к нижележащему
4) вызвать функцию чтобы информировать библиотеку NDIS о том, что определенные ранее интерфейсы нижнего и верхнего уровней для минипорта и протокола соответственно, принадлежат одному и тому же промежуточ ному драйверу. DriverEntry может инициализировать спин-блокировки для любых глобальных разделяемых ресурсов, размещаемых промежуточным драйвером.
Точки входа ProtocolXxx Возможные обязательные и необязательные функции протокольной части проме жуточного драйвера перечислены ниже (подробное их описание смотри в DDK): 1) ProtocoIBindAdapter - обязательная функция, вызываемая NDIS, чтобы попро сить промежуточный драйвер привязаться к нижележащей реальной или виртуальной сетевой карте, чье имя передано в качестве параметра этой функции. 2) обязательная функция, вызываемая NDIS, чтобы рыть соединение с нижележащей реальной или виртуальной сетевой картой, чье имя передано в качестве параметра этой функции. 3) обязательная функция. Если вызов функции промежуточным драйвером привел к возврату статуса PENDING, то в последствии будет вызвана функция ProtocoIOpenAdapterCompIete, чтобы завершить присоединение. 4) - обязательная функция. Если вызов функции N d i s C l o s e A d a p t e r п р о м е ж у т о ч н ы м д р а й в е р о м привел к возврату статуса S T A T U S P E N D I N G , т о в п о с л е д с т в и и будет в ы з в а н а ф у н к ц и я P r o t o c o l C l o s e AdapterComplete, чтобы завершить отсоединение. 5) ProtocoIReceive - обязательная функция. ProtocolReceive вызывается с указате лем на буфер lookahead, содержащий данные, полученные из сети. Если этот буфер содержит не весь сетевой пакет, то вызывает NdisTransferData с опи сателем пакета для того, чтобы получить оставшуюся часть сетевого пакета. Если ни жележащий драйвер вызвал NdisMIndicateReceive Packet для указания о получение сетевого пакета, то буфер lookahead, переданный ProtocolReceive, всегда будет содер жать весь сетевой пакет. 6) ProtocoIReceivePacket - необязательная функция. Обеспечивается, если про межуточный драйвер будет располагаться над драйвером сетевой карты, который пе редает сразу массив указателей на один или более описателей пакетов, или если драй вер сетевой карты передает дополнительные данные вместе с индикацией получения с помощью вызова NdisMIndicateReceivePacket. Если разработчик неуверен в среде, в которой будет исполняться его промежуточный драйвер, то эта функция должна быть обеспечена, потому что тогда промежуточный драйвер достигнет большей производи тельности, если нижележащий драйвер сетевой карты производит мультипакетную индикацию получения.
7) ProtocolReceiveComplete это обязательная функция. Она вызывается, если обработка какого-либо из пакетов, попавших перед этим в ProtocoIReceive, была отло жена. 8) ProtocolTransferDataComplete - это обязательная функция, если ProtocoIReceive когда-либо вызывает NdisTransferData. Если предыдущий вызов NdisTransferData с просьбой скопировать о с т а в ш у ю с я часть полученного пакета вернул статус то ProtocolTransferDataComplete вызовется, когда опера ция передачи оставшейся части будет завершена. 9) ProtocolResetComplete - это обязательная функция. Она вызывается, если опе рация сброса, начатая с п о м о щ ь ю вызова функции NdisReset, вернувшей статус была завершена. Обычно промежуточные драйверы не вызывают NdisReset, но драйверы выше их могут вызвать эту функцию, таким обра зом, промежуточный драйвер должен суметь передать такой запрос нижележащим 10) ProtocolRequestComplete это обязательная функция. Она вызывается по за вершении операции получения/установки параметров, начатой с помощью вызова функции NdisRequest, вернувшей статус 11) ProtocolSendComplete обязательная функция. Она вызывается для каж дого пакета, переданного с помощью вызова функции NdisSend, вернувшей статус операции отправки Если был отослан сразу массив па кетов путем вызова NdisSendPackets, то ProtocolSendComplete будет вызвана для каждого пакета из массива. Промежуточный драйвер может определить оконча тельный статус операции отправки только из параметра статуса у функции ProtocolSendComplete. 12) ProtocolStatus обязательная функция. Она вызывается библиотекой NDIS для сообщения об изменении статуса, инициированного нижележащим драйвером се тевой карты. 13) - обязательная функция. Она вызывается библиоте кой NDIS, чтобы сигнализировать, что изменение статуса, о котором перед этим сооб щалось в ProtocolStatus, теперь завершилось. 14) - это обязательная функция. NDIS вызывает ProtocoIPn PEvent, чтобы сигнализировать о событии Plug and или о событии управления питанием. 15) ProtocolUnload - это необязательная функция. NDIS вызывает в ответ на требование пользователя удалить промежуточный драйвер. выполняет определенные драйвером операции удаления.
Точки входа MiniportXxx Возможные обязательные и необязательные функции минипортовой части проме жуточного драйвера перечислены ниже (подробное их описание смотри в DDK):
1) MiniportHalt эта функция вызывается библиотекой NDIS, например, когда нижележащая сетевая карта зависла, и NDIS останавливает драйвер сетевой карты, или, например, когда ОС выполняет контролируемое закрытие системы. 2) эта функция вызывается в результате вызова ным драйвером функции чтобы инициализировать минипортовые операции для инициализируемой виртуальной сетевой карты. 3) - эта функция получает запросы вида организованные (или просто передаваемые далее) вышележащим драйвером, вызвав шем NdisRequest с типом запроса 4) MiniportReset. NDIS может вызвать эту функцию драйвера по приказу вышележащего драйвера протокола, вызвавшего NdisReset. Обычно, од нако, драйвер протокола не инициирует сброс. NDIS, главным образом, инициирует сброс нижележащего драйвера сетевой карты и вызывает функции промежуточного драйвера ProtocoIStatus и для того, чтобы информировать про межуточный драйвер о том, что нижележащий сбрасывает свою сетевую карту. 5) Эта функция обрабатывает запросы вида сделанные (или просто передаваемые далее) вышележащим драйвером, который выз вал NdisRequest с типом запроса NdisRequestSet Information. 6) MiniportSend. NDIS вызывает эту функцию для передачи одного пакета для н и ж е л е ж а щ е г о д р а й в е р а с е т е в о й карты (или д р а й в е р а у с т р о й с т в а ) . Ф у н к ц и я MiniportSend (или функция MiniportWanSend) требуется, если промежуточный драй вер не обеспечил функцию Но лучше, чтобы всегда обеспечи валась функция MiniportSendPackets, чем функция MiniportSend (если только проме жуточный драйвер не располагается всегда между драйверами, передающими по одному пакету за раз, или, если он не привязывается к нижележащему WAN NIC драйверу). 7) MiniportSendPackets. Эта функция получает массив из одного или более ука зателей на описатели пакетов для передачи в сеть. Лучше, чтобы каждый промежуточ ный драйвер обеспечивал функцию MiniportSendPackets, чем MiniportSend, если толь ко он не привязывается к нижележащему WAN NIC драйверу, и должен при этом обес печить функцию MiniportWanSend. Функция MiniportSendPackets обеспечивает луч шую производительность в независимости от того, располагается ли промежуточный драйвер над драйвером сетевой карты, который может передавать по несколько паке тов за раз, или только по одному пакету. И вне зависимости от того, располагается ли промежуточный драйвер под драйвером протокола, отправляющим по несколько паке тов за раз, или только по одному пакету. 8) MiniportTransferData. Эта функция вызывается, чтобы передать оставшу юся часть полученного из сети пакета, ранее представленного в буфере lookahead, переданном промежуточным драйвером в функцию Receive. Этот пакет может быть к о н в е р т и р о в а н н ы м пакетом, п о л у ч е н н ы м перед этим в
функции п р о м е ж у т о ч н о г о драйвера ProtocoIReceive или ProtocoIReceivePacket. Ф у н к ц и я MiniportTransferData требуется, если промежуточный драйвер представ ляет в ы ш е л е ж а щ и м драйверам полученные из сети пакеты путем вызова л ю б о й зависимой от среды функции Receive, кроме NdisMWan IndicateReceive. Если п р о м е ж у т о ч н ы й драйвер всегда представляет пакеты пу тем вызова NdisMIndicateReceivePacket, то ему не нужно обеспечивать функцию MiniportTransferData. 9) Эта функция получает возвращенный описатель паке та, который перед этим был представлен вышележащему драйверу путем вызова NdisMIndicateReceivePacket, тем самым возвращается контроль над ресурсами, пред ставленными вышележащему драйверу. После того как каждое такое представление было обработано вышележащим драйвером, описатель пакета, размещенный проме жуточным драйвером, и ресурсы, которые он описывает, будут возвращены функции MiniportReturnPacket. Функция не нужна, если промежуточный драйвер всегда представляет пакеты наверх путем вызова зависящей от среды функ ции Receive, или, если он всегда устанавливает статус в блоке дан ных ООВ, ассоциированном с каждым описателем пакета, в RESOURCES, прежде чем вызвать NdisMIndicateReceivePacket. 10) MiniportCheckForHang. Эта функция вызывается через интервал времени, определенный библиотекой NDIS или промежуточным драйвером. Обычно, промежу точным драйверам не обязательно реализовать функцию MiniportCheckForHang, му что подробные драйверы не могут удостовериться, что нижележащая сетевая карта зависла. Возможно, промежуточному драйверу следует обеспечить эту функцию, если он располагается над драйвером, не поддерживающим среду NDIS, чье состояние не доступно библиотеке NDIS.
4.7. Пример реализации драйвера шифрования сетевых пакетов В качестве основы драйвера, шифрующего сетевые пакеты, может быть использо ван пример промежуточного драйвера с сервера Microsoft (для NT 4.0) или промежуточный драйвер из D D K (NtDDK\src\network\ ndis\passthru, для Win2000). Этот NDIS-драйвер промежуточного уровня разработан так, чтобы распо лагаться между транспортным драйвером и драйвером сетевой карты (рис. 30). Един ственное что он делает - это получает пакеты от драйвера сетевой карты и передает их драйверу транспорта, и наоборот. Используя Network Control Panel Application (NCPA), можно привязать протокол TCP/IP к виртуальному адаптеру, создаваемому драйвером ImSamp, а все остальные привязки заблокировать. Для драйвера TCP/IP промежуточный драйвер ImSamp выг лядит как драйвер виртуальной сетевой карты. А для реальной сетевой карты Ethernet драйвер ImSamp выглядит как драйвер протокола.
Если разрабатываемый драйвер шифрования не реализует функции сетевого уров ня модели то его нельзя будет использоваться в сети, где в качестве средств пост роения объединенных сетей используются маршрутизаторы, работающие на сетевом уровне. Так как такой драйвер не рассчитан на то, что выпущенный зашифрованный пакет может быть фрагментирован, поэтому его можно использовать только в сегмен те сети до маршрутизаторов. Пользовательский режим Режим ядра С Е Р В И С Ы С И С Т Е М Ы ВВОДА/ВЫВОДА
Диспетчер ввода/ вывода
Драйвер транспорта TCP/IP
драйвер шифрования
NDIS Драйвер сетевой карты
Рис. 30. Расположение драйвера шифрования Как уже отмечалось выше, спецификация NDIS позволяет добавлять заголовки (или хвост) к без необходимости его перекопирования, во время переда чи пакета по стеку сетевых драйверов. Таким образом, промежуточный драйвер шиф рования может зашифровать пакеты двумя способами: Путем модификации исходного переданного драйвером транспор та, с добавлением к нему (при необходимости) новых NDIS- буферов. 2) Путем создания собственного NDIS-пакета с копированием в него исходного NDIS-пакета, переданного драйвером транспорта, а затем уже модификации ного пакета.
Первый способ использовать не рекомендуется, так как он чреват ошибками использования библиотекой NDIS зарезервированных областей в пакете (при этом они будут перезаписываться). Но в простейших случаях этот способ работает. При реализации первого способа важно помнить, что после завершения операции отправки зашифрованного пакета в сеть, в функции ProtocolSendComplete перед воз вращением описателя пакета драйверу транспорта необходимо преобразовать пакет в исходное состояние. То есть расшифровать его и отделить все добавленные фера. Драйвер может осуществлять зашифрование всех данных пакета, кроме Ethernet и IP-заголовков, IP-заголовок, при этом, можно создать свой, а первоначальный зашиф ровать. Ключ шифрования обычно передается в драйвер шифрования из кода пользова тельского режима (библиотеки DLL или приложения) с помощью вызова функции Обмен ключами между компьютерами можно, например, осуществить по схеме реализовав приложение (или библиотеку DLL), использую щее интерфейс API Если зашифрованный пакет имеет длину большую, чем первоначальный пакет, то для того, чтобы драйвер протокола не мог послать пакет такой длиной, что после до бавления к нему необходимого числа байт промежуточным драйвером, драйвер сете вой карты (или сама сетевая карта) обрезал бы его (например, если длина преобразо ванного пакета вместе с Ethernet заголовком - 1514 байт), то при обработке запроса от драйвера протокола на максимально возможную длину пакета, значение, которое в действительности получено от драйвера сетевой карты, уменьшается на необходимое число байт. Для определения функций, в код которых необходимо вставить зашифрование или расшифрование, рассмотрим функции интерфейсов нижнего и верхнего уровней, под держиваемые средой NDIS, которые участвуют в приеме и отправлении пакетов, и через которые эти пакеты проходят непосредственно: Отправление: 1) MiniportSendPackets; 2) MiniportSend; 3) ProtocolSendComplete. Получение: 1) ProtocoIReceive; 2) ProtocoIReceivePacket; 3) ProtocolTransferDataComplete; 4) MiniportReturnPacket; 5) MiniportTransferData-
Рассмотрим эти функции подробнее: 1) MiniportSendPackets функция интерфейса верхнего уровня. Она получает не сколько указателей на описатели от драйвера транспорта. Во время ини циализации, перед тем, как отправлять пакеты, драйвер транспорта опрашивает пара метры конфигурации и характеристики нижележащего драйвера, в том числе и то, сколь ко пакетов одновременно он может передавать. Эта функция должна, по меньшей мере, заменить первоначальные описатели NDIS-пакетов, полученные от драйвера транс порта, на свои собственные, сохранив первоначальные описатели, а затем отослать обновленные описатели NDIS-пакетов ниже - драйверу сетевой карты с помощью функции Ndis SendPackets. 2) MiniportSend функция интерфейса верхнего уровня, предназначенная для пе редачи но одному пакету нижележащему драйверу и требуется только в том случае, когда промежуточный драйвер не обеспечивает функцию MiniportSendPackets. 3) - функция интерфейса нижнего уровня. Она является за вершающей функцией для функций NdisSendPackets и NdisSend. Она получает в каче стве параметра статус завершения операции отправления пакета и описатель этого отправленного пакета (обновленный описатель). Эта функция, по меньшей мере, дол жна просигнализировать драйверу транспорта о завершении операции отправления, передав ему первоначальный описатель отправленного пакета. 4) ProtocolReceive - функция интерфейса нижнего уровня. Вызывается NDIS, пос ле того как драйвер сетевой карты передал выше полученный пакет (или его часть). По меньшей мере, эта функция должна скопировать предоставленные ей данные во вновь созданный и передать его выше драйверу транспорта (обычно описатель этого пакета и его буфера берутся, соответственно, из очереди NDIS-пакетов и очере ди буферов, созданных во время инициализации драйвера). Если драйвер сетевой кар ты предоставил не весь пакет, то необходимо вызвать функцию NdisTransferData для получения оставшейся части пакета. 5) ProtocolReceivePacket - это необязательная функция интерфейса нижнего уров ня. Она вовлекается в том случае, если промежуточный драйвер располагается над драйвером сетевой карты, поддерживающим многопакетное предоставление, и этот драйвер сетевой карты вызвал функцию NdisMIndicateReceivePacket либо с множе ством пакетов, либо с одним пакетом, имеющим дополнительную информацию. 6) ferData - это функция интерфейса верхнего уровня. Она вызы вается для того, чтобы передать вышележащему драйверу транспорта оставшуюся часть полученного от драйвера сетевой карты пакета, ранее не переданную в драй вер транспорта с помощью функции Эта функция требу ется только в том случае, если промежуточный драйвер сигнализирует о получен ных пакетах вышележащим драйверам путем вызова зависящих от типа сетевой кар ты функций NdisMXxxIndicateReceive (вместо Ххх могут быть Ethernet, TR, Fddi). Если же промежуточный драйвер всегда передает полученные пакеты наверх с по-
мощью вызова функции NdisMIndicateReceivePacket, то ему не надо обеспечивать функцию MiniportTransferData. 7) ProtocolTransferDataComplete - функция интерфейса нижнего уровня и являет ся завершающей функцией для NdisTransferData. Она вызывается библиотекой NDIS, когда драйвер сетевой карты завершит операцию передачи оставшейся части пакета, ранее не переданной в ProtocoIReceive. Эта функция, по меньшей мере, должна пере дать драйверу транспорта начало пакета, полученного ранее в функцию ProtocoIReceive, вместе с остатком, полученным только что. 8) MiniportReturnPacket - функция интерфейса верхнего уровня и вызывается биб лиотекой NDIS, чтобы вернуть который был ранее создан и предоставлен драйверу транспорта путем вызова функции NdisMIndicate ReceivePacket в функции ProtocoIReceive или ProtocoIReceivePacket или ProtocolTransferDataComplete. Эта фун кция должна, по меньшей мере, разобрать размещенный промежуточным драйвером NDIS-пакет и вернуть сам пакет и его буфера в соответствующие очереди, чтобы их можно было использовать в дальнейшем. При изучении этих функций видно, что процедуру зашифрования надо вставить в функцию либо если ее нет, то в а расшифрования в ф у н к ц и и ProtocoIReceive, P r o t o c o l T r a n s f e r D a t a C o m p l e t e , ProtocoIReceivePacket, ProtocolSendComplete. Ниже следуют объяснения этого утверждения. Зашифрование требуется перед тем, как пакеты передадутся драйверу сетевой карты для о т п р а в к и в сеть, а это в п р о м е ж у т о ч н о м д р а й в е р е п р о и с х о д и т в функции MiniportSendPackets, либо если ее нет, то в MiniportSend, в них и надо реализовывать процедуру зашифрования. С расшифрованием дела обстоят гораздо сложнее, так как в процессе получения пакета из сети в промежуточном драйвере участвуют сразу несколько функций: ProtocoIReceive, ProtocolTransferDataComplete, ProtocoIReceivePacket, Minipor MiniportTransferData. Так как в функцию ProtocoIReceive может попасть не весь пакет, а только его нача ло, то расшифрование в этой функции осуществляется только в том случае, если в нее был передан весь пакет. Если в эту функцию попало только начало пакета, то будет вызвана функция NdisTransferData, которая заставит драйвер сетевой карты передать оставшуюся часть пакета, и тогда его расшифрование будет осуществляться в завер ш а ю щ е й ф у н к ц и и для ф у н к ц и и N d i s T r a n s f e r D a t a - ф у н к ц и и P r o t o c o I T r a n s f e r которая уже будет обладать полным пакетом. ProtocoIReceivePacket необязательная функция, нужная только в том случае, если есть уверенность, что промежуточный драйвер будет располагаться над драй вером сетевой карты, имеющим способность предоставлять сразу несколько паке тов, полученных из сети. Если такой уверенности нет, то эту функцию можно не регистрировать, то есть при инициализации обнулить соответствующий указатель. Если эта функция все же реализуется, то она должна содержать процедуру рас шифрования.
Необходимость расшифрования пакетов (в случае зашифрования первоначально го пакета без его перекопирования), драйверу транспорта, в функции объясняется следующим образом. При отправлении данных из прикладной программы в сеть, переданные ею данные, проходя по стеку сетевых драй веров, не пере копируются в новые участки памяти, просто к ним добавляются разные сетевые заголовки, поэтому при з а ш и ф р о в а н и и данных пакета в функции MiniportSendPackets или MiniportSend (если не создавался новый пакет и первоначаль ный не перекопировался в него) шифруются на самом деле данные в буфере, принад лежащем приложению, а оно вовсе может и не рассчитывать на это. Может даже про изойти следующее: если ключ шифрования не изменять, то повторной посылке данных в сеть, эти данные, наоборот, расшифруются и пойдут по сети в открытом виде. Чтобы избежать этой неприятности, необходимо, перед возвращением отправ ленного пакета драйверу транспорта со статусом операции отправления, расшифро вать его. Функция MiniportTransferData предназначена для того, чтобы передавать драйве ру транспорта оставшуюся часть полученного от драйвера сетевой карты ра нее не переданную в драйвер транспорта с помощью функции NdisMXxxIndicateReceive. Но если промежуточный драйвер всегда передает полученные пакеты наверх с помо щью вызова функции NdisMIndicateReceivePacket, то ему не надо обеспечивать функ цию MiniportTransferData. При разработке драйвера шифрования важно помнить, что цепь в не всегда точно соответствует сетевым заголовкам (Ethernet, IP, NDIS-пакеты, передаваемые из драйвера протокола, например ТСРЯР, в промежуточ ный драйвер для отправки, могут иметь очень разнообразную структуру, то есть раз ное количество NDIS-буферов и их содержимое. Вот лишь некоторые примеры (смот ри рис. которые удалось пронаблюдать с помощью программы-отладчика Soft Ice. (Разделение означает различные заметьте, что Ethernet заголовок уже пристроен к пакету драйвером протокола TCP/IP.) При разборе пакета перед зашифрованием (чтобы определить, например, следует ли его шифровать) нужно учитывать, что данные пользователя и сетевые заголовки могут находиться в разных участках памяти, а иногда и вместе (в пакетах протокола При разработке драйвера шифрования полезно использовать анализатор про токолов, например, Network Monitor, наблюдая с его помощью содержимое пере хваченных пакетов. Если его драйвер-перехватчик, под названием привя зать к реальной сетевой карте, то перехваченные им пакеты будут зашифрованны ми. Если же его привязать к виртуальной сетевой карте, создаваемой промежуточ ным драйвером шифрования, то перехваченные им пакеты будут уже р а с ш и ф р о ванными.
IP
DP
данные
IP
DP
данные
IP
UDP
данные
данные
ARP
TCP
Ethernet
данные
данные
IP
Структуры
TCP
данные
Общие вопросы обеспечения безопасности в операционной среде Windows NT/2000 5.1. Механизм идентификации и аутентификации в ОС Windows Общее описание При входе в систему пользователь передает в с и с т е м н у ю ф у н к ц и ю LogonUser с в о е имя, пароль и имя р а б о ч е й станции или д о м е н а , в котором д а н н ы й п о л ь з о ватель з а р е г и с т р и р о в а н . Если пользователь у с п е ш н о и д е н т и ф и ц и р о в а н , функ ция LogonUser в о з в р а щ а е т указатель на маркер д о с т у п а пользователя, который в д а л ь н е й ш е м и с п о л ь з у е т с я при л ю б о м его о б р а щ е н и и к з а щ и щ е н н ы м объек там с и с т е м ы . Механизм идентификации и аутентификации пользователя в ОС Windows NT реализуется с п е ц и а л ь н ы м п р о ц е с с о м Winlogon, который активизируется на на чальном этапе загрузки ОС и остается активным на протяжении всего периода ее функционирования. Я д р о операционной системы регулярно проверяет состояние данного процесса и в случае его аварийного завершения происходит аварийное завершение работы всей операционной системы. П о м и м о идентификации пользо вателя Winlogon реализует целый ряд других функций, таких, как переключение рабочих полей (desktop), активизация х р а н и т е л е й экрана, а также ряд сетевых функций. П р о ц е с с Winlogon состоит из следующих модулей: • ядра процесса • библиотеки (Graphic Identification aNd Autentication - графической библиотеки идентификации и аутентификации) - динамической библиоте ки функций, используемых для идентификации пользователя (идентификации пользователя на рабочей станции); • библиотек сетевой поддержки (Network Provider DLLs), р е а л и з у ю щ и х «уда л е н н у ю » и д е н т и ф и к а ц и ю пользователей ( и д е н т и ф и к а ц и ю п о л ь з о в а т е л е й , о б р а щ а ю щ и х с я к ресурсам сервера через сеть). Библиотека и библиотеки сетевой поддержки являются з а м е н я е м ы м и компонентами процесса Winlogon. Конфигурация библиотек сетевой поддержки определяется протоколами и видами сервиса поддерживаемой сети, конфигура ция библиотеки G I N A определяется требованиями к механизму л о к а л ь н о й иден тификации.
5.2. Основные сведения о процессе Winlogon и его состояниях В каждый момент времени Winlogon может находиться в одном из следующих со стояний:
Рис. 32. Временная диаграмма процессов аутентификации Когда пользователь еще не вошел в систему, Winlogon находится в состоянии 1, пользователю предлагается идентифицировать себя и предоставить подтверждающую информацию (в стандартной конфигурации - пароль). Если информация, введенная пользователем, дает ему право входа в систему, активизируется оболочка системы (как правило, Program Manager) и Winlogon переключается в состояние 2. Хотя в состоянии 1 ни один пользователь не может непосредственно взаимодей ствовать с системой, в случае, если на рабочей станции запущен Server Service, пользо ватели могут обращаться к ресурсам системы через сеть.
Когда пользователь вошел в систему, Winlogon находится в состоянии 2. В этом состоянии пользователь может прекратить работу, выйдя из системы, или заблокиро вать рабочую станцию. В первом случае завершает все процессы, связанные с завер шающимся сеансом, и переключается в состояние Во втором случае Winlogon выво дит на экран сообщение о том, что рабочая станция заблокирована и переключается в состояние 3. В состоянии 3 Winlogon выводит экран приглашение пользователю идентифи цировать себя и разблокировать рабочую станцию. Это может сделать либо заблоки ровавший ее пользователь, либо администратор. В первом случае система возвраща ется в то состояние, в котором находилась непосредственно и пе реключается в состояние 2. Во втором случае все процессы, связанные с текущим се ансом, завершаются, и Winlogon переключается в состояние 1. Когда рабочая станция заблокирована, фоновые процессы, запущенные пользова телем до блокировки, продолжают
5.3. Протокол взаимодействия процесса Winlogon и библиотеки GINA Сразу после загрузки Winlogon инициализирует GINA, вызывая последовательно ее функции WlxNegotiate и Рабочая станция переходит в состояние «Пользователь не вошел в систему». Когда пользователь собирается войти в систему с помощью комбинации клавиш Ctrl-Alt-Del, Winlogon в ы з ы в а е т ф у н к ц и ю W l x L o g g e d O u t S a s библиотеки G I N A . WlxLoggedOutSas осуществляет попытку входа в систему, вызывая системную функ цию LogonUser. В зависимости от информации, введенной пользователем, GINA воз вращает процессу Winlogon одно из следующих значений: •
пользователь вошел в систему. Получив это зна чение, Winlogon вызывает функцию библиотеки GINA, которая загружает индивидуальную оболочку пользователя; • - пользователь не смог войти в систему. Состояние системы не изменяется; • WLX_SAS_ACTION_SHUTDOWN - пользователь потребовал завершить работу системы. Эта возможность может быть отключена (см. выше). Получив данное возвращаемое значение, Winlogon последовательно вызывает функции библио теки GINA WlxLogoff и Если пользователь нажал комбинацию Ctrl-Alt-Del, уже войдя в систему, Winlogon вызывает функцию WlxLoggedOnSas. GINA выводит на экран диалоговое окно и, в зависимости от решения пользователя, выполняет следующие действия: • если пользователь решил не предпринимать никаких действий, GINA возвращает в Winlogon значение Winlogon возвращает систему в то же состояние, в котором она была до нажатия комбинации
•
если пользователь желает просмотреть список активных процессов, воз вращает значение Winlogon возвращает систему в состояние, в котором она была до нажатия комбинации и активи зирует процесс Task если пользователь желает заблокировать рабочую станцию, возвращает значение Winlogon блокирует систему; • если пользователь желает выйти из системы, G I N A возвращает значение Winlogon в ответ вызывает функцию GINA WlxLogoff; • если пользователь желает завершить работу с компьютером, GINA возвращает значение Winlogon последовательно вызыва ет функции GINA WlxLogoff и • если пользователь желает перезагрузить компьютер, GINA возвращает значе ние Winlogon последовательно вы зывает функции GINA WlxLogoff и По окончании выгрузки систе мы компьютер автоматически перезагружается; • если пользователь желает закончить работу с компьютером и выключить его, GINA возвращает значение Winlogon последовательно вызывает функции GINA WlxLogoff и По окончании выгрузки системы компьютер автоматически вык лючается. Если аппаратная часть компьютера не допускает программного от ключения питания, данное возвращаемое значение имеет тот же эффект, что и SHUTDOWN; • если пользователь желает изменить свой пароль, GINA выводит на экран соот ветствующее диалоговое окно, по окончании ввода пользователем нового паро ля вызывает функцию и затем возвращает в Winlogon значение CHANGED. Когда рабочая станция заблокирована, а пользователь нажал комбинацию Ctrl-AltDel, Winlogon вызывает функцию GINA WlxWkstaLockedSas. GINA запрашивает у пользователя параметры идентификации и проверяет их. В зависимости от результата проверки GINA возвращает одно из следующих значений: • - разблокировать рабочую станцию; • - принудительный выход из системы с последующим входом в систему администратора; • WLX_NO_ACTION - рабочая станция остается заблокированной. Если пользователь вошел в систему и один из процессов вызывает системную фун кцию ExitWindowsEx, Winlogon в зависимости от параметров ExitWindowsEx вызывает либо WlxLogoff, либо последовательно WlxLogoff и WlxShutdown. При этом, соответ ственно, либо пользователь выходит из системы, либо система завершает работу. Если GINA получает от пользователя нестандартную SAS, она вызывает функцию Winlogon после чего Winlogon вызывает одну из вышеперечисленных функций GINA, в зависимости от контекста, в котором была получена SAS.
Для изучения процесса идентификации и аутентификации можно использовать приводимый ниже модуль (DLL), который является «переходником» между и стандартной MSGINA. Прототипы экспортируемых MSGINA.DLL описаны в файле winwlx.h стандартной поставки MS SDK.
#include #include
<io.h>
int HINSTANCE hMSGinaDLL HINSTANCE HANDLE pWlxFuncs t y p e d e f BOOL WINAPI DWORD, t y p e d e f BOOL WINAPI *WLXINITIALIZE LPWSTR, HANDLE, PVOID, PVOID, PVOID t y p e d e f VOID WINAPI PVOID typedef int WINAPI PVOID, DWORD, PDWORD, PHANDLE, t y p e d e f BOOL WINAPI PVOID, PWSTR, PWSTR, PVOID typedef int WINAPI PVOID, DWORD, PVOID t y p e d e f BOOL WINAPI t y p e d e f VOID WINAPI PVOID typedef int WINAPI PVOID, DWORD t y p e d e f BOOL WINAPI PVOID t y p e d e f VOID WINAPI PVOID t y p e d e f VOID WINAPI PVOID, DWORD WLXNEGOTIATE g _ l p W l x N e g o t i a t e NULL; WLXINITIALIZE g _ l p W l x I n i t i a l i z e NULL; WLXDISPLAYSASNOTICE g _ l p W l x D i s p l a y S A S N o t i c e WLXLOGGEDOUTSAS NULL; WLXACTIVATEUSERSHELL g _ l p W l x A c t i v a t e U s e r S h e l l WLXLOGGEDONSAS NULL; WLXISLOCKOK NULL; WLXDISPLAYLOCKEDNOTICE
g
NULL; NULL;
NULL;
WLXWKSTALOCKEDSAS WLXISLOGOFFOK WLXLOGOFF WLXSHUTDOWN BOOL hMSGinaDLL i f (hMSGinaDLL return
NULL; NULL; NULL; NULL;
NULL)
FALSE; Original Library WLXNEGOTIATE
g_lpWlxNegotiate
WLXINITIALIZE hMSGinaDLL, g_lpWlxDisplaySASNotice hMSGinaDLL,
(WLXDISPLAYSASNOTICE WLXLOGGEDOUTSAS
hMSGinaDLL, g_lpWlxActivateUserShell
(WLXACTIVATEUSERSHELL WLXLOGGEDONSAS
hMSGinaDLL, WLXISLOCKOK hMSGinaDLL, WLXDISPLAYLOCKEDNOTICE hMSGinaDLL, g_lpWlxWkstaLockedSAS hMSGinaDLL, hMSGinaDLL, g_lpWlxLogoff hMSGinaDLL,
WLXWKSTALOCKEDSAS
WLXLOGOFF WLXSHUTDOWN
hMSGinaDLL, if return
FALSE;
if return
FALSE;
if return
FALSE;
if return
FALSE;
if return
FALSE;
if return
FALSE;
if return if return
FALSE;
if return if return if return if
FALSE;
return
FALSE;
("All return
Attach
TRUE;
BOOL WINAPI
DWORD
LPVOID
dwReason case hlnstance if return
case break; break;
return
BOOL WINAPI
BOOL r e s
PDWORD
FALSE;
MessageBox res
g
pdwDllVersion)
BOOL WINAPI LPWSTR l p W i n s t a , HANDLE PVOID p v R e s e r v e d , PVOID pWlxContext
BOOL r e s
FALSE;
res pWinlogonFunctions, return
VOID
hWlx,
pvReserved,
res;
WINAPI
pContext)
MessageBox
i n t WINAPI PVOID p W l x C o n t e x t , DWORD d w S a s T y p e ,
C o n t e x t a s s o c i a t e d w i t h t h i s window I n d i c a t e s t h e t y p e o f SAS t h a t o c c u r r e d ID a s s o c i a t e d with c u r r e n t logon s e s s i o n . pLogonSid, SID i s u n i q u e t o t h e c u r r e n t l o g o n s e s s i o n . PDWORD p d w O p t i o n s , Options: load profile, etc. PHANDLE Token
pProfile int
Password i n f o r m a t i o n t o P o i n t t o one o f t h e
other
network p r o v i d e r s structures.
FILE
*out;
UCHAR res pLogonSid, pProfile (res
else
for break;
out)
return
BOOL WINAPI PVOID PWSTR PWSTR p s z M p r L o g o n S c r i p t , PVOID BOOL r e s
FALSE;
res pszDesktop, pszMprLogonScript, pEnvironment return
res
i n t WINAPI PVOID p W l x C o n t e x t , DWORD PVOID p R e s e r v e d int
if res
dwSasType,
return
BOOL
WINAPI
BOOL res return
pWlxContext)
VOID
WINAPI
(PVOID
i n t WINAPI
pWlxContext)
PVOID p W l x C o n t e x t ,
DWORD
int
res return BOOL WINAPI BOOL
PVOID p W l x C o n t e x t
res;
res return
VOID WINAPI
VOID WINAPI
WlxLogoff(
PVOID p W l x C o n t e x t
PVOID
DWORD
Файл экспорта LIBRARY DESCRIPTION
NT Logon"
EXPORTS WlxNegotiate Wlxlnitialize WlxDisplaySASNotice WlxActivateUserShell WlxLoggedOnSAS
WlxIsLockOk
WlxShutdown Файл xgina.h #ifndef #define e x t e r n HINSTANCE e x t e r n HANDLE extern typedef
struct
BOOL BOOL HANDLE PWSTR TR PWSTR SYSTEMTIME
hDllInstance; hGlobalWlx;
_Globals
hUserToken;
timeOfLogin;
#endif Для активизации модуля xgina.dll необходимо создать ключ реестра (например, при помощи Regini с использованием следующего ini файла):
NT\CurrentVersion\Winlogon c:\xgina.dll Вместо необходимо указать реальный путь, либо поместить библиоте ку по заданному пути. Процесс ИА регулируется следующими ключами реестра \Registry\Machine\Software\Microsoft\Windows Название
Значение но умолчанию
Значение
AutoAdminLogon
0/1
DefaultDomain-
имя домена
пароль
0
нет
DontDisplayLast-
имя пользова имя последне теля го успешно вошедшего в систему поль зователя 0
LegalNoticeCaption
строка заго ловка
нет
LegalNoticeText
строка текста сообщения
нет
DefaultUserName
1
Содержание Определяет необходимость автоматического входа в сис тему с именем Default UserName и паролем De faultPassword Определяет имя домена, для которого был произведен последний успешный вход в систему Определяет пароль для поль зователя, указанного в De faultUserName и использует ся при входе в систему по умолчанию Если определены значения AutoAdminLogon и DefaultPassword, то это имя исполь зуется для входа в систему по умолчанию
Определяет заголовок ального сообщения пользо вателю Определяет текст специаль ного сообщения пользова телю При установленном в 1 значении содержимое фай ла A U T O E X E C . B A T анали зируется при входе в Win dows NT
Название
Значение 0/1
0/1 outLogon
ReportBootOk
0/1
Shell
Имена испол няемых при ложений
System
Исполняемые имена
Taskman
Исполняемые имена
Исполняемые имена
Значение по Содержание умолчанию 0 для Windows Если этот ключ установлен в NT Server, 1 пользователь может выби для Windows рать действия Shutdown и NT Worksta из меню ShutDown tion и Logoff. Иначе кнопка Power Off не появляется 0 для Windows Если это значение установNT Server, 1 лено в пользователь может для Windows выбирать Shutdown без Pow NT Worksta erOff из диалогового окна tion Welcome. Иначе, ShutDown без Power Off не появляется 1 Если это значение установ лено в 0, то отключается ав томатическое принятие за пуска (по умолчанию), рое производится после пер вого успешного входа в сис тему. Это значение должно быть равно 0, если исполь зуются альтернативные на значения в ключах или taskman, progman, wowexec
spollss.exe
нет
nddeagnt.exe
Определяет исполняемые программы, которые должны быть выполнены как оболоч ки пользователя Определяет имена программ, которые должен выполнить процесс WinLogon в контек сте системы Позволяет определить имена программ, решающих раз личные административные задачи определяет исполняемые программы, которые должен выполнить процесс WinLo gon при входе пользователя в систему
5.4. Локальная аутентификация пользователя в Windows NT В ОС Windows NT для идентификации используется имя пользователя, а для под тверждения введенного имени - процедура аутентификации, использующая ный пароль пользователя. Обозначим через: - пароль пользователя в формате UniCode; - массив из 14 байт, пароль пользователя в формате ASCII; - массив из 16 байт, который назовем образом пароля пользо вателя, полученным при помощи алгоритма DES; - массив из 16 байт, который назовем образом пароля вателя, полученным при помощи алгоритма MD4. DES(ClearText, Key) - функция преобразования по алгоритму DES, где ClearText и EncryptedText массивы по 8 байт - открытый и шифрованный текст соответственно, Key массив из 7 байт, являющийся ключом; HashText - функция преобразования по алгоритму хэширования MD4, где ClearText массив из 64 байт открытый текст, HashText массив из 16 байт хэшированный текст. Алгоритм локальной идентификации пользователя состоит из следующих шагов: Шаг 1. Пользователь вводит с клавиатуры в ответ на запрос рабочей станции свое имя, имя домена и пароль в формате UniCode- PasswordUniCode. Шаг 2. PasswordUniCode преобразуется в формат ASCII, причем маленькие латин ские буквы преобразуются в большие, результат записывается в PasswordASCII - мас сив из 14 байт. Е с л и п а р о л ь короче 14 с и м в о л о в , о с т а в ш и е с я байты массива PasswordASCII заполняются нулями. Далее осуществляется преобразование: DES(CiearText, DES(ClearText, где ClearText (4B, 47, 53, 2 1 , 40, 23, 24, 25) «KGS!@#$%». Шаг 3. Осуществляется преобразование пароля пользователя с помощью ма хэширования MD4: MD4(PasswordUniCode). Шаг 4. PasswordMD4 сравнивается с данными, которые вычисляются путем рас шифрования данных, хранящихся в реестре, в начале загрузки операционной систе мы. Шаг 5. В случае успешной проверки на шаге 4 разрешается дальнейшая загрузка системы, в противном случае осуществляется переход к шагу При входе пользователя в систему из введенного им пароля выбираются 14 байт (при меньшей длине пароль дополняется нулями), которые затем дважды используют8 3ак. 910
ся в качестве ключей для шифрования согласно алгоритму DES 8-ми байт, а именно: находящихся в файле advapi32.dll и являю щихся заранее заданными постоянными значениями. При этом 14 байт пароля пользо вателя разбиваются на два отрезка по 7 байт каждый, которые затем представляются в виде последовательности из восьми 7-ми битовых векторов. Для получения ключа алгоритма DES с каждой из последовательностей выполняются следующие действия: каждый 7-ми битовый вектор переписывается в обратном порядке, расширяется до байта с приписыванием справа единицы, и к полученным 8-ми байтам применяется операция конкатенации. Полученный таким образом двоичный вектор длины 64 пользуется в качестве ключа. Результаты ш и ф р о в а н и я исходных 8-ми байт на двух различных ключах, по л у ч е н н ы х из пароля пользователя, разбитые на два блока по 8 байт, д о п о л н я ю т с я справа нулями и р а з б и в а ю т с я на три блока по 7 байт, из которых затем снова п о л у ч а ю т с я в соответствии с о п и с а н н ы м выше с п о с о б о м три 64-битовые векто ра, используемые в качестве трех р а з л и ч н ы х ключей при трех независимых вы зовах п р о ц е д у р ы ш и ф р о в а н и я , п р и м е н я е м ы х к п о л у ч е н н о м у от сервера значении с е а н с о в о г о ключа ( м е н я ю щ е г о с я от с е с с и и к с е с с и и ) . Результат ш и ф р о в а н и я в виде п о с л е д о в а т е л ь н о с т и 24-х байт рассматривается как результат обработки па р о л я пользователя. Оценка адекватности описанного преобразования реальному производилась пу тем соотнесения дизассемблированной процедуры шифрования и аналогичной про цедуры, записанной на языке высокого уровня. При этом выяснилось, что исполь зуется блочный алгоритм, имеющий итеративную структуру к исходному тексту - блоку из 64-х бит - применяется подстановка. Результат применения подстанов ки разбивается на два блока по 32 бита, один из которых путем повторения некото рых бит расширяется до 48-ми битового блока, который складывается покоорди натно с выборкой из ключа той же размерности и разбивается на восемь блоков по 6 байт, поступающих на узлы замены. К полученному в результате 32-х битовому блоку снова применяется подстановка. Затем блоки складываются и меняются ме стами. Этот процесс итеративно повторяется 16 раз. Затем снова применяется под становка. Таким образом, стало понятно, что речь идет о DES-подобной схеме, и вопрос состоит лишь в совпадении параметров алгоритма. Для того, чтобы решить этот вопрос, была проделана работа по выделению участков процедуры, реализу ющих ту или иную часть алгоритма, и их сравнению с аналогичными частями ал горитма D E S . Первоначальная Первоначальная подстановка открытого текста осуществляется в соответствии со следующей процедурой: for i<63; i++)
где массив Plaintext содержит исходный открытый текст, массив Text содержит об рабатываемый массив из 64 бит, поступающий затем на вход итеративной части алгоритма, а массив содержит нижнюю строку подстановки открытого текста и имеет вид: 9,1, 59, 5 1 , 43, 35, 27, 19, 1 1 , 3, 6 1 , 5 3 , 4 5 , 3 7 , 2 9 , 2 1 , 1 3 , 5, 63,55,47,39,31,23,15,7, 5 6 , 4 8 , 4 0 , 3 2 , 2 4 , 1 6 , 8, 0, 58,50,42,34,26,18,10,2, 60,52,44,36,28,20,12,4, 62, 54, 46, 38, 3 0 , 2 2 , 1 4 , 6 ; что совпадает с первоначальной подстановкой для открытого текста алгоритма DES с учетом различий в нумерации бит текста (в описании DES все нумерации начина ются с 1). Ф у н к ц и я р а с ш и р е н и я . Функция расширения 32-х битового блока обрабатывае мого текста применяется следующим образом: при i, изменяющемся от 0 до 47, бит результирующего блока вычисляется по формуле: где
имеет вид: 3 1 , 0, 1, 2, 3 , 4, 3 , 4, 5 , 6, 7, 8, 7, 8, 9 , 1 0 , 11,12,11,12,13,14,15,16, 15, 16, 17, 18, 19, 20, 19, 20, 21,22,23,24,23,24,25,26, 2 7 , 2 8 , 27, 28, 29, 30, 3 1 , 0; что непосредственно совпадает с функцией расширения алгоритма DES. S-боксы. Узлы замены, именуемые S-боксами, применяются следующим образом: Пусть ah(i) i-й 6-ти битовый блок 48-ми битовой последовательности, получае мой в результате побитового сложения выхода с функции расширения с ключом на итерацию bx=(i-l)*64, al(i) - i-й блок результирующей 32-х битовой последовательно сти - результат применения узла замены, Тогда al(i) вычисляется следующим образом:
при этом
имеет вид: Oxf 4 , 0x41, 0хе2,
0x18,
0x5
0x4
Легко видеть, что, с учетом изменения порядка адресации, узлы замены могут быть в традиционном виде в результате выполнения следующей процедуры: i < 8 ; i++) for(j=0;
j<64;
1)
j++)
Результаты выполнения этой процедуры выглядят следующим образом: бокс номер О 14 4 1 3 1 2 1 5 1 1 8 3 10 6 12 5 9 0 7 0 15 7 4 14 2 13 1 10 6 12 1 1 9 5 3 8 4 1 14 8 13 6 2 1 1 15 12 9 7 3 10 5 0 1 5 12 8 2 4 9 1 7 5 1 1 3 14 10 0 6 13 бокс номер 1 1 5 1 8 14 6 1 1 3 4 9 7 2 13 12 0 5 10 3 13 4 7 15 2 8 14 12 0 1 10 6 9 11 5 0 14 7 1 1 10 4 1 3 1 5 8 12 6 9 3 2 15 1 3 8 10 1 3 1 5 4 2 11 6 7 12 0 5 14 9 бокс номер 2 10 0 9 14 6 3 1 5 5 1 1 3 12 7 11 4 2 8 1 3 7 0 9 3 4 6 10 2 8 5 14 12 11 1 5 1 1 3 6 4 9 8 1 5 3 0 1 1 1 2 12 5 10 14 7 1 10 1 3 0 6 9 8 7 4 15 14 3 1 1 5 2 12 бокс номер 3 7 1 3 14 3 0 6 9 10 1 2 8 5 1 1 12 4 1 5 1 3 8 1 1 5 6 15 0 3 4 7 2 12 1 10 14 9 10 6 9 0 12 1 1 7 13 15 1 3 14 5 2 8 4 3 1 5 0 6 10 1 13 8 9 4 5 1 1 12 7 2 14 бокс номер 4 2 12 4 1 7 10 11 6 8 5 3 15 1 3 0 14 9 14 11 2 12 4 7 1 3 1 5 0 15 10 3 9 8 6 4 2 1 1 1 10 13 7 8 15 9 12 5 6 3 0 14 1 1 8 12 7 1 14 2 1 3 6 15 0 9 10 4 5 3 бокс номер 5 12 1 10 15 9 2 6 8 0 1 3 3 4 14 7 5 1 1 10 15 4 2 7 12 9 5 6 1 1 3 14 0 11 3 8 9 14 1 5 5 2 8 12 3 7 0 4 10 1 13 1 1 6 4 3 2 12 9 5 15 10 11 14 1 7 6 0 8 1 3 бокс номер 6 4 11 2 14 15 0 8 13 3 12 9 7 5 10 6 1 1 3 0 1 1 7 4 9 1 10 14 3 5 12 2 15 8 6 1 4 1 1 1 3 12 3 7 14 10 15 6 8 0 5 9 2 6 11 1 3 8 1 4 10 7 9 5 0 1 5 14 2 3 12 бокс номер 7 1 3 2 8 4 6 15 11 1 10 9 3 14 5 0 12 7 1 1 5 1 3 8 10 3 7 4 12 5 6 1 1 0 14 9 2 7 11 4 1 9 12 14 2 0 6 10 13 15 3 5 8 2 1 14 7 4 10 8 1 3 15 12 9 0 3 5 6 1 1 что полностью совпадает с узлами замены (подстановками) алгоритма DES.
5.5. Сетевая аутентификация пользователя в Windows NT Сетевая аутентификация пользователя осуществляется в процессе его подключе ния к серверу с целью получения доступа к сетевым ресурсам (например, к сетевому диску). Сетевая аутентификация осуществляется только в случае успешного заверше ния процедуры локальной аутентификации пользователя. Обозначим через: • массив из 8 байт - разовый ключ, получаемый рабочей станцией от сервера; • - массив из 48 байт билет-аутентификатор, направляемый рабо чей станцией на сервер в качестве запроса о разрешении доступа к сетевым ресурсам. Процедура сетевой аутентификации пользователя состоит из следующих шагов: Шаг 1. Рабочая станция получает от сервера разовый ключ SessionKey. Шаг 2. Разовый ключ и PasswordDES преобразуются с использованием алгоритма DES: DES ( S e s s i o n K e y , 6]
Шаг 3. Разовый ключ и DES:
преобразуются с использованием алгоритма
Шаг 4. Ticket передается в SMB-пакете на сервер. Сервер проверяет данные и в случае успешной проверки передает рабочей станции сообщение об успешном завер шении процедуры аутентификации.
5.6. Основные подходы к созданию изолированной программной среды Для создания ИПС можно использовать два подхода: Замещение пользовательской оболочки собственной задачей, которая предлага ет замкнутое меню. Этот путь достаточно тривиален и предусматривает написание простого оконного приложения и указания пути к нему в ключе Shell. 2. Задание ключей реестра, позволяющих ограничить число задач в меню Пуск стандартной оболочки.
В Windows NT есть возможность установить список программ, которые могут быть запущены пользователями. Для демонстрации данной возможности выполните следу ющие действия. 1. О т к р о й т е р е е с т р д л я р е д а к т и р о в а н и я и н а й д и т е к л ю ч 2. Установите значение параметра равным для использования дан ной функции или равной для ее блокировки. Создайте этот параметр, если его не существует. 3. Определите программы, которые смогут запускать пользователи, в ключе:
Создайте новый параметр для каждой программы, танию. Например:
их числами по возрас
Рассмотренный путь имеет с тем, что необходи мо редактировать ключи раздела Таким образом, необходимо предвари тельно регистрироваться в системе в качестве пользователя, для которого устанавли вается замкнутое меню. Следует учитывать - ничто не мешает пользователям любой файл в winword.exe (или любой другой, разрешенный к запуску) и обойти это ограни чение. В связи с этим для реализации надежной защиты необходимо «поддержать» работу ИПС драйвером уровня ядра, который контролирует операции с исполняемы ми файлами.
5.7. Макет системы защиты от несанкционированного доступа При создании системы защиты информации (СЗИ) от дос тупа необходимо реализовать следующие функциональные подсистемы: - подсистему идентификации и аутентификации (ИА) пользователей, - подсистему разграничения (контроля) доступа субъектов ОС к объектам, - подсистему регистрации событий. При этом подсистему идентификации и аутентификации целесообразно ассоци ировать со штатной подсистемой ИА, реализуемой MSGINA, а подсистему контроля доступа и регистрации событий - с отдельно работающим драйвером контроля дос тупа, связанным с подсистемой ИА. Такая связь необходима потому, что контроль доступа к объектам до входа конкретного пользователя в систему может представ лять собой достаточно нетривиальную задачу, например, если для конкретного пользо вателя задана изолированная программная среда (т.е. фиксированный список задач, которые он может запускать), то непонятно как быть с задачами, запускаемыми до
выполнения успешной процедуры ИА данного Кроме того, связь со штатным модулем ИА может служить для передачи ключевой информации, необхо димой для инициализации персональных криптографических модулей, предназна ченных для шифрования трафика (см. выше) или для периодического тестирования корректности работы криптографических функций или иных механизмов обеспече ния безопасности. Рассмотрим пример макета ядра СЗИ от несанкционированного доступа для Windows NT/2000. Основными компонентами этой системы защиты являются, как указано выше: • Драйвер контроля доступа, предназначенный для файловых опера ций на уровне ядра. • Модифицированная библиотека GINA (XGINA), построенная по принципу пол ного перехвата экспортируемых функций оригинальной M S G I N A (Graphical Identification aNd Authentication DLL for Winlogon). При входе пользователя библиотека GINA.DLL возвращает директивы приложению Winlogon, соглас но которым оно либо не должно менять текущий статус процесса ИА, либо выполнить некоторые действия (например, насильственно выгрузить пользо в а т е л я ) . В з а в и с и м о с т и от з н а ч е н и я п а р а м е т р а d w O p t i o n s в ф у н к ц и и WlxLoggedOutSas() библиотеки G I N A . D L L , приложение Winlogon либо не должно загружать профиль входящего пользователя, либо же должно выпол нить загрузку того пользовательского профиля, информацию о котором вер нула библиотека GINA.DLL.
Драйвер контроля доступа Перехват операций и удаления файлов
создания
Реализация драйвером перехвата файловых операций основана на недокументи рованном механизме перехвата системных сервисов, описанном в разделе «Реализа ция защиты на уровне собственного API для ОС Windows NT». Принцип функционирования драйвера основан на механизме замены адресов фун кций, предоставляемых «родным» API. Обработчик прерывания int 2Е для вызова со ответствующего системного сервиса использует таблицу распределения системных сервисов KeServiceDescriptorTable, экспортируемую ntoskrnl.exe. Во время инициализации драйвера в функции DriverEntry, после успешного созда ния объекта-устройства, устанавливаются собственные обработчики файловых опера ций. Для этого сначала надо получить адрес таблицы распределения системных сер висов: ServiceTable
Зная индексный номер функции, реализующей системный сервис, можно по таб лице определить адрес ее начала и заменить его на адрес начала собственного обра ботчика режима ядра. Это делается следующим образом:
- оригинальный обработчик создания файла; ZwCreateFile (PVOID) - собственный обработчик создания файла; RealOpenFile оригинальный обработчик открытия ZwOpenFile (PVOID) - собственный обработчик открытия При перехвате функций ZwCreateFile и Z w O p e n F i l e необходимо учитывать, что операции запуска исполняемых файлов, переименования и удаления объек тов производятся именно этими функциями при установленных следующим об разом флагах: DesiredAccess & DELETE - операция удаления файла, DesiredAccess & || (DesiredAccess & - запуск исполняемого О т д е л ь н у ю задачу представляет собой различение операций с объектами типа PIPE или В ряде случаев это может быть сделано по имени, напри мер, после выполнения фраг мента кода: ectName->Length; (USHORT) ectName->
TRUE
Собственный обработчик создания файла и собственный обработчик открытия файла Собственный обработчик создания (открытия) файла может, например, проверить права доступа текущего процесса к создаваемому (открываемому) файлу, и если дос-
туп запрещен, то вернуть статус создания (открытия) файла STATUS_ACCESS_DENIED запрещен), иначе вернуть управление оригинальному обработчику. При этом можно вести журнал как удавшихся, так и не удавшихся попыток доступа, руя имя процесса, в контексте которого осуществляется попытка доступа к ресурсу, и имя создаваемого (открываемого) файла. После исполнения собственного обработчи ка необходимо передать управление по старому адресу, чтобы дать возможность стан дартному обработчику выполнить запрошенные действия. PHANDLE F i l e H a n d l e , IN IN OUT I N PLARGE_INTEGER A l l o c a t i o n S i z e , I N ULONG F i l e A t t r i b u t e s , IN ULONG ULONG C r e a t e D i s p o s i t i o n , IN ULONG PVOID ULONG E a L e n g t h ) if return
CreateDisposition, return
Определение имени процесса Реализация драйвером функции получения имени обращающегося к ресурсу про цесса необходима для создания системы защиты от НСД не ниже 3 класса по класси фикации Руководящих документов Государственной технической комиссии РФ, оп ределяющих защищенность средств вычислительной техники (СВТ) или класса 1В по классификации защищенности автоматизированных систем (АС). При этом мо жет быть поддержана полная модель полномочного (мандатного) доступа, определя ющая возможность контроля доступа обращений к ресурсу от имени конкретного процесса. Имя обращающегося к ресурсу процесса находится в структуре, описывающей Чтобы получить смещение имени процесса в объекте-процессе, надо во время инициализации драйвера в функции DriverEntry (которая всегда исполняется в контексте процесса System) найти строку «System» в объекте-процессе, описываю щем процесс System: ULONG PEPROCESS int
curproc;
curproc i 0; i
3*PAGE_SIZE;
i++ curproc
return
i,
i;
не найдено return 0;
После удачного завершения этой функции можно использовать возвращенное ею значение для определения имени процесса: VOID
PCHAR Name PEPROCESS char ULONG curproc strncpy( else
PsGetCurrentProcess (PCHAR) c u r p r o c Name, n a m e p t r , 1 6 Name,
Получив имя процесса, можно определить его конкретные полномочия по доступу к объектам. Например, далее по тексту будет использоваться возможность вызова фун кций драйвера только процессом Winlogon, в контексте которого работает GINA. Та ким образом, можно гарантировать передачу параметров и управление драйвером только со стороны модифицированной библиотеки.
Вывод сообщений на «синий» экран Если флаг Start в реестре у драйвера установлен в или SERVICE_SYSTEM_START(1), то можно использовать функцию вывода на ний» экран для вывода информации о действиях драйвера во время инициализации. Например: on b l u e
Например, во время инициализации драйвер может произвести процедуру тести рования (контрольный пример) криптографических функций, либо иных функций, имеющих отношение к безопасности, и вывести результат тестирования на экран. Кроме того, в ряде случаев можно инициировать ввод ключевой информации для драйверов шифрования, описанных выше.
5.7.1.5. Процедура распределения драйвера контроля доступа Для взаимодействия с драйвером модифицированная библиотека GINA может ис пользовать определенные контрольные коды, которые должен обрабатывать драйвер, например: # d e f i n e TDRV_hook (ULONG) FILE_ANY_ACCESS # d e f i n e TDRV_unhook (ULONG) FILE_DEVICE_TDRV, 0 x 0 1 , # d e f i n e TDRV_setkey (ULONG) FILE_WRITE_ACCESS # d e f i n e TDRV_test (ULONG)
0x02, FILE_DEVICE_TDRV,
Тогда процедура обработки контрольных кодов драйвера ющей: BOOLEAN TdrvDeviceControl IN BOOLEAN IN PVOID I n p u t B u f f e r , IN ULONG OUT PVOID IN ULONG IN ULONG I o C o n t r o l C o d e , OUT IN UCHAR int IoStatus->Status switch case
может быть следу FileObject,
STATUS_SUCCESS; 0;
IoControlCode версию (ULONG
драйвера fer
контроля
0x03,
доступа
IoStatus,
IN
break; case обработчики и удаления файлов
case и
обработчики открытия, удаления файлов
case криптографические
функции
в
if
case
if if
TDRV_setkey: ключ ш и ф р о в а н и я д р а й в е р у , текущий п р о ц е с с w i n l o g o n
условная процедура перевода строчные "winlogon
символов
в
| | NULL 1;
UserKey,
USER I D S I Z E
USER_STATUS_SIZE break;
IoStatus->Status break; return
TRUE;
Драйвер может вызываться периодически по таймеру из модифицированной биб лиотеки Gina с помощью функции DeviceloControl для тестирования криптографичес ких функций и других функций обеспечения безопасности с заданной периодичнос тью, например, один раз в
5.7.2. Модифицированная библиотека Gina Модифицированная библиотека (полностью текст и описание состояний приве дено выше) Gina XGINA.dll реализует перехват экспортируемых оригинальной M S G I N A функций с последующей передачей им управления. Собственные обработ чики должны иметь такие же имена, что и оригинальные (экспортируемые текой MSGINA). Для того чтобы вместо оригинальной библиотеки MSGINA.dll в процесс winlogon подгружалась модифицированная библиотека XGINA.dll, нужно задать в ключе реес тра запись; GinaDll c:\xgina.dll. XGINA.dll во время загрузки в процесс Winlogon должна сначала подгрузить оригинальную библиотеку MSGINA.DLL, а затем получить адреса экспортируемых этой библиотекой функций, для того чтобы использовать эти адреса в собственных обработчиках: WLXNEGOTIATE WLXINITIALIZE
g_lpWlxNegotiate g_lpWlxInitialize
NULL; NULL; NULL;
WLXLOGGEDOUTSAS WLXLOGGEDONSAS WLXISLOCKOK WLXDISPLAYLOCKEDNOTICE WLXWKSTALOCKEDSAS
NULL;
NULL; NULL; g
NULL;
NULL; NULL; NULL;
WLXLOGOFF WLXSHUTDOWN BOOL hMSGinaDLL i f (hMSGinaDLL return
NULL)
FALSE; Original Library
g_lpWlxNegotiate "WlxNegotiate"
g_lpWlxDisplaySASNotice
WLXDISPLAYSASNOTICE "WlxDisplaySASNotice" WLXLOGGEDOUTSAS
(hMSGinaDLL, g_lpWlxActivateUserShell
WLXACTIVATEUSERSHELL "WlxActivateUserShell"
"WlxLoggedOnSAS" WLXISLOCKOK "WlxIsLockOk" g_lpWlxDisplayLockedNotice WLXWKSTALOCKEDSAS WLXISLOGOFFOK g_lpWlxLogoff
WLXLOGOFF WLXSHUTDOWN
"WlxShutdown" Function return
TRUE;
Attach
Ниже приведен пример нового обработчика экспортируемой функции WlxLoggedOutSAS: i n t WINAPI PVOID pWlxContext, DWORD dwSasType, ассоциированный с текущей с е с с и е й l o g o n pLogonSid, уникальный для текущей с е с с и е й logon PDWORD з а г р у з к и профиля
информация pProfile на одну из
структур
int FILE * o u t ; UCHAR UCHAR UCHAR оригинальной
функции
WlxLoggedOutSAS
res pLogonSid,
pdwOptions,
phToken,
break; else имени
пользователя
break;
else
пароля
пользователя
("с: if
имени п о л ь з о в а т е л я fwrite sizeof
в
файл
out) пароля пользователя fwrite
out)
return
в
файл
Список источников информации 1) 2) 3) 4) 5) 6) 7)
8) 9)
14)
Документация Microsoft Windows NT Device Driver Kit, Content Version: 4.0, разделы Kernel-mode drivers и Network drivers. Документация Microsoft Platform Software Development Kit, раздел Networking and Distributed Service. Документация Installable File System Kit. Хелен Кастер. Основы Windows NT и NTFS. Пер. с англ. Издательский отдел «Русская редакция», 1996. Peter G.Viscarola, W. Anthony Mason. Windows NT Device Driver Development. USA, Macmillan Technical Publishing, 1997. Rajeev Negar. Windows NT File System Internals: A Developer's Guide. USA, Inc., 1997. Джеффри для профессионалов: Программирование для Windows 95 и Windows NT 4 на базе Win32 API. Пер. с англ. Издательс кий отдел «Русская редакция», 1997. Mark the Native API Windows NT Magazine, 1998, № 3 . Special Edition Using Windows NT Server 4. Part I: Networking with Windows NT Server 4.0. Chapter 4: Choosing Network Protocols. Que Corporation, Schneier Applied Cryptography. 1993. Art Baker, Jerry Lozano. The Windows 2000 Device Driver Book. A Guide for programmers. Second edition. Prentice Hall PTR, Prasad Dabak, Sandeep Phadke, Milind Borate. Undocumented Windows NT. Books, 1999. Edward N. Dekker, Joseph M. Newcomer. Developing Windows NT Device Driver. http://www.osr.com. Подборка статей Ntinsider. http://www.pcausa.com.
Сорокина, А. Тихонов, А. Щербаков
Программирование драйверов и систем безопасности Учебное пособие
Редактор Компьютерная верстка Корректоры: Дизайн обложки Зав. производством
Игорь Сорокин Екатерины Мария Дрюкова, Виктория Пиотровская Игоря Николай Тверских
Подписано в 22.05.03. Гарнитура Печать офсетная. Усл. печ. л. Доп. тираж 3000 экз. Заказ 198005, Санкт-Петербург, Измайловский пр., 29. Гигиеническое заключение на продукцию, товар 77.99.02.953.Д.001537.03.02 от 13.03.2002 г. выдано Департаментом ГСЭН Минздрава России. Отпечатано с готовых диапозитивов в Академической типографии РАН Санкт-Петербург, 9 линия, 12.
Издатель 8. Лицензия ИД № 03567 от Г. Москва, (095) 954-30-44,
г. Санкт-Петербург,
Издательство "БХВ-Петербург" Лицензия ИД № 02429 от 24.07.00. Измайловский пр., 29, (812) 251-42-44, 251-65-01. www.bhv.ru