Помощь в написании студенческих работ
Антистрессовый сервис

Драйвера ядра Windows

КурсоваяПомощь в написанииУзнать стоимостьмоей работы

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

Драйвера ядра Windows (реферат, курсовая, диплом, контрольная)

1. Теоретические сведения

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Код ядра (собственно это и есть сама система) рассматривается как полностью доверительный. Поэтому, будучи загруженным в системное адресное пространство, драйвер становится частью системы и на него не накладываются какие-либо ограничения. В Windows — это практически единственный способ не разработчикам ОС писать системные компоненты уровня ядра.

Для написания и изучения способов разработки драйверов применяют DDK (Device Development Kit) — систему для разработки драйверов.

Помимо документации в DDK входит набор включаемых файлов (*.inc) и библиотечных файлов (*.lib).

Таким образом, Windows поддерживают различные типы драйверов устройств, но, кроме того, существуют драйверы, которые не являются драйверами устройств, а просто позволяют создавать программы, которые в Windows будут работать в режиме ядра, т. е. на 0 уровне привилегий. При этом они полностью получают доступ к ОС и оборудованию.

Рассмотрим самый простой драйвер режима ядра.

#include

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 посылается пересчитанная частота звука (1 193 167/частоту (Гц)) двумя порциями (сначала младшая часть, затем — старшая).

Например, мы хотим получить частоту звука в 100Гц. Пересчитываем частоту,

1 193 167/100 = 11 931

Затем:

mov ax, 11 931

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: winddk3790.1830).

Проект с драйвером должен быть типа MakeFile.

Сделать настройки проекта с помощью Application Settings и в поле Build Command Line записать строку

ddkbuildWNETXP chk. -ceZ

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

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

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

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

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

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

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

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

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

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

Когда приложению требуется операция в/выв, то происходит обращение к драйверу. Для этого приложение может давать запрос на чтение данных из устройства или запись данных на устройство. А если требуется какое-то другое действие, например, опрос или управление устройством, либо что-либо другое, то для этого используется т.н. 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 дескриптор устройства Чтобы передавать данные, модули (приложение и драйвер) должны договориться о протоколе взаимодействия (коды действий, структура буферов — входных и выходных).

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

Код действия в приложении и в драйвере можно записывать в 16-ричном виде, а можно использовать макрос CTL_CODE, как это сделано в примере лаб. работы в файле common.inc.

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

В случае виртуального устройства файловый флаг равен 0.

Тип устройства — FILE_DEVICE_UNKNOWN = 22h

Права доступа — FILE_READ_ACCESS+FILE_WRITE_ACCESS = 1+2=3=11b

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

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

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

— Прямой доступ к данным (без буфера) — 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 ().

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

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

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

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

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

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

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

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

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

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

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

Таким образом, уже при загрузке драйвера (в нашем случае, на этапе загрузки ОС) мы имеем три объекта в памяти: драйвер «Drivershablon», устройство «Deviceshablon» и символьную ссылку на устройство «??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.3 Доступ к существующим драйверам из приложений пользовательского режима

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

DeviceIoControl драйверу отправляется IRP_MJ_DEVICE_CONTROL. После завершения обработки запросы приложению возвращается управление и приложению остается проанализировать ответ драйвера и закрыть открытые дескрипторы.

Пример

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

#include

#include

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, в которой будет храниться количество возвращаемых байтов;

указатель на структуру OVERLAPPED, которая используется для асинхронного вызова функции.

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

// 4th part

Производится анализ и вывод информации. Перед возвращением управления операционной системе можно закрыть открытые дескрипторы. Это позволяет сделать функция CloseHandle (__in HANDLE). Если дескрипторы не закрыть, то это сделает операционная система за Вас.

2. Выполнение курсовой работы

2.1 Шаг 1

Задание: 1. Разработать драйвер ядра с доступом к портам, выполняющий действия согласно варианту и осуществляющий вывод информации в окно Debug View (по варианту), а также приложение, запускающее драйвер.

Воспроизвести 7 сигналов одинаковой длительности с постоянной частотой звучания.

Листинг Kurs_test.cpp

#include «stdafx.h»

#include «windows.h»

#include «stdlib.h»

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

SC_HANDLE hSCManager;

SC_HANDLE hService;

char acDriverPath[256];

hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);

