键盘过滤

来源:互联网 发布:湖北招生考试软件 编辑:程序博客网 时间:2024/05/17 22:10
#include <wdm.h>#define KBD_DRIVER_NAME  L"\\Driver\\Kbdclass"// Kbdclass驱动的名字#define KEY_MAKE 0#define KEY_BREAK 1#define KEY_E0 5#define KEY_E1 6#define KEY_TERMSRV_SET_LED 8#define KEY_TERMSRV_SHADOW 0X10#define KEY_TERMSRV_VKPACKET 0X20#define LCONTROL ((USHORT)0x1D) #define CAPS_LOCK ((USHORT)0x3A) #defineS_SHIFT1#defineS_CAPS2#defineS_NUM4static int kb_status = S_NUM;unsigned char asciiTbl[] = {0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,//normal0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,//caps0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,//shift0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E,0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,//caps + shift0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,0x32, 0x33, 0x30, 0x2E};typedef struct _C2P_DEV_EXT{ULONG NodeSize;// 这个结构的大小PDEVICE_OBJECT pFilterDeviceObject;// 过滤设备对象KSPIN_LOCK IoRequestsSpinLock;// 同时调用时的保护锁KEVENT IoInProgressEvent;// 进程间同步处理  PDEVICE_OBJECT TargetDeviceObject;// 绑定的设备对象 原始设备对象PDEVICE_OBJECT LowerDeviceObject;// 绑定前底层设备对象 真实设备} C2P_DEV_EXT, *PC2P_DEV_EXT;typedef struct _KEYBOARD_INPUT_DATA {USHORT UnitId;//是键盘设备的ID号,一般设备中/Device/KeyBoardPortN的N就是ID号。比如一个键盘的设备名为/Device/KeyBoardPort0,则它的UnitId为0.用来表示这个输入输出结构是来自哪个键盘设备的.USHORT MakeCode;//键盘扫描码,当前按下键的键盘扫描码USHORT Flags;//标识符,用来标识当前键位的状态.      KEY_MAKE 当前键被按下KEY_BREAK 当前键被释放     KEY_E0和KEY_E1是扩展用的.USHORT Reserved;//操作系统保留的.ULONG  ExtraInformation;//扩展信息.} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;void __stdcall print_keystroke(UCHAR sch)//打印字母 翻译下{UCHARch = 0;intoff = 0;if ((sch & 0x80) == 0)// 如果按下{if ((sch < 0x47) ||((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock已经按下了    数字 字母 可见{ch = asciiTbl[off + sch];//计算得到ascii码 在下面打印}switch (sch)//三个键状态计算{case 0x3A:kb_status ^= S_CAPS;break;case 0x2A:case 0x36:kb_status |= S_SHIFT;break;case 0x45:kb_status ^= S_NUM;}}else//break 如果键松开{if (sch == 0xAA || sch == 0xB6)//键锁松开了kb_status &= ~S_SHIFT;}if (ch >= 0x20 && ch < 0x7F)//键盘上的ascii值{DbgPrint("%C \n", ch);}}NTSTATUS c2pDevExtInit(IN PC2P_DEV_EXT devExt,IN PDEVICE_OBJECT pFilterDeviceObject,IN PDEVICE_OBJECT pTargetDeviceObject,IN PDEVICE_OBJECT pLowerDeviceObject){memset(devExt, 0, sizeof(C2P_DEV_EXT));devExt->NodeSize = sizeof(C2P_DEV_EXT);devExt->pFilterDeviceObject = pFilterDeviceObject;//过滤设备KeInitializeSpinLock(&(devExt->IoRequestsSpinLock)); //初始化一个自旋锁KeInitializeEvent(&(devExt->IoInProgressEvent), NotificationEvent, FALSE);//这个参数是初始化事件devExt->TargetDeviceObject = pTargetDeviceObject;//原始键盘设别devExt->LowerDeviceObject = pLowerDeviceObject;//真实设备return(STATUS_SUCCESS);}// 这个函数是事实存在的,只是文档中没有公开。声明一下// 就可以直接使用了。NTSTATUS ObReferenceObjectByName(PUNICODE_STRING ObjectName,ULONG Attributes,PACCESS_STATE AccessState,ACCESS_MASK DesiredAccess,POBJECT_TYPE ObjectType,KPROCESSOR_MODE AccessMode,PVOID ParseContext,PVOID *Object);extern POBJECT_TYPE IoDriverObjectType;ULONG gC2pKeyCount = 0;PDRIVER_OBJECT gDriverObject = NULL;NTSTATUS c2pAttachDevices(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)// 这个函数经过改造。能打开驱动对象Kbdclass,然后绑定它下面的所有的设备  过滤设备绑定真实设备{NTSTATUS status = 0;UNICODE_STRING uniNtNameString;PC2P_DEV_EXT devExt;PDEVICE_OBJECT pFilterDeviceObject = NULL;//过滤设备PDEVICE_OBJECT pTargetDeviceObject = NULL;//原始设备PDEVICE_OBJECT pLowerDeviceObject = NULL;//真实设备PDRIVER_OBJECT KbdDriverObject = NULL;//驱动对象KdPrint(("MyAttach\n"));RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME);// 初始化一个字符串,就是Kdbclass驱动的名字。status = ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,IoDriverObjectType,KernelMode,NULL,&KbdDriverObject);//  得到是驱动对象if (!NT_SUCCESS(status))// 如果失败了就直接返回{KdPrint(("MyAttach: Couldn't get the MyTest Device Object\n"));return(status);}else{ObDereferenceObject(DriverObject);// 这个打开需要解应用。早点解除了免得之后忘记。}pTargetDeviceObject = KbdDriverObject->DeviceObject;//  原始键盘设备 第一个while (pTargetDeviceObject)// 现在开始遍历这个设备链{status = IoCreateDevice(IN DriverObject,IN sizeof(C2P_DEV_EXT),IN NULL,IN pTargetDeviceObject->DeviceType,IN pTargetDeviceObject->Characteristics,IN FALSE,OUT &pFilterDeviceObject);// 通过原始设备 生成一个过滤设备if (!NT_SUCCESS(status))// 如果失败了就直接退出。{KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object\n"));return (status);}pLowerDeviceObject =IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject);// 得到真实设备。if (!pLowerDeviceObject){KdPrint(("MyAttach: Couldn't attach to MyTest Device Object\n"));// 如果绑定失败了,放弃之前的操作,退出。IoDeleteDevice(pFilterDeviceObject);//删除过滤设备pFilterDeviceObject = NULL;return(status);}devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension);// 设备扩展!下面要详细讲述设备扩展的应用。c2pDevExtInit(devExt,pFilterDeviceObject,pTargetDeviceObject,pLowerDeviceObject);//过滤驱动 原始驱动 真实驱动// 下面的操作和前面过滤串口的操作基本一致。这里不再解释了。 pFilterDeviceObject->DeviceType = pLowerDeviceObject->DeviceType;//给过滤设备赋值pFilterDeviceObject->Characteristics = pLowerDeviceObject->Characteristics;pFilterDeviceObject->StackSize = pLowerDeviceObject->StackSize + 1;pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);//next device pTargetDeviceObject = pTargetDeviceObject->NextDevice;//键盘设备链表}return status;}VOID c2pDetach(IN PDEVICE_OBJECT pDeviceObject){PC2P_DEV_EXT devExt;BOOLEAN NoRequestsOutstanding = FALSE;devExt = (PC2P_DEV_EXT)pDeviceObject->DeviceExtension;__try{__try{IoDetachDevice(devExt->TargetDeviceObject);//解除  原始键盘设备devExt->TargetDeviceObject = NULL;//扩展置零IoDeleteDevice(pDeviceObject);//删除 过滤设备devExt->pFilterDeviceObject = NULL;//扩展置零DbgPrint(("Detach Finished\n"));}__except (EXCEPTION_EXECUTE_HANDLER){}}__finally{}return;}#define  DELAY_ONE_MICROSECOND  (-10)#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)VOID c2pUnload(IN PDRIVER_OBJECT DriverObject)//卸载历程{PDEVICE_OBJECT DeviceObject;PDEVICE_OBJECT OldDeviceObject;PC2P_DEV_EXT devExt;LARGE_INTEGERlDelay;PRKTHREAD CurrentThread;//delay some time lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);//转换长整数CurrentThread = KeGetCurrentThread();//获取当前线程// 把当前线程设置为低实时模式,以便让它的运行尽量少影响其他程序。KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY);//设置优先级  实时UNREFERENCED_PARAMETER(DriverObject);//告诉编译器,已经使用了该变量,不必检测警告! 未引用 参数KdPrint(("DriverEntry unLoading...\n"));// 遍历所有设备并一律解除绑定DeviceObject = DriverObject->DeviceObject;//设备对象while (DeviceObject){c2pDetach(DeviceObject);// 解除绑定并删除所有的设备DeviceObject = DeviceObject->NextDevice;}ASSERT(NULL == DriverObject->DeviceObject); //其作用是如果它的条件返回错误,则终止程序执行 设备删除光了while (gC2pKeyCount)//如果 键盘有一个键的irp{KeDelayExecutionThread(KernelMode, FALSE, &lDelay);//睡眠100秒  延迟执行线程}KdPrint(("DriverEntry unLoad OK!\n"));return;}NTSTATUS c2pDispatchGeneral(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)//所有功能历程  {KdPrint(("Other Diapatch!"));IoSkipCurrentIrpStackLocation(Irp);// 其他的分发函数,直接skip然后用IoCallDriver把IRP发送到真实设备的设备对象。return IoCallDriver(((PC2P_DEV_EXT)DeviceObject->DeviceExtension)->LowerDeviceObject, Irp);//就是过滤设备}NTSTATUS c2pPower(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)//电源处理{PC2P_DEV_EXT devExt;devExt =(PC2P_DEV_EXT)DeviceObject->DeviceExtension;PoStartNextPowerIrp(Irp);//电源管理器处理irpIoSkipCurrentIrpStackLocation(Irp);return PoCallDriver(devExt->LowerDeviceObject, Irp);}NTSTATUS c2pPnP(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)//键盘即插即用{PC2P_DEV_EXT devExt;PIO_STACK_LOCATION irpStack;NTSTATUS status = STATUS_SUCCESS;KIRQL oldIrql;KEVENT event;devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension);// 过滤设备。irpStack = IoGetCurrentIrpStackLocation(Irp);//irp栈空间switch (irpStack->MinorFunction)//次用功能{case IRP_MN_REMOVE_DEVICE://删除设备KdPrint(("IRP_MN_REMOVE_DEVICE\n"));IoSkipCurrentIrpStackLocation(Irp);// 首先把请求发下去 现在在本irp进行加工IoCallDriver(devExt->LowerDeviceObject, Irp);IoDetachDevice(devExt->LowerDeviceObject);//解除 真实设备 绑定。IoDeleteDevice(DeviceObject);//删除过滤设备 windows要求卸载 不是自己的驱动卸载status = STATUS_SUCCESS;break;default:IoSkipCurrentIrpStackLocation(Irp);// 对于其他类型的IRP,全部都直接下发即可。  只是将IRP简单的转发给更低一层的驱动,至于结果怎么样就不得而知了。  跳过当前栈空间status = IoCallDriver(devExt->LowerDeviceObject, Irp);}return status;}NTSTATUS c2pReadComplete(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)// 这是一个IRP完成回调函数的原型 完成{PIO_STACK_LOCATION IrpSp;ULONG buf_len = 0;ULONG numkeys = 0;PUCHAR buf = NULL;PUCHAR keydate = NULL;size_t i;ULONG size=0;size_t j;PKEYBOARD_INPUT_DATA jianpanshuju;//键盘输入输出数据IrpSp = IoGetCurrentIrpStackLocation(Irp);//  如果这个请求是成功的。很显然,如果请求失败了,这么获取   进一步的信息是没意义的。if (NT_SUCCESS(Irp->IoStatus.Status))//io状态成功{buf = Irp->AssociatedIrp.SystemBuffer;// 获得读请求完成后输出的缓冲区buf_len = Irp->IoStatus.Information;// 获得这个缓冲区的长度。一般的说返回值有多长都保存在 Information中。for (i = 0; i < buf_len; ++i){DbgPrint("ctrl2cap: %2x\r\n", buf[i]);//这里可以做进一步的处理。我这里很简单的打印出所有的扫描码。}size = buf_len / sizeof(KEYBOARD_INPUT_DATA);//多少这个结构体的个数jianpanshuju = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;//缓冲区numkeys = Irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);//多少这个结构体的个数 跟上面一样for (j = 0; j < numkeys; j++){KdPrint(("numkeys %d\n", numkeys));KdPrint(("scancode %x\n", jianpanshuju->MakeCode));KdPrint(("%s\n", jianpanshuju ->Flags?"抬起":"按下" ));print_keystroke((UCHAR)jianpanshuju->MakeCode);if (jianpanshuju->MakeCode == CAPS_LOCK)//2个键一样的效果{jianpanshuju->MakeCode = LCONTROL;}}}gC2pKeyCount--;//全局变量键计数器减1if (Irp->PendingReturned)//如果等待返回{IoMarkIrpPending(Irp);//例程用于标记指定的IRP,标志着某个驱动的分发例程(分发函数)因需要被其他的驱动程序进一步处理最终返回STATUS_PENDING状态。 返回等待}return Irp->IoStatus.Status;}NTSTATUS c2pDispatchRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)//读{NTSTATUS status = STATUS_SUCCESS;PC2P_DEV_EXT devExt;//自定义结构体PIO_STACK_LOCATION currentIrpStack;KEVENT waitEvent;KeInitializeEvent(&waitEvent, NotificationEvent, FALSE);//初始化事件if (Irp->CurrentLocation == 1)//当前位置{ULONG ReturnedInformation = 0;KdPrint(("Dispatch encountered bogus current location\n"));status = STATUS_INVALID_DEVICE_REQUEST;Irp->IoStatus.Status = status;Irp->IoStatus.Information = ReturnedInformation;IoCompleteRequest(Irp, IO_NO_INCREMENT);//完成请求return(status);}gC2pKeyCount++;// 全局变量键计数器加1devExt =(PC2P_DEV_EXT)DeviceObject->DeviceExtension;// 得到设备扩展。目的是之后为了获得下一个设备的指针。// 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。// 剩下的任务是要等待读请求完成。currentIrpStack = IoGetCurrentIrpStackLocation(Irp);//得到当前irp位置指针IoCopyCurrentIrpStackLocationToNext(Irp);//函数都是将系统的IO_STACK_LOCATION 数组指针向下移动一次  将IRP简单的转发给更低一层的驱动 IRP完成后异步IRP计数器减一    IoSkipCurrentIrpStackLocation只是将IRP简单的转发给更低一层的驱动,至于结果怎么样就不得而知了。IoSetCompletionRoutine(Irp, c2pReadComplete,DeviceObject, TRUE, TRUE, TRUE);//设置完成历程 这个完成例程将会在调用此函数的驱动的下一层驱动完成IRP指定的操作请求时被调用  irp完成 调用这个函数//CompletionRoutine驱动提供的完成例程专用接口,当他的下一层驱动程序完成请求包时被调用     Context指向传递给完成例程的参数的指针。参数内容由驱动设置,并且只能存储在“非分页”(nonpaged)内存中,因为完成例程的中断请求级范围是IRQP <= DISPATCH_LEVEL.return  IoCallDriver(devExt->LowerDeviceObject, Irp);//到真实设备里}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)//进入到真实设备进入过滤设备 找到了驱动对象{ULONG i;NTSTATUS status;KdPrint(("c2p.SYS: entering DriverEntry\n"));for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)//28个历程{DriverObject->MajorFunction[i] = c2pDispatchGeneral;// 填写所有的分发函数的指针  }DriverObject->MajorFunction[IRP_MJ_READ] = c2pDispatchRead;// 单独的填写一个Read分发函数。因为要的过滤就是读取来的按键信息  其他的都不重要。这个分发函数单独写。DriverObject->MajorFunction[IRP_MJ_POWER] = c2pPower;// 单独的填写一个IRP_MJ_POWER函数。这是因为这类请求中间要调用一个PoCallDriver和一个PoStartNextPowerIrp,比较特殊。DriverObject->MajorFunction[IRP_MJ_PNP] = c2pPnP;//我们想知道什么时候一个我们绑定过的设备被卸载了(比如从机器上被拔掉了?)所以专门写一个PNP(即插即用)分发函数DriverObject->DriverUnload = c2pUnload;// 卸载函数。gDriverObject = DriverObject;status = c2pAttachDevices(DriverObject, RegistryPath);// 绑定所有键盘设备return status;}

原创粉丝点击