Разработка драйверов под 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 Кб (Скачать файл)

- Прямой доступ к данным (без буфера) - METHOD_OUT_DIRECT (2) – для  вывода либо METOD_IN_DIRECT (1) – для  ввода; поле из IRP - MdlAddress. Это непосредственное обращение - диспетчер в/выв фиксирует в памяти физические страницы, содержащие буфер пользовательского режима.  При этом создает вспомогательную структуру MDL (Memory Descriptor List) для описания зафиксированных страниц. И разработчик драйвера работает с MDL.

- Доступ через буфер  пользовательского уровня - METHOD_NEITHER (3); поле из IRP - SystemBuffer. Диспетчер в/выв передает в драйвер виртуальные адреса пользовательского режима. И в драйвере нужно очень осторожно с ними работать, потому что драйвер в этом случае должен работать только в контексте вызывающего потока.

Когда приложение посылает драйверу код действия, то начинает работу диспетчер  ввода-вывода. Он отвечает за формирование пакета запроса ввода-вывода (I/O request packet, IRP) и посылку его драйверу для дальнейшей обработки.

Мы будем рассматривать 3 типа запросов:

- IRP_MJ_CREATE - будет передан  при CreateFile,

- IRP_MJ_DEVICE_CONTROL - будет передан при DeviceIoControl

- IPR_MJ_CLOSE - при CloseHandle

Пакет IRP состоит из заголовка и стеков размещения в/выв. Диспетчер в/выв создает количество ячеек стека в/выв равное числу драйверных слоев, участвующих в обработке запроса. Каждому драйверу разрешен доступ к собственной ячейке стека. Когда драйвер передает пакет IRP драйверу нижнего уровня, указатель на ячейку стека перемещается на ячейку, необходимую этому драйвера. И, наоборот, после обработки запроса, указатель поднимается вверх на ячейку драйвера высшего уровня. Получение указателя с помощью функции – IoGetCurrentStackLocation().

     Заголовок IRP (Tail.Overlay)

                                │

     ячейка стека в/выв<───>драйвер-1

     ......                  ......

     ячейка стека  в/выв<───>драйвер-n

В каждом стеке размещения находится указатель на объект-устройство DeviceObject и на объект-файл FileObject, для  которого инициирован запрос. Пакеты IRP всегда хранятся в невыгружаемой памяти.

 

     IRP

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

│                  Заголовок (фиксированная часть)                     │

├──────────────────────────────────────────────────────────────────────┤

│- AssociatedIRP.SystemBuffer - указатель на системный  буфер, если     │ 

│     буферизованный в/выв - METHOD_BUFFERED. Создается копия буфера.  │

│      флаг DO_BUFFERED_IO            

- MdlAddress - указатель на перед. данные, если исп. прямой доступ;  │

│это прямой в/выв. Для передачи буфера драйверу создается таблица  MDL, │

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

│- UserBuffer - адрес пользовательского  буфера в/выв, если используется│

│никакой в/выв. В драйвер передается адрес буфера инициатора запроса.  │

│  флаг Neither               │

│- IoStatus (структура)                                                │

│   - IoStatus.Status                                                  │

│   - Io.Status.Information-количество переданных или полученных данных│

│- Cancel – запрошена ли отмена операции                     │

│- Указатель на ячейки стека в/выв - соответствуют количеству драйверных

│                                     слоев                            │

└ Tail.Overlay ──────────────────────────────────      ────────────────┘


  

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

 │     Структура ячейки  стека в/выв - IO_STACKLOCATION             │

 ├─────────────────────────────────────────────────────────────────┤

 │-MajorFunction - код IRP_MJ_xxx - описывает  базовый IRP драйвера │

 │-MinorFunction - субкод операции (т.н.второстепенные  коды)       │

 │-DeviceObject - указатель на объект  устройства, для которого IRP │

 │-FileObject - файловый объект для  запроса к у-ву

 │-Parameters                                                      │

 │ - DeviceIoControl (для IRP_MJ_CONTROL)                          │

 │    - OutputBufferLength                                         │

 │    - InputBufferLength                                          │

 │    - IoControlCode                                              │

 │ - для IRP_MJ_READ и IRP_MJ_WRITE                                │

 │    - Length                                                     │

 │    - Key                                                        │

 │    - ByteOffset                                                 │

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

 

Для работы драйвера создаются и применяются следующие объекты:

- объект драйвера;

- объекты устройств;

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

Этапы работы драйвера.

1) Создание объекта драйвера. Создается при загрузке драйвера  на этапе его запуска. В этот  момент запускается функция DriverEntry и заполняется массив MajorFunction, а  также указатель на объект  устройства и обратно.

В состав объекта устройства входят:

- ссылка  на объект драйвера, который обрабатывает запросы  к устройству;

- тип устройства.

2) Создание символьной  ссылки на устройство. Для того  чтобы объект "устройство" стал  доступен коду режима пользователя, драйвер должен создать в доступном  ему (коду режима пользователя) каталоге "\??" еще один объект - символьную ссылку (symbolic link). Драйвер  shablon.sys создает символьную ссылку "slshablon" на свое устройство "devshablon" в каталоге "\??", значением которой  является строка "\Device\devshablon".

Таким образом, уже при  загрузке драйвера (в нашем случае, на этапе загрузки ОС) мы имеем три  объекта в памяти: драйвер "\Driver\shablon", устройство  "\Device\shablon" и символьную ссылку на устройство "\??\slshablon".

3) Открытие. Дальше при  запуске приложения вызывается CreateFile. Там есть ссылка на устройство. Из структуры объекта устройства DEVICE_OBJECT извлекаются сведения об  обслуживающем его драйвере. Диспетчер  ввода-вывода формирует пакет  запроса ввода-вывода IRP типа IRP_MJ_CREATE и направляет его драйверу. Так  драйвер узнает о том, что  код режима пользователя пытается  получить доступ к его устройству. Если драйвер не имеет ничего  против, то он возвращает код  успеха. У нашего драйвера есть  специальная процедура диспетчеризации,  которая реагирует на это IRP - DispatchCreateClose (там совмещенная процедура  для открытия и закрытия устройства). В ней в поле Io.Status.Status передается STATUS_SUCCESS, а в Io.Status.Information - 0, т.к.  в этом случае ничего не  нужно передавать. Такой ответ  от драйвера является сигналом диспетчеру объектов о создании виртуального файла. При этом в таблице описателей (handle table) процесса создается новый элемент с указателем на объект "файл", и коду режима пользователя возвращается новый дескриптор.

Если все ОК, то мы сохраняем  дескриптор файла, возвращенный CreateFile, в переменной hDevice.

4) Операции в/выв. Теперь  мы имеем возможность осуществлять  операции управления этим устройством  посредством вызова функций DeviceIoControl. Поскольку драйвер устройства  может в принципе выполнять  много различных задач, необходимо  как-то дифференцировать запросы.  Для этого и предназначен второй  параметр dwIoControlCode, называемый управляющим  кодом ввода-вывода (I/O control code), который  строится по определенным правилам.

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

 Процедура DispatchControl выполняет  необходимые действия, в нашем  случае адрес пакета IRP из регистра ESI Затем передает результат через выходной буфер в приложение.

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

В приложении эти данные форматируются  и выводятся.

5) Закрытие. Как и полагается  поступать с дескрипторами, которые  больше не нужны, вызовом функции  CloseHandle, закрываем описатель устройства.

6) Выгрузка драйвера. Удаляем  символьную ссылку и удаляем  объект устройства.

Комплекс (2) состоит из двух программ:

- приложение, которое обращается  к драйверу за адресом IRP, а  затем этот адрес выводит в  стандартное окно Windows.

- shablon.sys - драйвер.

Драйвер shablon выполняет то, что нельзя сделать на уровне пользователя, в данном случае определяет содержимое регистра esi при работе драйвера.

Приложение в выходном буфере получает содержимое esi, преобразует его для вывода в шестнадцатеричном виде и выводит в стандартное окно Windows.

Если необходимо в драйвере получить информацию из CMOS, то требуется:

- послать в порт 70h смещение  в CMOS, которое нас интересует;

- небольшая задержка;

- взять из порта 71h информацию  в al.

Затем записать эту информацию в выходной буфер.

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

 

В этой лабораторной работе предполагается, что драйвер устанавливается  постоянно в Windows с помощью .inf-файла, используя из Панели управления пункт – Установка оборудования: Добавление нового устройства, Установка вручную, Показать все устройства, Установить с диска, с помощью обзора выбрать файл .inf (драйвер должен быть в той же папке).

