Windows程序都是基于消息设计的。当你按下一个按键,或者点击一下鼠标,都会想操作系统的内核发送一个Irp消息。你只要捕获这个消息,将这个消息复制下,然后将消息放行。将复制的消息传送给你的应用程序。这就是获取键盘按键消息的实现方法。
很多黑客软件获取别人计算的密码,大多也用这种方法。建议广大用户在使用计算机进行输入游戏账号密码或者银行账号密码等,多用软键盘。防止别人通过这种方式窃取您的密码。
下面我详细介绍下实现:
通过下面这个方法添加了键盘的驱动。加完后所有的键盘信息都可以被捕获。
状态= IoCreateDevice(驱动器
sizeof(DEVICE_EXTENSION)的,
NULL,
FILE_DEVICE_KEYBOARD
DriverObject-> MajorFunction [IRP_MJ_READ] = Ctrl2capDispatchRead;键盘按下时都会调用这个函数来处理发送来的IRP消息。
下面的方法是对具体键盘的Irp消息处理逻辑:当然还可以屏蔽相关的消息,也可以改变键盘按键,比如按了一次A,实际接收到的是B这也是可能的。也可以将您的所有按键组成报文发送到 服务器,这样分析您的按键,可能会得到您的密码。您的电脑如果被加载到这样的驱动,可能很不安全。本方法演示的是您按下capslk键,将其转换为Ctrl。可以在debugview中查看相关信息。
NTSTATUS Ctrl2capReadComplete(
IN
PIRP IRP的PDEVICE_OBJECT的DeviceObject,IN,
IN PVOID上下文
)
{
PIO_STACK_LOCATION IrpSp
PKEYBOARD_INPUT_DATA KEYDATA;
诠释numKeys的,我;
/ /
/ /请求已完成-看看结果。
/ /
IrpSp = IoGetCurrentIrpStackLocation(IRP);
(NT_SUCCESS(IRP->在IoStatus.Status)){
/ /
/ /不要大写锁定和大写锁定。需要注意的是
/ /只是frobbing的MakeCode的处理向上键
/ /向下键的情况下,因为上/下
/ /单独指定信息的标志字段中的键盘输入数据
/ /(0表示键按下, 1表示键)。
/ /
KEYDATA =的Irp-> AssociatedIrp.SystemBuffer;
numKeys = IRP-> IoStatus.Information /大小(KEYBOARD_INPUT_DATA);
(i = 0;我numKeys,我+ +){
DbgPrint(“扫描码:%X”,KEYDATA [I]。MakeCode);
DbgPrint((“%s \ n”,KEYDATA [i]的标志吗?“上”“下”)); / /将键盘信息都捕获了
(KEYDATA [I]:。MakeCode == CAPS_LOCK)
KEYDATA [I]。MakeCode = LCONTROL;
}
}
}
/ /
/ /标记的IRP有待如果需要的
/ /
如果(IRP-> PendingReturned){
IoMarkIrpPending的(IRP);
}
返回的Irp-> IoStatus.Status中;
}
#包括“NTDDK.H”
#包括<ntddkbd.h>的
#包括“stdarg.h”
的#include“stdio.h中”
#包括“ctrl2cap.h”的
包括的“ntddkbd.h”
#UNDEF WIN2K
#IFDEF ALLOC_PRAGMA
的#pragma alloc_text(INIT,DriverEntry中)
的#pragma alloc_text(PAGE,Ctrl2capDispatchGeneral)
#如果WIN2K
的#pragma alloc_text(PAGE,Ctrl2capAddDevice)
的#pragma alloc_text(PAGE,Ctrl2capUnload)
的#pragma alloc_text(PAGE,Ctrl2capPnP)
的#pragma alloc_text( PAGE,Ctrl2capPower)
#ENDIF / / WIN2K
#ENDIF / / ALLOC_PRAGMA
/ / ------------------------------------------------ ----------------------
/ /
/ /的的DriverEntry中
/ /
/ /初始化安装的驱动程序。在这里,我们只是为自己设定了
/ /
/ /被叫:NT4,WIN2K
/ /
/ / ------------------------------ ----------------------------------------
NTSTATUS DriverEntry中(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
ULONG我;
DbgPrint((“Ctrl2cap.SYS:进入DriverEntry中\ n”));
/ /
/ /填充在所有的调度入口点函数的通过
/ /和明确填写功能,我们要拦截
/ /
(i = 0;我IRP_MJ_MAXIMUM_FUNCTION,我+ +){
DriverObject-> MajorFunction [我] = Ctrl2capDispatchGeneral;
}
/ /
/ /我们的阅读功能,我们做我们的实际工作中。
/ /
DriverObject-> MajorFunction [IRP_MJ_READ] = Ctrl2capDispatchRead;
#如果WIN2K
/ /
/ /的电源IRP是唯一的
,因为它们需要特殊的PoCallDriver
/ / PoStartNextPowerIrp的函数调用,我们必须处理专门下/ / WIN2K 。
/ /
DriverObject-> MajorFunction [IRP_MJ_POWER] = Ctrl2capPower;
/ /
/ /唯一的原因,我们需要处理的PnP IRP的是知道什么时候
/ /我们的设备已经连接到消失(删除)
/ /
DriverObject-> MajorFunction [IRP_MJ_PNP] = Ctrl2capPnP;
/ /
/ /在Win2K下告诉我们得到的键盘
/ /设备通过我们的AddDevice例程入口点。
/ /
DriverObject-> DriverUnload = Ctrl2capUnload;
DriverObject - > DriverExtension>的AddDevice = Ctrl2capAddDevice;
返回STATUS_SUCCESS;
#ELSE / / WIN2K
/ /
/ /在NT 4,我们走出去,
/ /键盘0。
/ /
返回Ctrl2capInit的(DriverObject)挂钩的键盘类设备;
#ENDIF / / WIN2K
}
/ / ------------------------------------------------ ----------------------
/ /
/ /的Ctrl2capInit
/ /
/ /键盘上的路径上的钩子。这个例程返回
/ /状态成功,即使有一个问题,为什么呢?我发现,如果
/ /不,该键盘将不会回应!
/ /
/ /被叫:NT4
/ /
/ / ------------------- -------------------------------------------------- - :
NTSTATUS Ctrl2capInit(
IN PDRIVER_OBJECT DriverObject
)
{
CCHAR ntNameBuffer [64];
STRING ntNameString
UNICODE_STRING ntUnicodeString
PDEVICE_OBJECT设备
NTSTATUS状态,
PDEVICE_EXTENSION devExt
WCHAR messageBuffer [] = L“Ctrl2cap初始化\ n”;
UNICODE_STRING messageUnicodeString;
/ /
/ /只钩上的第一个键盘的链。
/ /的的
的sprintf(ntNameBuffer,“ http://ctc.qzs.qq.com/# “);
RtlInitAnsiString(ntNameString,ntNameBuffer);
RtlAnsiStringToUnicodeString(ntUnicodeString,ntNameString,TRUE) ;
/ /
/ /创建设备对象的键盘
/ /
状态= IoCreateDevice(DriverObject,
大小(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_KEYBOARD,
0,
FALSE,
&设备);
(NT_SUCCESS(状态)){
DbgPrint((“Ctrl2cap:键盘钩子创建设备失败!\ n”));
RtlFreeUnicodeString(ntUnicodeString);
返回STATUS_SUCCESS;
}
RtlZeroMemory(设备 - > DeviceExtension sizeof(DEVICE_EXTENSION)的);
(PDEVICE_EXTENSION devExt =)设备- > DeviceExtension / / / /键盘使用缓冲I / O,所以我们必须为好。 / / 设备- >标记|的= DO_BUFFERED_IO 设备- >旗&=〜DO_DEVICE_INITIALIZING;
/ /
/ /附加键盘链
/ /
状态= IoAttachDevice(设备,&ntUnicodeString,&devExt-> TopOfStack的);
(NT_SUCCESS(状态)){
DbgPrint((“Ctrl2cap:与键盘连接失败!\ n”));
IoDeleteDevice(设备);
RtlFreeUnicodeString(ntUnicodeString);
返回STATUS_SUCCESS;
}
/ /
/ /完成!只是我们的字符串的道路上...
/ /
RtlFreeUnicodeString(&ntUnicodeString);
DbgPrint((“Ctrl2cap:成功连接到键盘设备\ n”));
/ /
/ /这一行简单地演示了如何驱动程序可以打印
的东西/ /在系统初始化时的蓝屏。
/ /
RtlInitUnicodeString的(&messageUnicodeString,
messageBuffer。)
ZwDisplayString(messageUnicodeString);
返回STATUS_SUCCESS;
}
#如果WIN2K
/ / --------------------------------------------- -------------------------
/ /
/ / Ctrl2capAddDevice
/ /
/ / PnP管理器调用我们每个键盘都存在于系统中,
/ /我们附加到每个人,使我们可以翻转大写锁定控制
/ /
/ /调用: 的PDRIVER_OBJECT驱动, IN PDEVICE_OBJECT PDO ){ PDEVICE_EXTENSION devExt IO_ERROR_LOG_PACKET errorLogEntry PDEVICE_OBJECT设备 NTSTATUS状态= STATUS_SUCCESS;
/ /
/ /创建一个过滤装置,并将它附加到设备堆栈 / / DbgPrint((“Ctrl2capAddDevice \ n”)); 状态= IoCreateDevice( 大小(DEVICE_EXTENSION), NULL, FILE_DEVICE_KEYBOARD,/ /键盘驱动 , 驱动程序, FALSE, 设备 );
(NT_SUCCESS(状态)){
(状态);
}
RtlZeroMemory(设备 - > DeviceExtension sizeof(DEVICE_EXTENSION)的);
devExt =(PDEVICE_EXTENSION)的设备- > DeviceExtension
devExt - > TopOfStack = IoAttachDeviceToDeviceStack(设备,PDO);
ASSERT(devExt - > TopOfStack);
设备>标志| =(DO_BUFFERED_IO | DO_POWER_PAGABLE);
设备>旗=〜DO_DEVICE_INITIALIZING的
返回状态;
}
/ / ------------------------------------------------ ----------------------
/ /
/ /的Ctrl2capPnP
/ /
/ /,使我们脱离目标,我们必须处理随插即用的IRP
/ /设备在适当的时候
/ /
/主/被叫:WIN2K
/ /
/ / ---------------------------------------- ------------------------------
NTSTATUS Ctrl2capPnP(
IN PDEVICE_OBJECT的DeviceObject,
IN PIRP IRP
)
{
PDEVICE_EXTENSION devExt,
PIO_STACK_LOCATION irpStack
NTSTATUS状态= STATUS_SUCCESS;
KIRQL oldIrql;
KEVENT的事件;
devExt =(PDEVICE_EXTENSION)的DeviceObject-> DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(IRP);
(irpStack - > MinorFunction){
情况下IRP_MN_REMOVE_DEVICE: / / / / / /下devnode堆栈传递IRP的目标设备后脱离 / / IoSkipCurrentIrpStackLocation(IRP); IoCallDriver函数(devExt - > TopOfStack,IRP);
IoDetachDevice(devExt - > TopOfStack);
IoDeleteDevice(的DeviceObject);
状态= STATUS_SUCCESS;
突破;
案例IRP_MN_SURPRISE_REMOVAL:
/ /
/ /同一个删除设备,但,不叫IoDetach或IoDeleteDevice。
/ /
IoSkipCurrentIrpStackLocation的(IRP);
状态= IoCallDriver函数(devExt - > TopOfStack,IRP);
情况的IRP_MN_START_DEVICE:
情况IRP_MN_QUERY_REMOVE_DEVICE
的情况下IRP_MN_QUERY_STOP_DEVICE:
案例IRP_MN_CANCEL_REMOVE_DEVICE:
情况IRP_MN_CANCEL_STOP_DEVICE:的
情况下IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
情况IRP_MN_STOP_DEVICE:
案例IRP_MN_QUERY_DEVICE_RELATIONS:
情况IRP_MN_QUERY_INTERFACE:
案例IRP_MN_QUERY_CAPABILITIES:
案例IRP_MN_QUERY_DEVICE_TEXT:
情况IRP_MN_QUERY_RESOURCES:
案例IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
案例IRP_MN_READ_CONFIG:
案例IRP_MN_WRITE_CONFIG:
案例IRP_MN_EJECT:
情况IRP_MN_SET_LOCK :
情况IRP_MN_QUERY_ID:
案例IRP_MN_QUERY_PNP_DEVICE_STATE:
默认值:
/ /
/ /通过这些丝毫不受影响
/ /
IoSkipCurrentIrpStackLocation的(IRP);
状态= IoCallDriver函数(devExt - > TopOfStack,IRP); }
返回状态;
}
/ / ------------------------------------------------ ----------------------
/ /
/ / Ctrl2capPower
/ /
/ /我们有专门处理电源IRP。
/ /
/ /被叫:WIN2K
/ /
/ / -------------------------------------------------- --------------------
NTSTATUS Ctrl2capPower(
IN PDEVICE_OBJECT的DeviceObject,
IN PIRP IRP
)
{
PDEVICE_EXTENSION devExt devExt =(PDEVICE_EXTENSION)的DeviceObject-> DeviceExtension;
/ /
/ /让我们在下一次上电IRP的门
/ /
PoStartNextPowerIrp(IRP); / / / /通过这个IRP键盘类驱动程序 / / IoSkipCurrentIrpStackLocation(IRP); 返回PoCallDriver(devExt - > TopOfStack,IRP) ; }
/ / ------------------------------------------------ ----------------------
/ /
/ / Ctrl2capUnload的
/ /
/ /我们的Win2K系统的PnP卸载功能。我们不需要做任何事情
/ /
/ /被叫:WIN2K
/ /
/ / ------------------------------ ----------------------------------------“
VOID
Ctrl2capUnload(
IN PDRIVER_OBJECT驱动程序
)
{
UNREFERENCED_PARAMETER(驱动程序);
ASSERT(NULL ==驱动程序的DeviceObject);
}
#ENDIF / / WIN2K
/ / ------------------------------------------------ ---------------------- / / / /被叫:WIN2K,NT4 /
/ /
/ /的Ctrl2capReadComplete
/ /
/ /获取控制权后的读操作已完成。/ / / ----------------------------------------------- ----------------------- IN PDEVICE_OBJECT的DeviceObject, IN PVOID语境下 IN PIRP IRP,:NTSTATUS Ctrl2capReadComplete( ){ PIO_STACK_LOCATION IrpSp PKEYBOARD_INPUT_DATA KEYDATA; numKeys,我;
/ /
/ /请求已完成-看看结果。
/ /
IrpSp = IoGetCurrentIrpStackLocation(IRP);
(NT_SUCCESS(IRP->在IoStatus.Status)){
/ /
/ /不要大写锁定和大写锁定。需要注意的是
/ /只是frobbing的MakeCode的处理向上键
/ /向下键的情况下,因为上/下
/ /单独指定信息的标志字段中的键盘输入数据
/ /(0表示键按下, 1表示键)。
/ /
KEYDATA =的Irp-> AssociatedIrp.SystemBuffer;
numKeys = IRP-> IoStatus.Information /大小(KEYBOARD_INPUT_DATA);
(i = 0;我numKeys,我+ +){
DbgPrint(“扫描码:%X”,KEYDATA [I]。MakeCode);
DbgPrint((“%s \ n”,KEYDATA [i]的标志吗?“上”“下”)); / /将键盘信息都捕获了
(KEYDATA [I]:。MakeCode == CAPS_LOCK)
KEYDATA [I]。MakeCode = LCONTROL;
}
}
}
/ /
/ /标记的IRP有待如果需要的
/ /
如果(IRP-> PendingReturned){
IoMarkIrpPending的(IRP);
}
返回的Irp-> IoStatus.Status中;
}
/ / ------------------------------------------------ ----------------------
/ /
/ / Ctrl2capDispatchRead
/ /
/ /设置在读请求完成,因此,我们可以
/ /按摩输入队列IO的完成。
/ /
/ /被叫:WIN2K,NT4
/ /
/ / --------------------------------- -------------------------------------的
NTSTATUS Ctrl2capDispatchRead(
IN PDEVICE_OBJECT的DeviceObject,
IN PIRP IRP)
{
PDEVICE_EXTENSION devExt
PIO_STACK_LOCATION currentIrpStack
PIO_STACK_LOCATION nextIrpStack;
/ /
/ /收集我们的变量。
/ /
devExt的=(PDEVICE_EXTENSION)的DeviceObject-> DeviceExtension;
currentIrpStack = IoGetCurrentIrpStackLocation(IRP);
nextIrpStack = IoGetNextIrpStackLocation(IRP);
/ /
/ /推PARAMS键盘类驱动程序。
/ /
* nextIrpStack = * currentIrpStack;
/ /
/ /设置完成回调,所以我们可以在“FROB”键盘数据
/ /
IoSetCompletionRoutine函数(IRP,Ctrl2capReadComplete
的DeviceObject,TRUE,TRUE,TRUE); / /让Ctrl2capReadComplete的
/ /
/ /返回结果的调用的键盘类驱动程序。
/ /
返回IoCallDriver函数(devExt - > TopOfStack,IRP);
}
/ / ------------------------------------------------ ----------------------
/ /
/ /的Ctrl2capDispatchGeneral
/ /
/ / /处理多种功能,我们不感兴趣,
/ /沿着键盘类驱动程序。
/
/主/被叫:WIN2K,NT4
/ /
/ / -------------------------------------- --------------------------------:
NTSTATUS Ctrl2capDispatchGeneral(
IN PDEVICE_OBJECT的DeviceObject,
IN PIRP IRP
)
{
/ /
/ /通过IRP的目标没有接触IRP
/ /
#如果WIN2K
IoSkipCurrentIrpStackLocation的(IRP);
#/ / WIN2K
/ /
/ /这是相当于的IoSkipCurrentIrpStackLocation宏
不存在,/ /在NT 4 DDK
/ /
IRP-> CurrentLocation + +;
的Irp-> Tail.Overlay.CurrentStackLocation + +;
#ENDIF / / WIN2K
返回IoCallDriver函数(((PDEVICE_EXTENSION)的DeviceObject-> DeviceExtension) - > TopOfStack,IRP);
}
本文章献给从事驱动开发的相关人士。