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
This will show our hardware input devices, we can then correlate that what the data returned from running
This will show what handlers are currently registered.
Handlers can be unregistered and subsequent lines freed with the free_irq() function.
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.
How a keyboard works?
This process will be investigated further in the next blog post using systemtap.
- Keyboard sends a scancode to our device driver.
- Scancode handler in driver converts this into a series of key presses and release events using a translation table.
- These key codes and potential modifiers are matched to symbols.
- The character symbols are put in a tty queue.
- receive_buf() function is called periodically putting the characters into a read queue.
- 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.