Основні властивості і особливості драйвера VxD, Система, C / C + +, статті

Сенс і призначення драйвера


VxD розшифровується як Virtual x Driver – Драйвер віртуального
пристрої x. Оскільки Windows побудована на концепції віртуальних машин,
кожній віртуальній машині потрібно надати власний «образ» кожного з
наявних у системі пристроїв. Наприклад, віртуальний драйвер клавіатури
VKD одноосібно керує роботою фізичного пристрою – клавіатури,
отримуючи всі переривання при натисканні клавіш, включаючи / вимикаючи індикатори і т.п.
Кожна з віртуальних машин – системна, в якій працюють програми Windows, і
вікна DOS «бачать» тільки незалежні копії фізичного пристрою, кожна з
віртуальних машин може вважати, що має в своєму користуванні повноцінне
фізичний пристрій.


Поняття віртуалізації пристроїв (реальних чи штучних, віртуальних)
означає лише те, що робота кожної віртуальної машини з цим пристроєм
знаходиться під контролем драйвера пристрою. Найпростіший прийом віртуалізації –
заборона іншим віртуальним машинам звертатися до регістрів і функцій
пристрою, поки воно використовується «захопила» його віртуальною машиною. Таким
чином віртуалізуються, наприклад, послідовні (COM) порти.


Більш складний і зручний для користувача вигляд віртуалізації – впорядкування
доступу до пристрою, як це робиться для відеоадаптера в повноекранному режимі.
Режим адаптера, стан екрану і інші параметри запам’ятовуються драйвером для
кожної віртуальної машини, і відновлюються при перемиканні адаптера з одного
машини на іншу.


Найбільш складною і зручною є повна віртуалізація, яку можна
спостерігати на прикладі роботи додатків DOS у вікнах Windows. При цьому кожна
віртуальна машина DOS «бачить» практично повноцінний апаратний відеоадаптер,
може звертатися до його регістрів, безпосередньо працювати з відеопам’яттю тощо; VxD,
створює цей образ, коректно обробляє всі стандартні види звернень, а
стан відеопам’яті відображає у вікні Windows заданого розміру.


Крім свого основного призначення – віртуалізації пристроїв для віртуальних
машин – VxD виконують в Windows безліч інших функцій. Можна сказати, що VxD
в Windows 9x реалізує поняття «службовий привілейований процес» – з його
допомогою реалізуються практично всі завдання, які неможливо коректно
виконати за допомогою звичайного програми або DLL. При відсутності для
будь-якого апаратного пристрою стандартного системного уявлення
(Наприклад, вимірювального адаптера вузького застосування) для нього розробляється
VxD, за допомогою якого додатки можуть отримати доступ до функцій
пристрою, не заважаючи при цьому один одному.


Всі VxD в Windows управляються головним системним VxD – диспетчером
віртуальних машин
(VMM – Virtual Machine Manager). VMM надає
основний набір сервісних функцій, за допомогою яких інші VxD виконують
необхідні їм операції.


Ім’я та ідентифікатор драйвера


Кожен VxD в системі повинен мати ім’я (Name) і ідентифікатор
(Id). Ім’я драйвера (пристрої) складається з восьми-менш символів; воно часто
збігається з ім’ям файлу драйвера, однак це не обов’язково. Ідентифікатор
драйвера являє собою 16-розрядне число, присвоєне Microsoft даному
драйверу (власним або створеному сторонніми розробниками).


За ідентифікатором можливо однозначне знаходження драйвера в системі, що
виключає колізії в іменах. Драйвери, що не мають офіційно присвоєного
ідентифікатора, використовують нульової ідентифікатор, чому їх пошук можливий
тільки по імені. Оскільки ім’я вибирається розробником – можливі збіги і,
отже, – колізії при пошуку та обігу, тому рекомендується вибирати
нетривіальні імена для драйверів, які будуть працювати «на всю систему», а не
використовуватися локально окремим додатком.


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


Драйвери, що не мають ідентифікаторів, не можуть підтримувати механізм
сервісних функцій. Розробник VxD може самостійно вибрати для свого
драйвера ідентифікатор з числа вільних, якщо використання драйвера
планується на обмеженій кількості комп’ютерів. Випуск в широке користування
драйверів з «самостійних» ідентифікаторами не рекомендується.


