Windows驱动开发WDM (13)- 过滤驱动

之前用的驱动例子是一个功能型驱动,只不过它操作的是一个虚拟设备。这个驱动创建的FDO(功能设备对象)是附在虚拟总线驱动创建的虚拟PDO之上的。这次来介绍一下不同于功能型驱动的过滤驱动。过滤驱动可以在功能型驱动的上面,称之为上层过滤驱动,或者高层,反正就这个意思。过滤驱动在功能型驱动下面,称之为下层过滤驱动。看示意图:

从名字上就可以知道过滤驱动是干啥用的。就是起过滤作用。比如:

1. 可以对写入硬盘的数据做加密,然后读取的时候解密,这样对于用户来说,根本不知道有加密解密的过程,然后存在硬盘上的数据是加密的。

2. 可以对已有驱动做一些扩展,或者改变已有驱动的功能。比如已有驱动一次只能写1024字节的数据,那么过滤驱动可以扩展到任何长度,然后分段调用已有驱动就是了。

 

我们来尝试给之前用的驱动加个过滤驱动。这个过滤驱动代码,我是从前面的驱动copy过来的,然后修改一下。

其实过滤驱动跟功能型驱动从代码上看也没有太多不同,需要注意的是:

1. 因为过滤驱动是附在FDO上的,那么通常过滤驱动都不需要一个名字,因为caller不会直接调用过滤驱动;

2. 因为不知道下层驱动是缓冲模式还是直接模式操作内存,那么通常过滤驱动会同时支持缓冲和直接方式。

看一下AddDevice里面创建过滤驱动设备对象的代码:

  1. NTSTATUS CreateFDO(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo)  
  2. {  
  3.     NTSTATUS status;  
  4.     PDEVICE_OBJECT fido;  
  5.   
  6.     //创建FDO(Function Device Object)  
  7.     status = IoCreateDevice(  
  8.         DriverObject,  
  9.         sizeof(DEVICE_EXTENSION),  
  10.         NULL,//过滤驱动,无需名字  
  11.         FILE_DEVICE_UNKNOWN,  
  12.         0,  
  13.         FALSE,  
  14.         &fido);  
  15.     if( !NT_SUCCESS(status))  
  16.         return status;  
  17.   
  18.     KdPrint(("Create filter device\n"));  
  19.   
  20.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fido->DeviceExtension;  
  21.     pdx->fdo = fido;  
  22.     //将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下层设备。如果PDO上面有过滤驱动的话,NextStackDevice就是过滤驱动,如果没有就是PDO。  
  23.     pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fido, pdo);  
  24.   
  25.     if (pdx->NextStackDevice == NULL)  
  26.     {  
  27.         KdPrint(("attach failed\n"));  
  28.         IoDeleteDevice(fido);  
  29.         return STATUS_DEVICE_REMOVED;  
  30.     }  
  31.       
  32.   
  33.     fido->Flags |= DO_BUFFERED_IO | DO_DIRECT_IO| DO_POWER_PAGABLE;//  
  34.     fido->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保证设备初始化完毕,必须的。  
  35.   
  36.     return STATUS_SUCCESS;  
  37. }  
NTSTATUS CreateFDO(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo)
{
	NTSTATUS status;
	PDEVICE_OBJECT fido;

	//创建FDO(Function Device Object)
	status = IoCreateDevice(
		DriverObject,
		sizeof(DEVICE_EXTENSION),
		NULL,//过滤驱动,无需名字
		FILE_DEVICE_UNKNOWN,
		0,
		FALSE,
		&fido);
	if( !NT_SUCCESS(status))
		return status;

	KdPrint(("Create filter device\n"));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fido->DeviceExtension;
	pdx->fdo = fido;
	//将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下层设备。如果PDO上面有过滤驱动的话,NextStackDevice就是过滤驱动,如果没有就是PDO。
	pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fido, pdo);

	if (pdx->NextStackDevice == NULL)
	{
		KdPrint(("attach failed\n"));
		IoDeleteDevice(fido);
		return STATUS_DEVICE_REMOVED;
	}
	

	fido->Flags |= DO_BUFFERED_IO | DO_DIRECT_IO| DO_POWER_PAGABLE;//
	fido->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保证设备初始化完毕,必须的。

	return STATUS_SUCCESS;
}

看上去代码很简单,这就够了。主要就是创建一个设备,然后附在传进来的pdo之上。

我们在过滤驱动里面只做一件事情,把传进来的buffer的第一个字节改成x。

看code:

  1. NTSTATUS HelloWDMIOControl(IN PDEVICE_OBJECT fdo, IN PIRP Irp)  
  2. {  
  3.     KdPrint(("EX Enter HelloWDMIOControl\n"));  
  4.   
  5.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  6.     PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);  
  7.   
  8.   
  9.     //得到IOCTRL码  
  10.     ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;  
  11.   
  12.     NTSTATUS status;  
  13.     ULONG info = 0;  
  14.     switch (code)  
  15.     {  
  16.     case IOCTL_ENCODE:  
  17.         {              
  18.             PDEVICE_OBJECT pFdo = fdo->DriverObject->DeviceObject;  
  19.             do   
  20.             {  
  21.                 pdx = (PDEVICE_EXTENSION)pFdo->DeviceExtension;  
  22.                 KdPrint(("EX Device: %x, PDX::NextStackDevice: %x\n", pFdo, pdx->NextStackDevice));  
  23.                 pFdo = pFdo->NextDevice;  
  24.             } while (pFdo != NULL);  
  25.   
  26.             //过滤驱动里面修改一个输入缓冲的数据。  
  27.             char* inBuf = (char*)Irp->AssociatedIrp.SystemBuffer;  
  28.             inBuf[0] = 'x';  
  29.   
  30.             KdPrint(("Try to call lower driver Irp: %x\n", Irp));  
  31.             pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  32.             IoCopyCurrentIrpStackLocationToNext(Irp);  
  33.             status = IoCallDriver(pdx->NextStackDevice, Irp);  
  34.   
  35.             KdPrint(("Finished calling lower driver, Irp: %x", Irp));  
  36.         }  
  37.         break;  
  38.     default:  
  39.         IoSkipCurrentIrpStackLocation(Irp);  
  40.         status = IoCallDriver(pdx->NextStackDevice, Irp);  
  41.         break;  
  42.     }  
  43.   
  44.     KdPrint(("EX Leave HelloWDMIOControl\n"));  
  45.     return status;  
  46. }  
NTSTATUS HelloWDMIOControl(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
	KdPrint(("EX Enter HelloWDMIOControl\n"));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);


	//得到IOCTRL码
	ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

	NTSTATUS status;
	ULONG info = 0;
	switch (code)
	{
	case IOCTL_ENCODE:
		{			 
			PDEVICE_OBJECT pFdo = fdo->DriverObject->DeviceObject;
			do 
			{
				pdx = (PDEVICE_EXTENSION)pFdo->DeviceExtension;
				KdPrint(("EX Device: %x, PDX::NextStackDevice: %x\n", pFdo, pdx->NextStackDevice));
				pFdo = pFdo->NextDevice;
			} while (pFdo != NULL);

			//过滤驱动里面修改一个输入缓冲的数据。
			char* inBuf = (char*)Irp->AssociatedIrp.SystemBuffer;
			inBuf[0] = 'x';

			KdPrint(("Try to call lower driver Irp: %x\n", Irp));
			pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
			IoCopyCurrentIrpStackLocationToNext(Irp);
			status = IoCallDriver(pdx->NextStackDevice, Irp);

			KdPrint(("Finished calling lower driver, Irp: %x", Irp));
		}
		break;
	default:
		IoSkipCurrentIrpStackLocation(Irp);
		status = IoCallDriver(pdx->NextStackDevice, Irp);
		break;
	}

	KdPrint(("EX Leave HelloWDMIOControl\n"));
	return status;
}

如果是IOCTL_ENCODE的irp,那么把input buffer的第一个字节改成x,然后继续调用下层驱动。如果是其他irp直接调用下层驱动。

代码就这么简单,现在来看看inf文件:

  1. [Version]  
  2. Signature=$CHICAGO$  
  3. Provider=%MFGNAME%  
  4.   
  5. [DestinationDirs]  
  6. DefaultDestDir=10,system32\drivers  
  7. FiltJectCopyFiles=11  
  8.   
  9. [SourceDisksFiles]  
  10. HelloWDM_Ex.sys=1  
  11.   
  12. [SourceDisksNames]  
  13. 1=%INSTDISK%,,,MyFilter_Check  
  14.   
  15. ;------------------------------------------------------------------------------  
  16. ;  Windows 2000 Sections  
  17. ;------------------------------------------------------------------------------  
  18.   
  19. [DefaultInstall.ntx86]  
  20. CopyFiles=DriverCopyFiles,FiltJectCopyFiles  
  21.   
  22. [DriverCopyFiles]  
  23. HelloWDM_Ex.sys,,,0x60      ; replace newer, suppress dialog  
  24.   
  25. [DefaultInstall.ntx86.services]  
  26. AddService=HelloWDM_Filter,,FilterService  
  27.   
  28. [FilterService]  
  29. ServiceType=1  
  30. StartType=3  
  31. ErrorControl=1  
  32. ServiceBinary=%10%\system32\drivers\HelloWDM_Ex.sys  
  33.   
  34. ;------------------------------------------------------------------------------  
  35. ;  String Definitions  
  36. ;------------------------------------------------------------------------------  
  37.   
  38. [Strings]  
  39. MFGNAME="Kevin filter"  
  40. INSTDISK="Kevin disk"  
  41. DESCRIPTION="Kevin - Sample Filter Driver"  
[Version]
Signature=$CHICAGO$
Provider=%MFGNAME%

[DestinationDirs]
DefaultDestDir=10,system32\drivers
FiltJectCopyFiles=11

[SourceDisksFiles]
HelloWDM_Ex.sys=1

[SourceDisksNames]
1=%INSTDISK%,,,MyFilter_Check

;------------------------------------------------------------------------------
;  Windows 2000 Sections
;------------------------------------------------------------------------------

[DefaultInstall.ntx86]
CopyFiles=DriverCopyFiles,FiltJectCopyFiles

[DriverCopyFiles]
HelloWDM_Ex.sys,,,0x60		; replace newer, suppress dialog

[DefaultInstall.ntx86.services]
AddService=HelloWDM_Filter,,FilterService

[FilterService]
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%10%\system32\drivers\HelloWDM_Ex.sys

;------------------------------------------------------------------------------
;  String Definitions
;------------------------------------------------------------------------------

[Strings]
MFGNAME="Kevin filter"
INSTDISK="Kevin disk"
DESCRIPTION="Kevin - Sample Filter Driver"

比功能型驱动简单很多,没什么花头,就是要注意一下AddService=HelloWDM_Filter,,FilterService,这里的HelloWDM_Filter后面会被用到。

这个inf文件,可以直接右键点击,然后安装。

安装这个还不够,我们还需要改一下注册表,来指明过滤驱动应该对哪些设备起作用。之前那个驱动的ID是ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B0},那么我们可以在注册表里面找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{EF2962F0-0D55-4BFF-B8AA-2221EE8A79B0},给它加个子键,比如:


UpperFilters指上层过滤驱动(反之,下层驱动是LowerFilters),HelloWDM_Filter来自于inf文件的AddService=HelloWDM_Filter,,FilterService

一旦我们创建的这个子键后,我们可以从设备管理器上看到:

重启电脑,用devicetree可以看到:

因为功能驱动HelloWDM创建了3个设备对象,那么过滤驱动将会附在再上层的FDO上面。从上图可以看到附在MyWDMDevice3的上面。这个时候设备栈类似于:

filter device -> MyWDMDevice3 -> MyWDMDevice2 -> MyWDMDevice -> PDO(虚拟总线驱动创建的)

 

ok,测试一下。测试代码很简单,就是打开设备\\.\HelloWDM(MyWDMDevice),然后发一个IOCTL_ENCODE请求(DeviceIoControl)。

  1. // TestWDMDriver.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <windows.h>  
  6. #include <process.h>  
  7.   
  8. #define DEVICE_NAME L"\\\\.\\HelloWDM"  
  9. #define DEVICE_NAME2 L"\\\\.\\HelloWDM2"  
  10. #define DEVICE_EX L"\\\\.\\HelloWDM_EX"  
  11.   
  12. void Test(void* pParam)  
  13. {  
  14.     int index = (int)pParam;  
  15.     //设置overlapped标志,表示异步打开  
  16.     HANDLE hDevice;  
  17.       
  18.     hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,  
  19.             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL);  
  20.   
  21.     wprintf(L"CreateFile, name: %s, ret: %x\n", DEVICE_NAME, hDevice);  
  22.       
  23.       
  24.   
  25.     if (hDevice != INVALID_HANDLE_VALUE)  
  26.     {  
  27.         char inbuf[100] = {0};  
  28.           
  29.         sprintf(inbuf, "hello world %d", index);  
  30.         char outbuf[100] = {0};  
  31.         DWORD dwBytes = 0;  
  32.   
  33.         DWORD dwStart = GetTickCount();  
  34.   
  35.         printf("input buffer: %s\n", inbuf);  
  36.           
  37.         OVERLAPPED ol = {0};  
  38.         ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  
  39.   
  40.         BOOL b = DeviceIoControl(hDevice, CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS),   
  41.             inbuf, strlen(inbuf), outbuf, 100, &dwBytes, &ol);  
  42.   
  43.         printf("DeviceIoControl thread %d, returns %d, last error: %d, used: %d ms, input: %s\n", index, b, GetLastError(), GetTickCount() - dwStart, inbuf);  
  44.   
  45.   
  46.         WaitForSingleObject(ol.hEvent, INFINITE);  
  47.   
  48.         DWORD dwEnd = GetTickCount();  
  49.         //将输出buffer的数据和'm'亦或,看看是否能够得到初始的字符串。  
  50.         for (int i = 0; i < strlen(inbuf); i++)  
  51.         {  
  52.             outbuf[i] = outbuf[i] ^ 'm';  
  53.         }  
  54.   
  55.         printf("Verify thread %d, outbuf: %s, used: %d ms\n", index, outbuf, dwEnd - dwStart);  
  56.   
  57.         CloseHandle(hDevice);  
  58.   
  59.     }  
  60.     else  
  61.         printf("CreateFile failed, err: %x\n", GetLastError());  
  62. }  
  63.   
  64.   
  65. int _tmain(int argc, _TCHAR* argv[])  
  66. {  
  67.     HANDLE t1 = (HANDLE)_beginthread(Test, 0, (void*)0);  
  68.   
  69.     WaitForSingleObject(t1, INFINITE);  
  70.   
  71.     printf("Test ends\n");  
  72.     return 0;  
  73. }  
// TestWDMDriver.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <process.h>

#define DEVICE_NAME L"\\\\.\\HelloWDM"
#define DEVICE_NAME2 L"\\\\.\\HelloWDM2"
#define DEVICE_EX L"\\\\.\\HelloWDM_EX"

