- •Написание драйверов методические указания
- •Лабораторная работа №1 Изучение простого драйвера "в-стиле-nt"
- •1.1. Общие методические указания по выполнению лабораторной работы № 1
- •1.2. Выполнение работы
- •1.2.1. Структура драйвера
- •1.2.2. Компиляция и сборка драйвера Example.Sys
- •1.2.3. Тестирование драйвера
- •Лабораторная работа №2 Изучение драйвера виртуального устройства. Программно-управляемая передача данных
- •2.1. Общие методические указания по выполнению лабораторной работы № 2
- •2.2. Выполнение работы
- •2.2.1. Механизмы передачи данных
- •2.2.2. Структура драйвера
- •2.2.3. Компиляция и сборка драйвера pio.Sys
- •2.2.4. Тестирование драйвера
- •Библиографический список
- •394026 Воронеж, Московский просп., 14
- •Составители: а.М. Нужный н.И. Гребенникова
1.2.2. Компиляция и сборка драйвера Example.Sys
Для компиляции и сборки драйвера утилитой Build пакета DDK потребуется создать два файла описания проекта — Makefile и Sources.
Файл Makefile. Этот файл управляет работой программы Build и в нашем случае имеет стандартный вид (его можно найти практически в любой директории примеров DDK), а именно:
!INCLUDE $(NTMAKEENV)\makefile.def
Файл Sources. Файл sources отражает индивидуальные настройки процесса компиляции и сборки. В нашем случае файл Sources чрезвычайно прост и имеет вид:
TARGETNAME=Example
TARGETTYPE=DRIVER
TARGETPATH=obj
SOURCES=init.cpp
Данный файл задает имя выходного файла Example, параметр TARGETNAME. Поскольку проект (TARGETTYPE) имеет тип DRIVER, то выходной файл будет иметь расширение .sys. Промежуточные файлы будут размещены во вложенной директории .\obj. Строка SOURCES задает единственный файл с исходным текстом — это файл init.cpp.
Для компиляции «чистовой» версии драйвера нужно запустить .exe файл:
Пуск – Все программы – Development Kits – Windows DDK 3790.1830 – Build Environments – Windows XP – Windows XP Free Build Environment.exe
Для компиляции отладочной версии (данная версия позволяет получать отладочные сообщения от драйвера в программе DebugView) драйвера нужно запустить:
Пуск – Все программы – Development Kits – Windows DDK 3790.1830 – Build Environments – Windows XP – Windows XP Checked Build Environment.exe
Когда программы запущена, нужно выполнить консольную команду перехода к директории, в которой находятся файлы с кодом драйвера и файлы описания проекта и вызвать команду build (например):
C:\WINDDK\3790.1830>cd C:\Example\
C:\Example>build
После выполнения этих действий начнется компиляция и сборка драйвера. В случае ошибок компиляции или сборки вывод будет содержать и их диагностику. Рабочее окно сборки драйвера под Windows XP DDK версии checked изображено на рисунке 1.
Рис. 1. Рабочее окно сборки драйвера под Windows XP DDK версии checked
1.2.3. Тестирование драйвера
Работа с драйвером Example.sys
Как уже было сказано, из всех возможных способов инсталляции и запуска драйвера Example.sys, ниже будет использован способ тестирования с применением тестирующего консольного приложения, которое само будет выполнять инсталляцию и удаление драйвера (прибегая к вызовам SCM Менеджера). Для поэтапного ознакомления с процессом взаимодействия драйвера и обращающегося к нему приложения рекомендуется запустить программу ExampleTest под отладчиком в пошаговом режиме.
Перед запуском тестирующей программы ExampleTest рекомендуется загрузить программу DebugView, чтобы в ее рабочем окне наблюдать сообщения, поступающие непосредственно из кода драйвера Example.sys (отладочной сборки).
Приложение, работающее с драйвером
Перед тем, как приступить к тестированию драйвера путем вызова его сервисов из приложения, следует это приложение создать, хотя бы в минимальном виде, как это предлагается ниже. И хотя драйвер можно успешно запускать программой Monitor, воспользуемся функциями SCM, поскольку это будет существенно полезнее для будущей практики. Для загрузки и выгрузки драйверов используется диспетчер управления службами SC Manager (Service Control Manager). Прежде чем начать работу с интерфейсом SC, необходимо получить дескриптор диспетчера служб. Для этого следует обратиться к функции OpenSCManager(). Дескриптор диспетчера служб необходимо использовать при обращении к функциям CreateServise() и OpenService(). Дескрипторы, возвращаемые этими функциями необходимо использовать при обращении к вызовам, имеющим отношение к конкретной службе. К подобным вызовам относятся функции ControlService(), DeleteService() и StartService(). Для освобождения дескрипторов обоих типов используется вызов CloseServiceHandle().
Загрузка и запуск службы подразумевает выполнение следующих действий:
Обращение к функции OpenSCManager() для получения дескриптора диспетчера.
Обращение к CreateServise() для того, чтобы добавить службу в систему. Если такой сервис уже существует, то CreateServise() выдаст ошибку с кодом 1073 (код ошибки можно прочитать GetLastError()) данная ошибка означает, что сервис уже существует и надо вместо CreateServise() использовать OpenService().
Обращение к StartService() для того, чтобы перевести службу в состояние функционирования.
Если служба запустилась успешно, то можно вызвать CreateFile(), для получения дескриптора, который мы будем использовать уже непосредственно при обращении к драйверу.
По окончании работы необходимо дважды обратиться к CloseServiceHandle() для того, чтобы освободить дескрипторы диспетчера и службы.
Если на каком-то шаге этой последовательности возникла ошибка, нужно выполнить действия обратные тем, которые были выполнены до возникновения ошибки.
Надо помнить о том, что при обращении к функциям подобным CreateServise(), необходимо указывать полное имя исполняемого файла службы (в нашем случае полный путь и имя Example.sys).
Листинг 8.
//////////////////////////////////////////////
// (Файл ExampleTest.cpp)
// Консольное приложение для тестирования драйвера Example.sys
///////////////////////////////////////////////////// Заголовочные файлы, которые необходимы в данном //приложении:
#include <windows.h>
#include <stdio.h>
#include <winioctl.h>
#include <tchar.h>
// Имя объекта драйвера и местоположение //загружаемого файла
#define DRIVERNAME _T("Example")
#define DRIVERBINARY _T("C:\\Example\\ExampleDriver\\Example.sys")
// Функция установки драйвера на основе SCM вызовов
BOOL InstallDriver( SC_HANDLE scm, LPCTSTR DriverName, LPCTSTR driverExec )
{
SC_HANDLE Service =
CreateService ( scm, // открытый дескриптор к SCManager
DriverName, // имя сервиса - Example
DriverName, // для вывода на экран
SERVICE_ALL_ACCESS, // желаемый доступ
SERVICE_KERNEL_DRIVER, // тип сервиса
SERVICE_DEMAND_START, // тип запуска
SERVICE_ERROR_NORMAL, // как обрабатывается ошибка
driverExec, // путь к бинарному файлу
// Остальные параметры не используются - укажем //NULL
NULL, // Не определяем группу загрузки
NULL, NULL, NULL, NULL);
if (Service == NULL) // неудача
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_EXISTS) {/* уже установлен */}
// более серьезная ощибка:
else printf ("ERR: CanТt create service. Err=%d\n",err);
// (^^ Этот код ошибки можно подставить в //ErrLook):
return FALSE;
}
CloseServiceHandle (Service);
return TRUE;
}
// Функция удаления драйвера на основе SCM вызовов
BOOL RemoveDriver(SC_HANDLE scm, LPCTSTR DriverName)
{
SC_HANDLE Service =
OpenService (scm, DriverName, SERVICE_ALL_ACCESS);
if (Service == NULL) return FALSE;
BOOL ret = DeleteService (Service);
if (!ret) { /* неудача при удалении драйвера */ }
CloseServiceHandle (Service);
return ret;
}
// Функция запуска драйвера на основе SCM вызовов
BOOL StartDriver(SC_HANDLE scm, LPCTSTR DriverName)
{
SC_HANDLE Service =
OpenService(scm, DriverName, SERVICE_ALL_ACCESS);
if (Service == NULL) return FALSE; /* open fail */
BOOL ret =
StartService( Service, // дескриптор
0, // число аргументов
); // указатель на аргументы
if (!ret) // неудача
{
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
ret = TRUE; // OK, драйвер уже работает!
else { /* другие проблемы */}
}
CloseServiceHandle (Service);
return ret;
}
// Функция останова драйвера на основе SCM вызовов
BOOL StopDriver(SC_HANDLE scm, LPCTSTR DriverName)
{
SC_HANDLE Service =
OpenService (scm, DriverName, SERVICE_ALL_ACCESS );
if (Service == NULL)
// Невозможно выполнить останов драйвера
{
DWORD err = GetLastError();
return FALSE;
}
SERVICE_STATUS serviceStatus;
BOOL ret =
ControlService(Service, SERVICE_CONTROL_STOP, &serviceStatus);
if (!ret)
{
DWORD err = GetLastError();
// дополнительная диагностика
}
CloseServiceHandle (Service);
return ret;
}
#define SCM_SERVICE
/* ^^^^^^^^^^^^^^^^ вводим элемент условной компиляции, при помощи которого можно отключать использование SCM установки драйвера в тексте данного приложения. (Здесь Ц использование SCM включено.) Основная функция тестирующего приложения. Здесь минимум внимания уделен диагностике ошибочных ситуаций. В действительно рабочих приложениях следует уделить этому больше внимания*/
int __cdecl main(int argc, char* argv[])
{
#ifdef SCM_SERVICE
// Используем сервис SCM для запуска драйвера.
BOOL res; // Получаем доступ к SCM :
SC_HANDLE scm = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if(scm == NULL) return -1; // неудача
// Делаем попытку установки драйвера
res = InstallDriver(scm, DRIVERNAME, DRIVERBINARY );
if(!res)
// Неудача, но возможно, он уже инсталлирован
printf("Cannot install service");
res = StartDriver (scm, DRIVERNAME );
if(!res)
{
printf("Cannot start driver!");
res = RemoveDriver (scm, DRIVERNAME );
if(!res)
{
printf("Cannot remove driver!");
}
CloseServiceHandle(scm); // Отключаемся от SCM
return -1;
}
#endif
HANDLE hHandle = // Получаем доступ к драйверу
CreateFile( "\\\\.\\Example",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL );
if(hHandle==INVALID_HANDLE_VALUE)
{
printf("ERR: can not access driver Example.sys !\n");
return (-1);
}
DWORD BytesReturned;
// Переменная для хранения числа переданных байт
// Последовательно выполняем обращения к драйверу
// с запросами на запись/чтение данных:
unsigned char xdata = 0x21;
if( !WriteFile( hHandle,
&xdata,
sizeof(xdata),
&BytesReturned,
NULL) )
{
printf( "Error with byte receive!" );
return(-1);
}
// Вывод диагностического сообщения в консольном //окне:
printf("Byte send: BytesTransferred=%d xdata=%d\r\n",
BytesReturned, xdata);
// Получаем 1 байт данных из драйвера.
// По окончании данного вызова переменная //xdata должна
// содержать значение 0x21:
if( !ReadFile( hHandle,
&xdata,
sizeof(xdata),
&BytesReturned,
NULL))
{
printf( "Error with byte receive!" );
return(-1);
}
// Вывод диагностического сообщения в //консольном окне:
printf("Byte receive: BytesReturned=%d xdata=%d\r\n",
BytesReturned, xdata);
// Закрываем дескриптор доступа к драйверу:
CloseHandle(hHandle);
#ifdef SCM_SERVICE
// Останавливаем и удаляем драйвер. //Отключаемся от SCM.
res = StopDriver (scm, DRIVERNAME );
if(!res)
{
printf("Cannot stop driver!");
CloseServiceHandle(scm);
return -1;
}
res = RemoveDriver (scm, DRIVERNAME );
if(!res)
{
printf("Cannot remove driver!");
CloseServiceHandle(scm);
return -1;
}
CloseServiceHandle(scm);
#endif
return 0;
}