最近对驱动比较感兴趣,所以打算做个windows下面的驱动,正好自己电脑的鼠标左键的单击有的时候会变双击,所有打算弄个鼠标的过滤驱动来解决这个问题。
网上找了一下,原来早就有人做了这个功能 http://download.csdn.net/detail/guijc1/5263421(不是故意给这个打广告的),所以我的代码基本都是仿照这里的源码。
这篇文章的重点是如何动手写一个过滤驱动,不是驱动的技术细节。还有就是驱动需要支持32和64系统的问题,安装驱动的问题。
以下很多都是从网络上面获取的资料。我会把链接都放到文章里面。
1.开发环境的搭建
vs2008+GRMWDK_EN_7600_1.ISO,这些直接安装就行了 注意如果要64位版本需要安装vs2008的64编译环境,接下来就是环境的配置了。我参照的是
http://jacky-dai.iteye.com/blog/1536456 以及http://blog.csdn.net/huangxy10/article/details/15284881
(1)打开vs2008,选择“工具--选项--项目和解决方案”在右侧平台选择“win32”--“包含文件”,然后在下面添加自己的wdk中的inc相关路径
D:WINDDK7600.16385.1incwdfkmdf1.9
D:WINDDK7600.16385.1incapi
D:WINDDK7600.16385.1inccrt
D:WINDDK7600.16385.1incddk
注意:vc包含文件的D:WinDDK7600.16385.1incapi不一定是放最前面,编译不过去的时候可以放到$(VCInstallDir)include的后面。
然后选择“win32”--“库文件”添加自己的WDK中LIB路径
D:WINDDK7600.16385.1libwdfkmdfi3861.9
D:WINDDK7600.16385.1libwxpi386
如果是64位的需要添加的库文件是
D:WINDDK7600.16385.1libwdfkmdfamd641.9
D:WINDDK7600.16385.1libwin7amd64
(2) 工程配置
新建一个win32的控制台工程,把里面带的文件都删除掉
右键工程,选择属性
点击“c/c++”--
选择“常规”,“调试信息”选:“C7 兼容(/Z7)”,“警告等级”--“3级(/w3)”,“将警告视为错误”--选“是” (这个需要看情况设置)
选择“优化”,“优化”选“禁用(/0d)”
选择“预处理器”,“预处理器定义”输入“WIN32=100;_X86_=1;DBG=1” 如果是64的则把_X86_=1 替换为_AMD64_=1
选择“高级”,“调用约定”选“__stdcall (/Gz)”
点击“连接器”--
选择“常规”,“输出文件”输入“$(OutDir)$(ProjectName)32.sys”(64位的修改为"$(OutDir)$(ProjectName)64.sys"),“启用增量连接”,选“否(/INCREMENTAL:NO)”,“附加库目录”输入“D:WINDDK7600.16385.0libwxpi386”
如果是64的修改为"D:WinDDK7600.16385.1libwin7amd64”
选择“输入”,“附加依赖项”输入“ntoskrnl.lib $(NOINHERIT)”,“忽略所有默认库”选“是(/NODEFAULTLIB)”
选择“清单文件”,“启用用户账户控制(UAC)”选“否”
选择“调试”,“生成调试信息”选择“是”,“生成程序数据库文件”输入“MyChecked_Driver$(ProjectName).pdb”
选择“系统”,“子系统”选“本机(/SUBSYSTEM:NATIVE)”,“堆栈保留大小”输入“40000”,“堆栈提交大小”输入“1000”,“驱动程序”选“驱动程序(/DRIVER)”
选择“高级”,“入口点”输入“DriverEntry”,“基址”输入“0x10000”,“随机基址”选“默认值”,“数据执行保护(DEP)”选“默认值”,“目标计算机”选“MachineX86 (/MACHINE:X86)” 如果是64的就选择"MachineX64 (/MACHINE:X64)"
2.下面是工程源码 解决鼠标单击变双击的问题
MouseLeftKeyFilter.h
#pragma once #define DEVICE_NAME L"\Device\MouseFilter" #define DOS_DEVICE_NAME L"\DosDevices\MouseFilter" #define POOL_TAG 'tsiL' #ifdef __cplusplus extern "C" { #endif //一类是不支持即插即用功能的NT式的驱动程序 //NT式的驱动程序要导入的头文件时NTDDK.H,而WDM式的驱动要导入的头文件为WDM.H. #include <ntddk.h> typedef NTSTATUS (*READ_DISPATCH)(__in PDEVICE_OBJECT pDeviceObject, __in PIRP Irp); typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDeviceObject; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; typedef struct _MOUSE_FILTER_DATA{ PDRIVER_OBJECT pMouseDriverObject; // "\Driver\Mouclass" 的指针 READ_DISPATCH OldRead; // 原来的IRP_MJ_READ 派遣例程 ULONG LeftDownTime; // 上次鼠标左键按下的时刻 KMUTEX ReadMutex; // Read互斥体 SINGLE_LIST_ENTRY ListHead;// 保存Pending IRP的链表 SINGLE_LIST_ENTRY CancelHead;// 用来对上面链表的进行复制 }MOUSE_FILTER_DATA, *PMOUSE_FILTER_DATA; typedef struct _PENDING_IRP_LIST{ SINGLE_LIST_ENTRY SingleListEntry; PIRP PendingIrp; }PENDING_IRP_LIST, *PPENDING_IRP_LIST; extern POBJECT_TYPE *IoDriverObjectType; extern NTSTATUS ObReferenceObjectByName( PUNICODE_STRING ObjectName, ULONG Attributes, PACCESS_STATE AccessState, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PVOID ParseContext, PVOID *Object ); NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath ); VOID DriverUnload(IN PDRIVER_OBJECT pDeviceObject); NTSTATUS DispatchCreateClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ); NTSTATUS DispatchDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ); NTSTATUS MyDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ); NTSTATUS MyDispatchReadComplete(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp, IN PVOID Context ) ; VOID CleanUP(IN PDEVICE_OBJECT pDeviceObject); VOID CancelPendingIrp(); BOOLEAN IsFakeDoubleClick (); #ifdef __cplusplus }; #endif //#ifdef ALLOC_PRAGMA //#pragma alloc_text(INIT, DriverEntry) //#pragma alloc_text(PAGE, testDriverUnload) //#endif
下面是MouseLeftKeyFilter.cpp
#include "MouseLeftKeyFilter.h" #include <ntddmou.h> MOUSE_FILTER_DATA gFilterData = {0}; NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDeviceObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING DeviceName; UNICODE_STRING SymbolicLinkName; UNICODE_STRING MouseDriver; PDEVICE_OBJECT DeviceObject = NULL; PDEVICE_EXTENSION deviceExtension = NULL; //告诉编译器,已经使用了该变量,不必检测警告 UNREFERENCED_PARAMETER (RegistryPath); KdPrint(("Entered DriverEntry =================== ")); KeInitializeMutex(&gFilterData.ReadMutex, 0); gFilterData.ListHead.Next = NULL; gFilterData.CancelHead.Next = NULL; pDeviceObject->MajorFunction [IRP_MJ_CREATE] = pDeviceObject->MajorFunction [IRP_MJ_CLOSE] = DispatchCreateClose; pDeviceObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl; pDeviceObject->DriverUnload = DriverUnload; //创建设备对象 RtlInitUnicodeString(&DeviceName, DEVICE_NAME); status = IoCreateDevice( pDeviceObject, sizeof(DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject ); if (!NT_SUCCESS(status)) { KdPrint(("IoCreateDevice failed, 0x%0x! ", status)); return status; } //创建符号链接 RtlInitUnicodeString(&SymbolicLinkName, DOS_DEVICE_NAME); status = IoCreateSymbolicLink(&SymbolicLinkName, &SymbolicLinkName); if(!NT_SUCCESS(status)) { KdPrint(("IoCreateSymbolicLink failed, 0x%0x! ", status)); CleanUP(DeviceObject); return status; } deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; RtlZeroMemory(deviceExtension, sizeof(DEVICE_EXTENSION)); deviceExtension->pDeviceObject = DeviceObject; //DeviceObject->Flags |= DO_BUFFERED_IO; DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; //取得鼠标驱动对象 RtlInitUnicodeString(&MouseDriver, L"\Driver\Mouclass"); status = ObReferenceObjectByName ( &MouseDriver, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID *)&gFilterData.pMouseDriverObject ); if(!NT_SUCCESS(status)) { KdPrint(("Get mouse object failed, 0x%0x ", status)); CleanUP(DeviceObject); return status; } //保存原来的IRP_MJ_READ 派遣例程 gFilterData.OldRead = gFilterData.pMouseDriverObject->MajorFunction[IRP_MJ_READ]; if(gFilterData.OldRead) // IRP HOOK { InterlockedExchange( (PLONG)&gFilterData.pMouseDriverObject->MajorFunction[IRP_MJ_READ], (LONG)MyDispatchRead ); } else { CleanUP(DeviceObject); return STATUS_UNSUCCESSFUL; } return status; } //IRP_MJ_READ 完成函数 NTSTATUS MyDispatchReadComplete(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp, IN PVOID Context ) { ULONG_PTR i,num; PMOUSE_INPUT_DATA data; PSINGLE_LIST_ENTRY pSingleListEntry = &gFilterData.ListHead; PPENDING_IRP_LIST PendingList = NULL; if( NT_SUCCESS( Irp->IoStatus.Status ) ) { data = (PMOUSE_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; num = Irp->IoStatus.Information / sizeof(MOUSE_INPUT_DATA); for( i = 0; i < num; i++ ) { ////去除假的鼠标左键双击事件 //if(data[i].ButtonFlags & MOUSE_LEFT_BUTTON_DOWN ) //{ // // 鼠标左键按下 // if(IsFakeDoubleClick() ) // { //假双击 // KdPrint(("Got a Fake Double CLick!! ================= ")); // data[i].ButtonFlags = 0; //忽略掉 // } // else // { // //KdPrint(("Left Button Down")); // } //} //下面是实现左右互换的功能 if(data[i].ButtonFlags &MOUSE_LEFT_BUTTON_DOWN ) { data[i].ButtonFlags = data[i].ButtonFlags&0xfff0; data[i].ButtonFlags = data[i].ButtonFlags|MOUSE_RIGHT_BUTTON_DOWN; } else if (data[i].ButtonFlags &MOUSE_LEFT_BUTTON_UP) { data[i].ButtonFlags = data[i].ButtonFlags&0xfff0; data[i].ButtonFlags = data[i].ButtonFlags|MOUSE_RIGHT_BUTTON_UP; } else if (data[i].ButtonFlags &MOUSE_RIGHT_BUTTON_DOWN) { data[i].ButtonFlags = data[i].ButtonFlags&0xfff0; data[i].ButtonFlags = data[i].ButtonFlags|MOUSE_LEFT_BUTTON_DOWN; } else if (data[i].ButtonFlags &MOUSE_RIGHT_BUTTON_UP) { data[i].ButtonFlags = data[i].ButtonFlags&0xfff0; data[i].ButtonFlags = data[i].ButtonFlags|MOUSE_LEFT_BUTTON_UP; } } } KeWaitForMutexObject(&gFilterData.ReadMutex, Executive, KernelMode, FALSE, NULL); for(; pSingleListEntry->Next; pSingleListEntry = pSingleListEntry->Next) { PendingList = CONTAINING_RECORD(pSingleListEntry->Next, PENDING_IRP_LIST, SingleListEntry); if(PendingList->PendingIrp == Irp) {// 从链表中移除 pSingleListEntry->Next = pSingleListEntry->Next->Next; ExFreePoolWithTag(PendingList, POOL_TAG); break; } } KeReleaseMutex(&gFilterData.ReadMutex, FALSE); //KdPrint(("MyReadComplete ============ Irp= 0x%X, 0x%X", Irp, Irp->IoStatus.Status)); //调用原来的完成函数 if ((Irp->StackCount > 1) && (Context != NULL)) { return ((PIO_COMPLETION_ROUTINE)Context)(pDeviceObject, Irp, NULL); } else { return Irp->IoStatus.Status; } } NTSTATUS MyDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ) { PPENDING_IRP_LIST PendingList = NULL; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); //KdPrint(("MyDispatchRead ================ Irp= 0x%p ", Irp)); PendingList = (PPENDING_IRP_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(PENDING_IRP_LIST), POOL_TAG); if(PendingList) { PendingList->PendingIrp = Irp; KeWaitForMutexObject( &gFilterData.ReadMutex, Executive, KernelMode, FALSE, NULL); PushEntryList(&gFilterData.ListHead, &PendingList->SingleListEntry); KeReleaseMutex(&gFilterData.ReadMutex, FALSE); // 按理说,应该调用IoSetCompletionRoutine来设置完成例程的 // 但是,那样会导致完成例程根本不会被执行,Why? irpSp->Control = SL_INVOKE_ON_SUCCESS|SL_INVOKE_ON_ERROR|SL_INVOKE_ON_CANCEL; //保留原来的完成函数 irpSp->Context = irpSp->CompletionRoutine; irpSp->CompletionRoutine = (PIO_COMPLETION_ROUTINE)MyDispatchReadComplete; } return gFilterData.OldRead(pDeviceObject, Irp); } NTSTATUS DispatchCreateClose(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ) { //IoGetCurrentIrpStackLocation 需要在项目设置里面添加 hal.lib PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); UNREFERENCED_PARAMETER(pDeviceObject); PAGED_CODE(); KdPrint(("Entered ================ IRP_MJ_%s ", (irpStack->MajorFunction == IRP_MJ_CREATE) ? "CREATE" : "CLOSE" )); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Irp->IoStatus.Status; } NTSTATUS DispatchDeviceControl(IN PDEVICE_OBJECT pDeviceObject, IN PIRP Irp ) { PAGED_CODE(); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp,IO_NO_INCREMENT); return Irp->IoStatus.Status; } VOID DriverUnload(IN PDRIVER_OBJECT pDeviceObject) { PAGED_CODE (); KdPrint(("Entered ================== Unload ")); CleanUP(pDeviceObject->DeviceObject); CancelPendingIrp(); //等待所有的IRP 完成 while (gFilterData.ListHead.Next) { LARGE_INTEGER lDelay; lDelay.QuadPart = -10 * 1000 * 1000; // 1秒 KeDelayExecutionThread(KernelMode, FALSE, &lDelay); } } VOID CleanUP(IN PDEVICE_OBJECT pDeviceObject) { UNICODE_STRING SymbolicLinkName; if(!pDeviceObject) return; // 删除符号链接和设备对象 RtlInitUnicodeString(&SymbolicLinkName, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&SymbolicLinkName); IoDeleteDevice(pDeviceObject); //恢复IRP hook if(gFilterData.OldRead) { InterlockedExchange( (PLONG)&gFilterData.pMouseDriverObject->MajorFunction[IRP_MJ_READ], (LONG)gFilterData.OldRead ); } if(gFilterData.pMouseDriverObject) { ObDereferenceObject(gFilterData.pMouseDriverObject); } } // 取消等待的IRP VOID CancelPendingIrp() { PPENDING_IRP_LIST PendingList = NULL, CancelList = NULL; PSINGLE_LIST_ENTRY pSingleListEntry = NULL; // 获取互斥体,保护链表gFilterData.ListHead KeWaitForMutexObject(&gFilterData.ReadMutex, Executive, KernelMode, FALSE, NULL); pSingleListEntry = gFilterData.ListHead.Next; while(pSingleListEntry) { PendingList = CONTAINING_RECORD(pSingleListEntry, PENDING_IRP_LIST, SingleListEntry); KdPrint(("Copy ================= Single List = 0x%x", PendingList)); // 复制链表,然后将取消IRP的操作放到新的链表中处理 CancelList = (PPENDING_IRP_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(PENDING_IRP_LIST), POOL_TAG); if(CancelList) { RtlCopyMemory(CancelList, PendingList, sizeof(PENDING_IRP_LIST)); PushEntryList(&gFilterData.CancelHead, &CancelList->SingleListEntry); } pSingleListEntry = pSingleListEntry->Next; } // 释放互斥体 KeReleaseMutex(&gFilterData.ReadMutex, FALSE); // 之所以要复制一个新的链表来取消IRP (通过调用IoCancelIrp), // 是因为IoCancelIrp 会调用MyDispatchReadComplete 这个完成例程回调, // 而MyDispatchReadComplete里面又对链表进行操作,这样会破坏链表的结构 pSingleListEntry = PopEntryList(&gFilterData.CancelHead); while(pSingleListEntry) { CancelList = CONTAINING_RECORD(pSingleListEntry, PENDING_IRP_LIST, SingleListEntry); if(CancelList) { KdPrint(("CancelPendingIrp = 0x%x", CancelList->PendingIrp)); if(!CancelList->PendingIrp->Cancel) {// 在这里,取出复制链表中的IRP,然后进行取消 IoCancelIrp(CancelList->PendingIrp); } ExFreePoolWithTag(CancelList, POOL_TAG); pSingleListEntry = PopEntryList(&gFilterData.CancelHead); } } } //判断是否是假双击,根据时间间隔判断 BOOLEAN IsFakeDoubleClick () { LARGE_INTEGER CurrentTime; BOOLEAN Flag = FALSE; KeQueryTickCount(&CurrentTime); CurrentTime.QuadPart *= KeQueryTimeIncrement(); CurrentTime.QuadPart /= 10000; //两次点击时间间隔小于100ms,视为假双击 if( CurrentTime.LowPart - gFilterData.LeftDownTime < 100) { Flag = TRUE; } InterlockedExchange((volatile LONG *)&gFilterData.LeftDownTime, CurrentTime.LowPart); return Flag; }
上面就是全部的代码。如果编译没有错误就会生成对应的sys文件。
3.安装驱动
安装过滤驱动有两种方式,一种是写服务来安装,这个网上也有源码,还有就是inf文件 在cmd里面执行rundll32 SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 +inf路径
这里贴出来一个完整的inf 鼠标过滤驱动的文件
; HHD Software USB Monitoring Filter Driver ; Part of Device Monitoring Studio ; ; Copyright HHD Software Ltd. [Version] signature = "$Windows NT$" DriverPackageType=ClassFilter Class=Mouse ClassGUID={59D5B0A2-8479-4333-BCFE-3EB1F6BD506D} Provider=%ClasFilt.Provider% DriverVer=12/03/2010,5.25.0.3036 ; [SourceDisksNames] 1 = %ClasFilt.MediaDesc% [SourceDisksFiles] MyMouseDrive32.sys = 1 MyMouseDrive64.sys = 1 [DestinationDirs] DefaultDestDir = 12 ; DIRID_DRIVERS [DefaultInstall.ntx86] CopyFiles = @MyMouseDrive32.sys AddReg = ClassFilter_AddReg.ntx86 [DefaultInstall.ntamd64] CopyFiles = @MyMouseDrive64.sys AddReg = ClassFilter_AddReg.ntamd64 ; 指定过滤驱动对应的注册表项 [ClassFilter_AddReg.ntx86] HKLM, SystemCurrentControlSetControlClass{4D36E96F-E325-11CE-BFC1-08002BE10318}, UpperFilters, 0x00010008, MyMouseDrive32 [ClassFilter_AddReg.ntamd64] HKLM, SystemCurrentControlSetControlClass{4D36E96F-E325-11CE-BFC1-08002BE10318}, UpperFilters, 0x00010008, MyMouseDrive64 [DefaultInstall.ntx86.Services] AddService = MyMouseDrive32, , clasfilt_Service_Inst.ntx86, clasfilt_EventLog_Inst.ntx86 [DefaultInstall.ntamd64.Services] AddService = MyMouseDrive64, , clasfilt_Service_Inst.ntamd64, clasfilt_EventLog_Inst.ntamd64 [clasfilt_Service_Inst.ntx86] DisplayName = %ClasFilt.SvcDesc% ServiceType = %SERVICE_KERNEL_DRIVER% StartType = %SERVICE_DEMAND_START% ErrorControl = %SERVICE_ERROR_IGNORE% ServiceBinary = %12%MyMouseDrive32.sys [clasfilt_Service_Inst.ntamd64] DisplayName = %ClasFilt.SvcDesc% ServiceType = %SERVICE_KERNEL_DRIVER% StartType = %SERVICE_DEMAND_START% ErrorControl = %SERVICE_ERROR_IGNORE% ServiceBinary = %12%MyMouseDrive64.sys [clasfilt_EventLog_Inst.ntx86] AddReg = clasfilt_EventLog_AddReg.ntx86 [clasfilt_EventLog_Inst.ntamd64] AddReg = clasfilt_EventLog_AddReg.ntamd64 [clasfilt_EventLog_AddReg.ntx86] HKR,,EventMessageFile, %REG_EXPAND_SZ%,"%%SystemRoot%%System32IoLogMsg.dll;%%SystemRoot%%System32driversMyMouseDrive32.sys" HKR,,TypesSupported, %REG_DWORD%, 7 [clasfilt_EventLog_AddReg.ntamd64] HKR,,EventMessageFile, %REG_EXPAND_SZ%,"%%SystemRoot%%System32IoLogMsg.dll;%%SystemRoot%%System32driversMyMouseDrive64.sys" HKR,,TypesSupported, %REG_DWORD%, 7 [Strings] ; 不同的过滤驱动下面的信息要修改 ;Note: the following must be modified, if it is other filter drivers ClasFilt.SvcDesc="MyMouse Filter Driver" ClasFilt.MediaDesc="MyMouse Filter Package" ClasFilt.Provider="MyMouse Software Ltd." ;下面这个信息不需要修改 ; Useful constants SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_IGNORE = 0 REG_EXPAND_SZ = 0x00020000 REG_DWORD = 0x00010001