Linux hacking part 6: Linux kernel module with params. Simple C example
﷽
Hello, cybersecurity enthusiasts and white hackers!
In the previous post, we discussed the basics of kernel hacking and the process of writing and loading a simple kernel module. Now, let’s take things further by adding functionality to our module using parameters passed from the command line.
One of the coolest features of kernel modules is the ability to interact with them dynamically. Instead of hardcoding values, we can expose parameters that can be modified at runtime, giving us more flexibility and control over the module’s behavior.
In this post, I’m going to show you how to pass string and integer parameters to your kernel module, and based on those parameters, you can customize the behavior of your module.
practical example
In Linux kernel modules, you can expose parameters using the module_param
macro. This macro allows us to declare a parameter that will be passed to the module when it is loaded.
First of all, look at the previous module code:
/*
* hack.c
* introduction to linux kernel hacking
* author @cocomelonc
* https://cocomelonc.github.io/linux/2025/06/23/kernel-hacking-6.html
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cocomelonc");
MODULE_DESCRIPTION("kernel-test-01");
MODULE_VERSION("0.001");
static int __init hack_init(void) {
printk(KERN_INFO "Meow-meow!\n");
return 0;
}
static void __exit hack_exit(void) {
printk(KERN_INFO "Meow-bow!\n");
}
module_init(hack_init);
module_exit(hack_exit);
Recheck again, run the following command to compile the module:
make
and run:
sudo insmod hack.ko
sudo rmmod hack.ko
As you can, as in the previous post, everything is worked perfectly! (the only difference is ubuntu version: 20.04
vs 18.04
)
Ok, let’s go to add some arguments to this module.
Let’s break down the code and see how we can implement it. Let’s say we define a string parameter called pet
with a default value of "cat"
:
static char *pet = "cat"; // default value is "cat"
module_param(pet, charp, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(pet, "Pet name: can be cat, mice, bird, dog, or sheep");
module_param
macro declares pet
as a parameter that can be passed to the module when it’s loaded:
module_param(pet, charp, S_IRUSR | S_IWUSR)
Here charp
specifies the type of the parameter (a string), S_IRUSR | S_IWUSR
means the parameter is readable and writable by the user (the module’s owner).
Then, just update kernel module initialization function (hack_init
) logic:
static int __init hack_init(void) {
if (strcmp(pet, "cat") == 0) {
printk(KERN_INFO "Meow-meow!\n");
} else if (strcmp(pet, "mice") == 0) {
printk(KERN_INFO "Squeak-squeak!\n");
} else if (strcmp(pet, "bird") == 0) {
printk(KERN_INFO "Twit-twit!\n");
} else if (strcmp(pet, "dog") == 0) {
printk(KERN_INFO "Woof-woof!\n");
} else if (strcmp(pet, "sheep") == 0) {
printk(KERN_INFO "Baa-baa!\n");
} else {
printk(KERN_INFO "unknown pet: %s\n", pet);
}
return 0;
}
The logic is pretty simple: based on the value of pet
, a different message is printed using printk
.
When the module is unloaded, it logs the message meeeeeeeeeeeeeeeooooooow!
using printk
:
static void __exit hack_exit(void) {
printk(KERN_INFO "meeeeeeeeeeeeeeeooooooow!\n");
}
So, final source code is looks like this (hack2.c
):
/*
* hack.c
* kernel hacking: module params
* author @cocomelonc
* https://cocomelonc.github.io/linux/2025/06/23/kernel-hacking-6.html
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cocomelonc");
MODULE_DESCRIPTION("kernel-test-02");
MODULE_VERSION("0.001");
static char *pet = "cat"; // default value is "cat"
module_param(pet, charp, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(pet, "Pet name: can be cat, mice, bird, dog, or sheep");
static int __init hack_init(void) {
if (strcmp(pet, "cat") == 0) {
printk(KERN_INFO "Meow-meow!\n");
} else if (strcmp(pet, "mice") == 0) {
printk(KERN_INFO "Squeak-squeak!\n");
} else if (strcmp(pet, "bird") == 0) {
printk(KERN_INFO "Twit-twit!\n");
} else if (strcmp(pet, "dog") == 0) {
printk(KERN_INFO "Woof-woof!\n");
} else if (strcmp(pet, "sheep") == 0) {
printk(KERN_INFO "Baa-baa!\n");
} else {
printk(KERN_INFO "unknown pet: %s\n", pet);
}
return 0;
}
static void __exit hack_exit(void) {
printk(KERN_INFO "meeeeeeeeeeeeeeeooooooow!\n");
}
module_init(hack_init);
module_exit(hack_exit);
demo
Let’s go to see everything in action. Add one line to our first version of Makefile:
obj-m += hack2.o
So, updated file is like in the following:
obj-m += hack.o
obj-m += hack2.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
Ok, then copy to the linux machine and make. When you load the module, you can pass parameters to it using the insmod
command:
make
sudo insmod hack2.ko pet=sheep
Finally, unload the module:
sudo rmmod hack2
Recheck with different arguments:
sudo insmod hack2.ko pet=cat
sudo rmmod hack2
sudo insmod hack2.ko pet=bird
sudo rmmod hack2
sudo insmod hack2.ko pet=dog
sudo rmmod hack2
practical example 2
Let’s add new integer argument. The second parameter is count
, which is an integer specifying how many times to print the message. The default value is 1
.
In this case declaration of module params looks like this:
static char *pet = "cat"; // default value is "cat"
static int count = 1; // default is 1
module_param(pet, charp, S_IRUSR | S_IWUSR);
module_param(count, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(pet, "Pet name: can be cat, mice, bird, dog, or sheep");
MODULE_PARM_DESC(count, "count");
and kernel module initialization function (hack_init
) looks like the following:
static int __init hack_init(void) {
int i;
for (i = 0; i < count; i++) {
// output the pet sound depending on the pet parameter
if (strcmp(pet, "cat") == 0) {
printk(KERN_INFO "Meow!\n");
} else if (strcmp(pet, "mice") == 0) {
printk(KERN_INFO "Squeak!\n");
} else if (strcmp(pet, "bird") == 0) {
printk(KERN_INFO "Twit-twit!\n");
} else if (strcmp(pet, "dog") == 0) {
printk(KERN_INFO "Woof!\n");
} else if (strcmp(pet, "sheep") == 0) {
printk(KERN_INFO "Baa baa!\n");
} else {
printk(KERN_INFO "unknown pet: %s\n", pet);
}
}
return 0;
}
The loop prints the pet’s sound count
times (which is also passed by the user when the module is loaded).
So, the full source code is looks like this (hack3.c
):
/*
* hack.c
* kernel hacking: module params 2
* author @cocomelonc
* https://cocomelonc.github.io/linux/2025/06/23/kernel-hacking-6.html
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cocomelonc");
MODULE_DESCRIPTION("kernel-test-03");
MODULE_VERSION("0.001");
static char *pet = "cat"; // default value is "cat"
static int count = 1; // default is 1
module_param(pet, charp, S_IRUSR | S_IWUSR);
module_param(count, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(pet, "Pet name: can be cat, mice, bird, dog, or sheep");
MODULE_PARM_DESC(count, "count");
static int __init hack_init(void) {
int i;
for (i = 0; i < count; i++) {
// output the pet sound depending on the pet parameter
if (strcmp(pet, "cat") == 0) {
printk(KERN_INFO "Meow!\n");
} else if (strcmp(pet, "mice") == 0) {
printk(KERN_INFO "Squeak!\n");
} else if (strcmp(pet, "bird") == 0) {
printk(KERN_INFO "Twit-twit!\n");
} else if (strcmp(pet, "dog") == 0) {
printk(KERN_INFO "Woof!\n");
} else if (strcmp(pet, "sheep") == 0) {
printk(KERN_INFO "Baa baa!\n");
} else {
printk(KERN_INFO "unknown pet: %s\n", pet);
}
}
return 0;
}
static void __exit hack_exit(void) {
printk(KERN_INFO "meeeeeeeeeeeeeeeooooooow!\n");
}
module_init(hack_init);
module_exit(hack_exit);
demo 2
In this case, just add new param:
sudo insmod hack3.ko pet=sheep count=7
sudo rmmod hack3.ko
sudo insmod hack3.ko pet=cat count=3
sudo rmmod hack3.ko
sudo insmod hack3.ko pet=dog count=4
sudo rmmod hack3.ko
As usual, we use dmesg
to view the kernel messages, which will include the output from printk
.
This kernel module demonstrates how to pass parameters at runtime, including strings and integers, and how to use those parameters to influence the behavior of the module. Using module_param
, we can create flexible modules that can be customized when they are loaded into the kernel, making them far more powerful than static modules.
You can also modify the values of the parameters without unloading and reloading the module. Simply update the sysfs
interface:
cat /sys/module/hack/parameters/pet
cat /sys/module/hack/parameters/count
As you explore more complex modules, you’ll find that parameters can be used for many things, such as controlling logging levels, setting configuration options, or even tweaking performance parameters without needing to recompile or reload the entire module.
In the future posts of this series I will show more complex, more malicious behavior in our kernel modules. Stay tuned!
I hope this post with practical example is useful for malware researchers, linux programmers and everyone who interested on linux kernel programming techniques.
Linux malware development 1: intro to kernel hacking. Simple C example
Linux malware development 2: find process ID by name. Simple C example
Linux hacking part 5: building a Linux keylogger. Simple C example
source code in github
This is a practical case for educational purposes only.
Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine