Iczelion 的 Win32Asm VxD 彙編教程 (六) (轉)

worldblog發表於2007-12-12
Iczelion 的 Win32Asm VxD 彙編教程 (六) (轉)[@more@]

DeviceIoControl介面:namespace prefix = o ns = "urn:schemas--com::office" />

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

點選msg.zip">這裡例子

VxD介面

VxD總共提供了4種介面。

l  VxD services   VxD服務

l  V86 Interface   V86介面

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

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

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

DeviceIoControl介面

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

在Win32程式方面:

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

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

DeviceIoControl PROTO  hDevice:D,

  dwIoControlCode:DWORD,

  lpInBuffer:DWORD,

  nInBufferSize:DWORD,

   lpOutBuffer:DWORD,

  nOutBufferSize:DWORD,

  lpBytesReturned:DWORD,

  lpOverlapped:DWORD

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

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

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

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

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

nOutBufferSizelpOutBuffer所指向的緩衝區的大小(byte)。

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   是指向裝置描述塊(D)的控制程式碼。

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 'DE'  RESNT
  _SDATA  CLASS 'SCODE'  RESIDENT
  _DBOSTART  CLASS 'DBOCODE'  PRELOAD NONDISCARDABLE CONFONG
  _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 succesully 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擴充套件非常理想。

 


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

相關文章