Introduction
Please imagine that there's no input signal from the keyboard. It can be used to secure a computer from anybody. For example, if you leave the computer, you can lock the keyboard in this way. After returning to your computer, you can type specific keys to unlock the computer. So the other man who doesn't know your secret password can't access your computer. This can be implemented through Keyboard Hooking.
What is keyboard hooking?
We also called it keyboard filtering. And it can be explained in a simple manner. The user presses the key and from Keyboard device driver sends signals to User's application. And we hook the stream of that.
Keyboard hooking can be accomplished in several ways. In Userland and Kernelland.
You can see userland sample from Adam Roderick J's article.
He shows keyboard hooking skills in userland and few articles also explain keyboard hooking. But using this technique, you can't hook several times by several applications and it can also be uninstalled easily. So I'll introduce another way to hook keyboard in Kernelland. I focused on Driver Hooking in this article. You can not only stop key signals, but also modify signals from User by using this technique, then it can become a virus or malware. But I don't want that. Then how can it be implemented? Let's see together.
How To Use
Place Application.exe and HK_KBD.sys in the same folder, and execute Application.exe. Then press any key. You can see that there's no input from key. OK! That's it!
Using the Code
First, we should find keyboard device object handle. In order to do, you should list all the devices of your computer. And from them, you should find Keyboard Class Device.
You can implement that in the following way:
NTSTATUS
KeyFlt_CreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
...
switch (stack->MajorFunction)
{
case IRP_MJ_CREATE:
{
...
RtlInitUnicodeString(&uniOa, L"\\Device");
InitializeObjectAttributes(
&oa,
&uniOa,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status = ZwOpenDirectoryObject(
&hDir,
DIRECTORY_ALL_ACCESS,
&oa
);
if(!NT_SUCCESS(status))
{
break;
}
pBuffer = ExAllocatePoolWithTag
(PagedPool, ALLOC_SIZE, Tag);
pContext = ExAllocatePoolWithTag
(PagedPool, ALLOC_SIZE, Tag);
memset(pBuffer, 0, ALLOC_SIZE);
memset(pContext, 0, ALLOC_SIZE);
memset(arKbdCls, 0, 0x10);
counter = 0;
g_kbdclsnum = 0;
while(TRUE)
{
status = ZwQueryDirectoryObject(
hDir,
pBuffer,
ALLOC_SIZE,
TRUE,
FALSE,
pContext,
&RetLen
);
if(!NT_SUCCESS(status))
{
break;
}
pDirBasicInfo =
(PDIRECTORY_BASIC_INFORMATION)pBuffer;
pDirBasicInfo->ObjectName.Length -= 2;
RtlInitUnicodeString(&uniKbdDrv,
L"KeyboardClass");
if(RtlCompareUnicodeString(
&pDirBasicInfo->ObjectName,
&uniKbdDrv,
FALSE
) == 0)
{
KbdclsNum = (char*) ((ULONG)
(pDirBasicInfo->
ObjectName.Length) +
(ULONG) (pDirBasicInfo->
ObjectName.Buffer));
arKbdCls[counter] = *KbdclsNum;
counter++;
}
pDirBasicInfo->ObjectName.Length += 2;
}
ExFreePool(pBuffer);
ExFreePool(pContext);
ZwClose(hDir);
for(i = 0; i < 0x10; i++)
{
if(arKbdCls[i] == 0)
break;
else if(arKbdCls[i] == 0x30)
g_kbdclsnum |= KBDCLASS_0;
else if(arKbdCls[i] == 0x31)
g_kbdclsnum |= KBDCLASS_1;
else if(arKbdCls[i] == 0x32)
g_kbdclsnum |= KBDCLASS_2;
}
if(g_kbdclsnum & KBDCLASS_0)
{
RtlInitUnicodeString(
&uniKbdDeviceName,
L"\\Device\\KeyboardClass0"
);
}
else
{
if(g_kbdclsnum & KBDCLASS_2)
{
RtlInitUnicodeString(
&uniKbdDeviceName,
L"\\Device\\KeyboardClass2"
);
}
else if(g_kbdclsnum & KBDCLASS_1)
{
RtlInitUnicodeString(
&uniKbdDeviceName,
L"\\Device\\KeyboardClass1"
);
}
else
{
g_nop = 1;
break;
}
}
status = KeyFlt_IoGetDeviceObjectPointer(
&uniKbdDeviceName,
0,
&KbdFileObject,
&KbdDeviceObject
);
...
}
break;
}
...
}
Now we found the pointer. Then, we should attach to keyboard device object. Here IoAttachDeviceToDeviceStack()
function is used to filter keyboard device.
But unfortunately, if that function fails, then we should replace Major function of that keyboard driver.
if(NT_SUCCESS(status))
{
g_KbdDeviceObject = KbdDeviceObject;
ObDereferenceObject(KbdFileObject);
KdPrint(("KeyFlt: Attach Device. \n"));
__try
{
g_TopOfStack = IoAttachDeviceToDeviceStack
(DeviceObject, KbdDeviceObject);
if (g_TopOfStack != NULL)
{
g_OldFunction = g_KbdDeviceObject->
DriverObject->MajorFunction[IRP_MJ_READ];
g_KbdDeviceObject->DriverObject->
MajorFunction[IRP_MJ_READ] = KeyFlt_HookProc;
}
else
{
g_nop = 1;
g_TopOfStack = NULL;
break;
}
}
__except(1)
{
g_nop = 1;
break;
}
}
else
{
g_nop = 1;
break;
}
...
NTSTATUS KeyFlt_IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT *FileObject,
OUT PDEVICE_OBJECT *DeviceObject
)
{
NTSTATUS status;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK iostatus;
PVOID ptr;
InitializeObjectAttributes(
&oa,
ObjectName,
OBJ_KERNEL_HANDLE,
NULL,
NULL
);
status = ZwOpenFile(
&ObjectName,
DesiredAccess,
&oa,
&iostatus,
0x07,
0x40
);
if(!NT_SUCCESS(status))
{
return status;
}
status = ObReferenceObjectByHandle(
ObjectName,
DesiredAccess,
0,
KernelMode,
&ptr,
0
);
if(!NT_SUCCESS(status))
{
ZwClose(ObjectName);
return status;
}
*FileObject = ptr;
*DeviceObject = IoGetRelatedDeviceObject(ptr);
ZwClose(ObjectName);
return status;
}
You can change the key information in KeyFlt_DispatchPassThrough()
function and KeyFlt_DispatchRead()
function.
So the rest of the code is simple.
Conclusion
How did you find my post? I hope that this code can help in your development. Please vote for me.