Статичні і динамічні драйвери


За способом завантаження VxD поділяються на статичні – Файли один
раз в процесі старту Windows і працюють до її закриття, і динамічні
– Завантажувати і вивантажувати на запит системи та програм. Динамічні VxD
використовуються в тих випадках, коли постійна присутність драйвера в системі
необов’язково, однак із зрозумілих причин вони не можуть брати участь у початковій
ініціалізації системи. Статичні VxD можуть брати участь в ініціалізації
системи, однак не можуть бути вивантажені в процесі її роботи.


Порядок завантаження статичних драйверів


Статичні драйвери завантажуються системою в певному порядку при цьому
основні драйвери повинні завантажуватися першими, а після цього – залежні від них
драйвери більш високого рівня. Для цієї процедури кожен драйвер має параметр
Init Order – Числову константу, визначальну місце драйвера в списку
завантаження, яка відбувається в порядку зростання значень параметра. Системним
драйверам призначені певні значення, що відображають їх залежність один від
одного. Якщо порядок завантаження не має сенсу – використовується нульове значення
параметра; такі драйвери завантажуються після завершення ініціалізації «номерних»
VxD.


Системні повідомлення драйверу


Система взаємодіє з драйвером шляхом передачі йому системних
повідомлень
про завантаження / розвантаження драйвера, а також при настанні певних
системних подій, які можуть зажадати втручання драйвера
(Створення / видалення віртуальної машини, програми, завдання (thread), зміна
поточної віртуальної машини / завдання, перезавантаження системи, поява нового
пристрої тощо).


Для обробки системних повідомлень кожен драйвер повинен містити
диспетчер повідомлень – Процедуру, яка отримує управління при передачі
повідомлення драйверу. Процедурі передається код повідомлення і його параметри, після
завершення обробки процедура повертає результат, який визначає наступні
дії системи.


Сервісні функції драйвера


Кожен драйвер, що має ідентифікатор, може підтримувати набір сервісних
функцій
, Доступних для інших VxD в системі. Якщо VxD являє
якесь віртуальне пристрій, ці функції служать для управління пристроєм
або просто втілюють будь-які операції, заради яких створювався драйвер.


Сервісні функції драйвера мають номери починаючи з нуля, за якими їх можуть
викликати інші VxD. Драйвер надає системі таблицю адрес процедур –
обробників функцій, звернення до яких відбувається шляхом виклику процедури по
індексу з таблиці.


Обов’язкова для підтримки лише функція з нульовим номером – Get Version
(Запит версії). Підтримка та призначення інших функцій залишена на
розсуд розробника драйвера.


Механізм виклику сервісних функцій використовує ідентифікатор драйвера, по
якому відбувається пошук потрібного драйвера в системі. Тому драйвери, не
мають ідентифікатора, не можуть бути викликані для обробки сервісних
функцій.


Інтерфейс з прикладними програмами


Для взаємодії з прикладними програмами VxD може надавати три види
API (Application Program Interface – інтерфейс прикладних програм):



Для обробки запитів від 16-розрядних програм в драйвері передбачаються
дві різні функції – на запити від віртуальних машин DOS і для запитів від
додатків Win16. Запити від додатків Win32 передаються у вигляді системних
повідомлень їх загального диспетчеру.


16-розрядні програми отримують доступ до своїх API допомогою функції
0x1684 програмного переривання 2F, Яка повертає адресу шлюзу (gate)
для виклику VxD. Пошук потрібного драйвера можливий як за ідентифікатором, так і по
імені; пошук по імені був введений пізніше, тому документований не у всіх
описах функції int 2F.


При виконанні виклику через шлюз відбувається перемикання в 32-розрядний режим
із збереженням всіх регістрів викликала віртуальної машини (клієнта) у
спеціальній структурі, після чого управління передається відповідній
процедурі обробки в VxD. При поверненні відбувається відновлення значень
регістрів, які можуть бути модифіковані процедурою обробки.


Програми Win32 отримують доступ до API за допомогою функції CreateFile,
завантажує VxD, якщо він динамічний, і відкриває його інтерфейс. Пошук
драйвера відбувається на ім’я драйвера, імені його файлу або по імені ключа
реєстру, що описує драйвер.