if (hSCManager≠0){

GetFullPathName («beeper.sys», sizeof acDriverPath, acDriverPath, NULL);

// регистрация проигрывателя в таблице диспетчера SCManager

hService=CreateService (hSCManager," beeper11″ ," Nice Melody Beeper11″ ,

SERVICE_START+DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,

SERVICE_ERROR_IGNORE, acDriverPath, NULL, NULL, NULL, NULL, NULL);

if (hService≠0){

StartService (hService, 0, NULL);

// Удаляем запись о драйвере

DeleteService (hService);

CloseServiceHandle (hService);

}else MessageBox (NULL," Can’t register driver", NULL, MB_ICONSTOP);

CloseServiceHandle (hSCManager);

}else MessageBox (NULL," Can’t connect to SCManager", NULL, MB_ICONSTOP);

return 0;

}

Листинг beeper. sys

#include

#define TIMER_FREQUENCY 1 193 167 // 1,193,167 Hz

#define PITCH_C 523 // 523,25 Hz

#define PITCH_Cs 554 // 554,37 Hz

#define PITCH_D 587 // 587,33 Hz

#define PITCH_Ds 622 // 622,25 Hz

#define PITCH_E 659 // 659,25 Hz

#define PITCH_F 698 // 698,46 Hz

#define PITCH_Fs 740 // 739,99 Hz

#define PITCH_G 784 // 783,99 Hz

#define PITCH_Gs 831 // 830,61 Hz

#define PITCH_A 880 // 880,00 Hz

#define PITCH_As 988 // 987,77 Hz

void DO_DELAY (int time){

long i, j;

for (i=0; i<=time*0xfffff; i++) {}

}

void DO_BIG_DELAY (int time){

DO_DELAY (2*time);

}

void Xylophone (int nPitch){

int nTone = TIMER_FREQUENCY/nPitch

_asm {

mov al, 1 011 0110b;//запись управляющего слова в 43h

out 43h, al;//Канал управления звуком — логическая схема, использующая тональный сигнал таймера и программно-управляемые биты системного порта

mov eax, nTone;//запись пересчитанной частоты в 42

out 42h, al;//старшая часть

mov al, ah;//младшая часть

out 42h, al

in al, 61h;//изменение управляющей последовательности — преобразование последних битов в единицы

;//бит 0 — разрешение использования спикера

;//бит 1 — разрешение подключения таймер-2 к спикеру

or al, 11b; speaker ON

out 61h, al

}

DO_DELAY (0x7f);

_asm {

in al, 61h

and al, 1 111 1100b; speaker OFF

out 61h, al

}

}

int DriverEntry (IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pusRegistryPath){

Xylophone (PITCH_C);

Xylophone (PITCH_С);

Xylophone (PITCH_С);

Xylophone (PITCH_С);

Xylophone (PITCH_С);

Xylophone (PITCH_С);

Xylophone (PITCH_С);

return STATUS_DEVICE_CONFIGURATION_ERROR;

}

2.2 Шаг 2

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

В приложении выводим результат в стандартное окно Windows.

Задание

Определить наличие и тип дисковода А: и вывести в стандартное окно, есть ли он и какого типа, в виде текста.

Указание. Драйвер передает в вызывающую программу значение из CMOS, а именно из байта по смещению 10h — наличие и тип дисководов A: (старшая 16-ричная цифра) и B: (младшая). Значения:

0 — нет дисковода, 4 — на 1.44Мб

Листинг shablon. c

#include // various NT definitions

#include

#define IOCTL_GET CTL_CODE (FILE_DEVICE_UNKNOWN, 0×800, METHOD_BUFFERED, FILE_READ_ACCESS + FILE_WRITE_ACCESS)

UNICODE_STRING g_usDeviceName;

UNICODE_STRING g_usSymbolicLinkName;

void DriverUnload (IN PDRIVER_OBJECT pDriverObject){

IoDeleteSymbolicLink (&g_usSymbolicLinkName);

IoDeleteDevice (pDriverObject->DeviceObject);

}

NTSTATUS DispatchCreateClose (PDEVICE_OBJECT pDeviceObject, PIRP pIrp){//обработка MJ_CREATE MJ_CLOSE

pIrp->IoStatus.Status = STATUS_SUCCESS;

pIrp->IoStatus.Information = 0;

IoCompleteRequest (pIrp, IO_NO_INCREMENT);

return STATUS_SUCCESS;

}

