Click here to Skip to main content
Click here to Skip to main content

Keyboard Hooking In Kernel

By , 11 May 2011
 
screen_shot.GIF

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

cnli
China China
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionJust disable Print screen keymemberfakharmalik7 Mar '13 - 18:40 
hii!!
 
the article was very helpful as i am new in windows programming need little help. i just want to disable print screen button not the whole keyboard can any one help me and guide me through
 

regards
GeneralMy vote of 5memberMichael_Romanov9 Dec '12 - 3:20 
It's wonderful. please sharing whole project. ^_-
BugA minor correction for 64 bit compatibilitymemberozgurbtr28 Oct '12 - 13:54 
Example is very good on 32 bit, but results in a nasty BSOD if you build amd64 version with DDK and run on a 64 bit system. After little playing with WinDbg found the problem. Due to change in buffer addressing in 64 bit, ULONG should not be used on line 228, ULONG_PTR should be used instead. Of course you need to also kernel-mode sign the driver before trying.
GeneralMy vote of 5memberozgurbtr28 Oct '12 - 13:39 
Nice work, a great way to handle ps/2 keyboards without reboot.
GeneralMy vote of 5memberSteve Ween10 Apr '12 - 22:57 
Great.
Helpful for me!
Questionnice workmemberAlexAlvarez6 Oct '11 - 6:27 
nice article .. I just want to know if its possible to simulate a keystroke or send data to keyboard directly with this method and not with the known apis (keybd_event sendinput , etc)..and if you can write and example in couple of lines.it would be appreciated.
AnswerRe: nice workgroupxnli18 Mar '12 - 23:00 
I haven't thought about that till now. Smile | :)
But it can be implemented by sending IRP to Keyboard Driver.
 
Thanks.
QuestionMy vote of 5 and a small questionmembernbgangsta15 Jun '11 - 3:34 
You said you can stop and modify keys sent by the keyboard, but that would mean you can also simulate/send keys programmatically, right?
AnswerRe: My vote of 5 and a small questiongroupxnli15 Jun '11 - 4:00 
Thanks for your attention to my article.
And for your answer, it can be possible and can be implemented easily.
You can use keybd_event()function to simulate key pressing.
If you want, I can post another example which simulate keypressing according to your scenario.
Thanks.
GeneralRe: My vote of 5 and a small questionmemberIzybell27 Jun '11 - 16:42 
From looking at MSDN it looks like keybd_event() has been superseded by sendinput()?
 
I have attempted to use both of these in an effort to generate simulated keyboard input to another process / application that may not necessarily be the current active / foreground application. While these methods have given some success, in some applications the input is not received as expected. If there was a way to generate simulated input at a lower level, it should in theory be hard to distinguish from physical input. I know there must be ways to reliably simulate input because I have used other tools that clearly accomplish this, but I have yet to find the methods being used.
 
Perhaps you have some insight?
 
Regards,
 
creo
GeneralRe: My vote of 5 and a small questiongroupxnli27 Jun '11 - 16:59 
I hope to know your parameters which you put into keybd_event() function.
and keybd_event() function is used for simulating physical key pressing.
 
please try this in C++.
 
keybd_event('A', 0, 0, 0);
Sleep(100);
keybd_event('A', 0, KEYEVENTF_KEYUP, 0);
 
this code snippet is used to simulate pressing 'a' Key.
 
And while implementing, you can do delayed execution of this code snippet by using Sleep() function, so you can set focus on text edit box to see the effects.
 
Thanks
Xiannan
GeneralCool Post ;-)memberMember 156277916 May '11 - 23:02 
Thanx for posting.
...have no idea about the quality, but as last action at the internet cafe I'l try it (after having saved all personal stuff on an USB drive).
GeneralMy vote of 5memberSAKryukov16 May '11 - 15:50 
Very interesting post. It stands out when compared with regular articles on Hooks
GeneralMy vote of 2member428811 May '11 - 20:53 
not bad but plenty of code out there on how to do it
GeneralRe: My vote of 2memberSkymir12 May '11 - 7:13 
I can't run the app from here, but if I'm not mistaken the big difference with this keyboard hook is that it's at kernel level. Which should allow things like writing a keyboard remapper for DirectX programs. Because DirectX accesses the keyboard driver directly, normal keyboard hooks don't work most of the time.
The true man wants two things: danger and play. For that reason he wants woman, as the most dangerous plaything.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 11 May 2011
Article Copyright 2011 by cnli
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid