首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

動態 VxD 實戰:Win32 DeviceIoControl 介面讓 Ring 3 應用呼叫 Ring 0 代碼

     編輯:組合語言

為什麼需要 DeviceIoControl?

前文提到 VxD 提供四種介面,前三種 (Services, V86, PM) 均為 16-bit 環境 (V86/PM) 或 Ring 0 內部 (Services) 使用。**Win32 應用 (Ring 3, 32-bit) 無法直接使用。** 微軟在 Windows 95 引入 DeviceIoControl API,專門讓 Win32 應用與動態 VxD 通訊。

關鍵區別:DeviceIoControl 介面的功能碼 (dwIoControlCode) 與 VxD 服務編號**完全無關**,是獨立定義的介面契約。

Win32 應用端:標準流程

; 1. 開啟/載入 VxD (檔名格式固定:\\.\VxDName.vxd)
invoke CreateFile, addr VxDName, 0, 0, 0, 0, FILE_FLAG_DELETE_ON_CLOSE, 0
; FILE_FLAG_DELETE_ON_CLOSE: 關閉代碼時自動卸載 VxD
.if eax != INVALID_HANDLE_VALUE
    mov hVxD, eax

    ; 2. 呼叫 VxD 內部功能
    invoke DeviceIoControl, hVxD, 1, addr InBuffer, 8, NULL, NULL, NULL, NULL
    ; hDevice=VxD代碼, dwIoControlCode=1 (自定義), lpInBuffer=輸入緩衝區, nInBufferSize=8

    ; 3. 關閉代碼 -> 觸發 VxD 卸載
    invoke CloseHandle, hVxD
.endif

VxD 端:處理 w32_DeviceIoControl 控制訊息

動態 VxD 必須在控制分派表註冊處理 w32_DeviceIoControl 訊息的回呼。

最小動態 VxD 框架 (MASM + VMM.INC)

.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          ; 返回 0 = 成功,CreateFile 才會成功
    .endif
    ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS
end

模組定義檔 (.def) - 關鍵區段屬性

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
_ITEXT  CLASS 'ICODE' DISCARDABLE
_IDATA  CLASS 'ICODE' DISCARDABLE
_PTEXT  CLASS 'PCODE' NONDISCARDABLE
_STEXT  CLASS 'SCODE' RESIDENT
_SDATA  CLASS 'SCODE' RESIDENT
; ... (標準 VxD 區段定義)

EXPORTS
DYNAVXD_DDB @1          ; 匯出裝置描述區塊 (DDB) 符號,VMM 載入器需求

完整範例:VxDLoader.exe 呼叫 ShellMsg.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            ; 輸入緩衝區: 兩個字串指標 (8 bytes)
         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

動態 VxD:ShellMsg.asm

.386p
include vmm.inc
include vwin32.inc
include shell.inc        ; SHELL_sysmodal_Message 定義

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          ; 取得輸入緩衝區指標 (指向兩個 DWORD 字串指標)

        ; --- 複製標題字串到 VxD 可存取記憶體 (Ring 0 無法直接存取 Ring 3 位址) ---
        VMMCall _lstrlen, <[edi]>           ; 計算長度
        inc eax                             ; +1 for null
        push eax
        VMMCall _HeapAllocate,  ; 配置記憶體
        mov pTitle, eax
        pop eax
        VMMCall _lstrcpyn,    ; 複製字串

        ; --- 複製訊息字串 ---
        VMMCall _lstrlen, <[edi+4]>
        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           ; 取得 System VM 代碼
        VxDCall SHELL_sysmodal_Message      ; 彈出訊息框

        ; --- 釋放記憶體 ---
        VMMCall _HeapFree, pTitle, 0
        VMMCall _HeapFree, pMessage, 0
        xor eax, eax
    .endif
    ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS
end

關鍵技術細節深度解析

1. DIOCParams 結構:Win32 參數映射到 VxD

當 Win32 呼叫 DeviceIoControl,VWIN32 (負責 Win32/VxD 橋接的系統 VxD) 封裝參數並發送 w32_DeviceIoControl 給目標 VxD。ESI 指向 DIOCParams:

DIOCParams STRUC
    Internal1        DD ?   ; 指向 Win32 用戶端寄存器結構
    VMHandle         DD ?   ; 呼叫者 VM 代碼
    Internal2        DD ?   ; 指向 DDB (裝置描述區塊)
    dwIoControlCode  DD ?   ; 功能碼 (Win32 傳入)
    lpvInBuffer      DD ?   ; 輸入緩衝區指標 (Ring 3 位址)
    cbInBuffer       DD ?   ; 輸入緩衝區大小
    lpvOutBuffer     DD ?   ; 輸出緩衝區指標
    cbOutBuffer      DD ?   ; 輸出緩衝區大小
    lpcbBytesReturned DD ?  ; 實際返回位元組數指標
    lpoOverlapped    DD ?   ; OVERLAPPED 結構指標
    hDevice          DD ?   ; Ring 3 設備代碼
    tagProcess       DD ?   ; 進程標籤
DIOCParams ENDS

核心難點:lpvInBuffer / lpvOutBuffer 是 **Ring 3 線性位址**。VxD 運行在 Ring 0,**不能直接解引用**。必須:

  • 呼叫 VMMCall _HeapAllocate 在系統記憶體配置緩衝區。
  • 呼叫 VMMCall _lstrcpyn (或 CopyMemory) 從 Ring 3 複製資料到 Ring 0 緩衝區 (VMM 會處理跨環複製)。
  • 操作完成後 _HeapFree 釋放。

2. VMMCall vs VxDCall

  • VMMCall ServiceName, :呼叫 **VMM 導出服務** (如 _HeapAllocate, _lstrlen, Get_Sys_VM_Handle)。宏展開為 INT 20h + VMM Device ID + 服務索引 (經抓取優化後為直接 Call)。
  • VxDCall ServiceName, :呼叫 **其他 VxD 導出服務** (如 SHELL_sysmodal_Message 屬於 SHELL VxD)。

3. 系統模態訊息框

SHELL_sysmodal_Message 參數:EDI=標題指標, ECX=訊息指標, EAX=旗標 (MB_OK等)。它會凍結**整個系統** (所有 VM 暫停),直到用戶點擊確定。這在除錯時極有用,但生產環境慎用。

總結:動態 VxD 開發清單

  1. 定義 DECLARE_VIRTUAL_DEVICE,註冊 w32_DeviceIoControl 分派。
  2. 實作 OnDeviceIoControl,處理 DIOC_Open (返回 0) 與自定義功能碼。
  3. **嚴格遵守 Ring 0 記憶體存取規則**:輸入/輸出緩衝區必須透過 VMM 服務複製。
  4. 撰寫標準 VxD .def 區段定義,匯出 DDB 符號。
  5. Win32 端:CreateFile("\\.\name.vxd") -> DeviceIoControl -> CloseHandle (配合 FILE_FLAG_DELETE_ON_CLOSE 自動卸載)。

現代替代方案:Windows NT 系 (2000+) 使用 **WDM/KMDF 驅動 + DeviceIoControl (IRP_MJ_DEVICE_CONTROL)**,架構相似但支援 PnP、電源管理、簽名強制、安全模型完整。VxD 僅限 Win9x 歷史研究或極舊系統維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved