FAQ

What is KPlugs?

KPlugs is a linux kernel module that gives an interface for dynamically executing scripts inside the Linux kernel. The user can create KPlugs functions and execute them directly from user space. The functions then run on a virtual machine designed to be simple, but still give the user all he needs to safely implement any functionality. KPlugs comes with a Python library that compiles a subset of the Python language to the KPlugs bytecode, complete with bindings to the kernel module interface, allowing the user to simply load, execute and unload functions directly from a Python script.

License

KPlugs is licensed under GPLv3.

What architectures are supported by KPlugs?

KPlugs was written for x86, both 32 and 64 bits. Since version 1.1 KPlugs also supports ARM machines. KPlugs can be changed to support any other architecture by adjusting the assembly file "calling_wrapper.s". More information on that inside the file.

What Linux versions are supported by KPlugs?

KPlugs was only tested on Linux kernel version 4.14.15. However, because it uses only standard kernel functions, it should work on any recent kernel.

How do I use KPlugs?

Download KPlugs from the download section.



KPlugs is a kernel module and as such requires your kernel's sources to compile. The KPlugs Makefile assumes you have the sources of your Linux kernel in the standard location (/lib/modules/`uname -r`/). If you put the sources elsewhere, edit the Makefile and point it to the right location.

You should then be able to build the module using make.



If everything worked, you should have two modules inside the Release and Debug directories. The debug version just adds some debug prints to help you debug the kernel module if you wish.



Finally, loading the kernel module is done by running "insmod ./Release/kplugs.ko" (or ./Debug/kplugs.ko if you want to debug the module).

How do I use the KPlugs Python library?

The Python library is (surprisingly) located inside the python directory. It contains three files:

__init__.py - A standard Python init file (just loads kplugs.py).

- A standard Python init file (just loads kplugs.py). core.py - Implements the Python subset compiler and the Plug class. The compiler uses the Python ast library.

- Implements the Python subset compiler and the Plug class. The compiler uses the Python ast library. kplugs.py - Uses core.py to bring the user an easy interface to KPlugs.

- Uses core.py to bring the user an easy interface to KPlugs. world.py - Represents a KPlugs machine. can be this machine or a remote machine (Since KPlugs 1.1). To install the Python bindings, simply copy the python directory to your Python installation's Lib directory, and change the name to kplugs.



Now, when writing your Python script you should be able to "import kplugs" and use the library as shown in the





We invite you to look inside kplugs.py. The file is pretty simple, and will help you understand how KPlugs works. To install the Python bindings, simply copy the python directory to your Python installation's Lib directory, and change the name to kplugs.Now, when writing your Python script you should be able to "import kplugs" and use the library as shown in the examples section.You should always make sure that the context was closed correctly.We invite you to look inside kplugs.py. The file is pretty simple, and will help you understand how KPlugs works.

Why did you create your own bytecode instead of porting the Python engine to kernel mode?

At first, we considered porting the entire Python engine. We eventually decided against it because of two main reasons:

One of the things we wanted from KPlugs is the ability to call kernel functions, and to be called from a kernel function. The problem is that kernel functions works with words and pointers, and the Python engine works with Python objects. We would have to create a wrapper for every function to convert inputs to Python objects (and vice versa). The bytecode allows us to work directly with words and pointers, making the integration more natural.

We imagined KPlugs would be used when a certain kernel-only functionality is wanted. This doesn't mean that all the logic of the script has to run in kernel-mode. This makes porting the entire engine a potentially huge waste of time - and without good reason. For these reasons we chose to present a flexible interface, and let you keep most of the code in user-space where it belongs. An added benefit is that users who want to use other scripting languate to use KPlugs are free to do so - they only need to write a compiler. For these reasons we chose to present ainterface, and let you keep most of the code in user-space where it belongs. An added benefit is that users who want to use other scripting languate to use KPlugs are free to do so - they only need to write a compiler.

How do I create functions with KPlugs?