Звернення до обробника Win32 API в драйвері відбувається при виклику
додатком функції DeviceIoControl. При цьому виконується перемикання в
режим ядра і передача диспетчеру системних повідомлень драйвера повідомлення
W32_DEVICEIOCONTROL. Для обміну даними передаються два незалежних покажчика
(Буфер параметрів і буфер результату), які можуть посилатися і на одну і ту ж
область пам’яті. Якщо драйвер підтримує механізм асинхронних (overlapped)
операцій, фактичне завершення операції може відбуватися незалежно від
моменту повернення управління з диспетчера.


Структура і функціонування драйвера


VxD являє собою 32-розрядний виконуваний файл формату LE (Linear
Executable), який є окремим випадком DLL. Система може викликати VxD
трьома різними способами:



  1. Через диспетчер системних повідомлень.
  2. Через таблицю обробників сервісних функцій.
  3. Через точки входу інтерфейсів прикладних програм.

Функції драйвера можуть також викликатися в результаті запиту самого драйвера
– У відповідь на переривання від пристроїв, виклик перехоплених функцій або
програмних переривань, при настанні різних подій і т.п.


Секції файлу драйвера


Завантаження драйвера стандартним чином може ділитися на секції
(Сегменти) з різними атрибутами (резидентний, спочатку завантажений,
знищуваний і т.п.). Документація Microsoft стверджує, що секції повинні мати
певні імена, однак це зроблено лише для зручності користування стандартним
макросами і фактично система розпізнає лише самі атрибути секцій.


Рекомендовані наступні імена і класи секцій для модуля VxD:



Блок описувача драйвера


Ключовим елементом драйвера є структура даних DDB (Device
Descriptor Block – блок описувача пристрої), що описує параметри
драйвера. DDB містить такі найважливіші поля:



Символічне ім’я, призначене DDB, повинно бути описано в модулі драйвера в
Як першу експортованої точки входу (exported entry). Сам DDB повинен
знаходитися в одній секції резидентного коду разом з диспетчером системних
повідомлень.


Контексти


Контекстом називається стан процесора і системи, що відбиває
стан будь-якої віртуальної машини, програму та завдання. До контексту
відноситься стан регістрів процесора, стека, віртуального адресного
простору, системних таблиць і т.п. При перемиканні з задачі на задачу, з
однієї віртуальної машини на іншу відбувається зміна контексту – збереження
колишнього та завантаження нового.


Оскільки VxD не є повноцінним системним процесом і не має
власного контексту, його виклик завжди відбувається в контексті будь-якої
віртуальної машини (системної або DOS). Якщо виклик відбувається в контексті
системної віртуальної машини, то має місце також контекст поточного додатка,
а також завдання (thread), якщо поточним є додаток Win32.


При виклику VxD система просто перемикає поточний стек і режим виконання,
після чого передає управління відповідної функції VxD. Це економить час
на перемикання, однак накладає на драйвер певні обмеження щодо
поведінці в поточному контексті. Наприклад, постійно доступною є тільки
інформація в системній області пам’яті – змінні самого VxD, системні
таблиці, інші VxD тощо; відображення інших областей пам’яті, як правило,
змінюється при зміні контексту.


Виклик драйвера, що відноситься до певної віртуальної машині або завдання,
завжди відбувається в момент роботи цієї віртуальної машини / завдання; в таких
випадках контекст називається певним, Або невипадковим (non-arbitrary
context). Виклик, ініційований зовнішньої причиною – перериванням або іншим
подією, може трапитися в момент роботи довільній віртуальної
машини / завдання; в цьому випадку контекст називається невизначеним, або
довільним (arbitrary context).


Доступ до пам’яті


У контексті програми Win32 або віртуальної машини DOS драйвер має прямий
доступ до їх адресного простору. Для віртуальних машин потрібно лише
приведення 16-розрядних адрес типу «сегмент: зсув» до лінійних,
розташованим в межах першого мегабайта 32-розрядного адресного
простору.


У контексті 16-розрядного програми Windows подібної «прямої видимості» немає,
і для прямого доступу до даних додатка необхідно виконати
відображення (Mapping) фрагментів 16-розрядного адресного простору
програми в «плоске» (flat) 32-розрядне адресний простір драйвера. В
Внаслідок цієї операції в області системних адрес створюються сторінки,
відображені на ті ж адреси фізичної пам’яті, що і задані адреси
16-розрядного програми.


