[ главная ]   [ рейтинг статей ]   [ справочник радиолюбителя ]   [ новости мира ИТ ]



Ответов: 0
25-02-12 07:01







   Web - программирование
PHP


ASP






XML



CSS

SSI





   Программирование под ОС











   Web - технологии








   Базы Данных









   Графика






Данные




Программирование под ОС / Assembler /

Пишем драйвер WDM на Ассемблере

http://progrex.narod.ru/

Краткое содержание.

  1. Цель работы. Постановка задачи.
  2. API для WDM драйвера.
  3. Пишем рыбу.
  4. Детализация.
  5. Компиляция и сборка.
  6. Как правильно установить драйвер.
  7. Тестовая программа.
  8. Напоследок.

Часть 1. Цель работы. Постановка задачи.

Признаюсь честно: мне нравится Ассемблер. Вернее, даже не сам Ассемблер, а стиль общения с компьютером через него. В сети есть несколько примеров создания драйверов виртуальных устройств VxD на Ассемблере. Но нет ни одного аналогичного примера для драйвера WDM. Так исправим же эту досадную оплошность!

Создание несложного драйвера с использованием только лишь Ассемблера - довольно трудоёмкое занятие. По двум причинам:

  • Отсутствие ассемблерных заголовочных файлов для использования драйверного API.
  • Методически трудная отладка драйверов в системе Windows.

Первая причина может быть некритичной. Были бы руки да голова. Ведь известно, что значительная часть заголовков Win32 API была переведена энтузиастами на Ассемблер. И работа эта немалая.

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

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

Сразу оговорюсь: я не собираюсь подробно объяснять принципы функционирования драйверной системы Windows и растолковывать специальные понятия. Для этого существует специальная литература.

Что нам потребуется? Вот что:

1) Текстовый редактор.

Notepad. Но лучше что-нибудь поудобнее, например, Патриот XP.

2) MS Windows DDK.

DDK содержит почти всё, что требуется для создания драйверов. Но нам важны: справка DDK, Ассемблер MASM 6.1, компоновщик Link, также оттуда мы возьмём библиотеки и заголовочные файлы для C (что с ними делать -- см. далее).

3) Утилита для визуализации отладочного вывода. Я использую DbgView, который можно взять с сайта www.sysinternals.com

4) Delphi для компиляции тестовой программы.

Но вам необязательно набирать текст с нуля. К счастью, я сделал это до вас :) Скачайте файл с исходниками проекта AsmDrv и распакуйте его в подкаталог NTDDKsrcAsmDrv. Вот, кажется, всё. Можно начинать!

Часть 2. API для WDM драйвера.

Большинство функций драйверного API, которые нас интересуют, предоставляются модулем ntoskrnl.exe. Для их использования надо сделать следующее:

1) Объявить типы данных и определить константы.

Большинство определений для C находятся в файлах ntdef.h и wdm.h.

2) Объявить прототипы функций, которые мы намерены использовать.

Эти определения для C также находятся в wdm.h

3) Выполнить сборку драйвера с подключением библиотеки wdm.lib

Все три файла (wdm.h, ntdef.h и wdm.lib) входят в состав Windows DDK.

Я перевёл часть заголовков на Ассемблер и поместил их в файл usewdm.inc, который находится в базовом каталоге проекта.

Часть 3. Пишем рыбу.

3.1. Итак, приступим.

Вы можете проследить за последовательностью и содержанием действий, открыв файл main.asm для просмотра.

Начнём, пожалуй, так:

.586p ; Процессор Intel Pentium, разрешены инструкции защищённого режима
.model flat, stdcall ; Здесь всё ясно. Плоская модель адресации и тип вызовов stdcall.
option casemap:none ; "case-sensitive"

Дальше нужно задействовать файл включений usewdm.inc и библиотеку wdm.lib, чтобы мы смогли использовать драйверный API:

.include usewdm.inc
.includelib wdm.lib

Затем размещаем два сегмента -- данных и кода:

.data

; [...]

.code

; [...]

3.2. Процедура инициализации

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

У нас такая процедура называется DriverEntry. Объявим её как

Driver Entry proc near public, DriverObject:PDRIVER_OBJECT,
RegistryPath:PUNICODE_STRING

DriverObject -- это указатель на служебную структуру, сопоставленную драйверу. Она используется системой для вызова процедур драйвера. Её-то и следует инициализировать -- записать в эту структуру адреса соответствующих процедур нашего драйвера.

