Уроки Iczelion'а

       

Взаимодействие между VxD


VxD, включая VMM, могут взаимодействовать друг с другом тремя путями:

  • Управляющие сообщения
  • Сервисные API
  • Callback'и

Управляющие сообщения: VMM посылает системные управляющие сообщения всем загруженным VxD, когда происходит какое-то интересное событие. В этом отношении контрольные сообщения похожи на windows-сообщения приложений ring-3. У каждого VxD есть функция, которая получает и обрабатывает контрольные сообщения, называемая управляющей функцией устройства. Существует около 50 системных управляющих сообщений. Почему их так мало? Зачастую в системе загружено много VxD, а так как управляющее сообщение посылается всем загруженным VxD, то система могла бы повиснуть, если бы было слишком много управляющих сообщений. Поэтому существуют только по-настоящему важные управляющие сообщения, такие как создание виртуальной машины, ее уничтожение и так далее. В добавление к системным, управляющим сообщениям, VxD может определить свои собственные управляющие сообщения, чтобы взаимодействовать с другими VxD, понимающими эти сообщения.

Сервисные API: VxD, включая VMM, обычно экспортирует множество публичных функций, которые могут вызываться другими VxD. Эти функции называются сервисами VxD. Механизм вызова этих VxD отличается от того, как это происходит в приложениях ring-3. Каждый VxD, который экспортирует сервисы VxD должен иметь уникальный ID номер. Вы можете получить эти ID от Микрософта. ID - это 16-битный номер, который уникальным образом идентифицирует VxD. Hапример,

UNDEFINED_DEVICE_ID EQU 00000H VMM_DEVICE_ID EQU 00001H DEBUG_DEVICE_ID EQU 00002H VPICD_DEVICE_ID EQU 00003H VDMAD_DEVICE_ID EQU 00004H VTD_DEVICE_ID EQU 00005H

Вы можете видеть, что ID VMM - 1, VPICD - 3 и так далее. VMM использует эти уникальные ID, чтобы найти VxD, которая экспортирует требуемые сервисы. Вы также можете выбрать требующийся вам сервис по его индексу в service branch table. Когда VxD экспортирует сервисы, она сохраняет в таблице адреса сервисов. VMM будет использовать предоставленный индекс, чтобы найти адрес желаемого сервиса из сервисной таблицы. Hапример, если вы хотите вызвать GetVersion, которая является первым сервисом, вы должны указать 0 (индексы начинаются с нуля). Реальный механизм вызовов сервисов VxD использует int 20h. Ваш код будет выглядеть как int 20, за которым следует двойное слово, состоящее из ID устройства и индекса сервиса. Hапример, если вы хотите вызвать сервис под номером 1, которые экспортируется VxD с ID 000Dh, код будет выглядеть так:

int 20h dd 000D0001h


Старшая часть двойного слова, которое следует за инструкцией int 20h, содержит ID устройства. Младшая часть содержит начальный (нулевой) индекс элемента таблицы сервисов. Когда генерируется int20h, VMM получает контроль и проверяет двойное слово, которое непосредственно следует за инструкцией прерывания. Затем он извлекает ID устройства и использует его, чтобы найти VxD, а потом использует индекс сервиса, чтобы определить адрес требуемого сервиса в этом VxD.

Вы можете видеть, что эти операции пожирают время. VMM должна потратить время, чтобы найти VxD и адрес желаемого сервиса. Поэтому VMM немножко жульничает. После первого успешного вызова int 20h, VMM сохраняет связь. Это выглядит как замещение in20h и последующего слова на прямой вызов сервиса. Поэтому вышеприведенный кусок кода будет трансформирован в:

call dword ptr [VxD_Service_Address]

Этот прием работает, так как int 20h + dword занимает 6 байтов, что в точности равно инструкции call dword рtr. Поэтому последовательные вызовы быстры и эффективны. Этот метод имеет свои за и против. Положительным является то, что снижается загрузка VMM и VxD загрузчика, потому что они не должны фиксировать все сервисные вызовы VxD во время загрузки. Вызовы, которые никогда не загружались, останутся немодифицированными. Среди недостатков данного подхода можно назвать то, что он делает невозможным выгрузить статические VxD, сервисы которых используются, так как в противном случае, другие VxD, использующие сервисы выгруженного драйвера, завесили бы систему обращениями к неправильным адресам памяти. Hет механизма "pазлинковки". Из этого неизбежно следует, что динамические VxD не подходят для предоставления каких-либо сервисов.

Callback'и: callback'и или callback'овые функции - это функции в VxD, которые существуют. Callback'и не являются открытыми как сервисы. Они являются приватными функциями, чьи адреса VxD передает другим VxD в специальных ситуациях. Hапример, когда VxD обрабатывает хардварное прерывание, теми его функциями, которые могут вызвать рage faults, не могут пользоваться другие VxD. VxD может дать адрес одной из своей собственных функций (callback) VMM, чтобы тот мог вызвать функцию, когда он может допускать рage faults, и тогда VxD может продолжить свою работу когда вызывается его callback-функция. Идея обратного вызова (callback) используется не только в VxD. Многие Windows API используют ее. Вероятно, лучший пример - это оконная процедура. Вы указываете адрес процедуры окна в структуре WNDCLASS или WNDCLASSEX и передаете ее Windows с помощью функции RegisterClass или RegisterClassEx. Windows будет вызывать вашу процедуру окна, когда для этого окна появятся сообщения. Другим примером может являться hook-процедуры. Ваше приложение дает Windows адрес hook-процедуры, чтобы Windows могла вызвать ее, когда происходит событие, которое интересно приложению.

Приведенные три метода служат для взаимодействия между VxD. Существуют также интерфейсы для Windows-, V86- и Win32-приложений. Я расскажу об интерфейсе VxD для win32 приложений в следующих нескольких туториалах.

[C] Iczelion, пер. Aquila.



Содержание раздела