Создание драйверов. Часть 1. В данной статье мне хотелось бы показать, как создавать драйвера, если Вам необходима теоретическая информация то советую прочесть книгу Дэвида Соломона и Марка Руссиновича «Внутреннее устройство Microsoft Windows 2000». Для начала мы сделаем простой драйвер для Windows 2000, который послужит нам каркасом для дальнейших экспериментов. Для работы понадобится «Windows 2000 Driver Development Kit», именно с помощью данного пакета создаются драйвера, в его состав входят компилятор, линкер, заголовочные файлы, lib-файлы, документация, полезные инструменты и конечно множество примеров.
Для написания и редактирования исходных текстов драйверов можно воспользоваться редактором, входящим в состав MS Visual Studio или Блокнотом, незаменимым инструментом программиста. Для просмотра объектов операционной системы понадобиться программа WinObj, автором которой является Марк Руссинович, программу можно найти на сайте http://www.sysinternals.com/, так же с этого адреса можно скачать программу DebugView, которая позволяет просматривать диагностические сообщения, посылаемые функциями OutputDebugString и DbgPrint.
Начнем, создайте на диске С:\ папку и назовите её «MyFirstDriver», затем в ней папку «sys» и «loader», это нужно для того чтобы легче было работать с путями к бинарным файлам. В папке «sys» создайте три файла, с именами «makefile», «sources» и «MyFirstDriver.c». В файле «makefile» напишите следующее:
!INCLUDE $(NTMAKEENV)\makefile.def
Этот файл нужен для работы программы Build. В файле «sources» напишите следующее:
TARGETNAME=MyFirstDriver
TARGETTYPE=DRIVER
TARGETPATH=obj
SOURCES=MyFirstDriver.c
Этот файл нужен для настройки процесса компиляции:
TARGETNAME – имя драйвера;
TARGETTYPE – тип бинарного файла, который мы хотим создать, может иметь следующие значения: DRIVER, GDI_DRIVER, MINIPORT, LIBRARY, DYNLINK (for DLLs).
TARGETPATH – путь для папки с временными файлами;
SOURCES – путь к файлу с исходным текстом драйвера.
Теперь переходим к исходному файлу «MyFirstDriver.c», в нем напишите следующее:
#include "ntddk.h" /*--------------------------------------------------------------*/ NTSTATUS MyFirstDriverCreate (IN PDEVICE_OBJECT fdo, IN PIRP irp); NTSTATUS MyFirstDriverClose (IN PDEVICE_OBJECT fdo, IN PIRP irp); NTSTATUS MyFirstDriverControl (IN PDEVICE_OBJECT fdo, IN PIRP irp); VOID MyFirstDriverUnload (IN PDRIVER_OBJECT fdo); /*--------------------------------------------------------------*/ NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) { NTSTATUS status; PDEVICE_OBJECT fdo; UNICODE_STRING devName; UNICODE_STRING devLink; #if DBG DbgPrint("in DriverEntry"); #endif DriverObject->DriverUnload = MyFirstDriverUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = MyFirstDriverCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyFirstDriverClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyFirstDriverControl; RtlInitUnicodeString(&devName,L"\\Device\\MyFirstDriver"); status = IoCreateDevice(DriverObject,sizeof(PDEVICE_OBJECT),&devName,FILE_DEVICE_UNKNOWN,0,FALSE,&fdo); if(!NT_SUCCESS(status))return status; #if DBG DbgPrint("IoCreateDevice status ok"); #endif RtlInitUnicodeString(&devLink,L"\\??\\MyFirstDriver"); status = IoCreateSymbolicLink(&devLink,&devName); if(!NT_SUCCESS(status)) { IoDeleteDevice( fdo ); return status; } #if DBG DbgPrint("DriverEntry STATUS_SUCCESS"); #endif return STATUS_SUCCESS; } /*--------------------------------------------------------------*/ NTSTATUS MyFirstDriverCreate(IN PDEVICE_OBJECT fdo, IN PIRP irp) { #if DBG DbgPrint("Run MyFirstDriverCreate"); #endif irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS MyFirstDriverClose(IN PDEVICE_OBJECT fdo, IN PIRP irp) { #if DBG DbgPrint("Run MyFirstDriverClose"); #endif irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS MyFirstDriverControl (IN PDEVICE_OBJECT fdo, IN PIRP irp) { #if DBG DbgPrint("Run MyFirstDriverControl"); #endif irp->IoStatus.Status = STATUS_SUCCESS; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID MyFirstDriverUnload(IN PDRIVER_OBJECT fdo) { UNICODE_STRING deviceLink; RtlInitUnicodeString(&deviceLink,L"\\??\\MyFirstDriver"); IoDeleteSymbolicLink(&deviceLink); IoDeleteDevice(fdo->DeviceObject); #if DBG DbgPrint("Run MyFirstDriverUnload - delete link"); #endif }
Как Вы видите драйвер очень прост и имеет основные функции:
DriverEntry – данная функция есть в каждом драйвере и является точкой входа.
Параметры:
IN PDRIVER_OBJECT DriverObject – адрес объекта драйвера;
IN PUNICODE_STRING RegistryPath – путь в реестре, где содержится информация о драйвере.
Тип возвращаемого значения NTSTATUS, на самом деле это просто тип LONG, может принимать следующие значения:
STATUS_SUCCESS – успешно;
STATUS_N – где, N – номер ошибки.
В функции DriverEntry происходит заполнение полей структуры DriverObject:
DriverUnload – здесь регистрируется функция выгрузки драйвера;
MajorFunction[IRP_MJ_CREATE] – здесь регистрируется функция обработки запросов открытия драйвера;
MajorFunction[IRP_MJ_CLOSE] — здесь регистрируется функция обработки запросов закрытия драйвера;
MajorFunction[IRP_MJ_DEVICE_CONTROL] — здесь регистрируется функция обработки IOCTLзапросов. Так же в DriverEntry создается символьная ссылка и устройство. Для вывода отладочной информации с помощью функции DbgPrint мы используем возможности условной компиляции, то есть конструкция:
#if DBG
DbgPrint("");
#endif
будет присутствовать только в версиях Checked.
Для компиляции запустите «Checked Build Environment»,
Появится консольное окно, далее с помощью команды «cd» перейдите в папку с файлами драйвера и наберите команду build –cZ
Теперь драйвер готов, следующим шагом будет создание программы, которая будет динамически загружать наш драйвер с помощью SCM (Service Control Manager), подробно о нем написано в MSDN`е. Для этого создайте обыкновенную консольную версию программы и поместите все файлы проекта в папку «loader», в исходнике загрузчика напишите следующее:
#include <windows.h> #include <iostream> #include <conio.h> using namespace std; void ErrorView(); int main() { cout<<" ------------------- "<<endl;cout<<"| Loader is running |"<<endl;cout<<" ------------------- "<<endl; SC_HANDLE sch = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); if(sch) { SC_HANDLE schService = CreateService(sch,"MyFirstDriver", "MyFirstDriver",SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL, "C:\\MyFirstDriver\\sys\\objchk\\i386\\MyFirstDriver.sys",NULL,NULL,NULL,NULL,NULL); if(schService) { if(StartService(schService,NULL,NULL)) { HANDLE hHandl = CreateFile("\\\\.\\MyFirstDriver",GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hHandl != INVALID_HANDLE_VALUE) { DWORD dwReturnBytes; if(!DeviceIoControl(hHandl,NULL,/*(*/NULL,NULL,/*)(*/NULL,NULL/*)*/,&dwReturnBytes,NULL)) {cout<<"Error ControlService..."<<endl;ErrorView();} /**/if(CloseHandle(hHandl)) {cout<<"CloseHandle - status ok"<<endl;}else{cout<<"Error CloseHandle..."<<endl;ErrorView();}/**/ SERVICE_STATUS ServiceStatus; /**/if(ControlService(schService,SERVICE_CONTROL_STOP,&ServiceStatus)) {cout<<"ControlService - status ok"<<endl;}else{cout<<"Error ControlService..."<<endl;ErrorView();}/**/ /**/if(DeleteService(schService)) {cout<<"DeleteService - status ok"<<endl;}else{cout<<"Error DeleteService..."<<endl;ErrorView();}/**/ /**/if(CloseServiceHandle(schService)) {cout<<"CloseServiceHandle - status ok"<<endl;}else{cout<<"Error CloseServiceHandle..."<<endl;ErrorView();}/**/ /**/if(CloseServiceHandle(sch)) {cout<<"CloseServiceHandle - status ok"<<endl;}else{cout<<"Error CloseServiceHandle..."<<endl;ErrorView();}/**/ }else{cout<<"Error CreateFile..."<<endl;ErrorView();} }else{cout<<"Error StartService..."<<endl;ErrorView();} }else{cout<<"Error CreateService..."<<endl;ErrorView();} }else{cout<<"Error OpenSCManager..."<<endl;ErrorView();} cout<<" ------------------- "<<endl; cout<<"| Loader is stopped |"<<endl; cout<<" ------------------- "<<endl; cout<<"Press any key to continue"<<endl; while(!getch()); return 0; } void ErrorView()/*Функция вывода сообщений об ошибках...*/ { LPVOID lpMsgBuf;FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,0,NULL); CharToOem((LPTSTR)lpMsgBuf,(LPTSTR)lpMsgBuf); cout<<(LPCTSTR)lpMsgBuf<<endl; LocalFree(lpMsgBuf); }
Перед тем как запускать программу, запустите DebugView, только после этого запускайте загрузчик драйвера.
Как Вы видите наш драйвер заработал, на этом пока все.