Part 1: Stealing keyboard keys for fun & profit

In this blog post I will be further investigating the Linux keyboard
drivers and will be implementing a keylogger. Using systemtap I will
be probing various different functions within this process in the
hope of gaining a greater understanding of how the keyboard works
within the kernel.

There is a massive amount of interlinked keyboard related functionality built into the kernel as you can see in the image below.  I did my best to isolate the more interesting files from the rest and started reading through lots and lots of source code.

tty_2vt_2keyboard_8c__incl

This image above is from kerneldox.com This site proved very helpful in understanding a lot of how the keyboard works.

While going through these files I came across a lot of very useful     kernel functionality. The most useful of which was the notifier functions. These allow us to set a notifier or observer on many different elements within the kernel. At the following location you will see information detailing a Keyboard notifier. This information was found on kernel.org. When a keyboard key is pressed, we can be notified of this event within our kernel module. This will prove incredibly useful for implementing a key logging mechanism.

Further investigation with systemtap

Using systemtap I pulled a list of some keyboard related functions using the following script.

stap -L 'kernel.function("kbd*")'

This probes for the exact location of functions in the kernel whose name matches our search term of “kbd_*”, where the * is a wildcard term. The results of this search where are as follows:


kernel.function("kbd_bh@drivers/char/keyboard.c:1014")
$dummy:long unsigned int

kernel.function("kbd_connect@drivers/char/keyboard.c:1314") 
$handler:struct input_handler* $dev:struct input_dev* $id:struct input_device_id const* $i:int

kernel.function("kbd_disconnect@drivers/char/keyboard.c:1353") 
$handle:struct input_handle*

kernel.function("kbd_event@drivers/char/keyboard.c:1296") 
$handle:struct input_handle* $event_type:unsigned int $event_code:unsigned int $value:int

kernel.function("kbd_keycode@drivers/char/keyboard.c:1144") 
$hw_raw:int $down:int $keycode:unsigned int

kernel.function("kbd_rate@drivers/char/keyboard.c:272") 
$rep:struct kbd_repeat* $d:unsigned int $p:unsigned int

kernel.function("kbd_rawcode@drivers/char/keyboard.c:1136") 
$data:unsigned char

kernel.function("kbd_start@drivers/char/keyboard.c:1364") 
$handle:struct input_handle*

I then Looked further into some of these functions just out of curiosity and developed systemtap scripts to print out the variables within these functions.

kbd_bh
This defines the tasklet handler, initially disabled, which does nothing with its single argument. The reason for this is because of the need to handle the scenario when a keyboard handler is not registered yet but their are already updates from the device.

kbd_connect
When a keyboard device is attached or found, this function is called. The function looks at the device and then decides whether the passed in handler will handle events from it.

kbd_disconnect
This disconnects and closes our handler. The handler is then unregistered and freed.

kbd_event
This function takes the address of a handler an event_type as an integer, an event_code and a value as parameters. Depending on what the event_type is we pass our value into either the kbd_keycode or kbd_rawcode functions. Pressing a key triggers an event, so does releasing the key. Notice the different values for pressing down and releasing the key.


#!/usr/bin/env stap

probe kernel.function("kbd_event"){
	print("nkbd_event : %s", $$vars)
}

Pressing ‘a’ results in the following data being printed.
handle=0xf676f140 event_type=0x4 event_code=0x4 value=0x1e
handle=0xf676f140 event_type=0x1 event_code=0x1e value=0x1
handle=0xf676f140 event_type=0x0 event_code=0x0 value=0x0

Here we can see the address of the handler, value of the event_type, event_code and value. This data helps us to handle what way the key press is to be managed. For example a value of 0x1 means it is a “key press down” whereas a value of 0x0 means a “key release”.

kbd_keycode
This function handles raw, mediumraw, xlate and unicode keyboard modes as raw values. Scancodes are then converted into keycodes or other values and passed into tty queues or other handler functions.

kbd_rate
This deals with setting the keyboard rate. Taking in a delay and period to wait.

kbd_rawcode
This is used by kbd_event to handle certain events or rawcodes.

kbd_start
Start the keyboard handler on the new keyboard refreshing the LED states to match the rest of our system.

Implementation of my keylogger

I have implemented my keylogger using register_keyboard_notifier and register_chrdev. This stores the keys to a buffer and then dumps the data to a character device when a read is performed.

I will outline the specifics of how this works in the next blog post. I will also begin looking into methods of hiding files and the processes.

You can find the source here for the Keylogger-lkm.