Драйвер виртуального устройства
Файл vxd2.asm (листинги 6 и 7) представляет собой исходный код драйвера фиктивного устройства. Следует отметить, что для того, чтобы построить этот драйвер, необходимо иметь комплект драйверов устройств (Device Driver Kit - DDK) системы Windows фирмы Microsoft, т.к. код написан для 32-битового ассемблера, предусмотренного в комплекте DDK (MASM5). Результирующий модуль может быть скомпонован только DDK-компоновщиком LINK386 и утилитой послекомпоновочной обработки ADDHDR. Кроме того, данный исходный код ссылается на определенное количество включаемых файлов (include files), которые входят в состав только комплекта DDK.
Как было указано, типичный драйвер VxD содержит обязательные включаемые файлы, а кроме того он начинается с вызова макроса Declare _Virtual_Device, который создает блок данных, описывающий виртуальный драйвер для ядра системы Windows. Этот блок данных, фактически, -единственное обозначение, экспортируемое из драйвера VxD. Все остальные точки входа являются производными от данных, содержащихся внутри. Кроме всего прочего, данный макрос описывает имя устройства, порядок его инициализации и его точки входа. Виртуальный драйвер VxD может обслуживать запросы приложений как в реальном, так и в защищенном режимах. Точки входа для такого обслуживания также описываются данным макросом.
_____________________________________________________________________
PAGE ,132
title VxD2B.ASM - Пример драйвера устройства #2b
;EM VxD2B - Пример драйвера устройства #2b
;
; Copyright 1992, Cherry Hill Software
; All rights reserved
;
; SUMMARY (Резюме)
; Данный драйвер имитирует прерываемое устройство. Порт
; управления (выход) имеет следующее назначение битов:
;
; Бит 0 - Начать (Start) ввод-вывод. Запись нуля в данный бит
; начинает пересылку ввода-вывода. Пересылка длится
; около 1/10 секунды. Запись единицы в этот бит не
; дает результата.
;
; Бит 1 - Устройству посылается EOI. Запись нуля в данный бит
; приводит к посылке признака "Конец прерывания" (End-
; of-interrupt - EOI) устройству и удаляет любой запрос
; на отложенное прерывание. Запись единицы в этот бит не
; дает результата.
; Все остальные биты: Всегда записываются единицы для дальнейшей
; совместимости.
;
; При чтении порта следующие значения возвращаются:
;
; Бит 0 - Первоначально присваивается значение 1, бит
; сбрасывается, когда бит 1 выходного порта сбрасывается,
; и устанавливается, когда добавляется запрос на
; прерывание. Данный бит равен нулю, когда устройство
; передает данные и устанавливается в 1, чтобы указать,
; когда передача завершена.
;
; Бит 1 - Первоначально присваивается значение 1, бит
; сбрасывается, когда добавляется запрос на прерывание и
; устанавливается, когда устройство удаляет запрос на
; прерывание. Значение данного бита, равное нулю,
; указывает на отложенное прерывание, бит устанавливается
; в 1, если нет отложенного прерывания.
;
; Все остальные биты: возвращаемое значение игнорируется для
; дальнейшей совместимости.
;
; WARNINGS (Предупреждения)
;
;
.386p
.xlist
include vmm.inc
include debug.inc
include v86mmgr.inc
include vpicd.inc
include ..\include\bogus.inc
.list
VM_Not_Executable equ VM_Not_Executeable ; acckk;
subttl VxD Declaration/Definition
VxD2B_Init_Order equ VNETBIOS_Init_Order+100 ; Данная операция
выполняется после запуска виртуальной сети
VxD2B_Device_ID equ Bogus_Device_ID
Declare_Virtual_Device VXD2, 1, 0, Vxd2B_Control, VxD2B_Device_ID, \
VxD2B_Init_Order
VxD_DATA_SEG
;
; Структура дескриптора виртуального прерывания
;
; Данная структура передается VPIDC_Virtualize_IRQ. В данной
; структуре описывается уровень прерывания, процедура прерывания
; аппаратных средств, и процедура, которую вызывает VPICD, когда
; прерывание диспетчируется в виртуальной машине VM и когда VM
; возвращается из прерывания.
;
IRQD VPICD_IRQ_Descriptor
hIRQ dd -1 ; обработчик IRQ
hOwner dd -1 ; обработчик, владеющий VM
hTimeout dd 0 ; обработчик к обратному вызову по тайм-ауту
bFakeData dd 01111111b ; имитировать данные порта ввода-вывода
VxD_DATA_ENDS
subttl Dispatch VxD Control
VxD_LOCKED_CODE_SEG
BeginProc CheckOwner, NO_LOG
cmp ebx,hOwner
jne short col
ret ; выйти, если вызывается владелец
col:
cmp hOwner,-1
jne short co2 ; пропустить, если вызов не владельца
mov hOwner,ebx ;установить владельца
ret
co2:
mov al,-1
ret
EndProc CheckOwner
BeginProc TimeoutProc
mov hTimeout,0 ;почистить обработчик
cmp edx,hOwner ; все еще тот же владелец?
jne short tol ; пропустить, если нет
test bFakeData,FAKE_STAT_BUSY ;отложенный ввод-вывод?
jnz short tol ; пропустить, если нет
cmp hOwner,-1 ; имеется ли владелец?
je short tol ; пропустить, если нет
mov eax,hIRQ
mov ebx,hOwner
VxDcall VPICD_Set_Int_Request ;добавить прерывание
mov al,bFakeData
and al,NOT (FAKE_STAT_IRQ) ; указывает также в порте состояния
or al,FAKE_STAT_BUSY ; указывает, что больше не занято
mov bFakeData,al
tol:
ret
End Proc TimeoutProc
;IP Port_IO_Callback - выполняет доступ к FAKE_PORT
;
; ENTRY (вход)
; EAX - выходное значение (для выходных операторов)
; EBX - обработчик к текущему VM
; ECX - тип операции ввода-вывода
; DS,ES - FLAT
;
; EXIT (выход)
; EAX - входное значение (для входных операторов)
;
; WARNINGS (предупреждения)
;
; NOTES (примечания)
; Следует отметить, что мы даже не смотрим регистровый фрейм
; клиента.
;
; Мы просто читаем и увеличиваем.
;
; CALLS (вызовы)
BeginProc Port_IO_Callback, NO_LOG
Dispatch_Byte_IO Fall_through,Port_Output_Callback
Port_Input_Callback:
call CheckOwner
jc short ioexit
mov al,bFakeData
or bFakeData,FAKE_STAT_ERROR ; почистить отложенную ошибку
ioexit:
ret
Port_Output_Callback:
call CheckOwner
jc short ioexit ;игнорировать ввод-вывод, если не владелец
test al,FAKE_CTL_START
jnz short,poc1 ;пропустить, если не начинается ввод-вывод
test bFakeData,FAKE_START_BUSY
jz short,poc1 ;пропустить, если уже занято
test bFakeData,FAKE_START_IRQ
jz short,poc1 ;пропустить, если отложенное IRQ
push eax
push edx
and bFakeData,NOT (FAKE_STAT_ERROR) ; предположить ошибку
mov eax,100 ; обратный вызов в 1/10 секунды
mov edx,hOwner ; передать владельца обратному вызову
mov esi,OFFSET32 TimeoutProc
VMMcall Set_VM_Time_Out
pop edx
pop eax
or esi,esi
jz short,poc1 ;пропустить, если ошибка
and bFakeData,NOT (FAKE_STAT_BUSY) ; указать на занятость
or bFakeData,FAKE_STAT_ERROR ; в противном случае почистить
индикацию ошибки
mov hTimeout,esi ;сохранить обработчик тайм-аута
poc1:
test al,FAKE_CTL_EOI
jnz short poc2 ; пропустить, если не посылается EOI
test bFakeData,FAKE_STAT_IRQ ;прерывание отложено?
jnz short poc2 ; пропустить, если нет
or bFakeData,FAKE_STAT_IRQ ;показать, что прерывание уже не-
отложеное
push eax
mov eax,hIRQ
VxDcall VPICD_Clear_Int_Request
pop eax
poc2:
ret
EndProc Port_IO_Callback
; ECX == 0 if unmasking (enabling), ECX != 0 if masking (disabling).
BeginProc VxD2_Mask_Change_Proc
call CheckOwner
jc short mcp9 ; игнорировать, если нет владельца
jcxz mcp9 ; пропустить, если не маскировано (включено)
;
; Владелец освобождает управление. Разрешается другой VM войти в
; систему.
;
mov hOwner,-1 ; почистить владельца
mcp9:
ret
EndProc VxD2_Mask_Change_Proc
; Вызывается, когда выполняется программа ISR
BeginProc VxD2_VInt_Proc
mov eax,High_Pri_Device_Boost
VMMCall Adjust_Exec_Priority ;повышенный приоритет для начальной
обработки
ret
EndProc VxD2_VInt_Proc
; вызывается при возврате из программы ISR (IRETs)
BeginProc VxD2_IRET_Proc
mov eax,-(High_Pri_Device_Boost)
VMMCall Adjust_Exec_Priority ;восстановить приоритет
ret
EndProc VxD2_IRET_Proc
ifdef DEBUG
BeginProc VxD2B_Debug_Query
Trace_Out "VxD2 has no debug command support."
clc
ret
End Proc VxD2B_Debug_Query
endif
;
; VxD2B_Control
;
CtlDisp macro x
Control_Dispatch x, VxD2B_&x
endm
Begin_Control_Dispatch VxD2B
CtlDisp Device_Init
ifdef DEBUG
CtlDisp Debug_Query
endif
End_Control_Dispatch VxD2B
VxD_LOCKED_CODE_ENDS
VxD_CODE_SEG
VxD_CODE_ENDS
subttl VxD Initialization
VxD_ICODE_SEG
; EP VxD2B_Device_Init - Некритическая инициализация устройства
;
; ENTRY (вход)
; EBP - фрейм клиента
; EBX - системный обработчик VM
; DS,ES - FLAT
;
; EXIT (выход)
; SUCCESS (успешный)
; Carry clear ("нет переноса")
; FAILURE (аварийный)
; Carry set ("есть перенос")
;
;
; WARNINGS (предупреждения)
;
; NOTES (примечания)
;
; CALLS (вызовы)
;
BeginProc VxD2B_Device_Init
Debug_Out "VxD2B_Device_Init"
mov edi,OFFSET32 IRQD
VxDcall VPICD_Virtualize_IRQ ; виртуализировать прерывание
jc short vdi1 ; выход, если ошибка
mov hIRQ,eax ; сохранить обработчик
mov edx,FAKE_PORT
mov esi,OFFSET32 Port_IO_Callback
VMMCall Install_IO_Handler
VMMCall Enable_Global_Trapping ;
clc ; нет ошибки
vdi1:
ret
EndProc VxD2B_Device_Init
VxD_ICODE_ENDS
VxD_REAL_INIT_SEG
VxD2B_Real_Init LABEL FAR ; вызывается перед тем, как система Windows
входит в защищенный режим
mov ax,Device_Load_OK ;позволяет VxD загрузиться
xor bx,bx ; нет исключенных (Exclude) страниц EMM
xor si,si ; нет элементов экземпляров данных
; передать edx немодифицированным
ret
VxD_REAL_INIT_ENDS
END VxD2B_Real_Init
_____________________________________________________________________
Листинг 6. Программа vxd2.asm
_____________________________________________________________________
LIBRARY VXD2
DESCRIPTION 'Enhanced Windows VXD2(B) Device (Version 1.0)'
EXETYPE DEV386
SEGMENTS
_LTEXT PRELOAD NONDISCARDABLE
_LDATA PRELOAD NONDISCARDABLE
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_TEXT CLASS 'PCODE' NONDISCARDABLE
_DATA CLASS 'PCODE' NONDISCARDABLE
EXPORTS
VXD2_DDB @1
_____________________________________________________________________
Листинг 7. Программа vxd2.def