Основні властивості і особливості драйвера VxD

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


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;








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

Функції 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 – ініціалізація статичного драйвера
Повідомлення надсилається після завантаження статичного
драйвера для його ініціалізаціі.SYS_DYNAMIC_DEVICE_INIT –
ініціалізація динамічного драйвераСообщеніе надсилається після завантаження
динамічного драйвера для його ініціалізаціі.SYS_DYNAMIC_DEVICE_EXIT – завершення динамічного драйвераСообщеніе
надсилається перед вивантаженням динамічного драйвера для завершення його работи.CREATE_VM – створення нової віртуальної машини
Посилається в процесі створення нової віртуальної машини (VM), але до її
фактичного запуску. На цьому етапі VxD може визначити, чи зможе він
підтримувати створювану віртуальну машину, і запитати необхідні для
підтримки ресурси. Повернення встановленого прапора CF запобігає створення
машіни.VM_INIT – ініціалізація нової віртуальної машини
Посилається на початку
роботи нової віртуальної машини, при її ініціалізації, в контексті цієї
віртуальної машини. На цьому етапі VxD може ініціалізувати ресурси, виділені
для підтримки нової віртуальної машіни.Флаг CF завжди повинен повертатися
сброшенним.VM_TERMINATE – завершення віртуальної машини
Посилається на початку
процесу завершення віртуальної машіни.Флаг CF завжди повинен повертатися
сброшенним.DESTROY_VM – знищення віртуальної машини
Посилається в кінці
процесу завершення машини, перед безпосереднім видаленням її з сістеми.Флаг
CF завжди повинен повертатися сброшенним.CREATE_THREAD – створення
нового завдання
Посилається при
створення в системі нового завдання. Створювана завдання у цей момент ще не
є текущей.Возврат встановленого прапора CF запобігає створення
задачі.THREAD_INIT – ініціалізація нового завдання EDI
ідентифікатор задачі.Посилается при ініціалізації завдання, на початку її роботи, в
контексті завдання (нова задача є поточної). Прапор CF завжди повинен
повертатися сброшенним.TERMINATE_THREAD – завершення завдання
Посилається на початку
процесу завершення завдання. Завершується завдання ще якийсь час може
залишатися в системі, поки не будуть завершені всі чекають операції
введення / вивода.Флаг CF завжди повинен повертатися сброшенним.DESTROY_THREAD – знищення завдання
Посилається в кінці
процесу завершення завдання, перед безпосереднім видаленням завдання з
сістеми.Флаг CF завжди повинен повертатися сброшенним.SYSTEM_EXIT
– Завершення роботи системи
Посилається на початку процесу завершення роботи системи, при запиті
закриття системи (shutdown), перезавантаження (reboot) або при аварійному
завершеніі.W32_DEVICEIOCONTROL – запит від додатка Win32
0 – Обробка
завершена успішно;-1 – Розпочато асинхронна операція. Повертається тільки в
тому випадку, якщо параметром був заданий ненульовий параметр
lpoOverlapped.код помилки – Якщо операція завершена неудачно.Вместе
з поверненням результату в EAX драйвер може заносити необхідну інформацію в
буфер результату, якщо він зазначений у блоці параметров.Некоторие
сервісні функції VMMVMMCall, VMMJmp, VxDCall, VxDJmp – виклик
сервісних функцій VxD void xxxCall (DWORD Service); void xxxJmp (DWORD
Service);Service – Код сервісної функції. Старші 16 розрядів
представляють собою ідентифікатор VxD, молодші 15 розрядів – номер сервісної
функціі.Служіт для звернення до сервісних функцій VMM та інших VxD. Код функції
одночасно визначає і VxD, до якого відбувається звернення, і саму функцію
цього VxD, так що конструкції VMMxxx і VxDxxx рівнозначні.
Константи для кодів функцій визначені під включаються файлах відповідних VxD;
імена констант функцій VxD, відмінних від VMM, мають уточнюючі префікси, наприклад
VPICD_Get_Version – Запит номера версії VPICD, драйвера віртуального
контролера прериваній.Сервісние функції доступні тільки в драйверах, що мають
ідентифікатори. До драйверам без ідентифікаторів «чесний» доступ можливий тільки
з боку додатків. З боку інших VxD він можливий лише шляхом
безпосереднього пошуку даного VxD в системному списку з подальшим прямим
доступом до таблиці його сервісних функцій.Параметри для сервісної функції, як
правило, передаються в регістрах; результати повертаються в регістрах і прапорах
процесора. Деякі функції розраховано на виклик у стилі мов C, з приміщенням
параметрів у стек і поверненням результату в EAX. Імена функцій, оформлених у
стилі C, починаються зі знака підкреслення (_). Обидва виклику оформляються у вигляді
команди переривання int 0x20, Слідом за якою розміщується код функції.
При першій відпрацювання виклику VMM заміняє цю конструкцію на команду far call / jmp
до відповідного шлюз, що дає економію часу при наступних викликах. За
цієї причини конструкцію Int 20, якщо вона не орієнтується відладчиком як
VMMxxx / VxDxxx, не можна проходити командою типу Step Over, так як стоп-точка
буде встановлена відладчиком відразу за командою Int і при цьому буде зіпсований
розташований за нею код функції. Відладчик SoftICE коректно пізнає ці
конструкціі.Разлічіе викликів Call і Jmp полягає в тому, що виклик
Call запам'ятовує адресу повернення в стеку, а Jmp – Ні. Методом
Jmp викликаються «фатальні» функції, які не потребують повернення, а також
функції, після яких потрібен повернення відразу до викликала функції, без
відновлення регістрів та інших завершальних дій. Без доброго розуміння
механізму роботи виклику Jmp краще обмежитися використанням виклику
Call. _SelectorMapFlat – Відображення сегментної адреси в
лінейний_SelectorMapFlat (
DWORD VMHandle,
DWORD Sel,
DWORD
Reserved
);
Функція
отримує параметри в стеку, в стилі мов C, і повертає в EAX лінійний адресу,
відповідний початку сегмента, селектор якого заданий параметром Sel,
або – 1 у разі помилки. За допомогою цієї функції можливий доступ з VxD до
даними додатків Win16.Полученний адресу дійсний до моменту повернення в VMM,
після чого відображення може бути анульовано. Щоб зберігати відображення
тривалий час, необхідно використовувати спеціальні засоби роботи зі
сторінками – резервування сторінок, копіювання елементів таблиці сторінок і
т.п.Некоторие функції-обгортки, визначені в VXDWRAPSOut_Debug_String – висновок отладочного повідомлення void Out_Debug_String
(char *String);
Системний налагоджувальний потік
можна переглядати відладчиками WDEB386, SoftICE, А також будь-яким
налагодження монітором._Sprintf – форматування рядка ULONG
_Sprintf (Char * Buffer, char * Format, …); Функція аналогічна стандартної
функції sprintf мови C.К жаль, VMM не надає функції,
аналогічної vsprintf, Тому для реалізації функцій цільового призначення,
в основі яких лежить специфікація формату та список аргументів змінної довжини
(Наприклад, функцій отладочного висновку або формування рядків спеціального виду),
доводиться використовувати _Sprintf, Копіюючи змінну частину списку
аргументів (наприклад, 10-20 подвійних слів) з стекового кадру цільової функціі.Прімер побудови функції-оберткіПоскольку сервісна функція VMM
_SelectorMapFlat Не має стандартної обгортки, можлива функція-обгортка для неї
могла б виглядати таким чином:#pragma warning (disable: 4035) //
Блокування попередження про неповернення

