Thursday, July 15, 2021

Beginning Linux Kernel Development - My First Linux Kernel Module (LKM) - Hello World

I'm currently on the journey where I am learning more about Linux internals. This series of posts are meant to document my learning. As always, this allows me to fallback to my own materials when I need to refresh my memory. This first post is ... well .. Hello World :-)

While this first program is not a rootkit, it is meant to help me understand the basics of the Linux Kernel Modules (LKM). Without further ado, let's get going. 

Key points:
1.  Kernel Modules must have at least two functions. These are the initialization function and the cleanup function. The "__init" functions is what gets called when "insmod" command is executed. The cleanup function "__exit" is what gets executed when "rmmod" command is executed.
2. Every kernel module needs to include "linux/module.h" and "linux/init.h"
3. Need to include "linux/kernel.h" to use for example "printk()"
4. "printk()" is a logging mechanism for the kernel. Basically to give information, warnings, etc. 
5. In practice when using the "printk", don't use the number representations. Instead use the macro such as KERN_INFO.
6.  Kernel modules are compiled differently from user space apps
7.  ".ko" files have a ".modinfo" section which contains additional information. 
8.  LKMs are loaded at runtime
9.  LKMs are not loaded into user space and are thus essentially part of the kernel. 
10. Initialization functions should always be declared as static as they are not meant to be visible outside the specific file.
11. "module_init" is mandatory. It specifies where the initialization function is found.      
12. The cleanup function "module_exit" unregisters interfaces and return resources to the system before the module is removed.
13. If the module does not define a cleanup function, the kernel does not allow it to be unloaded.

Let's now get going with the code for the kernel mode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
kali@securitynik:~/rootkits/HelloWorld$ touch helloWorld.c
kali@securitynik:~/rootkits/HelloWorld$ vi helloWorld.c 
kali@securitynik:~/rootkits/HelloWorld$ cat helloWorld.c 
/*
 * This is my first crack at writing a Linux Kernel Module (LKM)
 * Author: Nik Alleyne
 * Blog: www.securitynik.com
 * File: helloWorld.c
 */

// Let the system know we are part of the kernel
// #define __KERNEL__

// Let the system know we are not a permanent part of the kernel
// #define MODULE

// init.h needed for entry and exit macros
#include <linux/init.h>

// Remember every module needs module linux/module.h
#include <linux/module.h>

// kernel.h is needed for printk() in this example
#include <linux/kernel.h>


// Specify the license information. License choice impacts the way the kernel treats your module.
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Nik Alleyne - www.securitynik.com");
MODULE_DESCRIPTION("My First Linux Kernel Module (LKM)");
MODULE_VERSION("0.1a");

// Setup the initialization module. Basically what the LKM does at startup
static int __init hello_init(void)
	{
		printk(KERN_INFO "[*] ENTERING!! Welcome to SecurityNik LKM World! \n");
		
		// Always return 0 to show success.
		// If a non-zero value is returned it more than likely means an error occurred while loading
		return 0;
	}


// Setup the cleanup module. Basically what the LKM does upon exit
static void __exit hello_exit(void)
	{
		printk(KERN_INFO "[*] LEAVING!! I've had enough of this. I'm leaving. See ya ...\n");
	}

// Now tell the system which module to load upon initialization
module_init(hello_init);

// Which module to call upon exit
module_exit(hello_exit);

/*
 * References:
 * https://elixir.bootlin.com/linux/latest/source/include/linux/module.h
 * https://elixir.bootlin.com/linux/latest/source/include/linux/kernel.h
 * https://elixir.bootlin.com/linux/latest/source/include/linux/printk.h
 * https://elixir.bootlin.com/linux/latest/source/include/linux/tty.h
 *
 */

To compile this code, we need a "Makefile". Below is my "Makefile"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#
# This is the Makefile for my Linux Kernel Module (LKM)
# Author: Nik Alleyne
# Blog: www.securitynik.com
# File: Makefile
# 

# The following line is the only one which is truly required.
# the file should match the name of the C file
obj-m += helloWorld.o

# 
# --directory (or the more used -C) - switches to the kernel directory before performing any make tasks
# `uname --kernel-release` returns the current kernel version. Basically we are building against the current kernel.
# M=$(PWD) - Tells the make command where the actual project file exists. This value represents the absolute path
# modules - This is the default target. It is the same as if no target was specified
# clean - Removes all generated files in the module directory only
#
#
all:
	make --directory=/lib/modules/$(shell uname --kernel-release)/build/ M=$(PWD) modules

