#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)
#define S_SHIFT 1
#define S_CAPS 2
#define S_NUM 4
static int kb_status = S_NUM;
unsigned char asciiTbl[] = {
0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09, //normal
0x71, 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, //caps
0x51, 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, //shift
0x51, 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 + shift
0x71, 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)//打印字母 翻译下
{
UCHAR ch = 0;
int off = 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_INTEGER lDelay;
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);//电源管理器处理irp
IoSkipCurrentIrpStackLocation(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--;//全局变量键计数器减1
if (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++;// 全局变量键计数器加1
devExt =(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;
}