Після завершення роботи з даними відображення необхідно припинити (unmap),
щоб звільнити створені в системній області сторінки.


Оскільки системна область (system arena) доступна для читання всім
додаткам Win32, вони можуть зчитувати локальні дані VxD за повернутих їм
вказівниками. Однак доступ додатків до системою області пам’яті в загальному випадку не
рекомендується.


Повторна входімость


VMM в загальному випадку не є повторно-входімим (Реєнтерабельним)
модулем. Опції VMM діляться на асинхронні, Які можуть бути викликані в
будь-який момент (навіть всередині обробника переривання), і звичайні, Які
можуть викликані лише всередині «вертикального» потоку управління, коли управління
передається строго зверху вниз, без рекурсій.


Більшість функцій VxD, що викликаються ззовні, не вимагає забезпечення повторної
входімості. Однак виклики функцій, що відбуваються в результаті асинхронних подій
(Зазвичай це переривання), можуть накладатися, якщо обробка попереднього виклику
не встигла завершитися або реалізована некоректно. Тому при проектуванні
VxD, що мають справу з асинхронними подіями, цьому питанню необхідно приділяти
особливу увагу.


Завантаження, робота і вивантаження драйвера


Всі VxD завантажуються в системну область пам’яті (system memory arena),
починається з адреси 0xC0000000. Відразу після завантаження статичному драйверу –
повідомлення DEVICE_INIT; динамічному драйверу передається повідомлення
SYS_DYNAMIC_DEVICE_INIT.


Послідовність передачі повідомлень при ініціалізації статичного драйвера
насправді трохи складніше; точний опис процесу можна знайти в
документації DDK.


Фаза ініціалізації драйвера зазвичай складається в установці початкових значень
змінних, запиті робочих областей пам’яті, налагодженні режимів роботи пристроїв,
призначення векторів переривань, каналів DMA і т.п.


Після відпрацювання фази ініціалізації драйвер може бути викликаний будь-яким з
передбачених способів за запитами від системи та / або додатків. Передані
системні повідомлення відображають відбуваються в системі події.


При розвантаженні динамічного драйвера він отримує повідомлення
SYS_DYNAMIC_DEVICE_EXIT, за яким зазвичай виконується звільнення
використовуваних областей пам’яті, закриття об’єктів, деактивація керованих
пристроїв і т.п. Після відпрацювання цього повідомлення динамічний драйвер видаляється
з пам’яті.


Відповідальність за коректну «чистку» перед вивантаженням динамічного драйвера
покладено на його розробника. Система не в змозі перевірити, чи дійсно
Чи видалені всі посилання на об’єкти драйвера; якщо, наприклад, драйвер зробив запит
на таймерні чи іншу подію, після чого був вивантажений і не анулював цього
запиту – при настанні події VMM спробує викликати задану процедуру
обробки, яка до цього часу вже не існує, що призведе до
непередбачуваних наслідків, аж до повного зависання системи.


Особливості розробки VxD на C + +


Середовище виконання


Середовище виконання (Working environment) драйвера VxD сильно відрізняється
від середовища, в якій виконуються програми Win32. VxD працюють в режимі ядра
операційної системи на нульовому рівні привілеїв (ring 0), на відміну від
додатків, що працюють на рівні 3. Для VxD в загальному випадку недоступні функції
Windows API; замість них необхідно користуватися спеціальними сервісними
функціями VMM та інших VxD.


Стандартні бібліотеки


Наявність спеціалізованої середовища виконання означає, що VxD, написаний на
C або C + +, в загальному випадку не може користуватися функціями стандартної
бібліотеки виконання
(RTL). Можливе використання лише тих функцій,
які завідомо не містять посилань до Windows API. Наприклад, в VxD можливо
використання функцій strlen, strcpy, strset або
strcmp, Однак функція strcoll в Visual C + + містить звернення до
API, щоб визначити параметри мови, і тому для використання в VxD НЕ
годиться. Те ж відноситься до функцій sprintf, time і багатьом іншим.
Серед сервісних функцій VMM міститься чимало аналогічних за змістом, але відмінних
за форматом виклику і схемі роботи операцій.


