DeviceIoControl介面(轉)

heying1229發表於2007-07-28
DeviceIoControl介面:

  在這一節中我們將要關於學習動態VXD,特別是如何建立,載入和使用。

點選這裡下載例子

VxD介面
VxD總共提供了4種介面。

l VxD services VxD服務

l V86 Interface V86介面

l Protected-mode (PM) Interface 保護模式介面

l Win32 DeviceIoControl Interface Win32裝置輸入輸出控制介面

我們已經知道了VxD服務,V86和保護模式介面是由V86和保護模式程式呼叫的。因為V86和保護模式程式是16位的,我們不能在Win32應用程式中使用那兩種介面。在Windows 95中,微軟給Win32應用程式加了另外一個介面所以Win32應用程式可以呼叫VxD的服務:DeviceIoControl介面(裝置輸入輸出控制介面)

DeviceIoControl介面
簡單的說,DeviceIoControl介面是一種為Win32程式準備的呼叫VxD內部函式的方法。不要混淆DeviceIoControl介面呼叫函式和用VxD服務呼叫函式,這兩種方法是不一樣的。比如說,DeviceIoControl function1 也許和Vxd service1是不一樣的。你應給把DeviceIoControl函式作為一種只為Win32應用程式提供的單獨的函式。

在Win32程式方面:
首先用CreateFile來開啟/載入一個VxD。如果呼叫成功的話,VxD將會建立/加再到記憶體中並且CreateFile把VxD的控制程式碼返回到eax中。

接著你呼叫DeviceIoControlAPI函式來選擇要執行的函式。DeviceIoControl函式遵循下面的語法:

DeviceIoControl PROTO hDevice:DWORD,

dwIoControlCode:DWORD,

lpInBuffer:DWORD,

nInBufferSize:DWORD,

lpOutBuffer:DWORD,

nOutBufferSize:DWORD,

lpBytesReturned:DWORD,

lpOverlapped:DWORD

l hDevice 是從CreateFile返回的VxD控制程式碼。

l dwIoControlCode是用來制定VxD將要進行的操作。你應該在你要選用那種操作之前得到可能的dwIoControlCode值得列表。

l lpInBuffer是包含了VxD完成dwIoControlCode所制定操作的資料的緩衝區地址。如果這個操作不需要資料,你可以傳為NULL。

l nInBufferSize是由lpInBuffer所指向的緩衝區的地址的大小(byte)。

l lpOutBuffer是VxD程式在操作成功之後要將輸出資料輸出到的緩衝區。如果這個操作沒有任何返回值,這個值可以為NULL。

l nOutBufferSize是lpOutBuffer所指向的緩衝區的大小(byte)。

l lpBytesReturned是一個dword型變數的地址。這個變數用來接收VxD在lpOutBuffer中寫入資料的大小。

l 如果你想要把操作設成非同步的,lpOverlapped是一個OVERLAPPED結構的指標。如果你要一直等直到操作完成,這個值為NULL。

在VxD方面:
VxD程式必須處理w32_deviceIoControl訊息。當VxD收到w32_deviceIoControl訊息,它的暫存器是如下值:

l ebx 是VM的控制程式碼。

l esi 是指向DIOCParams結構的指標。DIOCParams包含了從win32程式傳送的資訊。

DIOCParams是按照如下定義的:

DIOCParams STRUC

Internal1 DD ?

VMHandle DD ?

Internal2 DD ?

dwIoControlCode DD ?

lpvInBuffer DD ?

cbInBuffer DD ?

lpvOutBuffer DD ?

cbOutBuffer DD ?

lpcbBytesReturned DD ?

lpoOverlapped DD ?

hDevice DD ?

tagProcess DD ?

DIOCParams ENDS

l Internal1 是指向Win32應用應用程式使用者暫存器結構的指標。

l VMHandle 虛擬機器控制程式碼

l Internal2 是指向裝置描述塊(DDB)的控制程式碼。

l dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped是傳送到DeviceIoControl API的引數。

l hDevice是 ring-3級裝置控制程式碼。

l tagProces 是過程的標籤。

在DIOCParams結構中有所有從Win32應用程式傳送到你的VxD的資訊。

你的VxD至少要處理DIOC_Open(傳送到dwIoControlCode),那是當Win32程式呼叫CreateFile開啟你的VxD時VWIN32傳送給你的VxD的。如果你的VxD準備好了,它必須在eax中返回0而且CreateFile也會成功。如果你的VxD沒有準備好,它必須在eas中返回一個非零值而且CreateFile也會失敗。除了DIOC_Open,當Win32程式關閉這個裝置控制程式碼時,你的VxD將會從VWIN32收到DIOC_Closehandle。

能由CreateFile載入的最小的動態VxD框架:

.386p
include vmm.inc
include vwin32.inc

DECLARE_VIRTUAL_DEVICE DYNAVXD,1,0, DYNAVXD_Control,
UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

Begin_control_dispatch DYNAVXD
Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl
End_control_dispatch DYNAVXD

VxD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
assume esi:ptr DIOCParams
.if [esi].dwIoControlCode==DIOC_Open
xor eax,eax
.endif
ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS

end

;--------------------------------------------------------------------------------------------------------------------------------
; Module Definition File
;---------------------------------------------------------------------------------------------------------------------------------

VXD DYNAVXD DYNAMIC

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
DYNAVXD_DDB @1

完整例子:
下面是一段載入動態VxD並且透過DeviceIoControl API 來呼叫VxD內部函式的Win32應用程式的原始碼。

; VxDLoader.asm

.386
.model flat,stdcall
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib

.data
AppName db "DeviceIoControl",0
VxDName db ".shellmsg.vxd",0
Success db "The VxD is successfully loaded!",0
Failure db "The VxD is not loaded!",0
Unload db "The VxD is now unloaded!",0
MsgTitle db "DeviceIoControl Example",0
MsgText db "I'm called from a VxD!",0
InBuffer dd offset MsgTitle
dd offset MsgText
.data?
hVxD dd ?
.code
start:
invoke CreateFile,addr VxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0
.if eax!=INVALID_HANDLE_VALUE
mov hVxD,eax
invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION
invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL
invoke CloseHandle,hVxD
invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION
.else
invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR
.endif
invoke ExitProcess,NULL
end start

下面這段原始碼是由 vxdloader.asm 呼叫的動態VxD。
; ShellMsg.asm

.386p
include vmm.inc
include vwin32.inc
include shell.inc

DECLARE_VIRTUAL_DEVICE SHELLMSG,1,0, SHELLMSG_Control,
UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

Begin_control_dispatch SHELLMSG
Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl
End_control_dispatch SHELLMSG

VxD_PAGEABLE_DATA_SEG
pTitle dd ?
pMessage dd ?
VxD_PAGEABLE_DATA_ENDS

VxD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
assume esi:ptr DIOCParams
.if [esi].dwIoControlCode==DIOC_Open
xor eax,eax
.elseif [esi].dwIoControlCode==1
mov edi,[esi].lpvInBuffer
;-----------------------------------
; copy the message title to buffer
;-----------------------------------
VMMCall _lstrlen,
inc eax
push eax
VMMCall _HeapAllocate,
mov pTitle,eax
pop eax
VMMCall _lstrcpyn,
;-----------------------------------
; copy the message text to buffer
;-----------------------------------
VMMCall _lstrlen,
inc eax
push eax
VMMCall _HeapAllocate,
mov pMessage,eax
pop eax
VMMCall _lstrcpyn,
mov edi,pTitle
mov ecx,pMessage
mov eax,MB_OK
VMMCall Get_Sys_VM_Handle
VxDCall SHELL_sysmodal_Message
VMMCall _HeapFree,pTitle,0
VMMCall _HeapFree,pMessage,0
xor eax,eax
.endif
ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS

end

分析:
我們從VxDLoader.asm開始。

Invoke CreateFile,addrVxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0
.if eax!=INVALID_HANDLE_VALUE
mov hVxD,eax
....
.else
invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR
.endif

我們呼叫CreateFile來載入動態VxD。注意FILE_FLAG_DELETE_ON_CLOSE標記。當從CreateFile返回的VxD控制程式碼被關閉的時候,這個標誌通知Windows解除安裝VxD。如果CreateFile成功,我們把VxD控制程式碼儲存起來。

invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION
invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL
invoke CloseHandle,hVxD
invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION

當VxD載入/解除安裝的時候,這個程式會顯示一個訊息框。它令dwIoControlCode=1然後呼叫DeviceIoControl。將InBuffer的地址傳給lpInBuffer,將InBuffer的大小傳給nInBufferSize。InBuffer是一個包括兩個元素的陣列:每個元素都是一個字串的地址。

MsgTitle db "DeviceIoControl Example",0
MsgText db "I'm called from a VxD!",0
InBuffer dd offset MsgTitle
dd offset MsgText

現在我們看一下這段VxD。

它只處理w32_deviceIoControl訊息。當w32_deviceIoControl訊息傳送的時候,呼叫OnDeviceIoControl函式。

BeginProc OnDeviceIoControl
assume esi:ptr DIOCParams
.if [esi].dwIoControlCode==DIOC_Open
xor eax,eax

OnDeviceIoControl 處理DIOC_Open,再eas中返回0。

.elseif [esi].dwIoControlCode==1
mov edi,[esi].lpvInBuffer

它也處理control code 等於1。它做的第一件事是取出在lpyInBuffer中的資料。這個資料是傳送到DeviceIoControl API 的lpInBuffer中的兩個dword值。它把指向dword陣列的地址放到edi中。第一個dword是作為訊息框標題的字串地址。第二個dword是作為訊息框文字的字串地址。

;-----------------------------------
; copy the message title to buffer
;-----------------------------------
VMMCall _lstrlen,
inc eax
push eax
VMMCall _HeapAllocate,
mov pTitle,eax
pop eax
VMMCall _lstrcpyn,

它呼叫VMM服務lstrlen來計算訊息框標題的長度。lstrlen在eax中返回字串的長度。我們把這個長度加1來包括結束標記NULL。下一步我們透過呼叫HeapAllocate來分配一塊足夠大可以容納字串和它的結束標記NULL記憶體。加上HEAPZEROINIT標記使HeapAllocate將這塊記憶體清零。HeapAllocate在eax中返回這塊記憶體的地址。我們然後從win32 app的地址空間把字串複製到我們申請的記憶體中。我們對要做訊息框文字的字串做同樣的操作。

mov edi,pTitle
mov ecx,pMessage
mov eax,MB_OK
VMMCall Get_Sys_VM_Handle
VxDCall SHELL_sysmodal_Message

我們把標題和文字的地址分別存在edi和ecx中。把想要的標記放在eax中,透過呼叫Get_Sys_VM_handle得到系統VM的VM 控制程式碼。然後呼叫SHELL_sysbodal_Message 。SHELL_sysModal_Message是系統SHELL_Message的模式版本。它凍結系統直到使用者對訊息框做出反應。

VMMCall _HeapFree,pTitle,0
VMMCall _HeapFree,pMessage,0

當SHELL_sysmodal_Message返回時,我們用_HeapFree釋放記憶體。

總結:
DeviceIoControl介面使你的win32應用程式使用動態VxD作為一個ring-0 DLL擴充套件非常理想。

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928723/,如需轉載,請註明出處,否則將追究法律責任。

相關文章