Как писать драйвера

Как писать драйвера (часть 3) / Как писать драйвера

Как писать драйвера

  • Структура драйвера.
  • Когда мы программируем под Windows API, мы ставим на обработку сообщений от Windows функцию WindowProc, которая регистрируется в момент создания класса окна. Примерно так же при создании экземпляра драйвера, в системе происходит регистрация всех необходимых функций.

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

    Для тех, кому тяжело качать Win2000 DDK размером 41Мб мы выложили базисный код драйвера в архиве. Скачайте архив для более полного ознакомления с текстом и работы с нашим текстом.

    Сама DriverEntry запускается один раз, но важность правильной регистрации, думаю, понятна всем. Рассмотрим ее текст полностью.

    NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

    /*++

     Routine Description:

     Arguments:

     Return Value:

    –*/

    {

     NDIS_STATUS Status;

     NDIS_PROTOCOL_CHARACTERISTICS PChars;

     NDIS_MINIPORT_CHARACTERISTICS MChars;

     PNDIS_CONFIGURATION_PARAMETER Param;

     NDIS_STRING Name;

     NDIS_HANDLE WrapperHandle;

     //

     // Register the miniport with NDIS. Note that it is the miniport

     // which was started as a driver and not the protocol. Also the miniport

     // must be registered prior to the protocol since the protocol's BindAdapter

     // handler can be initiated anytime and when it is, it must be ready to

     // start driver instances.

     //

     NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL);

     NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_CHARACTERISTICS));

     MChars.MajorNdisVersion = 4;

     MChars.MinorNdisVersion = 0;

     MChars.InitializeHandler = MPInitialize;

     MChars.QueryInformationHandler = MPQueryInformation;

     MChars.SetInformationHandler = MPSetInformation;

     MChars.ResetHandler = MPReset;

     MChars.TransferDataHandler = MPTransferData;

     MChars.HaltHandler = MPHalt;

     //

     // We will disable the check for hang timeout so we do not

     // need a check for hang handler!

     //

     MChars.CheckForHangHandler = NULL;

     MChars.SendHandler = MPSend;

     MChars.ReturnPacketHandler = MPReturnPacket;

     //

     // Either the Send or the SendPackets handler should be specified.

     // If SendPackets handler is specified, SendHandler is ignored

     //

     // MChars.SendPacketsHandler = MPSendPackets;

     Status = NdisIMRegisterLayeredMiniport(WrapperHandle, &MChars, sizeof(MChars), &DriverHandle);

     ASSERT(Status == NDIS_STATUS_SUCCESS);

     NdisMRegisterUnloadHandler(WrapperHandle, PtUnload);

     //

     // Now register the protocol.

     //

     NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));

     PChars.MajorNdisVersion = 4;

     PChars.MinorNdisVersion = 0;

     //

     // Make sure the protocol-name matches the service-name under which this protocol is installed.

     // This is needed to ensure that NDIS can correctly determine the binding and call us to bind

     // to miniports below.

     //

     NdisInitUnicodeString(&Name, L”SFilter”); // Protocol name

     PChars.Name = Name;

     PChars.OpenAdapterCompleteHandler = PtOpenAdapterComplete;

     PChars.CloseAdapterCompleteHandler = PtCloseAdapterComplete;

     PChars.SendCompleteHandler = PtSendComplete;

     PChars.TransferDataCompleteHandler = PtTransferDataComplete;

     PChars.ResetCompleteHandler = PtResetComplete;

     PChars.RequestCompleteHandler = PtRequestComplete;

     PChars.ReceiveHandler = PtReceive;

     PChars.ReceiveCompleteHandler = PtReceiveComplete;

     PChars.StatusHandler = PtStatus;

     PChars.StatusCompleteHandler = PtStatusComplete;

     PChars.BindAdapterHandler = PtBindAdapter;

     PChars.UnbindAdapterHandler = PtUnbindAdapter;

     PChars.UnloadHandler = NULL;

     PChars.ReceivePacketHandler = PtReceivePacket;

     PChars.PnPEventHandler= PtPNPHandler;

     NdisRegisterProtocol(&Status, &ProtHandle, &PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));

     ASSERT(Status == NDIS_STATUS_SUCCESS);

     NdisIMAssociateMiniport(DriverHandle, ProtHandle);

     return(Status);

    }

    Майкрософт утверждает что нам нужны 4 функции минимум, присутствующие в DriverEntry:

    NdisMInitializeWrapper;

    NdisIMRegisterLayeredMiniport;

    NdisRegisterProtocol;

    NdisIMAssociateMiniport.

    NdisMInitializeWrapper – функция указывает системе NDIS, что пришло время инициировать miniport service в ее системе. Возвращаемое значение необходимо сохранить на будущее.

    Обязательно надо обратить внимание, что если происходит ошибка при инициализации любого объекта, то при уже нормально отработавшей функции NdisMInitializeWrapper нужно вызвать NdisTerminateWrapper для высвобождения ресурса.

    NdisIMRegisterLayeredMiniport Функция, регистрирующая все функции уровня miniport

    NdisRegisterProtocol Функция, регистрирующая все функции протокола

    NdisIMAssociateMiniport. Функция, информирующая NDIS о том, что есть, существует два уровня, минипорт и протокол, и говорящая об экспорте функций, если таковой присутствует.

    Теперь, для полной расшифровки этой шарады, посмотрим, как работает система драйвера, уже на уровне кода.

    После того, как драйвер зарегистрирует функции группы miniport, он должен зарегистрировать полученный HANDLE, для правильной un-регистрации этих функций в момент окончания работы драйвера. Это делает функция NdisMRegisterUnloadHandler. Затем мы регистрируем протокольную группу и связываем эти две группы, сообщая NDIS об их существовании – функцией NdisIMAssociateMiniport.

    На этом в нашем примере функция DriverEntry закачивается. Стоит немного пояснить еще несколько моментов работы инициализации. При работе на уровне kernel не стоит использовать функции: malloc, realloc, memset и т. д. Для этого существуют NdisZeroMemory, NdisAllocateMemory и др. Они более конкретно работают, и предназначены именно для использования в драйверах связанных с NDIS.

    Конкретно сами функции, которые принадлежат группам, мы с вами будем рассматривать в процессе написания кода, да и то не все. Полное их описание вы уже посмотрите в DDK help. А мы с вами посмотрим на неявную третью группу.

    Третья группа функций используется для коммуникации с аппликацией. Иногда и для непосредственного создания пакетов, и их отправки, как в случае с драйвером СОМ порта.

    В нашем случае драйвер пока никак не взаимодействует с уровнем аппликаций, поэтому их нет. Но, любой драйвер нуждается в управлении, наш пример, особенно, ибо он является тестовым и нам необходимо уметь давать нужные команды.

    Поэтому мы создадим и проинициализируем следующие функции:

    extern NTSTATUS FilterOpen(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

    extern NTSTATUS FilterClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

    extern NTSTATUS FilterRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

    extern NTSTATUS FilterWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

    extern NTSTATUS FilterIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

    Внесите их в файл passthru.h.

    Теперь внесем изменения в нашу функцию DriverEntry.

    Объявим массив:

    PDRIVER_DISPATCH MajorFunctions [IRP_MJ_MAXIMUM_FUNCTION + 1];

    Этот массив будет содержать все указатели функций для регистрации.

    И добавим еще несколько переменных:

    NDIS_STRING ntDeviceName; //имя для вызова виртуального device-а для NT

    NDIS_STRING win32DeviceName; // тоже для win32

    PDEVICE_OBJECT deviceObject; // и объект.

    Затем начнем инициализацию. Перед завершающей регистрацией протокольных функций и связкой их с группой miniport и NDIS, функцией NdisIMAssociateMiniport, добавим следующий код:

    // Name of control deviceObject.

    // DeviceName that names the device object.

    NdisInitUnicodeString(&ntDeviceName, L”\\Device\\passthru” );

    // SymbolicName that is the Win32-visible name of the device

    NdisInitUnicodeString(&win32DeviceName, L”\\DosDevices\\passthru” );

    //Создаем строку имени

    NdisZeroMemory(MajorFunctions, sizeof(MajorFunctions));

    // Регистрируем ее

    //Связываем имена функций с массивом

    MajorFunctions[IRP_MJ_CREATE] = FilterOpen;

    MajorFunctions[IRP_MJ_CLOSE] = FilterClose;

    MajorFunctions[IRP_MJ_READ] = FilterRead;

    MajorFunctions[IRP_MJ_WRITE] = FilterWrite;

    MajorFunctions[IRP_MJ_DEVICE_CONTROL] = FilterIoControl;

    //Регистрируем их

    Status = NdisMRegisterDevice(WrapperHandle, &ntDeviceName, &win32DeviceName, MajorFunctions, &deviceObject, &GlobalData.NdisDeviceHandle );

    // проверяем статус

    if (Status != NDIS_STATUS_SUCCESS ) {

     if (GlobalData.ProtHandle) NdisDeregisterProtocol(&Status, GlobalData.ProtHandle);

     if (GlobalData.NdisDeviceHandle) NdisMDeregisterDevice(GlobalData.NdisDeviceHandle);

     if (WrapperHandle) NdisTerminateWrapper(WrapperHandle, NULL);

     return (Status);

    }

    // set access method into deviceObject ( received from NdisMRegisterDevice() )

    // объявление буферизации для связывающих операций

    deviceObject->Flags |= DO_BUFFERED_IO;

    // все.

    Тела функций, типа Filter, объявите сразу после тела функции DriverEntry и оставьте пока пустыми, добавив, только возвращение значения

    return NDIS_STATUS_SUCCESS;

    Источник: http://www.tinlib.ru/kompyutery_i_internet/kak_pisat_draivera/p3.php

    § .18 Программирование драйверов: Пишем первый драйвер. Часть 1

    Как писать драйвера

    Дмитрий Иванов, 18 Ноября 2008 года
    Статья доработана и обновлена 14 мая 2014

    Файлы к статье скачать
    Имя: PS018.zip (ZIP архив)
    Размер: 19 КБ

    Ну вот мы и добрались до самого интересного – сейчас будем писать драйвер. Итак, приступим.

    Для удобства выполнения, всю последовательность действий по написанию драйвера я распишу по пунктам.

    1. Создание директории проекта

    Установив DDK, на Вашем компьютере в директории C:\WINDDK\2600.1106\ должны появиться файлы DDK. В этой директории создадим папку, в которой будут храниться наши проекты. Назовем ее, например, MyDrivers.

    В папке MyDrivers создадим папку FirstDriver – тут будет находится наш первый проект драйвера.

    2. Подготовка файлов проекта

    В папке FirstDriver создайте пустой текстовый файл и переименуйте его под именем FirstDriver.c

    При попытке переименовки со сменой расширения файла, появляется следующее предупреждение:

    Не обращаем внимания на это предупреждение, и нажимаем Да. При этом наш файл примет вид:

    Если же никакого предупреждения не было и переименованный файл так и остался текстовым с именем FirstDriver.c и расширением .txt, то в настройках своийств папки, которые можно найти в Пуск-> Настройка-> Панель управления-> Свойства паки уберите галочку напротив пункта “Скрывать расширения для зарегестрированных типов файлов”. Попробуйте еще раз и все должно быть в порядке.

    Теперь нам надо добавить еще два очень важных файла в наш проект, без которых драйвер нам не сделать. Они называются makefile и sources (обратите внимание, у них нет расширения). Их можно создать самим, но мы сделаем проще: скопируем готовые из какого либо примера проекта драйвера из DDK.

    Например, возьмем их из C:\WINDDK\2600.1106\src\general\cancel\sys\. Итак, копируем из указанной директории эти два файла и вставляем их в нашу папку проекта FirstDriver. Эти файлы управляют процессрм компиляции драйвера.

    Файл makefile оставляем без изменений, а вот sources надо подредактировать.

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

    TARGETNAME=Port TARGETPATH=c:\WINDDK\2600.1106\MyDrivers\FirstDriver TARGETTYPE=DRIVER SOURCES=FirstDriver.c

    Первым параметром идет TARGETNAME, которому мы присвоили Port. Это значит, что когда DDK откомпилирует наш код и создаст драйвер, имя этого файла будет Port.sys Следующем параметром идет TARGETPATH, которому мы указали путь к папке нашего проекта.

    Если Вы устанавливали DDK в другое место, или создали пупку проекта в другой директории, здесь Вам надо это поправить на тот путь, который у Вас. Параметр TARGETTYPE пока оставлю без комментариев. В параметре SOURCES указываем, из каких файлов будет компилироваться драйвер.

    У нас это файл FirstDriver.c, вот мы его и указали.

    3. Код драйвера

    Всю подготовительную работу мы сделали. Можно приступать к самой содержательной части – коду драйвера. Писать мы будем его на Си.

    Еще раз напомню решаемую нами задачу: надо написать драйвер под Windows 2000, XP с помощью которого можно будет работать с портами компьютера (читать и писать данные) .

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

    #include “ntddk.h” #define NT_DEVICE_NAME L”\\Device\TName” #define WIN32_DEVICE_NAME L”\\DosDevices\\MYDRIVER” #define IOCTL_READ CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WRITE CTL_CODE (FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) NTSTATUS CtlCreate(IN PDEVICE_OBJECT, IN PIRP); NTSTATUS CtlClose(IN PDEVICE_OBJECT, IN PIRP); NTSTATUS CtlDispatch(IN PDEVICE_OBJECT,IN PIRP); VOID UnloadOperation(IN PDRIVER_OBJECT pDriverObject); NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) { PDEVICE_OBJECT pDeviceObject; UNICODE_STRING uniNtName; UNICODE_STRING uniWin32Name; RtlInitUnicodeString(&uniNtName, NT_DEVICE_NAME); RtlInitUnicodeString(&uniWin32Name, WIN32_DEVICE_NAME); IoCreateSymbolicLink(&uniWin32Name, &uniNtName); IoCreateDevice(pDriverObject,0,&uniNtName,FILE_DEVICE_UNKNOWN,0,FALSE,&pDeviceObject); pDriverObject->MajorFunction[IRP_MJ_CREATE]=CtlCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE]=CtlClose; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=CtlDispatch; pDriverObject->DriverUnload = UnloadOperation; return STATUS_SUCCESS; } NTSTATUS CtlCreate(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { Irp->IoStatus.Status=STATUS_SUCCESS; Irp->IoStatus.Information=0; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS CtlClose(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { Irp->IoStatus.Status=STATUS_SUCCESS; Irp->IoStatus.Information=0; IoCompleteRequest(Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID UnloadOperation(IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT deviceObject = pDriverObject->DeviceObject; UNICODE_STRING uniWin32NameString; RtlInitUnicodeString( &uniWin32NameString, WIN32_DEVICE_NAME ); IoDeleteSymbolicLink( &uniWin32NameString ); IoDeleteDevice( deviceObject ); return; } NTSTATUS CtlDispatch(IN PDEVICE_OBJECT pDeviceObject,IN PIRP Irp) { PIO_STACK_LOCATION pIrpStack; PUSHORT pIOBuffer; USHORT Port; USHORT ValueToPort; pIrpStack=IoGetCurrentIrpStackLocation(Irp); pIOBuffer=Irp->AssociatedIrp.SystemBuffer; switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_READ: Port=pIOBuffer[0]; pIOBuffer[0]=READ_PORT_UCHAR((PUCHAR)Port); Irp->IoStatus.Information=2; break; case IOCTL_WRITE: Port=pIOBuffer[0]; ValueToPort=pIOBuffer[1]; WRITE_PORT_USHORT((PUSHORT)Port,(USHORT)ValueToPort); Irp->IoStatus.Information=0; break; } Irp->IoStatus.Status=STATUS_SUCCESS; IoCompleteRequest (Irp,IO_NO_INCREMENT); return STATUS_SUCCESS; }

    Представленный код – есть код простейшего драйвера, который может только записать данные в порт и прочесть их от туда. Выглядит страшновато? Ни чего, в следующей статье будем разбираться с тем что в нем понаписано.

    © Дмитрий Иванов 18 ноября 2008 года

    http://www.kernelchip.ru

    Источник: https://kernelchip.ru/pcports/PS018.php

    Как написать драйвер для Windows ?

    Как писать драйвера

    Видимо, перед вами стоит задача взаимодействия с аппаратурой. Наверное, эта аппаратура подключена не к COM порту. Для работы с RS232 Windows API имеет практически все возможности, за исключением взаимодействия с микроконтроллерами по протоколу с девятым адресным битом, да и это возможно осуществить.

      А вот если требуется вывести байт данных в параллельный порт или еще куда, то тут OpenFile(“LPT”,…) уже не работает. На ассемблере тоже бесполезно. Наверное все уже в курсе, что код типа  [ out dx,al ] под 2000-XP ничего кроме ошибки не дает.

    Что же делать ? Надо сделать драйвер под Windows. И тут возникает вопрос, а как бы побыстрее научиться писать драйвера, а еще лучше разработать один драйвер для Windowsи и забыть, как о кошмарном сне.

    Возможно многие сочтут, что  программировать драйвера, а особенно отлаживать драйвера под Windows, не самое приятное  занятие.

    Радует, тот факт, что за последние 5 лет появилась рускоязычная литература на тему разработки драйверов ибо осваивать эту тему по документации Microsoft DDK весьма утопичное занятие.

    Xочется отметить книгу:Программирование драйверов для Windows.” В.П. Солдатов. Издательство: Бином. ISBN: 5-9518-0099-4

    Данная книга не содержит диска с примерами, да и вообще примерами не изобилует, зато дает очень хорошее описание архитектуры ядра Windows, и может служить хорошим стартом для освоения программирования драйверов.

    Понятно, что для создания дравера необходимо инструментальное средство разработки драйверов, и чем оно мощнее, тем быстрее вы достигните цели. Самое примитивное из средств рахзработки драйверов это, это DDK, Driver Development Kit.

    И вот что досадно, если в 2000-2002 году DDK 2000 можно было скачать  бесплатно, то сейчас бесплатно доступна толькоWindows Server 2003 DDK, причем скачать нельзя, можно только получить CD по почте, при этом необходимо иметь кредитку, т.к. DDK то бесплатно, но пересылка диска будет стоить 25$. Деньги не великие,  так что если имеете кредитку и желаете DDK 2003, вот ссылка http://www.microsoft.com/whdc/devtools/ddk/default.mspx

    Более навороченный продукт DDK Suite 3, обойдется уже в 199 $ плюс те же 25 за доставку.

    Найти DDK на рынке за 60 рублей, наверное можно но сложно, слишком редко востребуемый продукт. Ну а если нет кредитки или времени на ожидание диска, не отчаивайтесь, и не благодарите 🙂

    Скачать DDK 2003.

    Скачивайте все файлы что там есть. Распакуете ddk.rar и запускайте ksetup.exe. Спасибо Яндексу за 67 мегабайт.

    Правда писать драйвера при помощи только DDK не самый удобный на сегодня способ, а посему предлагаю обратить внимания на два продукта.

    1. Jungo WinDriver

    2. Numega Driver Studio.

    Оба по сути похожи, а именно позволяют создавать драйвера двух типов.

    • Работающие через некий универсальный драйвер.
    • Полноценные драйвера работающие в режиме ядра.

    Как вы правильно догадались первый способ значительно легче и не требует от программиста глубокого понимания процессов происходящих в ядре, да вообще ни какого понимания не требует.

    Мастер создания драйверов, DriverWizard (Jungo) сгенерит код, который вы сможете вставить в свои программы и обращаться к портам в-в, памяти и даже использовать прерывания.

    Все очень просто: Утром установите WinDriver, вечером ваша программа будет работать с аппаратурой, но остерегайтесь WinDrivera.  Покупать вы его конечно не будете, и при установке, выберете 30-дневную копию. В течении месяца все будет работать и Driver Wizard и все программы его использующие.

    Потом DriverWizard работать перестанет, но к тому времени вы то уже поймете, что он и не нужен. Нужна только библиотека WINDRVR.SYS, которая и обеспечивает взаимодействие с портами Вашей программе. Так вот этот самый WINDRVR.

    SYS, через который будет работать ваша программа, в отличии от DriverWizarda не подает никаких признаков того, что срок истек и он больше работать не будет.

    Драйвер открывается как и раньше, все функции возвращают результат SUCCESS однако программа не работает, потому что драйвер пишет не то и не туда и читает не оттуда и не всегда. При этом можно подумать, что проблемы с аппаратурой, поэтому при использовании WinDrivera лучше отыскать serial number и зарегистрировать. Слишком коварная у него система защиты.

    Numega Driver Studio.имеет в своем составе аналогичный компонент: Driver Agent сгенерит все необходимое для использования универсального драйвера. Вы даже сможете создать свой файл.

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

    Оба пакета содержат средства для написания настоящих драйверов, НО при помощи DriverStudio или WinDriver это делать значительно проще, чем при помощи только DDK, в дополнение они имеют  хорошую документацию касающуюся общих вопросов драйверов, более читаемую чем в DDK. Кстати DDK для работы с ними необходимо установить.

    Установив Numega Driver Works, Вы открываете студию (не Driver , а Microsoft Visual Studio), выбираете там NewProject и видете в окне мастера рядом с
    MFC App Wizard
    NT/WDM Driver Wizard
    Мастер за 9 шагов, сгенерит проект из которого получается драйвер и exe-шник, который этот драйвер тестирует. Кроме того в Driver Studio входит (я бы назвал это Driver Developmen Foundational Classes) набор классов, которые содержат все необходимое для разработки драйверов, так же как MFC для разработки приложений.

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

    Универсальный драйвер, который позволяет обращаться к портам ввода-вывода. Больше он ничего не может, но в большинстве случаев можно обойтись и этим. Драйвер этот называется PortIo.

    sys и входит в состав примеров прилагаемых к Numega Driver Works. Я его скомпилировал, вам остается скачать и использовать. Драйвер предоставляет 4 функции: Open, Close,Read и Write.

    Есть тестовое приложение которое демонстрирует как эти функции использовать.
    Чтобы установить драйвер надо:

    1. Поместить файл portio.sys в C:\WINDOWS\SYSTEM32\DRIVERS\
    2. Прописать его в реестре. Для XP и 2000 это делается командой regini portio.ini в результате в разделе HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services появится раздел PortIo.
      В нем параметры:
      ErrorControl0x00000001
      ImagePath\SystemRoot\System32\Drivers\portio.sys
      Type0x00000001
      Start0x00000002

      Перезагрузите систему.

      Драйвер установлен можно использовать.
      Смотрите тестовое приложение.

      portio.sys

    Источник: http://www.evmsoft.net/ru/ddk.html

    Драйвер — это просто

    Как писать драйвера

    Многие считают что самому создать драйвер для Windows это что-то на грани фантастики. Но на самом деле это не так. Конечно, разработка драйвера для какого-то навороченного девайса бывает не простой задачей. Но ведь тоже самое можно сказать про создание сложных программ или игр.

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

    Будем использовать один из диалектов бейсика — PureBasic. Из коробки он не обучен создавать драйверы, но у него удачный набор файлов, используемых для компиляции и небольшое шаманство позволяет добавить эту возможность. Процесс компиляции состоит из нескольких этапов.

    Если кратко, то он происходит следующим образом: Сначала транслятор «перегоняет» basic-код в ассемблер, который отдается FASM'у (компилятор ассемблера), который создает объектный файл. Далее в дело вступает линкер polink, создающий исполняемый файл.

    Как компилятор ассемблера, так и линкер могут создавать драйверы и если немного изменить опции компиляции, то получим не исполняемый файл, типа EXE или DLL, а драйвер режима ядра (SYS).

    Скачать немного модифицированную бесплатную демо версию PureBasic 4.61 x86 можно на файлопомойке, зеркало.

    Если нужно создать драйвер для x64 системы, качайте эту версию, зеркало. Дистрибутивы имеют небольшие размеры, около 3 МБ каждый. С помощью этой версии можно создавать только драйвера. Скачиваем, распаковываем и запускаем, кликнув по файлу «PureBasic Portable». При этом запустится IDE и вылезет окошко с сообщением что это демо-версия и списком ограничений. Из него наиболее существенным является ограничение числа строк кода, равное 800, а для создания простых драйверов этого может хватить. Остальные ограничения в нашем случае, не существенны. Окно IDE с загруженным кодом драйвера показано на скрине. Компиляция драйвера выполняется через меню «Компилятор» (это если кто не понял). Теперь определимся что будет делать наш первый драйвер. Обычно при изучении программирования начинают с простых вещей, скажем, выполнения математических операций и вывода результата. Вот пусть наш драйвер делает тоже самое, ведь банальная математика производимая в режиме ядра это очень круто! Код драйвера: Declare DriverEntry(*DriverObject, *RegistryPath) !public PureBasicStart!section '.code' code readable executable align 8!PureBasicStart:*A=@DriverEntry()!jmp [p_A] ; Переход в процедуру DriverEntry(). #IOCTL_MyPlus = $200 !extrn PB_PokeL CompilerSelect #PB_Compiler_Processor CompilerCase #PB_Processor_x86 !extrn _IoCompleteRequest@8 ; Объявление импортируемых функций ядра. !extrn _RtlInitUnicodeString@8 !extrn _IoCreateDevice@28 !extrn _IoDeleteDevice@4 !extrn _IoCreateSymbolicLink@8 !extrn _IoDeleteSymbolicLink@4 !extrn _PB_PeekI@4 Import “ntoskrnl.lib” CompilerCase #PB_Processor_x64 !extrn IoCompleteRequest; Объявление импортируемых функций ядра. !extrn RtlInitUnicodeString !extrn IoCreateDevice !extrn IoDeleteDevice !extrn IoCreateSymbolicLink !extrn IoDeleteSymbolicLink !extrn PB_PeekI ImportC “ntoskrnl.lib” CompilerEndSelect ; Импорт функций ядра системы. IoCompleteRequest(*IRP, PriorityBoost) RtlInitUnicodeString(*UString, *String) IoCreateDevice(*DriverObject, DeviceExtensionSize, *UDeviceName, DeviceType, DeviceCharacteristics, Exclusive, *DeviceObject) IoDeleteDevice(*DeviceObject) IoCreateSymbolicLink(*SymbolicLinkName, *DeviceName) IoDeleteSymbolicLink(*SymbolicLinkName)EndImport Structure MyData ; Данные, передаваемые в драйвер. Plus_1.l Plus_2.lEndStructure ; Прцедура обмена данными с программой.Procedure DeviceIoControl(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) Protected *Stack.IO_STACK_LOCATION Protected *InpBuff, *OutBuff Protected InBuffSize, OutBuffSize Protected ntStatus, *MyData.MyData ntStatus = #STATUS_SUCCESS ; Все ОК. *Stack = *pIrp\Tail\Overlay\CurrentStackLocation ; Размеры буферов (см. WinAPI функцию DeviceIoControl()) InBuffSize = *Stack\Parameters\DeviceIoControl\InputBufferLength OutBuffSize = *Stack\Parameters\DeviceIoControl\OutputBufferLength If InBuffSize >= SizeOf(Integer) And OutBuffSize >= 4 Select *Stack\Parameters\DeviceIoControl\IoControlCode Case #IOCTL_MyPlus *Point = *pIrp\SystemBuffer If *Point *MyData = PeekI(*Point) If *MyData Result.l = *MyData\Plus_1 + *MyData\Plus_2 PokeL(*pIrp\SystemBuffer, Result) *pIrp\IoStatus\Information = 4 Else ntStatus = #STATUS_BUFFER_TOO_SMALL *pIrp\IoStatus\Information = 0 EndIf EndIf Default ntStatus = #STATUS_UNSUCCESSFUL *pIrp\IoStatus\Information = 0 EndSelect Else ntStatus = #STATUS_BUFFER_TOO_SMALL ; Размер буфера слишком мал. *pIrp\IoStatus\Information = 0 EndIf *pIrp\IoStatus\Status = ntStatus IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn ntStatusEndProcedure ; Выгрузка драйвера. Вызывается при завершении работы драйвера.Procedure UnloadDriver(*DriverObject.DRIVER_OBJECT) Protected uniDOSString.UNICODE_STRING ; Инициализация объектов-строк. RtlInitUnicodeString(@uniDOSString, ?DosDevices) ; Удаление символьной связи. IoDeleteSymbolicLink (@uniDOSString) ; Удаление устройства. IoDeleteDevice(*DriverObject\DeviceObject)EndProcedure ; Вызывается при доступе к драйверу с помощью функци CreateFile().Procedure CreateDispatch(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) *pIrp\IoStatus\Information = 0 *pIrp\IoStatus\Status = #STATUS_SUCCESS IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn #STATUS_SUCCESSEndProcedure ; Вызывается при осовбождении драйвера функцией CloseHandle().Procedure CloseDispatch(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) *pIrp\IoStatus\Information = 0 *pIrp\IoStatus\Status = #STATUS_SUCCESS IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn #STATUS_SUCCESSEndProcedure ; Процедура загрузки драйвера. Вызывается однократно при его запуске.Procedure DriverEntry(*DriverObject.DRIVER_OBJECT, *RegistryPath.UNICODE_STRING) Protected deviceObject.DEVICE_OBJECT Protected uniNameString.UNICODE_STRING Protected uniDOSString.UNICODE_STRING ; Инициализация объектов-строк. RtlInitUnicodeString(@uniNameString, ?Device) RtlInitUnicodeString(@uniDOSString, ?DosDevices) ; Создание устройства. status = IoCreateDevice(*DriverObject, 0, @uniNameString, #FILE_DEVICE_UNKNOWN, 0, #False, @deviceObject) If status #STATUS_SUCCESS ProcedureReturn status EndIf ; Создане символьной связи между именем этого устройства и именем, ; находящимся в видимой области для user-mode, для того, чтобы ; приложение могло получить доступ к этому устройству. status = IoCreateSymbolicLink(@uniDOSString, @uniNameString) If status #STATUS_SUCCESS IoDeleteDevice(@deviceObject) ProcedureReturn status EndIf ; Указатель на функцию выгрузки драйвера. *DriverObject\DriverUnload = @UnloadDriver() *DriverObject\MajorFunction[#IRP_MJ_CREATE] = @CreateDispatch() *DriverObject\MajorFunction[#IRP_MJ_CLOSE] = @CloseDispatch() ; Указываем какая функция будет обрабатывать запросы WinAPI DeviceIoControl(). *DriverObject\MajorFunction[#IRP_MJ_DEVICE_CONTROL] = @DeviceIoControl() ProcedureReturn #STATUS_SUCCESSEndProcedure ; Имя драйвра (юникод).DataSection Device: !du '\Device\pbDrPlus', 0, 0 DosDevices: !du '\DosDevices\pbDrPlus', 0, 0EndDataSection Может показаться что это куча бессмысленного кода, но это не так. У каждого драйвера должна быть точка входа, обычно у нее имя DriverEntry() и выполнена она в виде процедуры или функции. Как видите, в этом драйвере есть такая процедура. Если посмотрите на начало кода, то в первых строках увидите как ей передается управление. В этой процедуре происходит инициализация драйвера. Там же назначается процедура завершения работы драйвера, которая в нашем случае имеет имя UnloadDriver(). Процедуры CreateDispatch() и CloseDispatch() назначаются обработчиками соединения и отсоединения проги из юзермода. Процедура DeviceIoControl() будет обрабатывать запросы WinAPI функции DeviceIoControl(), являющейся в данном драйвере связью с юзермодом. В конце кода расположена так называемая ДатаСекция (DataSection), в которой находятся имена драйвера, сохраненные в формате юникода (для этого использована одна из фишек ассемблера FASM). Теперь рассмотрим как драйвер будет взаимодействовать с внешним миром. Это происходит в процедуре DeviceIoControl(). В ней отслеживается одно сообщение, а именно — #IOCTL_MyPlus, которое отправляет юзермодная прога, когда ей нужно сложить два числа в режиме ядра (круто звучит, правда?). Когда такое сообщение получено, то считываем из системного буфера, адрес указателя на структуру со слагаемыми, производим сложение и результат помещаем в системный буфер. Собственно это основная задача нашего первого драйвера. Видите сколько понадобилось кода для выполнения простейшей математической операции — сложения двух чисел? А теперь рассмотрим программу, работающую с этим драйвером. Она написана на том же PureBasic. #DriverName = “pbDrPlus”#IOCTL_MyPlus = $200 XIncludeFile “..\DrUserModeFramework.pbi” Structure MyData ; Данные, передаваемые в драйвер. Plus_1.l Plus_2.lEndStructure ; Абсолютный путь к файлу-драйверу.DrFile.s = GetPathPart(ProgramFilename())+#DriverName+”.sys” ; Загружает драйвер и если успешно, то порлучаем его хэндл.hDrv=OpenDriver(DrFile, #DriverName, #DriverName, #DriverName) If hDrv=0 ; Деинсталляция драйвера из системы. Driver_UnInstall(#DriverName) MessageRequester(“”, “Ошибка загрузки драйвера”) EndEndIf ; Обмен данными с драйвером.Procedure.q Plus(hDrv, x1, x2) Protected MyData.MyData, Result, *Point MyData\Plus_1=x1 MyData\Plus_2=x2 *Point = @MyData DeviceIoControl_(hDrv, #IOCTL_MyPlus, @*Point, SizeOf(MyData), @Result, 4, @BytesReturned, 0) ProcedureReturn ResultEndProcedure OpenWindow(1,300,300,140,90,”Title”,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)StringGadget(1,10,10,50,20,””)StringGadget(2,10,40,50,20,””)TextGadget(3,70,30,70,20,””)Repeat ev=WaitWindowEvent() If ev=#PB_Event_Gadget op1=Val(GetGadgetText(1)) op2=Val(GetGadgetText(2)) Result = Plus(hDrv, op1, op2) SetGadgetText(3,Str(Result)) EndIfUntil ev=#PB_Event_CloseWindow ; Если драйвер загружен, то закрываем связь с ним.If hDrv CloseHandle_(hDrv) hDrv=0EndIf ; Деинсталляция драйвера из системы.Driver_UnInstall(#DriverName) При старте программы вызывается функция OpenDriver(), которая загружает драйвер. Для упрощения, имя драйвера, имя службы и описание службы заданы одинаковыми — «pbDrPlus». Если загрузка неудачная, то выводится соответствующее сообщение и программа завершает свою работу. Процедура Plus() осуществляет связь с драйвером. Ей передаются хэндл, доступа к драйверу и слагаемые числа, которые помещаются в структуру и указатель на указатель которой, передается драйверу. Результат сложения чисел будет в переменной «Result».

    Далее следует код простейшего GUI калькулятора, скопированного из википедии.

    Когда закроют окно, то перед завершением работы программы, закрывается связь с драйвером и производится его деинсталляция из системы. Результат сложения чисел 8 и 2 на скриншоте. Исходные коды драйвера и программы, можно найти в папке «Examples», PureBasic на файлопомойке, ссылку на который давал в начале статьи. Там так же найдете примеры драйвера прямого доступа к порам компа и пример работы с памятью ядра. PS. Помните, работа в ядре чревата мелкими неожиданностями аля, BSOD (синий экран смерти), поэтому экспериментируйте осторожно и обязательно всё сохраняйте перед запуском драйвера.

    За возможную потерю данных, я ответственности не несу!

    • PureBasic
    • Driver
    • Драйвер
    • DDK

    Хабы:

    • Системное программирование
    • 23 января 2020 в 08:32
    • 24 мая 2009 в 14:57
    • 24 октября 2007 в 09:40

    Источник: https://habr.com/ru/post/145926/

    Создание драйверов. Часть 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=MyFirstDriverTARGETTYPE=DRIVERTARGETPATH=objSOURCES=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 #include #include using namespace std; void ErrorView(); int main() { cout

    Источник: http://blagin.ru/sozdanie-drajverov-chast-1/

    Поделиться:
    Нет комментариев

      Добавить комментарий

      Ваш e-mail не будет опубликован. Все поля обязательны для заполнения.