The KPlugs module creates the device file "/dev/kplugs" to communicate with the user. Every file descriptor a user holds to that device is called a plug . Each plug receives a context. A user can load functions to a plug's private context, so that only he could use those functions (unless he gives another process a pointer to his function), or he could load them to a special context - The global context. When a user tries to execute a function using the KPlugs interface, KPlugs tries to find it in the plug's context first, and then it tries to find it in the global context.

Functions loaded to a local context are automatically unloaded when its plug is unplugged - when the user closes the file descriptor, but functions loaded to the global context are unloaded only when the user issues the unload command (or if the KPlugs kernel module is removed using rmmod).



KPlugs doesn't allow loading two functions with the same name to the same context. You may create an anonymous function - a function without a name. An anonymous function uses the function's address as an identifier instead of the function's name.

What are the KPlugs bytecode's variable types?

KPlugs has four variable types: word , pointer , buffer and array :

word - An integer the size of the processor word.

- An integer the size of the processor word. pointer - Like a word, but can be used to read and write to memory.

- Like a word, but can be used to read and write to memory. buffer - A buffer of bytes.

- A buffer of bytes. array - An array of words. When the KPlugs VM accesses memory, it performs bounds checking in order to avoid crashes. Pointers are always considered unsafe and are checked before every dereference. Buffers and arrays are slightly more complicated because they can come from user space, but they can't move. for that reason, when a user's buffer or array is used, it is mapped to kernel space so it can be used safely during the function's execution.



If you want to call a kernel function with a buffer (or an array ) from user-space as an argument, you can just simply call it from your compiled KPlugs function (i.e., from the KPlugs bytecode), and the VM will map the buffer to kernel-space before calling the function. Naturally, It will not work with pointers because there is no way for KPlugs to know the size of the buffer the pointer points to. When the KPlugs VM accesses memory, it performs bounds checking in order to avoid crashes.are always considered unsafe and are checked before every dereference.andare slightly more complicated because they can come from user space, but they can't move. for that reason, when a user'soris used, it is mapped to kernel space so it can be used safely during the function's execution.If you want to call a kernel function with a(or an) from user-space as an argument, you can just simply call it from your compiled KPlugs function (i.e., from the KPlugs bytecode), and the VM will map theto kernel-space before calling the function. Naturally, It will not work withbecause there is no way for KPlugs to know the size of the buffer thepoints to.

How does KPlugs resolve function names at run-time?

Most of the functions inside the Linux kernel use the same calling convention. This is why you can compile a kernel module and let other modules call your functions. The problem is that you have to know the function prototype at compile-time, which is obviously problematic when you want to load scripts inside the kernel. Other solutions such as SystemTap tackle this problem by compiling a module for every script you want to load.



For KPlugs we had to find another solution because we wanted the symbols to be resolved at run-time. Most of the kernel functions use either fastcall or cdecl as their calling convention (the vast majority uses fastcall). Lucky for us, these two conventions don't require knowing beforehand how many arguments were passed to us, i.e., two identical functions with the same calling convention and a different number of arguments are compiled exactly the same.



We exploit that fact when calling kernel functions from within bytecode. We create functions pointers for every calling convention, and use the right function pointer depending on the chosen calling convention for the specific function. The reason we need more than just the standard calling convention is because sometimes the compiler chooses another calling convention for functions with a variable number of arguments (like printk). For now KPlugs support the standard calling convention and the calling convention of variable-length argument functions. Knowing the function's calling convention, the KPlugs VM calls find_symbol to find the requested function's address, and can then set up the argument and call it.



Also, when you create a KPlugs function you can choose the calling convention for it. Your choice doesn't have any effect when you call your function through the KPlugs interface, but it does when you call your function directly by its address. In that case a proper wrapper function (depending on which calling convention you chose) will be executed. The wrapper will get the caller's arguments (It can't know the exact number so it will just pop the maximum number of arguments that a function takes), and start the VM.

What if I have more questions?