Допоміжні функції (wrappers)


Оскільки багато сервісні функції VMM та інших VxD призначені для виклику
на мові асемблера, при цьому отримують параметри і повертають результати в
регістрах і прапорах процесора, їх безпосереднє використання в C + +
утруднено. У таких випадках часто робляться невеликі допоміжні функції,
звані обгортками (Wrappers), єдиним завданням яких є
перенесення параметрів зі стеку в регістри, виклик потрібної сервісної функції і повернення
результату стандартним для C + + способом.


Обгортки для ряду часто використовуваних сервісних функцій VMM та інших
стандартних VxD визначені у відповідних включаються файлах з DDK –
VMM.H, VTD.H, SHELL.H тощо, а також у файлі
VXDWRAPS.H. Написання решти надається розробнику VxD. В кінці
статті наведено приклад написання обгортки SelectorMapFlat.


Перед включенням файлів необхідно визначити символічне ім’я
WANTVXDWRAPS, Інакше будуть визначені тільки константи і типи, але не самі
функції.


Функції, що викликаються ззовні


Деякі внутрішні процедури VxD, оформлені у вигляді функцій C + +, повинні
викликатися системою (Callback). До них відносяться, наприклад, диспетчер
системних повідомлень, обробники сервісних функцій, переривань, подій тощо
Угоди про зв’язки в таких викликах часто розраховані на використання мови
асемблера, коли параметри передаються в регістрах, а результат повертається в
регістрах і / або прапорах процесора. У такому випадку стандартне оформлення
функції, прийняте в C + +, може стати істотною перешкодою.


Розширення Visual C + + містить кваліфікатор __declspec (naked),
приміщення якого в заголовку обумовленою функції забороняє генерацію
прологу / епілогу функції – початкової (збереження регістрів, установка покажчика
кадру) і завершальній (відновлення регістрів, команда повернення)
послідовностей команд. Результат компіляції буде містити тільки код,
присутній в тілі функції в явному вигляді. Це дозволяє програмісту виконати
потрібні дії в необхідній послідовності, однак накладає вимоги по
дотримання внутрішніх правил мови. Зокрема, мають бути збережені регістри
EBX, ESI, EDI, EBP; При використанні параметрів у
такої функції повинен бути явно встановлений покажчик кадру в регістрі EBP,
а при використанні локальних змінних – зарезервовано достатню
кількість байтів в стеку.


Для повернення з naked — функції в її тіло має бути явно поміщена
команда _asm ret, Якщо тільки функція не використовує засобів начебто VxDJmp,
які самі виконують повернення у функцію, з якої був зроблений дзвінок.


Неявні звернення до функцій підтримки


Visual C + + може генерувати неявні звернення до функцій із стандартної
бібліотеки
при використанні деяких конструкцій мови, наприклад
виключень (Exceptions) і RTTI, Тому для застосування цих
можливостей необхідно написання власної підсистеми їх підтримки в VxD.


При використанні віртуальних функцій компілятор призначає кожній чисто
віртуальної (pure virtual) функції посилання на бібліотечну функцію
_purecall. Це робиться для того, щоб відловити всі помилкові звернення
до чисто віртуальної функції, для якої не визначена реальна функція-адресат.
Стандартна функція _purecall виводить діагностичне повідомлення і
завершує роботу програми, використовуючи Windows API; щоб зробити можливим
застосування віртуальних функцій в VxD, необхідно в одному з його модулів
визначити свій варіант:

int _purecall (void) {
return 0;
}

При бажанні можна помістити всередину тіла функції висновок діагностичного
повідомлення засобами, доступними для VxD.


Експорт посилання на DDB


Експорт посилання на DDB за номером зазвичай прийнято робити в DEF-файлі,
проте в Visual C + + для цього є зручний кваліфікатор __declspec
(dllexport)
, Який досить помістити в заголовку визначення структури
DDB.


Структура DEF-файлу для побудови VxD


DEF-файл для побудови VxD може містити опції VXD,
DESCRIPTION і SECTIONS.


Опція VXD має вигляд:


VXD ім’я тип



Опція DESCRIPTION:


DESCRIPTION строка_опісанія



Опція SECTIONS:


SECTIONS


ім’я [CLASS “клас”] спісок_атрібутов



Установки компілятора і компонувальника


