Написание драйверов на си

[Перевод] Пишем USB-драйверы для заброшенных устройств

Написание драйверов на си

Недавно на eBay мне попалась партия интересных USB-девайсов (Epiphan VGA2USB LR), которые принимают на вход VGA и отдают видео на USB как веб-камера. Меня настолько обрадовала идея, что больше никогда не придётся возиться с VGA-мониторами, и учитывая заявленную поддержку Linux, я рискнул и купил всю партию примерно за 20 фунтов (25 долларов США).

Получив посылку, я подключил устройство, но оно даже не подумало появиться в системе как UVC. Что не так?

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

К сожалению, поддержка драйверов именно для этих устройств закончилась в Linux 4.9. Таким образом, его не увидит ни одна из моих систем (Debian 10 на Linux 4.

19 или последняя версия LTS Ubuntu на Linux 5.0).

Но ведь это можно исправить, верно? Конечно, файлы ведь идут в пакете DKMS, который по требованию собирает драйвер из исходного кода, как и многие обычные драйверы…

Печально. Но здесь не так.

Внутри пакета оказался только предварительно скомпилированный бинарник vga2usb.o. Я начал его изучать, прикидывая сложность реверс-инжиниринга, и нашёл несколько интересных строк:

$ strings vga2usb.ko | grep 'v2uco' | sort | uniqv2ucom_autofirmwarev2ucom_autofirmware_ezusbv2ucom_autofirmware_fpga

Так это на самом деле FPGA-on-a-stick? Как же заставить работать нечто подобное?

Ещё одной забавной и слегка тревожной находкой стали строки с параметрами закрытого ключа DSA. Это заставило меня задуматься: что же он может защищать внутри драйвера?

$ strings vga2usb.ko | grep 'epiphan' | sort | uniqepiphan_dsa_Gepiphan_dsa_Pepiphan_dsa_QЧтобы изучить драйвер в его нормальной среде, я поднял виртуальную машину с Debian 9 (последний поддерживаемый релиз) и сделал KVM USB Passthrough, чтобы дать прямой доступ к устройству. Затем установил драйвер и убедился, что он работает.

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

Для этого я загрузил на хост виртуальной машины модуль usbmon и запустил Wireshark для захвата USB-трафика на устройство и с него во время запуска и захвата видео.

Я обнаружил, что при запуске на устройство передаётся большое количество мелких пакетов, прежде чем оно начинает захватывать картинку. Вероятно, оно действительно основано на платформе FPGA без хранилища данных. Каждый раз после подключения драйвер передавал на устройство прошивку в виде битстрима FPGA.

Я убедился в этом, открыв одну из коробок:

Поскольку для «загрузки» устройства нужно отправить ему битстрим/прошивку, придётся поискать его в предварительно скомпилированных бинарниках. Я запустил binwalk -x и начал искать какие-нибудь сжатые объекты (zlib). Для этого я написал скрипт поиска hex-последовательностей — и указал три байта из перехваченного пакета.

$ bash scan.sh “03 3f 55″trying 0.elftrying 30020trying 30020.zlibtrying 30020.zlib.decompressed…trying 84BB0trying 84BB0.zlibtrying 84BB0.zlib.decompressedtrying AA240trying AA240.zlibtrying AA240.zlib.decompressed000288d0 07 2f 03 3f 55 50 7d 7c 00 00 00 00 00 00 00 00 |./.?UP}|……..|trying C6860trying C6860.zlib

После распаковки файла AA240.zlib оказалось, что там недостаточно данных для полного битстрима. Поэтому я решил захватить прошивку из пакетов USB.

Считывать USB-пакеты из файлов pcap может и tshark, и tcpdump, но обе сохраняют их лишь частично. Поскольку у каждой утилиты были разные части головоломки, я написал небольшую программу, которая объединяет выходные данные обеих программ в структуры go, чтобы воспроизвести пакеты обратно на устройство.

В этот момент я заметил, что загрузка происходит в два этапа: сначала USB-контроллер, а затем FPGA.

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

В итоге я решил проблему, тщательно изучив pcap с учётом времени ответа на каждый пакет — и заметил большую разницу во времени одного конкретного пакета:

