Tag Archives: keylogging

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.

Interrupt Requests, Handlers and Keyboards

Low Level:

Interrupts are the means by which hardware devices signal the processor. In the case of a keyboard, this would involve the keyboard controller signalling the processor to let it know there is a new available key press. The processor then notifies the linux kernel. This is called an IRQ or Interrupt request.

It is up to the kernel to run a request handler that associates with each specific interrupt. This handler functionality is part of the device driver for the specified device. The handler functions must run with great efficiency. These quickly move large amounts of data and return to an accepting state. Passing the work into queues and manipulating memory.

Each device driver must register a request handler using the request_irq() function. This registers a given interrupt handler for a given interrupt line. If we run the following

cat /proc/bus/input/devices

This will show our hardware input devices, we can then correlate that what the data returned from running

cat /proc/interrupts

This will show what handlers are currently registered.
Handlers can be unregistered and subsequent lines freed with the free_irq() function.

High Level:

This is where the larger, less time sensitive chunk of our interrupt handling occurs. This manages work that was deferred in queues from the lower level of our interrupt handling process. The 2.6 kernel takes advantage of 3 higher level mechanisms, softirqs, tasklets and work queues.

Softirqs are used for things such as networking and block devices, they are statically added to the kernel at compile time. Tasklets run on top of these softirqs, they are much more dynamic and work fine for the vast majority of cases. Tasklets are managed by a scheduling mechanism. Work queues are an interface for creating kernel threads to handle work queued from elsewhere.

images (1)

How a keyboard works?

This process will be investigated further in the next blog post using systemtap.

  1. Keyboard sends a scancode to our device driver.
  2. Scancode handler in driver converts this into a series of key presses and release events using a translation table.
  3. These key codes and potential modifiers are matched to symbols.
  4. The character symbols are put in a tty queue.
  5. receive_buf() function is called periodically putting the characters into a read queue.
  6. sys_read() is called on stdin of process and returns to process.

 

 Different rootkit key-logging options?

The first option that comes to mind is to build an interrupt handler but as this occurs at a much lower level, it means complete platform dependency. This means it would require a lot more work to implement and even if we did, it’s not guaranteed at all to work on any other devices.

We could also hijack any of the functions above involved in the operation of a keyboard and replace their functionality with some form of logging mechanism.

Our final option is that we could hijack the sys_read() system call and use this to log the keys.

I will look into both function hijacking and system call hijacking  in my next post as a method of implementing our key-logger.