程式簡介
虛擬設備驅動程式被簡稱為VxD。x代表各種設備的名字,如虛擬鍵盤驅動程式(vkd),虛擬滑鼠驅動程式(vmd)等等。VxD程式是硬體成功初始化的途徑。記得dos程式認為它們擁有系統的一切,當它們在虛擬機中運行時,Windows需要給它們一個實機器的替身。VxD程式就是這些替身。VxD程式通常虛擬一些硬體設備,所以,例如當一個dos程式認為它在同鍵盤通訊時,實際是虛擬鍵盤驅動程式在和dos程式通訊。一個VxD程式通常控制真正的硬體設備並對該設備在各個虛擬機之間的共享進行管理。 儘管如此,並不是說每個VxD程式必須和一個硬體設備相連。雖然VxD程式是用來虛擬硬體設備的,但是我們也可以把VxD程式看作是在第0級別的dll。例如,如果你需要做一些只有在第0級別才能做的工作,你就可以編一個VxD程式來為你完成這個工作。這樣,由於此VxD程式並沒有虛擬任何設備,你就可以把它僅僅看作是你的程式的擴展。
VxD程式是Windows 9x特有的,它在Windows NT下不能運行。所以如果你的程式是依靠VxD的,它就不能被移植到Windows NT平台上去。
VxD是系統中權力最大的實體。由於它們可以對系統作任何事情,所以它們是極度危險的。一個惡意的/錯誤的VxD程式可以毀掉整個系統。對於惡意的/錯誤的VxD程式沒有任何的保護措施。
種類
Windows 95下有兩種VxD:
靜態VxD
動態VxD
靜態VxD
靜態VxD是那些從系統啟動就被載入,在系統關閉之前一直存在於記憶體中的VxD程式。這種VxD可以追溯至Windows 3.x的時代。動態VxD時只有Windows 9x下才有的。
動態VxD
動態VxD程式可以在需要的時候被載入/卸載。這些程式大多數都是用來控制設定管理器和輸入輸出監視器載入的即插即用設備的。你可以在你的win32應用程式里載入或卸載動態VxD程式。
程式通訊
VxD程式,包括VMM,通過以下三種途徑在相互之間進行通訊:
控制訊息
服務API
回調
控制訊息
當有VMM感興趣的事件發生時,它就向系統中 所有載入的VxD程式傳送控制訊息。控制訊息就像是第三層級別的Windows應用程式的訊息。每個VxD程式都有一個接受和處理控制訊息的函式,叫做 設備控制函式。系統控制訊息總共有50多個。控制訊息不多的原因是系統中通常載入了很多VxD程式,而每個VxD程式在收到一個控制訊息時都要進行處理。如果控制訊息太多,就會導致系統停滯。所以控制訊息只包括那些與虛擬機有關的重要訊息,如:一個虛擬機被創建,被銷毀等等。作為對系統控制訊息的附加,一個VxD程式可以定義自己的控制訊息,這些訊息可以用來和那些能回響這些訊息的VxD程式通訊。
服務函式
一個VxD程式,包括VMM在內,通常要導出一系列的被別的VxD程式調用的公共函式,這些函式被稱為VxD服務。調用這些服務的機制和在第三層級別運行的的應用程式有很大的不同:每個導出VxD服務的VxD程式 必須有一個唯一的ID,你可以從Microsoft得到一個這樣的ID。這個ID是一個包含了一個VxD唯一的身份驗證的16位的數字,例如: 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你可以看到VMM的ID是1,VPICD的ID是3,等等。VMM用這些ID來找到導出所需VxD服務的VxD程式。當一個VxD程式導出VxD服務時,它把所有服務的地址存在一個表裡面。所以,你還需要通過服務分支表裡面服務的索引來找到你所要的服務。例如,如果你要調用第一個服務,GetVersion服務,你就要指定0(這個索引是從0開始的)。調用VxD服務的實機制包括中斷20h,你的代碼產生一個中斷20h,並帶有一個雙字的值,這個值包含了設備ID和服務索引。例如,如果你要調用一個VxD程式導出的VxD服務,假設VxD程式設備ID是000DH,服務號碼是1,那么代碼應該是:
int 20h
dd 000D0001h跟在中斷20H後的雙字的高字包含設備ID。低字是在服務列表中的索引。
當20H中斷執行時,VMM得到了控制權,並馬上檢測跟著的雙字。然後它提出設備ID用來找到VxD程式,用服務索引來定位在那個VxD程式中的所要求的服務的地址。
你可以看到這個操作時很費時的。VMM必須浪費很多時間來定位VxD程式和所要服務的地址,所以VMM作了個小小的 弊 。當中斷20H操作成功後,VMM 抓取連結。這就是說,VMM用直接的服務調用來替代20H中斷和它後面的雙字。所以上面的20H中斷代碼片斷就被改變成:
call dword ptr [VxD_Service_Address]這個把戲是成功的,因為 int 20h+dword加一個雙字用6個位元組,正好和 call dword ptr結構相等。所以接下來的服務調用是快速而有效的。這個方法具有直接性,簡潔性。在好的一方面,它減輕了VMM和VxD載入器的工作量,因為它們不用定位VxD中 所有的服務,那些沒有執行過的服務將會保持原樣。再不那么好的一方面,一旦一個靜態VxD程式導出的服務被調用,那么就不可能把這個靜態的VxD程式卸載了。由於VMM把調用鎖定到VxD服務的實際地址上,如果提供這個服務的VxD程式從記憶體中被卸載了,其他VxD程式調用這個服務時就會很快的因為調用無效的記憶體地址而導致系統崩潰。沒有辦法來 消除抓取的連結。這個問題的結論是動態VxD不適合作為服務提供者。
回調
回調或者回調函式是在VxD程式中給其他的VxD程式調用的函式,不要把回調函式和VxD服務搞混淆了。回調函式不像服務那樣是公共的,它們是私有函式,VxD在特定的情況下把它們的地址送給其他的VxD程式。例如,當一個VxD程式在處理一個硬體中斷時,由於VMM是不可重入的,這個VxD程式不能使用VxD服務,否則會引起頁面錯誤(重入VMM)。這個VxD程式可以把它自己的一個回調函式的地址給VMM,這樣VMM就可以在能忍受頁面錯誤時調用這個函式。回調函式的想法不是VxD獨有的。許多Windows API都在用。最好的例子也許是視窗函式,你把視窗函式的地址填在WINDCLASS或WINDCLASSEX結構里並把它當作函式來調用RegisterClass或者RegisterClassEx。當有這個視窗的訊息傳來時,Windows就會調用你的視窗函式。另一個例子是視窗接管函式。你的程式把接管函式的地址送給Windows,這樣當你感興趣的事件發生時,Windows就會調用你的接管函式。
上述三種方法是VxD之間通訊的,我們還要講對V86,保護模式和Win32應用程式的接口。