Kernel space hooking allows user space applications to access system specific information without the necessary level of privilege. This can be extremely useful for security, parental control, system monitoring, and other applications.
In fact, there are various libraries and frameworks for Linux that allow for kernel space hooking. However, as with many tools, they have their problems. In this article, we'll look at some restrictions that you might face when trying to find kernel symbols in the system call table in order to identify specific functions that you need to hook, as well as how Linux kernel symbol search process can be improved with the use of kernel probes (kprobes).
The system call table facilitates the interaction between user space code and kernel space code in Linux. Various open source projects aimed at kernel hooking provide different implementations for getting identifiers of specific functions from the system call (or syscall) table.
Some of the more traditional approaches to getting syscall table identifiers include:
- https://gist.github.com/ilammy/f39b7366f9dd2f15479d#file-locate_sct-c :45
- https://github.com/mncoppola/suterusu/blob/master/main.c :158
Both of the solutions linked to above are perfectly valid, but they also both have several restrictions when it comes to detecting kernel symbols:
- The syscall table has limited scope from the general heap of kernel functions. Usually, this is enough, but generalization of the approach is always welcome
- Both of these solutions contain platform-dependent implementations of the syscall table’s pointer lookup
- Some internal functions contain the version in their own name, for example do_execveat_common.isra.XX, where XX is the – kernel version dependent number
These cases cannot be processed via the canonical pointer lookup, but the kernel is flexible and always has at least one alternative way to do things, or even a backdoor.
An alternative approach that allows you to overcome these restrictions is hidden in the kprobes API, which is also part of the Linux API. The kprobes API can be found in “include/linux/kallsyms.h” and is available only if the kernel is built with the CONFIG_KALLSYMS flag, which is turned on by default.
In terms of hooking, the most interesting functions of kprobes internals are:
- kallsyms_lookup_name_ptr – This function is straightforward and can look through all existing symbols to get a pointer to the symbol with exactly the same name as its parameter. This call can be used instead of the syscall table
- kallsyms_on_each_symbol – This is a more powerful and complex function that can help a developer perform a wide range of tasks. The signature of this function is shown in Figure 1. where:
- data is the name (or part of the name) of the symbol you're looking for;
- fn is a callback that can implement a variety of algorithms for incoming symbol analysis.
The function above provides a platform-independent solution that isn’t bound by the scope of the available syscall function. Figure 2 shows a kprobes example of a kernel version independent implementation of a lookup for do_execveat_common.isra.XX.
The approach in Figure 2 lets you search for a symbol using only part of its name. This example is simple one, but if we replace strstr with a regular expression from the kernel’s API, the callback becomes much more versatile.
The kprobes API covered in this article is not a unique solution for getting kernel symbols, but it’s a very reliable and elegant approach that we at Apriorit use extensively in client projects.
We have a slew of experienced Linux developers on board, so if you ever need a dedicated team to work on a Linux project, don’t hesitate to send us your request for proposal.