我們將學習本教程中另外一個重要的結構,叫客戶寄存器結構。在本文中,V86指虛擬8086模式。在這裡下載例子程序
理論
VxDs與正常的win32/win16/DOS應用程序有很大不同。大多數情況下,當其他應用程序正常工作時,它們是休眠的。它們象一個監管者一樣工作,其作用是監視ring-3應用程序並在其出錯時改正它們。下面是其工作時的典型的情況:
1、中斷發生時
2、VMM得到控制權時
3、VMM存貯寄存器組的值時
4、VMM服務於中斷或調用其他VxDs完成此工作時
5、VMM交還控制權給被中斷的程序時
在以上過程中令人感興趣的是,VMM只有這一種方式能影響被中斷的應用程序,即修改存儲的寄存器映象。例如,VMM認為被中斷的程序應該返回到另外一個地址,它就修改存儲的寄存器映象中CS:IP的值,當這個程序被重新分派時,它將在新的CS:IP處開始執行。
VMM在客戶寄存器結構中存儲中斷點處的寄存器值。
Client_Reg_Struc STRUC
Client_EDI DD ?
Client_ESI DD ?
Client_EBP DD ?
Client_res0 DD ?
Client_EBX DD ?
Client_EDX DD ?
Client_ECX DD ?
Client_EAX DD ?
Client_Error DD ?
Client_EIP DD ?
Client_CS DW ?
Client_res1 DW ?
Client_EFlags DD ?
Client_ESP DD ?
Client_SS DW ?
Client_res2 DW ?
Client_ES DW ?
Client_res3 DW ?
Client_DS DW ?
Client_res4 DW ?
Client_FS DW ?
Client_res5 DW ?
Client_GS DW ?
Client_res6 DW ?
Client_Alt_EIP DD ?
Client_Alt_CS DW ?
Client_res7 DW ?
Client_Alt_EFlags DD ?
Client_Alt_ESP DD ?
Client_Alt_SS DW ?
Client_res8 DW ?
Client_Alt_ES DW ?
Client_res9 DW ?
Client_Alt_DS DW ?
Client_res10 DW ?
Client_Alt_FS DW ?
Client_res11 DW ?
Client_Alt_GS DW ?
Client_res12 DW ?
Client_Reg_Struc ENDS
你可以看到這個結構分為兩個部分:Client_xxx和Client_Alt_xxx。在這稍作說明,在一個給定的VM中,可能有兩個運行的線程:V86和保護模式。當V86程序運行時,假如一個中斷產生,Client_xxx將包含V86程序的寄存器映象,Client_Alt_xxx將包含保護模式程序的寄存器映象。相應的,當保護模式程序運行時,假如一個中斷產生,Client_xxx將包含保護模式程序的寄存器映象,Client_Alt_xxx將包含V86程序的寄存器映象。Client_resX被保留而沒有使用。
在查看過這個結構後,你可能有一問題:怎樣改變寄存器中的一個字節,比如al?上面的結構僅僅描述了字和雙字大小的寄存器組。不用擔心,在vmm.inc找一找。那有兩個為此附加的結構:Client_Word_Reg_Struc和Client_Byte_Reg_Struc。假如你想以字或字節大小來訪問寄存器,根據你的需要轉換Client_Reg_Struc到Client_Word_Reg_Struc或Client_Byte_Reg_Struc。
下一個問題:我們如何得到一個指向客戶寄存器結構的指針?
這相當簡單:一般地,當VMM調用我們的VxD時,把客戶寄存器結構的地址放在ebp中。在這裡的客戶寄存器結構是當前VM的。你可以從VM的句柄中得到這個指針。記住,VM的句柄是VM控制塊的線性地址。
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
CB_Client_Pointer包含指向VM的客戶寄存器結構的指針。例如:你可用下邊的代碼得到指向當前VM中的客戶寄存器結構的指針:
VMMCall Get_Cur_VM_Handle ; return the current VM handle in ebx
assume ebx:ptr cb_s
mov ebp,[ebx+CB_Client_Pointer] ; pointer to client reg struct
現在我們了解了客戶寄存器結構,我們可以用它來開始工作了。我們將使用客戶寄存器結構去傳送寄存器組的值到一個DOS中斷中,也就是,int 21h,功能2h,顯示一個字符。這個DOS服務把要顯示的字符放在dl中。假如我們傳送響鈴字符(07h)到這個服務,將通過PC喇叭發出一聲響。
記住,int 21h是一個DOS服務,因而其在V86模式下是可用的,我們如何在VxD中調用一個V86中斷?一個方法是使用Exec_Int服務。這個VMM服務把要調用的中斷號放在eax中。它模擬指定的中斷然後返回到調用的VM。然而,它必須在一個嵌套執行塊中被調用。嵌套執行塊被Begin_Nest_V86_Exec (或 Begin_Nest_Exec)和End_Nest_Exec包括起來。如果我們要調用int 21h功能2h,我們需要在嵌套執行塊內轉換Client_Byte_Reg_Struc結構的Client_ah和Client_Dl,然後把值21h放在eax中。當一切准備好了,就調用Exec_Int。
例子:
例子是一個動態VxD,它調用int 21h的功能2使PC喇叭發聲。
.386p
include \masm\include\vmm.inc
include \masm\include\vwin32.inc
include \masm\include\v86mmgr.inc
VxDName TEXTEQU
ControlName TEXTEQU
VxDMajorVersion TEXTEQU <1>
VxDMinorVersion TEXTEQU <0>
VxD_STATIC_DATA_SEG
VxD_STATIC_DATA_ENDS
VXD_LOCKED_CODE_SEG
;----------------------------------------------------------------------------
; Remember: The name of the vxd MUST be uppercase else it won't work/unload
;----------------------------------------------------------------------------
DECLARE_VIRTUAL_DEVICE %VxDName,%VxDMajorVersion,%VxDMinorVersion, %ControlName,UNDEFINED_DEVICE_ID,UNDEFINED_INIT_ORDER
Begin_control_dispatch %VxDName
Control_Dispatch W32_DEVICEIOCONTROL, OnDeviceIoControl
End_control_dispatch %VxDName
VXD_LOCKED_CODE_ENDS
VXD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
assume esi:ptr DIOCParams
.if [esi].dwIoControlCode==1
Push_Client_State
VMMCall Begin_Nest_V86_Exec
assume ebp:ptr Client_Byte_Reg_Struc
mov [ebp].Client_dl,7
mov [ebp].Client_ah,2
mov eax,21h
VMMCall Exec_Int
VMMCall End_Nest_Exec
Pop_Client_State
EndI:
.endif
xor eax,eax
ret
EndProc OnDeviceIoControl
VXD_PAGEABLE_CODE_ENDS
end
講解
Push_Client_State
這沒什麼好講解的。當一個VxD接收到一個DeviceIoControl消息,ebp已經指向了當前VM的客戶寄存器結構。我們調用Push_Client_State宏在堆棧中存儲客戶寄存器結構的狀態。然後用Pop_Client_State宏恢復客戶寄存器。
VMMCall Begin_Nest_V86_Exec
通過調用Begin_Nest_V86_Exec開始嵌套執行塊。
assume ebp:ptr Client_Byte_Reg_Struc
mov [ebp].Client_dl,7
mov [ebp].Client_ah,2
改變在客戶寄存器中的dl和ah寄存器的映象。這個改變的值將由中斷使用。
mov eax,21h
VMMCall Exec_Int
Exec_Int要求在eax存有一個中斷號。我們想使用int 21h。等會我們調用Exec_Int去模擬中斷。
VMMCall End_Nest_Exec
Pop_Client_State
當Exec_Int返回,我們完成了嵌套執行塊,並且由堆棧中恢復了客戶寄存器結構的值。 你將聽到你的PC喇叭發出一聲響。