在上一節教程裡,我們講了編寫一個VxD程序的方法。現在我們要學以致用。在這一節裡,我們要編寫一個靜態VxD,這個靜態VxD在一個虛擬機創建或銷毀時就會彈出一個消息框。
在這裡下載例子。
捕獲虛擬機創建和結束事件
當一個虛擬機創建時,VMM發送Create_VM控制消息給所有的VxD程序。當一個虛擬機退出時,它也發送VM_Terminate和VM_Terminate2消息給所有的VxD程序。我們的工作很簡單:在設備控制程序裡處理Create_VM and VM_Terminate2消息。當我們的VxD程序收到這兩個控制消息時,它在屏幕上彈出一個消息框。
當VxD程序收到Create_VM或者 VM_Terminate2消息時,該虛擬機的句柄保存在ebx中。一個虛擬記的句柄可以看作它的唯一的ID。每一個虛擬機都有它自己唯一的ID(虛擬機句柄)。你可以像使用進程ID一樣使用虛擬機ID:調用函數時,把它當作參數傳送。
更進一步的來看,一個虛擬機句柄實際上是一個指向虛擬機控制塊(VMCB)的32位線性地址。
一個虛擬機控制塊是一個包括了許多關於該虛擬機的重要屬性的結構。它的定義如下:
cb_s STRUC
CB_VM_Status DD ?
CB_High_Linear DD ?
CB_Client_Pointer DD ?
CB_VMID DD ?
CB_Signature DD ?
cb_s ENDS
CB_VM_Status 包含了反映虛擬機狀態的一些標志位。
CB_High_Linear 是一個指向在系統共享區(約3GB)的虛擬機鏡像的一個線性地址。這個概念需要解釋一下。在Window95下,一個VxD不能直接訪問到V86區域,代替地,VMM把每個虛擬機的V86區域都映射到系統共享區。當一個VxD程序要訪問或修改虛擬機中的V86區時,它就在虛擬機的高線性區域進行操作。例如,如果顯存的地址是0B8000H,而你的VxD程序要訪問這個區域。它就要把CB_High_Linear中的值上0B8000H,然後訪問那個區域。你在高線性鏡像區域所作的修改都會被保存到虛擬機中去,因為這兩個區域共享一個頁面目錄入口。使用高線性鏡像在它多數情況下非常有效,因為你甚至可以修改一個不是當前虛擬機的虛擬機。
CB_Client_Pointer 包含了客戶寄存器結構的地址。一個客戶寄存器結構包含了在一個虛擬機中被中斷的V86或保護模式程序的所有寄存器的值。如果你的VxD程序要讀取/改動V86或PM程序的狀態,它可以改動客戶寄存器結構裡的值,當VMM返回執行該程序時,這些改動會被保存到程序裡去。
CB_VMID 虛擬機的身份驗證數字。當VMM創建一個虛擬機時,就給該虛擬機分配一個數字。系統虛擬機的VMID是1。
CB_Signature 包含字串“VMcb”。這個元素是用來檢測虛擬機句柄是否有效的。
顯示一個對話框
一個VxD程序可以通過Virtual Shell Device服務來同用戶通訊。在這個例子裡我們要用到其中的一個:SHELL_Message.
SHEll_Message是一個寄存器法的服務函數,通過寄存器來傳送參數:
ebx 顯示這個消息的虛擬機的句柄。
eax 消息框的標志位。你可以在shell.inc中查查它們的詳細信息,它們都是以MB_開頭的。
ecx 要顯示的消息的32位線性地址。
edi 消息框的標題的32位線性地址。
esi 如果你要知道用戶對你的消息框作的反應操作,就在這裡填寫返回函數的32位線性地址。如果你不想知道,就用NULL。
edx 用來傳送你的返回函數所需要的參數(如果你在esi中填了這個函數的地址)。
返回後,如果調用成功,返回標志被清零,否則,返回標志被置位。
例子:
.386p
include VMM.inc
include shell.inc
DECLARE_VIRTUAL_DEVICE MESSAGE,1,0, MESSAGE_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER
Begin_control_dispatch MESSAGE
Control_Dispatch Create_VM, OnVMCreate
Control_Dispatch VM_Terminate2, OnVMClose
End_control_dispatch MESSAGE
VxD_PAGEABLE_DATA_SEG
MsgTitle db "VxD MessageBox",0
VMCreated db "A VM is created",0
VMDestroyed db "A VM is destroyed",0
VxD_PAGEABLE_DATA_ENDS
VxD_PAGEABLE_CODE_SEG
BeginProc OnVMCreate
mov ecx, OFFSET32 VMCreated
CommonCode:
VMMCall Get_sys_vm_handle
mov eax,MB_OK+MB_ICONEXCLAMATION
mov edi, OFFSET32 MsgTitle
xor esi,esi
xor edx,edx
VxDCall SHELL_Message
ret
EndProc OnVMCreate
BeginProc OnVMClose
mov ecx,OFFSET32 VMDestroyed
jmp CommonCode
EndProc OnVMClose
VxD_PAGEABLE_CODE_ENDS
end
分析:
Begin_control_dispatch MESSAGE
Control_Dispatch Create_VM, OnVMCreate
Control_Dispatch VM_Terminate2, OnVMClose
End_control_dispatch MESSAGE
此VxD程序處理兩個控制消息,Create_VM和VM_Terminate2當收到Create_VM 控制消息時,它調用OnVMCreate函數。當收到VM_Terminate2 消息時,它調用OnVMClose 函數。
VxD_PAGEABLE_DATA_SEG
MsgTitle db "VxD MessageBox",0
VMCreated db "A VM is created",0
VMDestroyed db "A VM is destroyed",0
VxD_PAGEABLE_DATA_ENDS
我們把這些數據放在可調頁段裡面。
BeginProc OnVMCreate
mov ecx, OFFSET32 VMCreated
CommonCode:
VMMCall Get_sys_vm_handle
mov eax,MB_OK+MB_ICONEXCLAMATION
mov edi, OFFSET32 MsgTitle
xor esi,esi
xor edx,edx
VxDCall SHELL_Message
ret
EndProc OnVMCreate
我們用BeginProc和 EndProc宏來創建OnVMCreate。OnVMCreate函數把調用SHELL_Message服務所需要的參數放到寄存器裡面去。因為我們要在系統虛擬機上顯示消息框,所以不能使用ebx中的值(ebx包含了在創建的虛擬機的句柄,而我們要的是系統虛擬機的句柄)。於是,我們用另一個VMM服務,Get_Sys_VM_Handle來得到系統虛擬機的虛擬機句柄。我們分別把消息的地址和消息框標題的地址放在ecx和edi裡面。我們不需要知道客戶的反應,所以我們把esi和edx置零。當每個參數都在相應的寄存器內後,我們就調用 SHELL_Message 來顯示消息框。
BeginProc OnVMClose
mov ecx,OFFSET32 VMDestroyed
jmp CommonCode
EndProc OnVMClose
OnVMCloseOnVMClose函數本身是很簡單的。因為它要使用的代碼和OnVMCreate相同,所以它在用另一個消息的地址初始化ecx後,就轉到OnVMCreate中的代碼去了。
改變定義文件(.def)
VxD MESSAGE
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LMGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_LMSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PMSGTABLE CLASS 'MCODE' NONDISCARDABLE IOPL
_PMSGDATA CLASS 'MCODE' NONDISCARDABLE IOPL
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'
EXPORTS
MESSAGE_DDB @1
匯編過程:
ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32 message.asm
link -VxD -def:message.def message.obj
VxD 的安裝:
把message.VxD放到\system目錄下。
在system.ini文件裡的[386enh]部分裡加上如下的一行:
device=message.VxD
重啟你的計算機。
測試這個VxD:
打開一dos窗口,你會看到彈出的消息框,顯示“A VM is created."。當你關閉一個dos窗口,又會彈出一個消息框顯示“A VM is destroyed"。