Наш драйвер довольно прост. Он будет отрабатывать только 4 стандартных запроса:

  • IRP_MJ_CREATE -- Вызов CreateFile() в приложении пользователя для установления связи с драйвером;
  • IRP_MJ_CLOSE -- Вызов CloseHandle() в приложении пользователя для разрыва связи с драйвером;
  • IRP_MJ_DEVICE_CONTROL -- Вызов DeviceIoControl() в приложении пользователя для запроса выполнения какой-либо функции в драйвере.

Все эти три запроса мы адресуем некоей диспетчерской функции OnDispatch. Мы узнаем о ней позже.

Четвёртый запрос -- на выгрузку. Об этом пойдёт речь ниже.

А пока необходимо сделать ещё 2 важные вещи - создать логический объект устройства при помощи функции IoCreateDevice() и символическую связь, имя которой пользовательские приложения будут использовать для связи с драйвером при помощи функции CreateFile(). Символическая связь создаётся при помощи вызова IoCreateSymbolicLink():

; Инициализируем юникодовые строки с именами устройства и линка

invoke RtlInitUnicodeString, offset NtDeviceName, offset wsNtDeviceName
invoke RtlInitUnicodeString, offset Win32DeviceName, offset wsWin32DeviceName

; [...]

; Создаём логический объект устройства

invoke IoCreateDevice, DriverObject, 0, offset NtDeviceName,
; Проверим, не было ли ошибки.
FILE_DEVICE_UNKNOWN,0,FALSE,offset DeviceObject; cmp eax,STATUS_SUCCESS
jnz @F
; Создаём symbolic link
; в eax останется код результата
invoke IoCreateSymbolicLink, offset Win32DeviceName, offset NtDeviceName
@@:
ret

Итак, только что мы завершили разбор процедуры инициализации.

3.3. Процедура выгрузки.

У нас она реализуется функцией OnUnload. Эта функция производит действия, обратные процедуре инициализации по отношению к связанным объектам: она удаляет символическую связь (вызов IoDeleteSymbolicLink()), и затем логическое устройство, сопоставленное драйверу (IoDeleteDevice()):

; Удаляем символическую связь
invoke IoDeleteSymbolicLink, offset Win32DeviceName
; Удаляем логическое устройство
invoke IoDeleteDevice, DeviceObject

3.4. Главная диспетчерская процедура.

Она называется OnDispatch и объявлена как

OnDispatch proc near, pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP

Здесь нам важен указатель на структуру с данными запроса pIrp. Данная структура довольно сложна. Вы можете найти её объявление в файле usewdm.inc.

Но нам понадобятся лишь некоторые данные.

Сначала мы должны определить код запроса -- он будет один из трёх: IRP_MJ_CREATE, IRP_MJ_CLOSE или IRP_MJ_DEVICE_CONTROL.

Мы получаем этот код из структуры IO_STACK_LOCATION, указатель на которую мы получаем из структуры IRP (в свою очередь, указатель на IRP был передан нам в пераметре pIrp):

mov ebx,pIrp
; Восстанавливаем указатель на структуру IO_STACK_LOCATION
mov eax,(_IRP ptr [ebx]).Tail.Overlay.CurrentStackLocation
mov pIrpStack,eax
mov ebx,pIrpStack
; al -- Код сообщения
mov al,(IO_STACK_LOCATION ptr [ebx]).MajorFunction

Дальше отрабатываем запросы по-разному.

Для IRP_MJ_CREATE и IRP_MJ_CLOSE обработка фиктивная. Мы просто возвращаем код успеха STATUS_SUCCESS в регистре eax.

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

Мы размещаем эти данные в локальных переменных, чтобы потом вызвать вторичную функцию DeviceIoControlHandler, где и будет выполнена обработка.

Часть 4. Детализация.

Детализация заключается в размещении пользовательского кода во вторичной процедуре обработчика IRP_MJ_DEVICE_CONTROL.

Мы будем отрабатывать 2 запроса:

  • IOCTL_USER_REQUEST_1 -- отправка переданной строки в отладочный вывод, и
  • IOCTL_USER_REQUEST_2 -- перевод литер переданной строки в нижний регистр.

Коды запросов объявлены в файле-включении ioctlcodes.inc

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

Часть 5. Компиляция и сборка.

Для компиляции программы следует выполнить командный файл assemble.cmd.

Его содержимое:

....inml.exe -coff -Fl -c -Foasmdrv.obj main.asm

В результате мы получим листинг main.lst и объектный модуль asmdrv.obj.

Дальше мы должны собрать бинарник драйвера из объектного модуля. Для этой цели существует команда link.cmd:

....inlink.exe @linkcmd.rsp

в файле linkcmd.rsp размещены настройки линкера. Полный список выглядит так:

-MACHINE:IX86 
-STACK:32768,4096
-OPT:REF
-OPT:ICF
-INCREMENTAL:NO
-FORCE:MULTIPLE
-RELEASE
-DEFAULTLIB:wdm.lib
-DRIVER
-ALIGN:0x20
-SUBSYSTEM:NATIVE
-BASE:0x10000
-ENTRY:DriverEntry@8
-OUT:disk1asmdrv.sys

asmdrv.obj

В результате сборки мы получаем файл AsmDrv.sys в подкаталоге Disk1.

Часть 6. Как правильно установить драйвер.

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

Это так называемый inf-файл.

Опять-таки, к счастью для вас, я уже написал этот файл -- asmdrv.inf. Вы можете открыть его для просмотра и изучить.

Файл находится в подкаталоге Disk1 проекта.

Ну что же? -- Пробуем установиться.

Открываем Панель управления, запускаем Мастер установки оборудования и указываем ему путь к файлу asmdrv.inf в режиме ручного выбора.

Завершаем установку.

Windows 98 на этом месте может попросить перезагрузки. Не отказывайте ей :)

Windows 2000/XP запускает драйвер сразу.

Вы можете проконтролировать установку, убедившись в наличии устройства "Простейший WDM драйвер на Ассемблере" в списке менеджера устройств.

Поздравляю, если вы всё сделали правильно, наш драйвер -- о, чудо! -- работает.

Часть 7. Тестовая программа.

Проект тестовой программы расположен в подкаталоге TestApp.

Откройте его в Delphi и перекомпилируйте.

В результате вы получите файл AsmDrvTest.exe, который нужно будет запустить.

В принципе, это одна из самых простых программ в мире. Она занимается отправкой драйверу AsmDrv.sys запросов IOCTL_USER_REQUEST_1 и _2 по требованию пользователя, передавая драйверу строку символов.

Работу этой программы рекомендуется изучить самостоятельно.

Одновременно с тестовой программой используйте утилиту DbgView для просмотра отладочного вывода.

Часть 8. Напоследок.

Мы убедились ещё раз, что не боги горшки обжигают. Следует ли писать драйверы WDM на ассемблере? -- Зависит от желания и возможностей. Однако вопрос оставлю открытым.




Комментарии

 Ваш комментарий к данному материалу будет интересен нам и нашим читателям!



Последние статьи: Программирование под ОС / Assembler /

Первая программа на linux
23-05-2010   

Ассемблер, который я буду использовать - NASM (Netwide Assembler, nasm.2y.net). Этот выбор объясняется тем, что: Во первых, он мультиплатформенный, т.е. для портирования программы на разные ОС достаточно только изменить код взаимодействия с системой, а всю программу переписывать не нужно... подробнее

Кол. просмотров: общее - 4752 сегодня - 0

Использование пакета NuMega Driver Studio для написания WDM - драйверов устройств
17-05-2010   

Разработка WDM - драйвера с использованием только DDK является сложной и трудоемкой задачей. При этом приходится выполнять много однотипных операций: создание скелета драйвера, написание inf - файла для его установки, создание приложения для тестирования и т.п... подробнее

Кол. просмотров: общее - 4621 сегодня - 1

Система классов DriverWorks
17-05-2010   

Возможно, идея писать драйвера объектно-ориентированными и кажется на первый взгляд нелогичной. Но при более близком знакомстве с DriverStudio и с драйверами в общем, оказывается, что это не так уж страшно и довольно удобно... подробнее

Кол. просмотров: общее - 4406 сегодня - 1

Объект устройства device object
17-05-2010   

Объекты устройств являются экземплярами класса KDevice или KPnpDevice. Эти классы являются краеугольными камнями архитектуры DriverWorks: они представляют собой как бы программный образ тех устройств, которые присутствуют в системе... подробнее

Кол. просмотров: общее - 4591 сегодня - 1

Объекты для управления оборудованием
17-05-2010   

Как было упомянуто выше, объект устройства управляет работой устройства при помощи специальных объектов, управляющих работой оборудования - портами В/В, прерываниями, памятью, контроллерами ПДП. Драйвер создает эти объекты для представления физических параметров устройства... подробнее

Кол. просмотров: общее - 4638 сегодня - 0



  WWW.COMPROG.RU - 2009-2012 | Designed and Powered by Zaipov Renat | Projects