Разработка драйверов под Windows

Автор работы: Пользователь скрыл имя, 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 файл

КР_СПО_ЕрмаковNew.doc

— 388.73 Кб (Скачать файл)



СОДЕРЖАНИЕ

 

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. Теоретические сведения

    1. Разработка  драйверов ядра Windows

 

Краткие теоретические сведения

Разработка драйверов  ядра

Драйвер (от англ. driver) - это  компьютерная программа, с помощью  которой другая программа (обычно, операционная система) получает доступ к аппаратному  обеспечению стандартным образом.

Схематично, чтобы показать, как работают разные типы  драйверов:

                          ┌──────────┐               ┌──────────┐

                           │приложение│               │приложение│

                           └────┬─────┘               └────┬─────┘

                                │                          │

     ┌────────────┐      ┌──────┴───────┐           ┌──────┴───────┐

     │драйвер  ядра│      │ драйвер  вир- │           │ драйвер реа- │

     └────────────┘      │туального у-ва│           │ ального у-ва │

                         └──────────────┘           └──────┬───────┘

                                                           │

                                    контроллер

                                                           │

                                                      ┌────┴─────┐

                                                      │устройство│

                                                      └──────────┘

Удобно разделить на 2 типа:

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

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

Компоненты ядра выполняются  в привилегированном режиме процессора (называемом режимом ядра), могут  выполнять все, а именно:

- они могут выполнять  привилегированные команды процессора (например, lgdt),

- могут иметь доступ  к системным данным и коду,

- имеют прямой доступ  к оборудованию, например, через  порты

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

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

Код ядра (собственно это  и есть сама система) рассматривается  как полностью доверительный. Поэтому, будучи загруженным в системное  адресное пространство, драйвер становится частью системы и на него не накладываются  какие-либо ограничения. В 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_ERROR;

}

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

pDriverObject - указатель на  объект только что созданного  драйвера. Загружая драйвер, система  создает объект "драйвер" (driver object), представляющий для нее образ  драйвера в памяти. Через этот  объект система управляет драйвером.  Объект "драйвер" представляет  собой обыкновенную структуру  данных типа DRIVER_OBJECT.

pusRegistryPath - указатель на  раздел реестра, содержащий параметры  инициализации драйвера.

Этот наш первый драйвер только загружается в систему и тут же выгружается.

Теперь рассмотрим программу-шаблон, которую нужно будет использовать для разработки программы на первом шаге курсовой работы (драйвер режима ядра beeper.sys).

Задача этого драйвера - исполнять на системном динамике одну ноту до первой октавы. Для этого  драйвер использует инструкции процессора 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

 что означает вызов  специального скрипта для связи  VC с DDK

В текущей папке проекта  должны присутствовать файлы:

      sources,  makefile, ddkbuild.cmd (скрипт), исходный файл драйвера .c

После построения проекта  драйвер должен иметь расширение .sys.

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

Чтобы приложение могло запустить  драйвер, после построения решения, которое состоит из двух проектов – приложения и драйвера, нужно  поместить исполнимый файл приложения и драйвер в одну папку, а затем запустить приложение..

Драйверы очень трудно отлаживать. При ошибках в работе ОС чаще всего зависает, и требуется  перезагрузка. А для нашего драйвера после перезагрузки еще и необходимо удалить службу beeper06 из реестра с помощью regedit (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\), а потом снова перезагрузиться.

 

    1. Драйверы виртуальных устройств  Windows.

 

До сих пор мы разрабатывали  драйвер режима ядра, который может  делать то, что нельзя на пользовательском уровне, в частности, работать с портами  в/выв. Такой драйвер называется драйвером ядра, но не драйвером  устройства, потому что не передаются данные между программой пользователя и устройством (Драйвер ведь обычно зачем нужен? Чтобы организовывать  обмен данными между приложением  пользователя и устройством).

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

 

Работа приложения с драйвером

            ┌───────────────────────┐

            │Приложение             │

            │- открывает устройство │

            │- управляет устройством│

            │- читает или пишет     │

            │- закрывает устройство │

            └────────^──────────────┘

                     │

               ┌─────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_ACCESS = 1+2=3=11b

Функциональный код - в  диапазоне от 800h до FFFh. У нас - 800h.

Метод буферизации - способ передачи данных между приложением и  драйвером (возможны три):

- Для небольшого объема  данных используется обычно METHOD_BUFFERED (00b) - выделяется дополнительный  буфер в нестраничной памяти, достаточный для размещения входного  и выходного буфера. Адрес этого  буфера размещается в IRP в поле AssociatedIrp.SystemBuffer. Диспетчер в/выв сам берет на себя работу перезаписи данных между пользовательским и дополнительным буфером.

Информация о работе Разработка драйверов под Windows