Part 2: Stealing keyboard keys for fun & profit

This is the 2nd part of my research into Key logging from a kernel module. In this post I would like to dive into my implementation and explain the various techniques I have used. The code related to this blog post is available at the following location:
https://github.com/maK-/stealthy-Keylogger-lkm/

Keyboard notifier

As I mentioned in the previous post, I use a keyboard notifier function to capture the key press data. This allows me to access the raw KBD_KEYCODE and param->value data. Allowing me to map what keys are pressed to their ASCII character equivalents. I also needed to take into consideration whether the shift key was pressed in order to change the characters to caps or to get the alternative keyboard symbols.

On initiation of our module, I load a keyboard notifier using the following code.

register_keyboard_notifier(&nb);

This accepts a notifier_block struct &nb that contains a handler function of key_notify to manage our key presses. It is very easy to follow how the handler function works. It simply converts the param->value to our characters and stores them to a global character keyBuffer. On the exiting of our module we also need to unregister the notifier.

Registering a character device

I came across character devices when reading the following chapter of the Linux programming guide – Character devices. The character device allows us to create a file and manipulate it from within the kernel using the following defined file operations. We can create handler functions for each of these elements.

//File operations for device
struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = open_dev,
    .read = read_dev,
    .write = write_dev,
    .release = release_dev,
};

It is possible to register a character device with the following code. In order to register the device you need a name that will appear in /dev/DEVICE_NAME and a Major number for the device. This must be unique also. If you pass in 0 the kernel dynamically allocates a Major number for you. For my proof of concept code I used a Major number of 33. You can view the Major numbers and the associated devices by running the following:

ls -al /dev/ | awk '{print $5,$10}'

To register a character device you must use the following format. This is done on initiation of our module also and the device must be unregistered on exit. We can see our file operations struct is passed in also.

major = register_chrdev(DEVICE_MAJOR, DEVICE_NAME, &fops);

What the character device allowed me to do, was avoid having to read and write to a file from within the kernel. I can now perform operations when the file is accessed or written to. I used this to dump our logged keyBuffer characters whenever the /dev/.maK_it file is read. I could now also read commands that have been echo’d into the file that I store to another character buffer called commands. This allows me to control the module. I can turn on or off certain functionality or run functions within the kernel on request.

Hiding the module and Stealth

As I now have the option of passing in my own commands to the kernel. I added functionality to hide and reveal the module. This was very easy as the kernel provides a method for doing so.

list_del(&THIS_MODULE->list);
kobject_del(&THIS_MODULE->mkobj.kobj);

This removes the module from the module list (that is used by lsmod) and also removes the kobject from the /sys/module directory.This is very effective as it hides the module from all of the following commands. These are normally used to reveal kernel modules.

lsmod | grep maK
grep maK /proc/modules
grep maK_it /proc/kallsyms
ls /sys/module | grep maK
modinfo maK_it
modprobe -c | grep maK
ls /dev/

I needed to include a reveal functions also so I could rmmod our kernel module. What this does is add the module back to the list. This just involved storing the list items before I removed it and then adding it back afterwards.

list_add(&THIS_MODULE->list, modKobjList);

I also had to include the following in order to avoid ¬†warning messages flooding the logs. When we remove our objects from the /sys/module directory, the kernel complains when it can’t find them as you try remove the module. All this does is set the pointers to NULL so the kernel won’t look for them.


THIS_MODULE->sect_attrs = NULL;
THIS_MODULE->notes_attrs = NULL;

I hope this exploration has been informative and helps others if they decide to look into such things. There are other avenues for key logging I will be investigating in the later stages of the project. I would like to time the differences in the kernel keyboard stack with a key logger and without one using systemtap. The goal of this would be to develop a systemtap script that could detect such things in real time. I will cover this when I start looking at detection and mitigation techniques.

The next immediate step I will be moving on to, is the escalation of a normal user to root privileges. I will also be looking at methods of hiding and revealing files and processes.

Appendices

http://www.tldp.org/LDP/lkmpg/2.6/html/

Love, Robert. (2012). Devices and Modules. In: Linux Kernel Development. 3rd ed. USA, Indiana: Addison Wesley. 337-363.

http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/

http://stapbofh.krunch.be/systemtap-bofh-fosdem2011020501.pdf