Оказалось, что из-за небольшой опечатки запись происходила в неправильную область устройства. Будет мне уроком, как вводить значения вручную…

Тем не менее, на устройстве наконец-то замигал светодиод! Огромное достижение!

Было относительно просто реплицировать те же пакеты, которые запускали передачу данных, так что я смог написать конечную точку USB Bulk и мгновенно сбросить данные на диск!

Вот тут и начались настоящие сложности. Потому что после анализа оказалось, что данные не были явно закодированы каким-либо образом.

Для начала я запустил perf для общего представления о трассировке стека драйверов во время работы:

Хотя я мог выловить функции с данными фреймов, но понять кодировку самих данных никак не удавалось.

Чтобы лучше понять, что происходит внутри настоящего драйвера, я даже попробовал инструмент Ghidra от АНБ:

Хотя Ghidra невероятна (когда я впервые использовал её вместо IDA Pro), но всё ещё недостаточно хороша, чтобы помочь мне понять драйвер. Для реверс-инжиниринга требовался другой путь.

Я решил поднять виртуальную машину Windows 7 и взглянуть на драйвер Windows, вдруг он подбросит идеи. И тогда заметил, что для устройств имеется SDK. Один из инструментов оказался особенно интересным:

Источник: https://SE7EN.ws/perevod-pishem-usb-drayvery-dlya-zabroshennykh-ustroystv/

Пишем свой первый Windows-драйвер

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

В этой статье я решил разобрать простую программу-драйвер, которая делает только то, что пишет отладочное сообщение «Hello world!» при старте драйвера и «Goodbye!» при завершении, а также опишу те средства разработки, которые нам понадобятся для того, чтобы собрать и запустить драйвер. Итак, для начала приведем текст этой несложной программы.

  1. // TestDriver.

    c

  2. #include
  3. NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
  4. VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject);
  5. #pragma alloc_text(INIT, DriverEntry)
  6. #pragma alloc_text(PAGE, UnloadRoutine)
  7. NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
  8. {
  9.   DriverObject->DriverUnload = UnloadRoutine;
  10.   DbgPrint(«Hello world!»);
  11.   return STATUS_SUCCESS;
  12. }
  13. VOID UnloadRoutine(IN PDRIVER_OBJECT DriverObject)
  14. {
  15.   DbgPrint(«Goodbye!»);
  16. }

* This source code was highlighted with Source Code Highlighter.

Итак, теперь сначала разберемся, что делает каждая инструкция. Перво-наперво мы подключаем заголовочный файл ntddk.h. Это один из базовых подключаемых файлов во всех драйверах: в нем содержатся объявления типов NTSTATUS, PDRIVER_OBJECT, PUNICODE_STRING, а также функции DbgPrint.

Далее идет объявление двух функций: DriverEntry и UnloadRoutine. Расскажу о первой поподробнее.

Итак, как уважаемые читатели знают, в каждой программе есть точка входа, в программах на языке C это функция main или WinMain.

В драйвере роль точки входа выполняет функция DriverEntry, которая получает на вход указатель на структуру DriverObject, а также указатель на строку реестра, соответствующую загружаемому драйверу.

Структура DriverObject содержит множество полей, которые определяют поведение будущего драйвера. Наиболее ключевые из них — это указатели на так называемые вызываемые (или callback) функции, то есть функции, которые будут вызываться при наступлении определенного события. Одну из таких функций мы определяем: это функция UnloadRoutine.

Указатель на данную функцию помещается в поле DriverUnload. Таким образом при выгрузке драйвера сначала будет вызвана функция UnloadRoutine. Это очень удобно, когда драйвер имеет какие-то временные данные, которые следует очистить перед завершением работы.

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

Для того, чтобы выводить отладочные сообщения мы используем функцию DbgPrint, которая имеет синтаксис, аналогичной функции printf из пользовательского режима (userspace).

В этом простом примере мы использовали также директивы #pragma alloc_text(INIT, DriverEntry) и #pragma alloc_text(PAGE, UnloadRoutine).

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

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

Вы можете задуматься, мол ну с первой-то директивой понятно, типа оптимизация и все такое, но зачем мы используем вторую директиву, зачем помечать код как возможный к выгрузке в файл подкачки? Поясню этот вопрос: каждый процесс в системе имеет такой параметр, как IRQL (подробнее читаем по ссылке Interrupt request level ибо это материал отдельной статьи), то есть некоторый параметр, отвечающий за возможность прерывания процесса: чем выше IRQL тем меньше шансов прервать выполнение процесса. Возможности процесса так же зависят от IRQL: чем выше IRQL тем меньше возможности процесса, это вполне логично, т.е. такой подход побуждает разработчиков выполнять только самые необходимые операции при высоком IRQL, а все остальные действия делать при низком. Вернемся к основной теме, о том, почему мы делаем для функции UnloadRoutine возможность выгрузки в файл подкачки: все опять же сводится к оптимизации: работа с файлом подкачки недоступна при высоком IRQL, а процедура выгрузки драйвера гарантированно выполняется при низком IRQL, поэтому мы специально указываем руками что код функции выгрузки драйвера можно поместить в своп.

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

  • WDK или DDK
  • Текстовый редактор
  • Программа DbgView — бесплатная программа для просмотра отладочных сообщений, получаемых от драйверов, ее можно найти на сайте sysinternals
  • Программа KmdManager — бесплатная программа для регистрации, запуска и тестирования драйвера, ее можно найти на сайте wasm.ru

Теперь последовательность действий: сначала мы пишем два файла, один называется MAKEFILE, с таким содержимым

################################################## # DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source # file to this component. This file merely indirects to the real make file # that is shared by all the driver components of the Windows NT DDK # !INCLUDE $(NTMAKEENV)\makefile.def ##################################################

а второй называется sources и содержит в себе следующее:

################################################## TARGETNAME=TestDriver TARGETTYPE=DRIVER SOURCES=TestDriver.c ##################################################

Эти файлы нужны для сборки драйвера. Да, забыл сказать, что в WDK нет встроенной среды разработки, поэтому и нужен текстовый редактор, чтобы набирать текст драйверов. Для этой цели можно использовать и Visual Studio (некоторые даже интегрируют возможность сборки драйверов из VS), и любой другой текстовый редактор.

Сохраняем код драйвера в файл TestDriver.c и кладем его в ту же директорию, что и файлы MAKEFILE и souces.

После этого запускаем установленный build environment (это командная строка с заданными переменными окружения для компиляции драйвера; она входит в WDK, и запустить ее можно как-то так: «Пуск->Программы->Windows Driver Kits->….->Build Environments->WindowsXP->Windows XP x86 Checked Build Environment»).

Переходим в директорию, куда мы положили файл с драйвером (у меня это C:\Drivers\TestDriver) с помощью команды cd (у меня команда выглядит следующим образом: cd C:\Drivers\TestDriver) и набираем команду build.

Данная команда соберет нам драйвер TestDriver.sys и положит его в папку «objchk_wxp_x86\i386».

Теперь нам нужно запустить программу DbgView чтобы увидеть сообщения, которые будет выдавать драйвер. После запуска данной программы нам нужно указать, что мы хотим просматривать сообщения из ядра (Capture->Capture Kernel).

Теперь запукаем программу KmdManager, указываем путь к нашему драйверу (файл TestDriver.sys) нажимаем кнопку Register, затем Run. Теперь драйвер зарегистрирован в системе и запущен. В программе DbgView мы должны увидеть наше сообщение «Hello World!». Теперь завершаем работу драйвера кнопкой Stop и убираем регистрацию драйвера кнопкой Unregister. Кстати, в DbgView дожна появиться еще одна строка. Итак, чего же мы достигли: мы написали, скомпилировали и запустили свой первый Windows-драйвер! Добавлю только, что при написании сложный драйверов для отладки используется двухмашинная конфигурация, когда на одном компьтере ведется написание драйвера, а на другом — запуск и тестирование. Это делается из-за того, что неправильно написанный драйвер может обрушить всю систему, а на ней может быть очень много ценных данных. Часто в качестве второго компьютера используется виртуальная машина.

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

Создание драйверов. Часть 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/

§ .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

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

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

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

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