NTSTATUS DispatchControl (PDEVICE_OBJECT pDeviceObject, PIRP pIrp){//обработка IRP_MJ_DEVICECONTROL

NTSTATUS status;

int regEsi;

// берем указатель на IO_STACK_LOCATION, в нем на IoControlCode

if (pIrp->Tail.Overlay.CurrentStackLocation->Parameters.DeviceIoControl.IoControlCode ==IOCTL_GET){

//Сравниваем код действия и если это таки наш клиент, то:

_asm{

mov eax, 0

mov al, 10h

out 70h, al

in al, 71h

cbw

cwde

mov regEsi, eax

}

// это наша функциональность — берем содержимое регистра esi

// записываем его в системный буфер

*((int*)pIrp->AssociatedIrp.SystemBuffer) = regEsi;

pIrp->IoStatus.Information = 4; // и задаем размер результат

status = STATUS_SUCCESS;

} else status = STATUS_INVALID_DEVICE_REQUEST;

pIrp->IoStatus.Status = status;

IoCompleteRequest (pIrp, IO_NO_INCREMENT);

return (status);

}

int DriverEntry (IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pusRegistryPath){

NTSTATUS Status;

PDEVICE_OBJECT pDeviceObject;

// инициализируем Unicode-строки

RtlInitUnicodeString (&g_usDeviceName, L" \Device\DevGet");

RtlInitUnicodeString (&g_usSymbolicLinkName, L" \??\sldevGet");

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

pDriverObject->DriverUnload = &DriverUnload;

pDriverObject->MajorFunction[IRP_MJ_CREATE] = &DispatchCreateClose;

pDriverObject->MajorFunction[IRP_MJ_CLOSE] = &DispatchCreateClose;

pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = &DispatchControl;

// создаем логический объект виртуального устройства

Status = IoCreateDevice (pDriverObject, 0, &g_usDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);

if (!NT_SUCCESS (Status)){return Status;}

//создаем символьную ссылку на устройство

Status = IoCreateSymbolicLink (&g_usSymbolicLinkName, &g_usDeviceName);

if (!NT_SUCCESS (Status)){

IoDeleteDevice (pDeviceObject);

return Status;

}

return Status;

}

Листинг курсовая2. cpp

#include «stdafx.h»

#include «windows.h»

#include «stdlib.h»

#define IOCTL_GET CTL_CODE (FILE_DEVICE_UNKNOWN, 0×800, METHOD_BUFFERED, FILE_READ_ACCESS + FILE_WRITE_ACCESS)

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

HANDLE hDevice;

BOOL DevControl;

DWORD dwBytesReturned;

LPVOID adwInBuffer, adwOutBuffer;

char stroka[7];

