Создание драйверов. Часть 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, только после этого запускайте загрузчик драйвера.
Создание драйверов
Как Вы видите наш драйвер заработал, на этом пока все.