_declspec (naked)
void
*SelectorMapFlat (DWORD VM, DWORD Sel, DWORD Rsv = 0) {

VMMJmp
(_SelectorMapFlat) / / Передача управління VMM

(void)(VM, Sel, Rsv); //
Імітація використання параметрів

}

#pragma warning (default:
4035) / / Відновлення попереджень про неповернення
Кваліфікатор
_declspec (naked) пригнічує генерацію прологу / епілогу, так що від функції
залишається лише конструкція VMMJmp. При виклику цієї функції-обгортки параметри
VM, Sel і Rsv поміщаються в стек, потім туди ж міститься
адреса повернення, управління передається у функцію обгортку, при цьому VMMJmp
передає управління сервісної функції VMM _SelectorMapFlat, Не запам'ятовуючи
нового адреси повернення в стеку. VMM використовує значення параметрів з стека,
потім виконує повернення за адресою, що знаходиться на верхівці стека, при цьому
управління відразу повертається в точку за викликом функції-обгортки, де знаходиться
код, видаляє з стека параметри і використовує значення, повернене VMM в
EAX.Такая схема типова для побудови функцій-обгорток, так як у повній мірі
використовує особливості створення стекового кадру в мовах C, а також можливості
компілятора Visual C + + з оформлення функцій. Без використання VMMJmp довелося
б здійснювати повернення двічі, а без використання кваліфікатора naked
двічі поміщати в стек список параметров.Прімер простого VxDВ
Як приклад наводиться найпростіший VxD, що відслідковує події створення та
знищення завдань (threads). Єдина мета проекту – ілюстрація побудови
VxD цілком на C + +. Драйвер навмисно зроблений максимально простим, щоб
продемонструвати прозорість і відносну нескладність базового VxD.Для
повного розуміння всіх розглянутих питань, а також для розробки
власних VxD вам знадобиться Windows 9x DDK – набір включення файл і
бібліотек Microsoft. Приблизно до весни 1999 року, коли з'явився DDK для Windows
98, DDK для Windows 95 поширювався Microsoft вільно і безкоштовно; зараз
вільно можна отримати тільки DDK для Windows 98, який набагато менш зручний
для невеликих проектів. Однак DDK для Windows 95 до цих пір можна знайти в
Інтернеті за допомогою пошукових систем – наприклад, http://www.filesearch.ru/ – за ключовими словами Windows, 95,
DDK. Обсяг архіву – близько 17 Мбайт (повного комплекту DDK для Windows 98 – у
кілька разів більше). При бажанні можна обійтися без «офіційної», повної
установки DDK, яка створює середовище для розробки, прийняту в Microsoft.
Достатньо лише розпакувати підкаталоги INC32 і LIB і внести шляху до них у список
шляхів пошуку включаються і бібліотечних файлів компілятора (Tools -> Options
-> Directories). Після цього можна будувати практично будь-які VxD (для
деяких – наприклад, мультимедійних – може знадобитися додавання каталогів
INC16, MMEDIAINC та інших спеціалізованих). У комплект файлів прикладу
включені необхідні для побудови файли з DDK для Windows 95
(basedef.h, vmm.h, vxdwraps.h, vxdwraps.clb).
Створений VxD буде працездатний під Windows 95, 98 і Me.Для завантаження і
тестування драйвера можна скористатися утилітою Monitor, Включеної
в комплект прикладу. Для запуску утиліти достатньо розпакувати архів і запустити
файл Monitor.exe; У разі виникнення проблем потрібно зробити зміни
в реєстрі, запустивши файл dbgmsg.reg.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*