hDevice = CreateFile («\\.\sldevGet», GENERIC_READ+GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

/*

Параметры:

lpFileName Адрес нульзаканчивающейся строки, которая определяет имя объекта для его создания или открытия.

dwDesiredAccess Тип досупа к объекту. Данный параметр может принимать любую комбинацию из следующих значений:

Значение: Описание:

0 Определяет запрос о доступности объекта на указанном устройстве. Приложение может запросить атрибуты устройства, без доступа к нему.

GENERIC_READ Определяет доступ на чтение из объекта. Данные могут быть прочитаны из файла и файловый указатель может быть перемещен. Комбинируйте с GENERIC_WRITE для доступа к чтению-записи.

GENERIC_WRITE Определяет доступ на запись в объект. Данные могут быть записаны в файл и файловый указатель может быть перемещен. Комбинируйте с GENERIC_READ для доступа к чтению-записи.

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

Значение: Описание:

FILE_SHARE_DELETE Допускает последовательность операций открытия объекта для запроса доступа на удаление.

FILE_SHARE_READ Допускает последовательность операций открытия объекта для запроса доступа на чтение.

FILE_SHARE_WRITE Допускает последовательность операций открытия объекта для запроса доступа на запись

lpSecurityAttributes Адрес структуры SECURITY_ATTRIBUTES, которая определяет может или нет возвращаемый функцией дескриптор быть унаследован дочерним процессом.

Если lpSecurityAttributes равен NULL, то дескриптор не может быть унаследован.

dwCreationDisposition

Значение: Описание:

CREATE_ALWAYS Создает новый файл, всегда.

Если файл существует, функция перезаписывает файл.

CREATE_NEW Создает новый файл. Функция завершится с ошибкой если файл существует.

OPEN_ALWAYS Открыть файл, всегда. Если файл не существует, функция создает его так же, если dwCreationDisposition был бы CREATE_NEW.

OPEN_EXISTING Открывает файл. Функция завершится с ошибкой если файл не существует.

TRUNCATE_EXISTING Открывает файл и обрезает его до нулевого размера. Функция завершится с ошибкой если файл не существует.

dwFlagsAndAttributes Флаги и атрибуты файла.

Когда открывается существующий файл, то CreateFile игнорирует файл-шаблон.

Возвращаемые значения:

Если функция успешна, возвращается открытый дескриптор указанного файла. Если указанный файл существует до вызова функции и параметр dwCreationDisposition равен CREATE_ALWAYS или OPEN_ALWAYS, вызов GetLastError вернет ERROR_ALREADY_EXISTS, даже если функция успешна. Если файл не существует перед вызовом, GetLastError вернет 0 (ноль).

При ошибке, функция вернет INVALID_HANDLE_VALUE. Для получения дополнительной информации об ошибке, вызывайте GetLastError.

*/

if (hDevice ≠ 0){

DevControl = DeviceIoControl (hDevice, IOCTL_GET,&adwInBuffer, sizeof (adwInBuffer),&adwOutBuffer, sizeof (adwOutBuffer), &dwBytesReturned, NULL);

/*

hDevice — это хэндл, возвpащенный CreateFile’ом.

dwIocontrolCode — это значение, котоpое указывает опеpацию, котоpую должен выполнить.

lpInBuffer — это адpес буфеpа, котоpый содеpжит данные, необходимые для выполнения опеpации, указанные в dwIoControlCode. Если опеpация не тpебует данных, вы можете пеpедать NULL.

nInBufferSize — это pазмеp в байтах данных в буфеpе, на котоpый указывает lpInBuffer.

lpOutBuffer — это адpес буфеpа, котоpый заполнится выходными данными, когда опеpация будет успешно пpоизведена. Если опеpация не пpедполагает выходных данных, это поле должно pавняться NULL’у.

nOutBufferSiz — это pазмеp в байтах буфеpа, на котоpый указывает lpOutbuffer.

lpBytesReturned — адpес пеpеменной типа dword, котоpая получит pазмеp данных, вписанных в lpOutBuffer.

lpOverlapped — это адpес стpуктуpы OVERLAPPED, если вы хотите, чтобы опеpация была асинхpонной. Если вы хотите подождать, пока опеpация будет выполнена, поместите NULL в это поле.

*/

if ((DevControl ≠ 0)&&(dwBytesReturned ≠ 0)){

wsprintf ((LPSTR) stroka, «%X», adwOutBuffer);//запись строки в буфер (adwOutBuffer —> stroka)

if (stroka =="100″) MessageBox (NULL," Found 1.44 Mb" ," Yermakov FDD scaner", MB_OK);

else MessageBox (NULL," Not found" ," Yermakov FDD scaner", MB_OK);

}else MessageBox (NULL," Can’t send control code", NULL, MB_OK);

CloseHandle (hDevice);

}else MessageBox (NULL, «Dev is not present», NULL, MB_ICONSTOP);

return 0;

}

драйвер ядро компьютерный программа

2.3 Шаг 3

Вариант

Задание

Вывести номер одного из разделов жесткого диска (IOCTL_DISK_GET_PARTITION_INFO_EX)

Листинг курсовая. cpp

#include

#include

#include

BOOL GetPartitionNumber (PARTITION_INFORMATION_EX *pex)

{

HANDLE hDevice; // дескриптор проверяемого устройства

BOOL bResult; // флажок результата

DWORD junk;

hDevice = CreateFile (TEXT («\\.\c:»), // открываемое устройство

GENERIC_READ | GENERIC_WRITE, // доступ к устройству

FILE_SHARE_READ |FILE_SHARE_WRITE, // режим совместного использования

NULL, // атрибуты безопасности по умолчанию

OPEN_EXISTING, // расположение

0, // атрибуты файла

NULL); // не копировать атрибуты файла

if (hDevice == INVALID_HANDLE_VALUE){ // невозможно открыть устройство

printf («CreateFile () failed! n»);

return (FALSE);

}

bResult = DeviceIoControl (hDevice, // запрошенное устройство

IOCTL_DISK_GET_PARTITION_INFO_EX, // выполняемая операция

NULL, // указатель на входной буфер

0, // размер входного буфера

pex, sizeof (*pex), // выходной буфер

&junk, // количество возвращаемых байтов

(LPOVERLAPPED) NULL); // синхронизация ввода/вывода (I/O)

CloseHandle (hDevice);

return (bResult);

}

int main (int argc, char *argv[])

{

PARTITION_INFORMATION_EX pex; // структура устройства

/*

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;

*/

BOOL bResult;

bResult = GetPartitionNumber (&pex);

if (bResult){ printf («PartitionNumber = %dn», pex. PartitionNumber);

}else{ printf («GetPartitionNumber () failed. Error %d.n», GetLastError ());}

system («PAUSE»);

return ((int)bResult);

}

2.4 Шаг 4

1) Объединить всю функциональность, разработанную на шагах 1−3, в один комплекс программ.

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