clean:
	make --directory=/lib/modules/$(shell uname --kernel-release)/build/    M=$(PWD) clean


#
# Reference
# http://www.tldp.org/LDP/lkmpg/2.6/html/x181.html
# https://www.kernel.org/doc/Documentation/kbuild/modules.txt
# 

Now that we have those two files, let's "make all".

1
2
3
4
5
6
7
8
9
kali@securitynik:~/rootkits/HelloWorld$ make all
make --directory=/lib/modules/5.5.0-kali2-amd64/build/ M=/home/kali/rootkits/HelloWorld modules
make[1]: Entering directory '/usr/src/linux-headers-5.5.0-kali2-amd64'
  CC [M]  /home/kali/rootkits/HelloWorld/helloWorld.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  /home/kali/rootkits/HelloWorld/helloWorld.mod.o
  LD [M]  /home/kali/rootkits/HelloWorld/helloWorld.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.5.0-kali2-amd64'

Looks like the "make" was successful. Let's confirm.

1
2
3
kali@securitynik:~/rootkits/HelloWorld$ ls
helloWorld.c   helloWorld.mod    helloWorld.mod.o  Makefile       Module.symvers
helloWorld.ko  helloWorld.mod.c  helloWorld.o      modules.order

Most important file above for us is the "helloWorld.ko". Let's get some information on the module before installing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
kali@securitynik:~/rootkits/HelloWorld$ sudo modinfo helloWorld.ko
filename:       /home/kali/rootkits/HelloWorld/helloWorld.ko
version:        0.1a
description:    My First Linux Kernel Module (LKM)
author:         Nik Alleyne - www.securitynik.com
license:        GPL v2
srcversion:     8B4C656BD1E37221F0FBC7C
depends:        
retpoline:      Y
name:           helloWorld
vermagic:       5.5.0-kali2-amd64 SMP mod_unload modversions 

Looks good!  Let's perform a "dmesg --clear" before installing the module:

1
kali@securitynik:~/rootkits/HelloWorld$ sudo dmesg --clear

Let's now install our first LKM

1
kali@securitynik:~/rootkits/HelloWorld$ sudo insmod helloWorld.ko

Let's now confirm the module was properly installed by looking at our "dmesg" by using "dmesg --ctime".

1
2
kali@securitynik:~/rootkits/HelloWorld$ sudo dmesg --ctime
[Sat Jul  4 13:47:07 2020] [*] ENTERING!! Welcome to SecurityNik LKM World! 

Looks like it the module might have been installed properly. Let's look at the "/var/log/messages" file to get a different view of this message:

1
2
kali@securitynik:~/rootkits/HelloWorld$ sudo tail --lines 1 /var/log/messages
Jul  4 13:46:32 securitynik kernel: [86342.320847] [*] ENTERING!! Welcome to SecurityNik LKM World!

Let's now finally confirm our module is installed by executing "lsmod"

1
2
3
4
kali@securitynik:~/rootkits/HelloWorld$ lsmod 
Module                  Size  Used by
helloWorld             16384  0
....

Nice! Let's now uninstall the module via "sudo rmmod helloWorld".

1
kali@securitynik:~/rootkits/HelloWorld$ sudo rmmod helloWorld

Let's look at our "dmesg" again to see if the exit note was written.

1
2
3
kali@securitynik:~/rootkits/HelloWorld$ sudo dmesg --ctime
[Sat Jul  4 13:47:07 2020] [*] ENTERING!! Welcome to SecurityNik LKM World! 
[Sat Jul  4 13:51:52 2020] [*] LEAVING!! I've had enough of this. I'm leaving. See ya ...

Looks good. Believe I have achieved my objectives in this post. 

Let's execute "make clean" to remove the previously created files

1
2
3
4
5
kali@securitynik:~/rootkits/HelloWorld$ make clean
make --directory=/lib/modules/5.5.0-kali2-amd64/build/    M=/home/kali/rootkits/HelloWorld clean
make[1]: Entering directory '/usr/src/linux-headers-5.5.0-kali2-amd64'
  CLEAN   /home/kali/rootkits/HelloWorld/Module.symvers
make[1]: Leaving directory '/usr/src/linux-headers-5.5.0-kali2-amd64'

Let's now confirm only our original files exists.
1
2
kali@securitynik:~/rootkits/HelloWorld$ ls
helloWorld.c  Makefile


1 comment:

  1. Can you recommend books and resources to sharpen my skills on systems programming and kernel programming with C?

    ReplyDelete