程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 虛擬設備驅動程序初步

虛擬設備驅動程序初步

編輯:匯編語言

在本教程裡,我假定讀者對諸如虛8086模式,調頁,GDT,LDT,IDT之類的INTEL 80x86保護模式的操作比較熟悉。如果你不了解這些,那你要先在 http://developer.intel.com/design/pentium/manuals/閱讀INTEL的文檔。
內容:
Windows95是一個運行在最高級特權,第0層級別的多線程操作系統。所有的應用程序都運行在最低級特權,第3層級別上。這樣就限制了應用程序對系統的操作。它們不能使用cpu特權指令,不能直接訪問I/O端口,等等。你對gdi32,kernal32和user32這三個大的系統組件一定很熟悉。你肯定會認為這樣重要的代碼段一定是在第0層級別下運行的。但是實際上,它們和其他的應用程序一樣,是在第三層級別下運行的。這就是說它們並不比Windows計算器,或者掃雷游戲有更多的權限。系統的控制實權掌握在虛擬級管理器(VMM) 和虛擬設備驅動程序(VxD)手中。
這一切都是由dos引起的。在Window 3.x的時代,在市場上有很多成功的dos軟件。Windows 3.x必須同時運行普通的Windows程序和dos程序,否則,它就會失去市場。
這個局面是很難處理的,因為dos程序和Windows程序有本質的不同。dos程序認為它們擁有系統的一切:鍵盤,cpu,內存,硬盤等等。dos程序不知道怎樣和其他程序合作,而Windows程序(從那時候起)是可靠的多任務合作系統。也就是每個Windows程序都必須通過GetMessage或PeekMessage來和其他程序進行交流。
解決辦法就是,在一個8086虛擬機上運行所有的dos程序,而在另一個叫做系統虛擬機的虛擬機上運行其他所有的Windows程序。Windows負責把cpu運算時間輪流的分給每個虛擬機。這樣,在Windows 3.x裡。Windows程序之間用的是合作多任務,而虛擬機之間用的是優先級多任務。
什麼是一個虛擬機?一個虛擬機是被軟件創建的一個假象。一個虛擬機和在它上面運行的程序交互,就像這個程序是在真正的機器上運行一樣。這樣,一個程序不知道也不關心自己是否是在虛擬機上運行。只要虛擬機准確的像一個真的機器一樣響應程序,我們就可以把它當成一個真正的機器。
你可以把虛擬機這種實機器和軟件之間的接口看作一種API。這種不尋常的API由中斷,BIOS調用和I/O端口組成。如果Windows能夠以某種方法完美的模擬這個API,那麼在虛擬機上運行的程序就會表現的和它們在實際器上運行時完全一樣。
這就是為什麼會出現VMM和VxD的原因。為了協調和監視虛擬機(VMs),Windows需要一個程序來分配任務。這個程序就是虛擬機管理器(VMM)。
虛擬機管理器
VMM是一個32位的保護模式程序。它的主要任務是建立和維護一個支持虛擬機的框架。例如,它要創建,運行和結束一個虛擬機。VMM是眾多的系統VxD程序之一,它被放在你的系統目錄下的VMM32.VxD文件中。VMM本身是一個VxD程序,但它被當作一個監視其他VxD程序的監視器。讓我們來看一下Windows95的啟動次序:
加載io.sys。
執行config.sys和autoexec.bat。
調用win.com。
win.com運行VMM32.VxD,VMM32.VxD實際上是個簡單的dos的exe文件。
VMM32.VxD用xms驅動程序把VMM加載到擴展內存。
VMM初始化自身及其它的默認VxD。
VMM把機器轉入到保護模式並創建系統虛擬機。
最後被加載的虛擬外殼設備在系統虛擬機上通過運行krnl386.exe來啟動Windows。
krnl386.exe加載所有的文件,最後是Windows95外殼。
正如你所看到的,VMM是第一個被加載到內存的VxD程序。它創建系統虛擬機並初始化其他的VxD程序。它也為這些VxD程序提供許多服務。
VMM和VxD的操作模式和真正的程序不同。在大多數時候,它們是潛伏的。當應用程序在系統中運行時,這些VxD程序沒有被激活。當某些需要它們處理的中斷/錯誤/事件發生時,它們才被喚醒。
VMM是不可重入的。這意味著VxD程序必須使它們的訪問和VMM服務同步。在有些情況下調用VMM服務是不安全的,比如VMM正在處理一個硬件中斷。在這段時間內,VMM是不允許重進入的。作為一個VxD編寫者,你必須對你的所作所為極度的小心。記住,你是在最高特權級別,第0層級別,如果你代碼有錯的話,誰也管不到。
虛擬設備驅動程序
虛擬設備驅動程序被簡稱為VxD。x 代表各種設備的名字,如虛擬鍵盤驅動程序(vkd),虛擬鼠標驅動程序(vmd)等等。VxD程序是硬件成功初始化的途徑。記得dos程序認為它們擁有系統的一切,當它們在虛擬機中運行時,Windows需要給它們一個實機器的替身。VxD程序就是這些替身。VxD程序通常虛擬一些硬件設備,所以,例如當一個dos程序認為它在同鍵盤通訊時,實際是虛擬鍵盤驅動程序在和dos程序通訊。一個VxD程序通常控制真正的硬件設備並對該設備在各個虛擬機之間的共享進行管理。
盡管如此,並不是說每個VxD程序必須和一個硬件設備相連。雖然VxD程序是用來虛擬硬件設備的,但是我們也可以把VxD程序看作是在第0級別的dll。例如,如果你需要做一些只有在第0級別才能做的工作,你就可以編一個VxD程序來為你完成這個工作。這樣,由於此VxD程序並沒有虛擬任何設備,你就可以把它僅僅看作是你的程序的擴展。
在我們更深入的討論VxD和創建我們的VxD程序之前,讓我先說一些有關於VxD的事情。
VxD程序是Windows 9x特有的,它在Windows NT下不能運行。所以如果你的程序是依靠VxD的,它就不能被移植到Windows NT平台上去。
VxD是系統中權力最大的實體。由於它們可以對系統作任何事情,所以它們是極度危險的。一個惡意的/錯誤的VxD程序可以毀掉整個系統。對於惡意的/錯誤的VxD程序沒有任何的保護措施。
通常的,不用VxD也有很多辦法能達到你的目的。在采用VxD的解決辦法之前一定要三思。如果用其他的可以在第三層級別實施的辦法,就使用這個辦法。

Windows 95下有兩種VxD:
靜態VxD
動態VxD
靜態VxD是那些從系統啟動就被加載,在系統關閉之前一直存在於內存中的VxD程序。這種VxD可以追溯至Windows 3.x的時代。動態VxD時只有Windows 9x下才有的。動態VxD程序可以在需要的時候被加載/卸載。這些程序大多數都是用來控制設置管理器和輸入輸出監視器加載的即插即用設備的。你可以在你的win32應用程序裡加載或卸載動態VxD程序。
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應用程序的接口。在下一章裡,我們要學習VxD對Win32應用程序的接口。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved