當前位置:編程學習大全網 - 編程語言 - 如何構造壹個簡單的USB過濾驅動程序

如何構造壹個簡單的USB過濾驅動程序

本文分三部分來介紹如何構造壹個簡單的USB過濾驅動程序,包括“基本原理”、“程序的實現”、“使用INF安裝”。此文的目的在於希望讀者了解基本原理後,可以使用除DDK以外最流行也最方便的驅動開發工具DriverStudio來實現壹個自己的過濾驅動,並正確地安裝。

壹、基本原理

我們知道,WDM(和KDM)是分層的,在構造設備棧時,IO管理器可以使壹個設備對象附加到另外壹個初始驅動程序創建的設備對象上。與初始設備對象相關的驅動程序決定的IRP,也將被發送到附加的設備對象相關的驅動程序上。這個被附加的驅動程序便是過濾驅動程序。如右圖,過濾驅動可以在設備棧的任何層次中插入。IO管理器發出的IRP將會沿著右圖的順序從上往下傳遞並返回。因此,我們可以使用過濾驅動程序來檢查、修改、完成它接收到的IRP,或者構造自己的IRP。

上面這種文字是很枯燥的,好在“前人”已經寫過壹些範例以供我們更好地理解這些概念。讀過Waltz Oney的《Programming Windows Driver Mode》壹書的讀者大概都知道Waltz Oney提供的範例中有壹個關於USB過濾器(第九章)的例子,而在此基礎上,《USB Design By Example》()的作者John Hyde實現了壹個USB鍵盤過濾驅動程序,即給此程序增加了壹個“攔截(Intercept)”功能來處理USB鍵盤的Report以實現特定的功能:當驅動程序在IRP_MJ_INTERNAL_DEVICE_CONTROL設置的完成例程從USB設備攔截到壹個Get_Report_Descriptor時,攔截程序將此Descriptor中的USAGE值從“Keyboard”改為“UserDefined”,再返回給系統。

我們可以從這個例子中獲得壹些靈感,比如,在Win2k下,鍵盤是由OS獨占訪問的,我們可以通過這種方式使之可以讓用戶自由訪問;我們也可以攔截其他Report_Descriptor,將部分鍵重新定義,以滿足特殊的要求;如果妳願意再做壹個用戶態的程序,妳還可以將妳攔截到的鍵值傳遞給妳的用戶態程序,以實現象聯想、實達等國內電腦大廠出品的那些鍵盤上的各種實用的功能。

二、程序的實現

Waltz Oney和John Hyde的例子已經寫得很詳細了,讀者可以不用修改壹個字節便順利地編譯生成壹個過濾驅動程序。本文的目的在於使用DriverStudio組件Driverworks來實現同樣的功能。

相信讀者讀到這篇文章時,已經對DriverStudio有了很多的了解。DriverStudio作為壹個以C++為基礎的“快速”驅動開發工具,它封裝了基本上所有的DDK的函數,其集成在VC++中的DriverWizard,可以很方便地引導妳完成設備驅動程序開發的全過程,能根據妳的硬件種類自動生成設備驅動程序源代碼,並提供了很多範例程序。當然,這些例子中便包含壹個USB Filter驅動程序的框架。在不侵犯版權的前提下,充分利用現有***享的、免費的、授權的代碼是我們的壹貫作法。我們下面便以此範例為基礎來作修改。

我們的目的是做壹個HID小驅動程序hidusb.sys的Lower Filter,它附加在“人機接口設備” ,通過攔截USB的Get_Report_Descriptor來修改其返回值,當它發現該Descriptor的Usage 為“Keyboard”時,將其改為“UserDefined”,如此我們便可以完全控制這只鍵盤。具體做法是,攔截IRP_MJ_INTERNAL_DEVICE_CONTROL,並檢查其IOCTL代碼及URB,如果滿足IOCTRL功能代碼為IOCTL_INTERNAL_USB_SUBMIT_URB以及URB功能代碼為URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE的條件,即上層驅動發來Get_Report_Descriptor請求時,設置壹個完成例程,在這個完成例程中,我們將判斷Usage的值,將Usage由“6(Keyboard)”時,將其改為“0(UserDefined)”。

打開C:\Program Files\NuMega\DriverStudio\DriverWorks\Examples\wdm\usbfilt目錄(具體目錄依妳的DriverStudio所安裝的目錄不同而不同) ,再打開工程文件usbfilt.dsw,我們先看壹下代碼。

程序由兩個類組成,壹個是Driver類,壹個是Device類。Driver類包括:

入口函數DriverEntry:

DECLARE_DRIVER_CLASS(UsbFilterDriver, NULL)

/////////////////////////////////////////////////////////////////////

// Driver Entry

//

NTSTATUS UsbFilterDriver::DriverEntry(PUNICODE_STRING RegistryPath)

{

T << "UsbFilterDriver::DriverEntry\n";

m_Unit = 0;

return STATUS_SUCCESS;

// The following macro simply allows compilation at Warning Level 4

// If you reference this parameter in the function simply remove the macro.

UNREFERENCED_PARAMETER(RegistryPath);

}

AddDevice函數

NTSTATUS UsbFilterDriver::AddDevice(PDEVICE_OBJECT Pdo)

{

T << "UsbFilterDriver::AddDevice\n";

UsbFilterDevice * pFilterDevice = new (

static_cast<PCWSTR>(NULL),

FILE_DEVICE_UNKNOWN,

static_cast<PCWSTR>(NULL),

0,

DO_DIRECT_IO

)

UsbFilterDevice(Pdo, m_Unit);

if (pFilterDevice)

{

NTSTATUS status = pFilterDevice->ConstructorStatus();

if ( !NT_SUCCESS(status) )

{

T << "Failed to construct UsbFilterDevice"

<< (ULONG) m_Unit

<< " status = "

<< status

<< "\n";

delete pFilterDevice;

}

else

{

m_Unit++;

}

return status;

}

else

{

T << "Failed to allocate UsbFilterDevice"

<< (ULONG) m_Unit

<< "\n";

return STATUS_INSUFFICIENT_RESOURCES;

}

}

這兩段代碼基本上和自動生成的代碼差不多。AddDevice的作用是構造壹個過濾器的實例。

關鍵的代碼在Device類。在這個類裏,我們把過濾器插入設備棧,並攔截IRP,用自己的完成例程來實現特定的功能。

Device構造函數

UsbFilterDevice::UsbFilterDevice(PDEVICE_OBJECT Pdo, ULONG Unit) :

KWdmFilterDevice(Pdo, NULL)

{

T << "UsbFilterDevice::UsbFilterDevice\n";

// Check constructor status

if ( ! NT_SUCCESS(m_ConstructorStatus) )

{

return;

}

// Remember our unit number

m_Unit = Unit;

// initialize the USB lower device

m_Usb.Initialize(this, Pdo);

NTSTATUS status = AttachFilter(&m_Usb); //Attach the filter

if(!NT_SUCCESS(status))

{

m_ConstructorStatus = status;

return;

}

SetFilterPowerPolicy();

SetFilterPnpPolicy();

}

在DDK中,我們用IoAttachDevice將設備對象插入設備棧中。DriverStudio封裝了這個函數。在DriverStudio中,其他驅動程序需要用Initialize來初始化設備對象和接口,對於過濾驅動,我們關鍵是需要Attachfilter將其附加在堆棧中。

對於大部分如IRP_MJ_SYSTEM_CONTROL等IRP,我們所做的只需用PassThrough(Irp)將其直接往設備棧下層傳遞,不需要做任何工作。這些代碼我們就不壹壹列舉了。下面的部分才是本文的關鍵。

我們知道,HIDUSB.SYS是使用內部IOCTRL發出URB給USB類驅動程序(USBD)讀取數據的,那麽,HIDUSB首先必須構造壹個IRP_MJ_INTERNAL_DEVICE_CONTROL,它的IOCTL功能碼為IOCTL_INTERNAL_USB_SUBMIT_URB(發出URB的內部IOCTL)。另外,因為我們要檢查並修改的是USB鍵盤某個接口的報告描述,那麽這個URB應該是URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE,如下:

NTSTATUS UsbFilterDevice::InternalDeviceControl(KIrp I)

{

T << "UsbFilterDevice::InternalDeviceControl\n";

// Pass through IOCTLs that are not submitting an URB

//不是我們感興趣的IOCTL不要理它

if (I.IoctlCode() != IOCTL_INTERNAL_USB_SUBMIT_URB)

return DefaultPnp(I);

PURB p = I.Urb(CURRENT); // get URB pointer from IRP

//不是我們感興趣的URB,也不要理它,

if (p->UrbHeader.Function !=

URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE)

return DefaultPnp(I);

//符合要求的IRP才被設置完成例程

return PassThrough(I, LinkTo(DeviceControlComplete), this);

}

在設置好條件以後,再來實現完成例程。所有的檢查、修改等動作都是在完成例程裏面完成的。

NTSTATUS UsbFilterDevice::DeviceControlComplete(KIrp I)

{

PURB p = I.Urb(CURRENT);

if(p)

{

//攔截到設備返回的描述表,

char* DescriptorBuffer = (char*)p->UrbControlDescriptorRequest.TransferBuffer;

//指向第三個字節,表示設備Usage屬性的值

DescriptorBuffer += 3;

//如果值為6則改成0,6表示hid鍵盤,0表示未知設備

//在設備管理器裏面,原來的hid兼容鍵盤就不復存在了,取而代之的則是hid兼容設備

if ((*DescriptorBuffer&0xff) == 6)

*DescriptorBuffer = 0;

}

return I.Status();

}