— Наш драйвер встраивается в систему и загружается на этапе загрузки Windows.

— Приложение запрашивает у драйвера необходимую (заданную на этапе 2) информацию, драйвер передает эту информацию, при этом также включается динамик на некоторое время и проигрывается мелодия (заданная на этапе 1). Затем в приложении производится запрос к существующему в системе драйверу (в соответствии с вариантом к этапу 3).

2) Оформить пояснительную записку, в которой должны быть описаны все 4 этапа, а также — теоретические сведения для каждого этапа.

#include «stdafx.h»

#include «windows.h»

#include «stdlib.h»

#define IOCTL_GET CTL_CODE (FILE_DEVICE_UNKNOWN, 0×800, METHOD_BUFFERED, FILE_READ_ACCESS + FILE_WRITE_ACCESS)

BOOL GetPartitionNumber (PARTITION_INFORMATION_EX *pex)

{

HANDLE hDevice; // дескриптор проверяемого устройства

BOOL bResult; // флажок результата

DWORD junk;

hDevice = CreateFile (TEXT («\\.\c:»), // открываемое устройство

GENERIC_READ | GENERIC_WRITE, // доступ к устройству

FILE_SHARE_READ |FILE_SHARE_WRITE, // режим совместного использования

NULL, // атрибуты безопасности по умолчанию

OPEN_EXISTING, // расположение

0, // атрибуты файла

NULL); // не копировать атрибуты файла

if (hDevice == INVALID_HANDLE_VALUE){ // невозможно открыть устройство

printf («CreateFile () failed! n»);

return (FALSE);

}

bResult = DeviceIoControl (hDevice, // запрошенное устройство

IOCTL_DISK_GET_PARTITION_INFO_EX, // выполняемая операция

NULL, // указатель на входной буфер

0, // размер входного буфера

pex, sizeof (*pex), // выходной буфер

&junk, // количество возвращаемых байтов

(LPOVERLAPPED) NULL); // синхронизация ввода/вывода (I/O)

CloseHandle (hDevice);

return (bResult);

}

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

SC_HANDLE hSCManager;

SC_HANDLE hService;

char acDriverPath[256];

HANDLE hDevice;

BOOL DevControl;

DWORD dwBytesReturned;

LPVOID adwInBuffer, adwOutBuffer;

char stroka[7];

PARTITION_INFORMATION_EX pex; // структура устройства

BOOL bResult;

hDevice = CreateFile («\\.\sldevGet», GENERIC_READ+GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

if (hDevice ≠ 0){

DevControl = DeviceIoControl (hDevice, IOCTL_GET,&adwInBuffer, sizeof (adwInBuffer),&adwOutBuffer, sizeof (adwOutBuffer), &dwBytesReturned, NULL);

if ((DevControl ≠ 0)&&(dwBytesReturned ≠ 0)){

wsprintf ((LPSTR) stroka, «%X», adwOutBuffer);//запись строки в буфер (adwOutBuffer —> stroka)

if (stroka =="100″) MessageBox (NULL," Found 1.44 Mb" ," Yermakov FDD scaner", MB_OK);

else MessageBox (NULL," Not found" ," Yermakov FDD scaner", MB_OK);

hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);

if (hSCManager≠0){

GetFullPathName («beeper.sys», sizeof acDriverPath, acDriverPath, NULL);

// Регистрация музыканта в таблицах SCM

hService=CreateService (hSCManager," beeper11″ ," Nice Melody Beeper11″ ,

SERVICE_START+DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,

SERVICE_ERROR_IGNORE, acDriverPath, NULL, NULL, NULL, NULL, NULL);

if (hService≠0){

StartService (hService, 0, NULL);

DeleteService (hService);

CloseServiceHandle (hService);

}else MessageBox (NULL," Can’t register driver", NULL, MB_ICONSTOP);

CloseServiceHandle (hSCManager);

}else MessageBox (NULL," Can’t connect to SCManager", NULL, MB_ICONSTOP);

}else MessageBox (NULL," Can’t send control code", NULL, MB_OK);

CloseHandle (hDevice);

}else MessageBox (NULL, «Dev is not present», NULL, MB_ICONSTOP);

bResult = GetPartitionNumber (&pex);

if (bResult){ printf («PartitionNumber = %dn», pex. PartitionNumber);

}else{ printf («GetPartitionNumber () failed. Error %d.n», GetLastError ());}

system («PAUSE»);

return ((int)bResult);

}

3. Работа приложения

Рисунок 3.1. Драйвер из шага 2

Рисунок 3.2. Драйвер из шага 3

Показать весь текст
Заполнить форму текущей работой