void Test(void* pParam)
{
	int index = (int)pParam;
	//设置overlapped标志,表示异步打开
	HANDLE hDevice;
	
	hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL);

	wprintf(L"CreateFile, name: %s, ret: %x\n", DEVICE_NAME, hDevice);
	
	

	if (hDevice != INVALID_HANDLE_VALUE)
	{
		char inbuf[100] = {0};
		
		sprintf(inbuf, "hello world %d", index);
		char outbuf[100] = {0};
		DWORD dwBytes = 0;

		DWORD dwStart = GetTickCount();

		printf("input buffer: %s\n", inbuf);
		
		OVERLAPPED ol = {0};
		ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

		BOOL b = DeviceIoControl(hDevice, CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS), 
			inbuf, strlen(inbuf), outbuf, 100, &dwBytes, &ol);

		printf("DeviceIoControl thread %d, returns %d, last error: %d, used: %d ms, input: %s\n", index, b, GetLastError(), GetTickCount() - dwStart, inbuf);


		WaitForSingleObject(ol.hEvent, INFINITE);

		DWORD dwEnd = GetTickCount();
		//将输出buffer的数据和'm'亦或,看看是否能够得到初始的字符串。
		for (int i = 0; i < strlen(inbuf); i++)
		{
			outbuf[i] = outbuf[i] ^ 'm';
		}

		printf("Verify thread %d, outbuf: %s, used: %d ms\n", index, outbuf, dwEnd - dwStart);

		CloseHandle(hDevice);

	}
	else
		printf("CreateFile failed, err: %x\n", GetLastError());
}


int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE t1 = (HANDLE)_beginthread(Test, 0, (void*)0);

	WaitForSingleObject(t1, INFINITE);

	printf("Test ends\n");
	return 0;
}

看输出:


输进去hello world 0, 输出变成了xello world 0,说明过滤驱动起作用了。看看debugview的输出:

嘿嘿,确实有过滤驱动的输出,成功。

 

总结,看起来过滤驱动还是蛮简单的,当然这只是一个极其简单的例子,实际情况怕是没那么简单了。要点:

1. 代码和功能驱动没什么大分别,如果无需过滤,则直接调用下层驱动。

2. INF文件需要相应修改,看上面的例子。

3. 需要改一下注册表,找到相应的ID, 如HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{EF2962F0-0D55-4BFF-B8AA-2221EE8A79B0},然后增加子键UpperFilters或者LowerFilters,看上面的例子。(其实INF文件可以增加这个子键,在INF文件里面改下就好)

 

有关这个例子,如果我们设置为LowerFilters,那么过滤驱动就不会被调用到,为什么?很简单,因为这个例子的功能驱动直接把IRP完成了,也就不会往下走了。

 

代码:http://download.csdn.net/detail/zj510/4906910

WDK(7600) 编译驱动, VS2008编译测试例子。过滤驱动可以直接右键inf文件安装。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
想要入门Windows驱动开发,可以采用以下步骤和资源: 1. 阅读微软官方文档:微软提供了详细的Windows驱动程序入门指南,其中包括了驱动程序的基本概念、开发环境的设置和开发流程等。可以参考微软官方文档进行学习和实践。 2. 学习驱动开发模型:在刚接触驱动开发的新手中,不建议采用WDMWindows驱动程序模型)进行开发。推荐学习并使用WDF(Windows驱动框架)进行开发,因为WDF提供了更高层次的抽象和更简化的开发流程。可以参考《竹林溪径——深入浅出Windows驱动开发》和《Developing Drivers with the Windows Driver Foundation》这两本基于WDF开发驱动的中文和英文书籍。 3. 掌握驱动开发的基本知识和技能:对于新手来说,了解驱动开发的基本概念和原理是非常重要的。可以学习如何编写驱动程序、设备管理、内存管理和处理中断等。可以参考《竹林溪径——深入浅出Windows驱动开发》一书中的内容和微软官方文档提供的实践指南。 4. 实践和调试:驱动开发是一个需要不断实践和调试的过程。建议使用虚拟机或者物理设备进行测试和调试,以确保驱动程序的正确性和稳定性。 总之,入门Windows驱动开发需要学习官方文档、选择合适的开发模型、掌握驱动开发的基本知识和技能,并进行实践和调试。通过不断的学习和实践,可以逐渐掌握Windows驱动开发的技术和能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Windows驱动开发入门指引](https://blog.csdn.net/fuhanghang/article/details/123333156)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Windows驱动开发之入门篇(一)](https://blog.csdn.net/Sagittarius_Warrior/article/details/51000241)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值