讀者可以對照DriverWorks中的例子,直接替換掉(或者修改)上面這兩個函數,再編譯壹下,便可以得到壹個完整的鍵盤過濾器驅動程序。

其實,只要弄清楚了我們需要做些什麽動作,在DriverStudio裏面只需要寫少量的關鍵代碼,便可實現我們的要求,其余的大部分工作,或有範例可供參考,或有Driver Wizard自動生成。

從上面可以看出,我們只需要修改這兩個函數,攔截合適的IRP,便可以在完成例程裏面實現我們特定的要求。正如開頭所說,我們也可以攔截其他的IRP,攔截其他的URB,或者攔截特定鍵盤的按鍵鍵值,將之傳遞到用戶態,以方便實現聯想、實達等隨機配備的多功能鍵盤的功能。

三、使用INF安裝驅動

在完成了驅動以後,還必須把它安裝到系統裏面,驅動程序才會起作用。壹般來說,我們都必須為我們的驅動程序提供壹個inf文件,以便於用戶安裝或者維護。對於新手來說,過濾驅動程序的inf或許有些棘手。所以,針對本文所描述的驅動,我們提供壹個Win98下的安裝範例usbkey.inf,範例中“;”後的文字是註解,以方便讀者理解。

; usbkey.INF

; Installs Lower Level Filter for a HID keyboard device

; (c) Copyright 2001 SINO Co., Ltd.

;

[Version]

;”CHICAGO”表示Win9x平臺

Signature="$CHICAGO$"

;鍵盤所屬類名

Class=HID

ClassGUID={745a17a0-74d3-11d0-b6fe-00a0c90f57da}

;驅動程序提供者,此信息會顯示在設備屬性的“常規”頁

Provider=%USBDBE%

LayoutFile=layout.inf

;顯示在驅動程序文件詳細資料窗口

DriverVer=11/12/2001,4.10.2222.12

;[ControlFlags]

;ExcludeFromSelect = *

;驅動程序安裝目錄,inf會將我們的驅動程序安裝到如下目錄

;記得Destinationdir後面壹定要帶壹個“s”

[DestinationDirs]

DefaultDestDir = 10,system32\drivers

;要增加的註冊表項

[ClassInstall]

Addreg=HIDClassReg

[HIDClassReg]

HKR,,,,%HID.ClassName%

HKR,,Icon,,-20

;制造商

[Manufacturer]

%USBDBE%=USBDBE

[USBDBE]

;我們所要附加過濾驅動程序的設備ID。這個ID可以從IC的規範上得來,也可以

;用hidview.exe讀出,或者從註冊表HKLM\Enum\hid和usb項找出

%HID.DeviceDesc% = Keypad_Inst, USB\VID_05AF&PID_0805&MI_00

;要安裝的文件和需要修改的註冊表項

;Install usbkey driver

[Keypad_Inst]

CopyFiles=Keypad_Inst.CopyFiles

AddReg=Keypad_Inst.AddReg

[Keypad_Inst.CopyFiles]

hidusb.sys

hidparse.sys

hidclass.sys

usbfilt.sys

[Keypad_Inst.AddReg]

HKR,,DevLoader,,*ntkern

HKR,,NTMPDriver,,"hidusb.sys"

[Keypad_Inst.HW]

AddReg=Keypad_Inst.AddReg.HW

;Lowerfilters表示是低層過濾驅動,如果是上層過濾驅動,則必須改為upperfilters

[Keypad_Inst.AddReg.HW]

HKR,,"LowerFilters",0x00010000,"usbfilt.sys"

;HID設備所需要安裝的文件和註冊表中需要修改的地方

;Install USBHIDDevice

[USBHIDDevice]

CopyFiles=USBHIDDevice.Copy

AddReg=USBHIDDevice.AddReg

[USBHIDDevice.Copy]

hidclass.sys

hidusb.sys

hidparse.sys

[USBHIDDevice.AddReg]

HKR,,DevLoader,,*ntkern

HKR,,NTMPDriver,,"hidusb.sys"

;以下定義需要在上面某些地方使用時替換的字符串

[strings]

USBDBE = "SINO Co., Ltd."

HID.DeviceDesc = "SINO USB MultiKeyboard"

HID.HIDDeviceDesc = "Human Interface Devices"

HID.DefaultDevice = "HID Default Device"

HID.ClassName = "Human Input Devices (HID)"

HID.SvcDesc = "Microsoft HID Class Driver"

其實最簡單的寫inf的方式,是找壹些類似設備的inf文件或範例來修改。在不侵權的前提下,充分利用現有資源是我們的壹貫原則。

  • 上一篇:日語軟件開發也要學英語嗎?
  • 下一篇:西門子PLC的快捷鍵有哪些?
  • copyright 2024編程學習大全網