В установках компілятора в середовищі Visual C + + необхідно заборонити
обробку виключень і RTTI, встановити однозадачной (single-threaded)
стандартну бібліотеку (RTL). Для коректної компіляції включення файл з
DDK, які призначені не тільки для VxD (наприклад, MMSYSTEM.H)
необхідно визначити (в установках препроцесора або в тексті програми)
символічне ім’я Win32_VxD.


В установках компоновщика (Linker) необхідно прибрати всі бібліотеки
Windows API, бо вони призначені тільки для стандартного середовища виконання. При
використанні стандартних функцій-обгорток з DDK необхідно підключити
бібліотеку VXDWRAPS.CLB з DDK.


У командному рядку компонувальника, яка відображається внизу вікна налаштувань, необхідно
вручну додати опцію /VXD. Також потрібно внести в список файлів проекту
DEF-файл, керівник побудовою модуля і містить ім’я драйвера і
опис використовуваних секцій (сегментів). У середовищі Visual C + + є можливість
обійтися без використання DEF-файлу, вносячи всі необхідні опції в командну
рядок компонувальника, проте це менш зручно, так як рядок швидко
захаращується і стає важкою для сприйняття і редагування.


Параметри секцій


Компілятор Visual C + + за замовчуванням створює секції чотирьох типів:



У DEF-файлі цим секціях повинні бути приписані атрибути EXECUTE,
PRELOAD (Як для класу резидентного коду LCODE).


При необхідності можна поміщати код і дані в інші секції за допомогою
директив #pragma code_seg, #pragma data_seg і #pragma
alloc_text
, Приписавши їм необхідні атрибути. Це може знадобитися,
наприклад, для виділення частини коду / даних, що використовуються тільки при ініціалізації
або дозволених для відкачування (атрибут DISCARDABLE).


Бібліотечні функції також можуть бути «розкладені» по секціях з іншими
іменами, тому при їх використанні необхідно стежити, щоб атрибути секцій
відповідали їх призначенням і поведінки при роботі VxD.


Функції, імпортовані з бібліотеки VXDWRAPS.CLB, Використовують в
основному секції _LTEXT і _LDATA.


Налагодження


Унаслідок роботи в спеціалізованій середовищі налагодження VxD за допомогою
вбудованого отладчика середовища Visual C + + неможлива. Для повного налагодження драйвера
найзручніше використовувати відладчик SoftICE фірми NuMega. SoftICE
сприймає всю зневадження, сгенерированную компілятором, так що
при розробці VxD, як і звичайних додатків, можна використовувати налагоджувальний
(Debug) і крайовий (release) види проектів. Для витягання отладочной
інформації з модуля драйвера SoftICE має в комплекті утиліту
nmsym, Виклик якої зручно включати в список спеціальних дій
(Custom build) середовища Visual C + +.


Для поверхневої налагодження допомогою трасувань повідомлень
(Out_Debug_String і т.п.) можна використовувати різні системні монітори –
наприклад Monitor (Vireo Software), DbgTerm (TechSoft Pvt.) І будь-які
інші, які перехоплюють системні налагодження повідомлення та відображають їх у
вікні. Наприклад, драйвер KbdFlip я налагоджував виключно за допомогою утиліти
Monitor, Ні разу не вдавшись до SoftICE.


Monitor і DbgTerm також дозволяють завантажувати і вивантажувати
динамічні VxD, причому Monitor робить це більш надійно. При розробці
динамічних VxD можна використовувати Monitor і SoftICE разом:
перший – для завантаження / розвантаження драйвера, другий – для налагодження. SoftICE
перехоплює весь налагоджувальний потік Windows, так що виводяться повідомлення будуть
видно тільки в ньому.


Створення VxD на Visual C + + без асемблерних модулів # 2



