Создание драйверов. Часть 5. Фильтр драйвер

В этой части цикла статей, рассматривается создание и тестирование фильтр драйвера – это драйвер, основной функцией которого является обработка проходящих через него IRP пакетов, это возможно благодаря многоуровневой модели драйверов Windows, где несколько драйверов можно выстроить в одну цепочку, соответственно добавляя новые возможности нижележащему драйверу.
Это может быть шифрование данных, форматирование данных, обработка ошибок, вплоть до уничтожения информации или ее логирования и скрытия.
Передача и обработка запросов ввода/вывода производится в виде пакетов IRP, которые формирует диспетчер ввода/вывода, находящийся в ядре Windows, где для каждого драйвера выделяется своя собственная структура стека IRP, то есть если в цепочке находится три драйвера, то пакет будет иметь в своей структуре стека три указателя на IO_STACK_LOCATION, для каждого драйвера. Схематически многоуровневая модель показана на рисунке:
Создание драйверов. Часть 5. Фильтр драйвер.
В качестве устройства-цели к которому мы будем добавлять наш фильтр драйвер, выступит \Device\Beep, это устройство которое отвечает за работу системного динамика. Драйвер фильтр должен обрабатывать те же самые функции, что драйвер находящийся ниже его, а также должен иметь одинаковые флаги создаваемого объекта устройства. Ниже приводится код драйвера фильтра и пояснения:

#include "ntddk.h"
PDEVICE_OBJECT    pdDevObjTarg;
PFILE_OBJECT      FileObj = NULL;
/*--------------------------------------------------------------*/
VOID     FilterDriverUnload (IN PDRIVER_OBJECT fdo);
NTSTATUS DispatchPass       (IN PDEVICE_OBJECT fdo, IN PIRP irp);
/*--------------------------------------------------------------*/
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
        NTSTATUS          status;
        UNICODE_STRING    devName;
        PDEVICE_OBJECT    fdo;
        int i;
 
        DbgPrint("DriverEntry...");
 
        for(i=0; i<IRP_MJ_MAXIMUM_FUNCTION; i++)
        {
                DriverObject->MajorFunction[i] = DispatchPass;
        }
 
        DriverObject->DriverUnload = FilterDriverUnload;
        
        status = IoCreateDevice(DriverObject,
                                          sizeof(PDEVICE_OBJECT),NULL,FILE_DEVICE_BEEP,
                                          0,FALSE,&fdo);
        if(!NT_SUCCESS(status))return status;
        fdo->Flags |= 0x44;
 
        RtlInitUnicodeString(&devName,L"\\Device\\Beep");
 
        status = IoGetDeviceObjectPointer(&devName,STANDARD_RIGHTS_ALL,&FileObj,&pdDevObjTarg);
        if(!NT_SUCCESS(status))return status;
        
        DbgPrint("IoGetDeviceObjectPointer -ok-");
 
        IoAttachDeviceToDeviceStack(fdo,pdDevObjTarg);
        if(!NT_SUCCESS(status))return status;
        
        DbgPrint("IoAttachDeviceToDeviceStack -ok-");
 
        
        return STATUS_SUCCESS;
}
 
VOID FilterDriverUnload(IN PDRIVER_OBJECT fdo)
{
        IoDetachDevice(pdDevObjTarg);
        ObDereferenceObject(FileObj);
        IoDeleteDevice(fdo->DeviceObject);
 
        DbgPrint("FilterDriverUnload");
 
}
 
NTSTATUS DispatchPass(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp);
        if(IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
        {
                DbgPrint("-IOCTL-");
                                
        }
        
        IoSkipCurrentIrpStackLocation(irp);
        return IoCallDriver(pdDevObjTarg,irp);
}

PDEVICE_OBJECT pdDevObjTarg; — глобальная переменная для хранения указателя на устройство находящееся ниже фильтра.
PFILE_OBJECT FileObj = NULL; — переменная для хранения указателя на объект-файл, для выгрузки нижележащего драйвера.

for(i=0; iMajorFunction[i] = DispatchPass;
}
В примере мы делаем одну точку входа в драйвер для всех рабочих процедур драйвера, выглядит она вот так:

NTSTATUS DispatchPass(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp);
if(IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
DbgPrint(«-IOCTL-«);

}

IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(pdDevObjTarg,irp);
}
где мы просто получаем указатель на текущий стек IRP пакета, и если к драйверу обратились при помощи функции DeviceIoControl, то мы отлавливаем код рабочей процедуры и выводим сообщение.

IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(pdDevObjTarg,irp);
здесь фильтр просто пропускает пакеты без модификаций.

status = IoCreateDevice(DriverObject,sizeof(PDEVICE_OBJECT),NULL,FILE_DEVICE_BEEP,0,FALSE,&fdo);
if(!NT_SUCCESS(status))return status;
fdo->Flags |= 0x44;
Создаем безымянное устройство и не имеющее символьной ссылки, и добавляем флаги как у нижележащего устройства.

RtlInitUnicodeString(&devName,L»\\Device\\Beep»);
Копируем строку с именем устройства к которому хотим присоединить наш фильтр.

status = IoGetDeviceObjectPointer(&devName,STANDARD_RIGHTS_ALL,&FileObj,&pdDevObjTarg);
if(!NT_SUCCESS(status))return status;
получаем указатель на объект-устройство нижележащего драйвера.

IoAttachDeviceToDeviceStack(fdo,pdDevObjTarg);
if(!NT_SUCCESS(status))return status;
подключаем наш фильтр к устройству.

VOID FilterDriverUnload(IN PDRIVER_OBJECT fdo)
{
IoDetachDevice(pdDevObjTarg);
ObDereferenceObject(FileObj);
IoDeleteDevice(fdo->DeviceObject);

DbgPrint(«FilterDriverUnload»);

}
Функция выгрузки драйвера, в которой удаляется из стека фильтр, удаляется объект-файл и сам фильтр.

Код программы запуска драйвера:

#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,"FilterDriver","FilterDriver",
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
"C:\\FilterDriver\\sys\\objchk\\i386\\FilterDriver.sys",
NULL,NULL,NULL,NULL,NULL);
                if(schService)
                {
                        if(StartService(schService,NULL,NULL))
                        {
                                cout<<"PAUSE: Press any key to exit pause..."<<endl;
                                while(!getch());
 
                                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 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 exit"<<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);
}

Когда программа запуска драйвера запущена и находится в режиме паузы, то можно увидеть что фильтр присоединился к устройству:
Создание драйверов. Часть 5. Фильтр драйвер.
Создание драйверов. Часть 5. Фильтр драйвер.
И теперь достаточно запустить любую программу, которая будет вызывать функцию Beep, например такую:

#ifdef NDEBUG
#pragma optimize("gsy",on)
#pragma comment(linker,"/IGNORE:4078 /IGNORE:4089")
#pragma comment(linker,"/RELEASE")
#pragma comment(linker,"/merge:.rdata=.data")
#pragma comment(linker,"/merge:.text=.data")
#pragma comment(linker,"/merge:.reloc=.data")
#pragma comment(linker,"/ENTRY:WinMainX")
#pragma comment(linker,"/NODEFAULTLIB")
#if _MSC_VER >= 1000
#pragma comment(linker,"/FILEALIGN:0x200")
#endif
#endif
#pragma comment (exestr, "Code by Lazy_elf")
 
const do1=130,re=146,mi=164,fa=174,sol=196,
      la=220,si=246,do2=261,re2=293,mi2=329;
 
#include <windows.h>
int WinMainX(void)
{
        Beep(sol,500/2);
        Beep(do2,1500/2);
        Beep(sol,1000/2);
        Beep(la, 500/2);
        Beep(si, 1500/2);
        Beep(mi, 1000/2);
        Beep(mi, 500/2);
        Beep(la, 1500/2);
        Beep(sol,1000/2);
        Beep(fa, 500/2);
        Beep(sol,1500/2);
        Beep(do1,1000/2);
        Beep(do1,500/2);
        Beep(re, 1000/2);
        Beep(re, 500/2);
        Beep(mi, 500/2);
        Beep(fa, 1000/2);
        Beep(fa, 500/2);
        Beep(sol,500/2);
        Beep(la, 1000/2);
        Beep(si, 500/2);
        Beep(do2,500/2);
        Beep(re2,2000/2);
        return 0;
}

Сразу становится видно, что идет фильтровка кода IRP_MJ_DEVICE_CONTROL:
Создание драйверов. Часть 5. Фильтр драйвер.

Создание драйверов. Часть 5. Фильтр драйвер: 2 комментария

  1. Артур

    Добрый день!

    Необходимо написание фильтрующего драйвера Штрих-ФР-К для задач нашей компании.

    Задача несложная: при отбитии чека (всегда одно наименование, количество Х цену = сумма) по определенному отделу (8)

    8 Наим. 5 X 25 руб

    Итого: 125 руб

    Необходимо разбивать один заказ на два по алгоритму (второй заказ по 10 отделу равен: количество первого наименования Х 1 руб,
    при этом цена товара по 8 отделу уменьшается на 1 руб., чтобы общий итог чека не изменился )

    8 Наим. 5 Х 24 руб

    10 Авансы 5 Х 1 руб

    Итого: 125 руб

    При отбитии чека по любым другим отделам кроме 8, разбивка не нужна, чеки без изменений.

    Прошу сообщить сколько будет стоить такая доработка и какой срок необходим.

    С уважением, специалист по эксплуатации, Артур Фратович.

    +7 (3435) 92 00 83

    1. Lazy_elf Автор записи

      Здравствуйте Артур, к сожалению, не занимаюсь более написанием драйверов и вообще программированием. Советую вам обратится по следующим адресам: http://freelansim.ru и https://www.free-lance.ru/.

Комментарии запрещены.