Чтобы проверить, что драйвер  установлен, выбираем в панели управления Система, Оборудование, Диспетчер устройств. 

    1. Доступ  к существующим драйверам из приложений пользовательского режима

 

Алгоритм работы приложения работающего  с драйвером

Для работы с драйвером  приложению пользовательского режима необходимо получить манипулятор(хэндл) драйвера. Этот манипулятор можно  получить, используя API-функцию CreateFile или CreateFileA, которая работает с ASCII-символами. Далее используется API-функция DeviceIoControl, которой, в качестве одного из параметров, передается IOCTL-код. IOCTL-код это управляющий код, с помощью которого драйвер узнает об операции, выполнение которой запрашивает приложение, методе передачи параметров и правах доступа, которые необходимы приложению для выполнения этой операции. После того как приложение вызвало DeviceIoControl драйверу отправляется IRP_MJ_DEVICE_CONTROL. После завершения обработки запросы приложению возвращается управление и приложению остается проанализировать ответ драйвера и закрыть открытые дескрипторы.

 

Пример

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

#include <Windows.h>

#include <WinIoCtl.h>

 

int _tmain(int argc, _TCHAR* argv[])

    {

    // 1st part

    DWORD dwBytesReturned = 0;

    char cPartitionStyle[64] = {0};

    PARTITION_INFORMATION_EX piPartitionInfo;

    // 2nd part

    HANDLE hDevice = CreateFileA(

        /*1*/"\\\\.\\c:",

        /*2*/GENERIC_READ | GENERIC_WRITE,

        /*3*/FILE_SHARE_READ | FILE_SHARE_WRITE,

        /*4*/0,

        /*5*/OPEN_EXISTING,

        /*6*/0,

        /*7*/NULL);

    if (hDevice == INVALID_HANDLE_VALUE)

        {

        MessageBoxA(NULL, "CreateFileA error!", "Error", 0);

        return 1;

        }

    // 3rd part

    if (DeviceIoControl(

        /*1*/(HANDLE) hDevice,          

        /*2*/IOCTL_DISK_GET_PARTITION_INFO_EX,

        /*3*/NULL,

        /*4*/0,

        /*5*/&piPartitionInfo,

        /*6*/sizeof(piPartitionInfo),

        /*7*/&dwBytesReturned,

        /*8*/NULL

        ))

        {

        // 4th part

        if(piPartitionInfo.PartitionStyle == PARTITION_STYLE_MBR)

            {

            MessageBoxA(NULL, "PARTITION_STYLE_MBR", "Caption", 0);

            }

        else if(piPartitionInfo.PartitionStyle == PARTITION_STYLE_GPT)

            {

            MessageBoxA(NULL, "PARTITION_STYLE_GPT", "Caption", 0);

            }

        else

            {

            MessageBoxA(NULL, "PARTITION_STYLE_RAW", "Caption", 0);

            }

        }

    else

        {

        MessageBoxA(NULL, "DeviceIoControl error", "Error", 0);

        return 2;

        }

    CloseHandle(hDevice);

    return 0;

    }

 

Разбор примера

// 1st part

Объявляются переменные, необходимые  для работы приложения. PARTITION_INFORMATION_EX это структура, которая описывает информацию о разделе жесткого диска.

typedef struct {

  PARTITION_STYLE PartitionStyle;  // формат раздела

  LARGE_INTEGER   StartingOffset;  // смещение начала раздела

  LARGE_INTEGER   PartitionLength;  // размер раздела

  DWORD           PartitionNumber;  // номер раздела

  BOOLEAN         RewritePartition;  // если раздел перезаписываемый то TRUE

  union {

    PARTITION_INFORMATION_MBR Mbr;  // дополнительная информация MBR Style раздела

    PARTITION_INFORMATION_GPT Gpt;  // дополнительная информация GPT Style раздела

  } ;

} PARTITION_INFORMATION_EX;

 

// 2nd part

В этой части программы  вызывается функция CreateFileA для получения манипулятора, который записывается в переменную hDevice.

// 3rd part

Синхронно вызывается функция DeviceIoControl. Ей передаются:

дескриптор устройства;

IOCTL-код IOCTL_DISK_GET_PARTITION_INFO_EX;

указатель на входной буфер, NULL в нашем случае;

размер входного буфера;

указатель на  выходной буфер;

размер выходного буфера;

указатель на переменную типа DWORD, в которой будет храниться количество возвращаемых байтов;

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