Загальна схема драйвера VxDМінімальний віртуальний драйвер повинен містити
секцію резидентного коду, в якій розташовані блок описувача пристрої,
диспетчер системних повідомлень і обробники сервісних функцій.Обработчік
системних повідомлень аналізує код повідомлення, переданий в EAX, виділяє
цікавлять його повідомлення, обробляє їх і повертає скинутий прапор CF в
разі успіху, і встановлений – у разі невдачі. Для всіх необроблюваних
повідомлень повинен повертатися скинутий прапор CF.Обработчікі сервісних функцій
викликаються по таблиці, так що кожної функції відповідає власний
обробник. Спосіб доступу до параметрів і повернення результатів визначається
разработчіком.Прі необхідності драйвер може містити обробники запитів V86
і PM API. Для доступу до даних віртуальних машин DOS, покажчики на які можуть
передаватися в регістрах при запиті, достатньо перетворити їх в лінійні
32-розрядні адреси, бо перший мегабайт адресного простору поточної
віртуальної машини безпосередньо «видно» з VxD. Для доступу до даних
додатків Win16 потрібно виконати відображення адрес за допомогою функції
VMM _SelectorMapFlat. Програмування VxD Засоби
розробки, що включаються файли і бібліотекіМінімально необхідний набір включаються
файлів і бібліотек міститься в Windows 95 DDK (підкаталоги Inc32 і
Lib). Зазвичай потрібно включення хоча б файлів BASEDEF.H і
VMM.H. Файли VXDWRAPS.H, CONFIGMG.H і деякі інші
оформлені в стилі звичайної мови C, тому при включенні їх у файли типу
CPP директиви #include необхідно поміщати всередину кваліфікатор
extern “C”:extern “C” {
#include <vxdwraps.h>
} Файли з
DDK можна включати і в тексти модулів звичайних програм, визначивши перед цим
символічне ім’я Not_VxD. При цьому визначаються тільки корисні
константи і типи, а визначення специфічних для VxD конструкцій відключається.
Структури, які зазвичай використовуються в VxDVxD_Desc_Block –
блок описувача устройстваОпісивает структуру DDB. Заповнюється статично, щоб
до моменту завантаження драйвера всі поля мали потрібні
значенія.ULONG DDB_Next;
USHORT DDB_SDK_Version;
USHORT DDB_Req_Device_Number;
UCHAR DDB_Dev_Major_Version;
UCHAR DDB_Dev_Minor_Version;
USHORT DDB_Flags;
UCHAR DDB_Name
[8];
ULONG DDB_Init_Order;
ULONG DDB_Control_Proc;
ULONG DDB_V86_API_Proc;
ULONG DDB_PM_API_Proc;
ULONG DDB_V86_API_CSIP;
ULONG DDB_PM_API_CSIP;
ULONG DDB_Reference_Data;
ULONG DDB_Service_Table_Ptr;
ULONG DDB_Service_Table_Size;
ULONG DDB_Win32_Service_Table;
ULONG DDB_Prev;
ULONG DDB_Size;
ULONG DDB_Reserved1;
ULONG DDB_Reserved2;
ULONG DDB_Reserved3;

Client_Reg_Struc – структура пакета регістрів
кліентаОпісивает стан регістрів процесора в викликала віртуальної
машині / додатку (клієнта). ULONG Client_EDI;
ULONG Client_ESI;
ULONG
Client_EBP;
ULONG Client_res0;
ULONG Client_EBX;
ULONG
Client_EDX;
ULONG Client_ECX;
ULONG Client_EAX;
ULONG
Client_Error;
ULONG Client_EIP;
USHORT Client_CS;
USHORT
Client_res1;
ULONG Client_EFlags;
ULONG Client_ESP;
USHORT
Client_SS;
USHORT Client_res2;
USHORT Client_ES;
USHORT
Client_res3;
USHORT Client_DS;
USHORT Client_res4;
USHORT
Client_FS;
USHORT Client_res5;
USHORT Client_GS;
USHORT
Client_res6;
ULONG Client_Alt_EIP;
USHORT Client_Alt_CS;
USHORT
Client_res7;
ULONG Client_Alt_EFlags;
ULONG Client_Alt_ESP;
USHORT
Client_Alt_SS;
USHORT Client_res8;
USHORT Client_Alt_ES;
USHORT
Client_res9;
USHORT Client_Alt_DS;
USHORT Client_res10;
USHORT
Client_Alt_FS;
USHORT Client_res11;
USHORT Client_Alt_GS;
USHORT
Client_res12; Поля з іменами Client_xxx містять значення відповідних
регістрів на момент звернення віртуальної машини або додатки до системної
функції. У полі Client_Error може бути занесений код ошібкі.Для структури
введений синонім типу (typedef) з ім’ям CRS. У файлі VMM.H
визначені також допоміжні структури Client_Word_Reg_Struc і
Client_Byte_Reg_Struc і об’єднання всіх трьох структур
CLIENT_STRUCT.
DIOCParams – параметри запиту
DeviceIoControlDWORD Internal1;
DWORD VMHandle;
DWORD Internal2;
DWORD
dwIoControlCode;
DWORD lpvInBuffer;
DWORD cbInBuffer;
DWORD
lpvOutBuffer;
DWORD cbOutBuffer;
DWORD lpcbBytesReturned;
DWORD
lpoOverlapped;
DWORD hDevice;
DWORD tagProcess;

  • VMHandle – Ідентифікатор віртуальної машини, яка зробила запит.
  • dwIoControlCode – Код функції. Константи для визначених у системі
    кодів функцій мають префікс DIOC_, Інші функції визначаються
    розробником.








GETVERSION (0) Відкривання і опитування інтерфейсу. Якщо драйвер не підтримує Win32 API, він
повинен повернути в EAX ненульове значення. В іншому випадку в EAX повертається
нуль, а якщо задано буфер результату, то в нього заноситься номер версії
драйвера.
CLOSEHANDLE (-1) Закривання інтерфейсу. Драйвер повинен перервати обробку всіх асинхронних
запитів по цьому пристрою і звільнити пов’язані з нього
ресурси.


  • lpvInBuffer – Покажчик вихідного буфера.
  • cbInBuffer – Розмір вихідного буфера в байтах.
  • lpvOutBuffer – Покажчик буфера результату.
  • cbOutBuffer – Розмір буфера результату в байтах.
  • lpcbBytesReturned – Поле для об’єму в байтах даних, занесених
    драйвером в буфер результату.
  • lpoOverlapped – Покажчик структури типу OVERLAPPED (Описувач
    адреси всередині файлу та / або дані асинхронної операції).
  • hDevice – Ідентифікатор пристрою.
  • tagProcess – Ідентифікатор запиту. Разом з полем hDevice
    утворює унікальний усередині системи ідентифікатор запиту, за яким запит
    може бути знайдений і аварійно перерваний при отриманні запиту CLOSEHANDLE.
Опції VxD, що викликаються з системи Диспетчер системних сообщенійДіспетчер системних повідомлень драйвера
являє собою функцію, яка одержує параметри в регістрах і повертає
результат у прапорі процесора CF (carry flag). Код повідомлення передається в
регістрі EAX. При поверненні прапор CF повинен бути скинутий, якщо повідомлення
оброблено успішно, і встановлено, якщо відбулися помилка або відмову в
обслуговуванні. Для всіх повідомлень, які не обробляються даними VxD, повинен
повертатися скинутий прапор CF.Все регістри, які беруть у поверненні
результату, повинні бути збережені. Рекомендується оформляти диспетчер у вигляді
naked-функції, щоб гарантувати збереження стану прапора CF після повернення,
або стежити за кодом, який породжується компілятором.
Оброблювачі сервісних функцій Оброблювачі сервісних функцій зазвичай
теж отримують параметри в регістрах і повертають результати в регістрах і прапорах
процесора, однак вони можуть бути оформлені і відповідно до угод
мов C. Обов’язковою є тільки функція з нульовим номером, через яку
виконується запит версії драйвера, вона не отримує параметрів і повертає
версію в регістрі EAX.Все регістри, які беруть у поверненні результату, повинні
бути збережені.
Оброблювачі викликів API Оброблювачі викликів API
отримують в регістрі EBX ідентифікатор (handle) поточної віртуальної машини (VM)
клієнта, а в регістрі EBP – адреса структури регістрів кліента.По стандартному
угодою, при спілкуванні до API драйвера в регістрі AH передається номер функції, а
в AL – номер підфункції. Решта регістри можуть передавати інші параметри
запроса.Прі необхідності повернути інформацію клієнту VxD модифікує поля
відповідних регістрів клієнтської структури; в момент повернення управління
клієнту значення регістрів відновлюються з нее.Обработчік API може
використовувати всі регістри, крім EBP та сегментні.
Системні
засоби підтримки VxD
Деякі системні повідомлення DEVICE_INIT – ініціалізація статичної драйвера