Автор работы: Пользователь скрыл имя, 28 Апреля 2013 в 23:02, курсовая работа
Драйвер (от англ. driver) - это компьютерная программа, с помощью которой другая программа (обычно, операционная система) получает доступ к аппаратному обеспечению стандартным образом.
1. Теоретические сведения 4
1.1. Разработка драйверов ядра Windows 4
1.2. Драйверы виртуальных устройств Windows. 11
1.3. Доступ к существующим драйверам из приложений пользовательского режима 21
2. Выполнение курсовой работы 27
2.1. Шаг 1 27
Листинг Kurs_test.cpp 27
Листинг beeper.sys 28
2.2. Шаг 2 30
Листинг shablon.c 30
Листинг курсовая2.cpp 32
2.3. Шаг 3 34
Листинг курсовая.cpp 34
2.4. Шаг 4 35
3. Работа приложения 38
1. Теоретические сведения 4
1.1. Разработка драйверов ядра Windows 4
1.2. Драйверы виртуальных устройств Windows. 11
1.3. Доступ к существующим драйверам из приложений пользовательского режима 21
2. Выполнение курсовой работы 27
2.1. Шаг 1 27
Листинг Kurs_test.cpp 27
Листинг beeper.sys 28
2.2. Шаг 2 30
Листинг shablon.c 30
Листинг курсовая2.cpp 32
2.3. Шаг 3 34
Листинг курсовая.cpp 34
2.4. Шаг 4 35
3. Работа приложения 38
Краткие теоретические сведения
Разработка драйверов ядра
Драйвер (от англ. driver) - это
компьютерная программа, с помощью
которой другая программа (обычно, операционная
система) получает доступ к аппаратному
обеспечению стандартным
Схематично, чтобы показать, как работают разные типы драйверов:
┌──────────┐ ┌──────────┐
│приложение│ │приложение│
└────┬─────┘ └────┬─────┘
┌────────────┐ ┌──────┴───────┐ ┌──────┴───────┐
│драйвер ядра│ │ драйвер вир- │ │ драйвер реа- │
└────────────┘ │туального у-ва│ │ ального у-ва │
└──────────────┘ └──────┬───────┘
контроллер
Удобно разделить на 2 типа:
- Драйверы ядра (работают
на 0 уровне привилегий, но никак
не взаимодействуют ни с
- Драйверы устройств -
необходимы, чтобы осуществлять
взаимодействие между
Компоненты ядра выполняются в привилегированном режиме процессора (называемом режимом ядра), могут выполнять все, а именно:
- они могут выполнять
привилегированные команды
- могут иметь доступ к системным данным и коду,
- имеют прямой доступ к оборудованию, например, через порты
- имеют доступ к ячейкам
памяти; драйвер не может
Если требуется написать сколь-нибудь серьезное приложение, для работы которого необходим доступ к внутренним функциям или структурам данных системы, то можно столкнуться со множеством ограничений, преодолеть которые можно только разместив свой код в системном адресном пространстве. Из документированных существует только один способ это сделать - установить драйвер. Способ этот относительно прост, надежен, а главное, полностью обеспечен поддержкой со стороны самой операционной системы.
Код ядра (собственно это и есть сама система) рассматривается как полностью доверительный. Поэтому, будучи загруженным в системное адресное пространство, драйвер становится частью системы и на него не накладываются какие-либо ограничения. В Windows - это практически единственный способ не разработчикам ОС писать системные компоненты уровня ядра.
Для написания и изучения способов разработки драйверов применяют DDK (Device Development Kit) - систему для разработки драйверов.
Помимо документации в DDK входит набор включаемых файлов (*.inc) и библиотечных файлов (*.lib).
Таким образом, Windows поддерживают различные типы драйверов устройств, но, кроме того, существуют драйверы, которые не являются драйверами устройств, а просто позволяют создавать программы, которые в Windows будут работать в режиме ядра, т.е. на 0 уровне привилегий. При этом они полностью получают доступ к ОС и оборудованию.
Рассмотрим самый простой драйвер режима ядра.
#include <ndis.h>
int DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pusRegistryPath ) {
return STATUS_DEVICE_CONFIGURATION_
}
Точкой входа является DriverEntry, которая оформлена в виде процедуры, принимающей два параметра:
pDriverObject - указатель на
объект только что созданного
драйвера. Загружая драйвер, система
создает объект "драйвер" (driver
object), представляющий для нее образ
драйвера в памяти. Через этот
объект система управляет
pusRegistryPath - указатель на
раздел реестра, содержащий
Этот наш первый драйвер только загружается в систему и тут же выгружается.
Теперь рассмотрим программу-шаблон,
которую нужно будет
Задача этого драйвера - исполнять на системном динамике одну ноту до первой октавы. Для этого драйвер использует инструкции процессора in и out, обращаясь к соответствующим портам ввода-вывода. Общеизвестно, что доступ к портам ввода-вывода – это свято охраняемый Windows системный ресурс. Попытка обращения к любому из них, как на ввод, так и на вывод, из режима пользователя, неизбежно приводит к появлению исключения.
В нашем примере будет работать динамик (для этого используется, в частности, порт 61h, 0 и 1 биты, порт 43h и 42h).
В начале драйвера определены все 12 нот.
Нужно будет не просто включить динамик, а установить частоту звука. Для этого используется подсистема таймера, которая работает независимо от процессора и имеет 3 канала. Выход канала 2 связан с динамиком, который используется для генерации звука разной частоты. Слышимый диапазон звука - 30Гц-6000Гц.
Чтобы задать частоту звука, в порт 43h (регистр команд таймера) посылается управляющее слово 0Bh:
mov al,0Bh
out 43h,al
Это значение определяет номер канала, которым мы будем управлять, тип операции, режим работы канала и формат счетчика.
Затем в порт 42h посылается пересчитанная частота звука (1193167/частоту (Гц)) двумя порциями (сначала младшая часть, затем - старшая).
Например, мы хотим получить частоту звука в 100Гц. Пересчитываем частоту,
1193167/100 = 11931
Затем:
mov ax,11931
out 42h,al
mov al,ah
out 42h,al
На первом шаге курсовой работы необходимо изменить программу так, чтобы она выдавала другие музыкальные звуки (у каждого по варианту).
Зачастую в заданиях нужно задавать разные длительности. Для этого удобно использовать процедуру DO_DELAY, передав в нее косвенно определенный параметр "время звучания".
Чтобы было удобно отлаживать драйвер, существуют различные средства. Самое простое – осуществлять вывод необходимой информации в специальное окно утилиты Debug View. Предварительно эта программа запускается, настраивается на перехват сообщений с уровня ядра. Для вывода в программе вызывается функция DbgPrint, имеющая один параметр – выводимую строку. После запуска драйвера в окне Debug View отображается весь вывод.
Существуют различные способа установки драйвера в ядро. Так как наш драйвер, вообще-то, системе не нужен (он не руководит никаким устройством), то мы будем временно подключать драйвер к системе, а затем его удалять.
Для этого необходимо создать приложение, которое будет запускать драйвер. Каким образом? Драйвер – это служба уровня ядра. Поэтому приложение будет использовать SCM - Диспетчер управления службами (Service Control Manager), который входит в состав Windows и работает на пользовательском уровне.
Таким образом, необходимо построить решение из двух проектов: консольное приложение и драйвер.
Для разработки драйверов на С нужно предварительно:
- проинсталлировать DDK,
- установить переменную среды WNETBASE (значение – путь к DDK, например, e:\winddk\3790.1830).
Проект с драйвером должен быть типа MakeFile.
Сделать настройки проекта с помощью Application Settings и в поле Build Command Line записать строку
ddkbuild -WNETXP chk . –ceZ
что означает вызов
специального скрипта для
В текущей папке проекта должны присутствовать файлы:
sources, makefile, ddkbuild.cmd (скрипт), исходный файл драйвера .c
После построения проекта драйвер должен иметь расширение .sys.
Приложение запускает драйвер beeper.sys, т.е. прописывает его в реестре, и запускает в работу. Затем по окончании удаляет из реестра.
Чтобы приложение могло запустить драйвер, после построения решения, которое состоит из двух проектов – приложения и драйвера, нужно поместить исполнимый файл приложения и драйвер в одну папку, а затем запустить приложение..
Драйверы очень трудно
отлаживать. При ошибках в работе
ОС чаще всего зависает, и требуется
перезагрузка. А для нашего драйвера
после перезагрузки еще и необходимо
удалить службу beeper06 из реестра с помощью
regedit (HKEY_LOCAL_MACHINE\SYSTEM\
До сих пор мы разрабатывали
драйвер режима ядра, который может
делать то, что нельзя на пользовательском
уровне, в частности, работать с портами
в/выв. Такой драйвер называется
драйвером ядра, но не драйвером
устройства, потому что не передаются
данные между программой пользователя
и устройством (Драйвер ведь обычно
зачем нужен? Чтобы организовывать
обмен данными между
Сейчас мы будем разрабатывать драйверы устройств, которые передают данные туда и обратно между приложением пользователя и устройством.
Работа приложения с драйвером
┌───────────────────────┐
│Приложение │
│- открывает устройство │
│- управляет устройством│
│- читает или пишет │
│- закрывает устройство │
└────────^──────────────┘
│
┌─────v─────┐
│ Драйвер │
│устройства │
└───────────┘
Когда приложению требуется операция в/выв, то происходит обращение к драйверу. Для этого приложение может давать запрос на чтение данных из устройства или запись данных на устройство. А если требуется какое-то другое действие, например, опрос или управление устройством, либо что-либо другое, то для этого используется т.н. IOCTL-интерфейс (Device In-Out Control).
Мы будем рассматривать именно такой случай для виртуальных устройств, потому что чаще всего, зачем нужно виртуальное устройство в драйвере? Чтобы можно было передавать ему данные, которые драйвер может как-то обработать (как нельзя в приложении) и вернуть в приложение результат. Вспомним, что обычный драйвер ядра, рассмотренный ранее, ничего не брал из приложения и ничего туда не возвращал, а просто делал действия, недоступные приложению.
Когда приложению требуется операция в/выв, то происходит обращение к драйверу. Для этого может использоваться т.н. IOCTL-интерфейс (Device In-Out Control).
Вызывающее приложение выполняет следующие действия:
1) Открытие файла и получение его дескриптора:
invoke CreateFile, ссылка на устройство, \
GENERIC_READ + GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL
В результате, если все произошло успешно, мы получаем дескриптор устройства.
2) Посылка драйверу кода действия (что делать, драйвер может реализовывать много различных действий):
invoke DeviceIoControl, дескриптор, код действия, адрес входного буфе-
ра, размер входных данных, адрес выходного буфера, размер выходных
данных, адрес буфера для реального количества байтов
3) Закрытие файла и, соответственно, освобождение дескриптора.
invoke CloseHandle дескриптор устройства
Чтобы передавать данные, модули (приложение и драйвер) должны договориться о протоколе взаимодействия (коды действий, структура буферов - входных и выходных).
Драйвер получает код действия:
31 30 16 15 14 13 2 1 0
фай- Тип устройства Права доступа Функционал. код метод буферизации
ловый
флаг
Такой же код действия используется и в приложении, и в драйвере.
Код действия в приложении и в драйвере можно записывать в 16-ричном виде, а можно использовать макрос CTL_CODE, как это сделано в примере лаб. работы в файле common.inc.
Рассмотрим пример кода действия из драйвера виртуального устройства, который используется в лабораторной работе. Имя - IOCTL_GET.
0 000000000100010 11 100000000000 00 = 0022E000h
22h 3 800h
В случае виртуального устройства файловый флаг равен 0.
Тип устройства - FILE_DEVICE_UNKNOWN = 22h
Права доступа - FILE_READ_ACCESS+FILE_WRITE_
Функциональный код - в диапазоне от 800h до FFFh. У нас - 800h.
Метод буферизации - способ передачи данных между приложением и драйвером (возможны три):
- Для небольшого объема
данных используется обычно METHOD_BUFFERED
(00b) - выделяется дополнительный
буфер в нестраничной памяти,
достаточный для размещения