First LKM

Posted on Sat 10 May 2014 in Linux Kernel Hacking

A loadable kernel module (LKM) is the easiest way to create a rootkit, although it is also the most noisy and easiest to defend against. Once root (or system level privileges) is gained on a machine, a rootkit is the best way to maintain root access to that machine.

Here I will try to explain the basics of what a LKM actually is and how to create and test a very basic one for Linux.

An LKM is a plugin to the kernel. It allows you to run code with the same permissions as the kernel, which isn't possible for normal userland applications. Device drivers are LKM's as they need permission to access the computers hardware, so either with or without knowing it, you already have some experience with LKM's. Throughout this post I will be using LKM and module interchangeably.

Creating A Hello World LKM

Here is the code for the LKM that we will be creating:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <linux/module.h>
#include <linux/init.h>

MODULE_AUTHOR("0xe7, 0x1e");
MODULE_DESCRIPTION("A simple hello world module");
MODULE_LICENSE("GPL");

static int __init hello_init(void)
{
    printk("Hello World!\n");
    return 0;
}

static void __exit hello_exit(void)
{
    printk("Unloading hello.\n");
    return;
}

module_init(hello_init);
module_exit(hello_exit);

Lines 4 and 5 and just some information about the module. Line 6 is needed otherwise when we load the module we get the following error message in the systems log:

hello: module license 'unspecified' taints kernel.

The module will still load but as we are learning to write a rootkit, we want as little 'noise' as possible.

The function hello_init on lines 8 - 12 runs when the module is loaded, here we are just printing "Hello World!\n" to the system log. The function hello_exit on lines 14 - 18 runs when the module is unloaded, here we are just printing "Unloading hello.\n" to the system log. They are defined as such on lines 20 and 21.

Compiling The LKM

To compile it we need a Makefile, the makefile below will do:

1
2
3
4
5
6
7
obj-m += hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

With both of these files in the same directory we can now compile our first LKM:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@dev:~/lkms# make
make -C /lib/modules/3.12-kali1-686-pae/build M=/root/lkms modules
make[1]: Entering directory `/usr/src/linux-headers-3.12-kali1-686-pae'
  CC [M]  /root/lkms/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/lkms/hello.mod.o
  LD [M]  /root/lkms/hello.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.12-kali1-686-pae'
root@dev:~/lkms# ls -l
total 160
-rw-r--r-- 1 root root   384 May 12 19:35 hello.c
-rw-r--r-- 1 root root 70621 May 12 19:35 hello.ko
-rw-r--r-- 1 root root   650 May 12 19:35 hello.mod.c
-rw-r--r-- 1 root root 39088 May 12 19:35 hello.mod.o
-rw-r--r-- 1 root root 32540 May 12 19:35 hello.o
-rw-r--r-- 1 root root   156 May 12 19:35 Makefile
-rw-r--r-- 1 root root    27 May 12 19:35 modules.order
-rw-r--r-- 1 root root     0 May 12 19:35 Module.symvers

As we can see, the make command has created a number of files (hello.ko, hello.mod.c, hello.mod.o, hello.o, modules.order, Module.symvers). The file we are interested in is hello.ko on line 13, this is our module.

Loading/Unloading The LVM

I am using a 32 bit Debian based Linux system (Kali) for my development environment but this should work on any modern Linux system (Do not try this on a production machine! Working with the kernel always has the possiblity to crash the kernel and bring the whole system down! You have been warned!), older systems might require some changes.

Here is how we load and unload the module; and check that everything has worked:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
root@dev:~/lkms# uname -r
3.12-kali1-686-pae
root@dev:~/lkms# insmod ./hello.ko
root@dev:~/lkms# dmesg | tail -n 1
[692908.561165] Hello World!
root@dev:~/lkms# lsmod | grep hello
hello                  12363  0 
root@dev:~/lkms# rmmod hello
root@dev:~/lkms# dmesg | tail -n 1
[692925.071683] Unloading hello.
root@dev:~/lkms# lsmod | grep hello
root@dev:~/lkms#

So first I have shown you the Linux kernel version I am using with the uname command on line 1, this is just so if it doesn't work for you, you can check if they are the same version. The insmod command is used to load the module on line 3 and we check the system log to make sure it has printed the string "Hello World!\n" using the dmesg command on line 4. The lsmod command is used on line 6 to check if the module is actually loaded. The rmmod command is used on line 8 to unload the module and the system log is checked again on line 9 to check that our printk has run correctly. Lastly we check with lsmod again to make sure the module has been unloaded correctly.

So we have a working LKM.

Conclusion

It is very easy to make mistakes with any programming but the majority of mistakes in a normal application will not bring a system down. While its always important to build and test code in a development environment, its even more important when coding an application that runs in kernelland as any tiny mistake can, and most likely will, bring the system down.

Happy Hacking :-)