In the previous post, I explained a challenge I faced a few months ago. We saw that I tried to handle the challenge with a hybrid solution(I named it hybrid because it uses both user-space and kernel-space code). The solution used a regular file as a shared object between user-space and kernel-space code. Honestly, my first solution did not appeal to me. Now, I want to improve the idea. Let’s see.
Shared Objects Solution
I really hate files when I write kernel-related codes, especially when I want to do this stealthily. After much floundering, I went for a second idea. How about POSIX shared memory objects? It seemed so Amazing to me. I was thinking about storing an object in a temporary filesystem(tmpfs), and then the kernel module can open and read the shared object that resides in the tmpfs. So, the idea can be explained as follows:
The user-space program creates a shared object in tmpfs(in /dev/shm), maps the shared object to the user-space process aaddress space, and writes the address to. Then, the kernel module will open the shared object and read it.
Note that we don’t create any regular file but a shared object, and the shared object is stored in the virtual file system.
What happens in the user space?
I’m going to explain the user-space tasks by explaining the user-space code.
First of all, we know that we want to store an address from the unsigned long type to the shared object(my_shm):
#define SHM_NAME "/my_shm"
#define SHM_SIZE sizeof(unsigned long)
CThen, we need to create a shared object(SHM_NAME) with the appropriate permissions(O_CREAT,O_RDWR, 0666) and store it in a file descriptor(fd):
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
CAnd setting its size:
ftruncate(fd, SHM_SIZE);
CNow, we want to write into a shared object and read from it. So, we should map it to the process address space:
shmaddr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
CFinally, assign the extracted address from the /proc/kallsyms to the shared object:
* shmaddr = address;
CSo far we stored the desired symbol address to our shared object and it’s ready to read from by kernel code. The user-space code is here.
What happens in the kernel space?
So far, we store the desired symbol address to our shared object, and it’s ready to read by kernel code. First, we should open the shared object and store it in a file:
file = filp_open("/dev/shm"SHM_NAME, O_RDONLY, 0);
CThen we should read the address from the shared object:
kernel_read(file, shmaddr, SHM_SIZE, & pos);
CAnd that’s it. Now this code can be used as a helper for more complex kernel modules. The kernel module code is here.
Test It!
To make the test simple, I implement the kernel code in the form of a kernel module. Like before, we need to compile the source codes.
Compile the user-space program:
# gcc -o userspace_shm userspace_shm.c
BashMake the kernel code:
# make
BashLoad the kernel module:
# insmod lkm_shm.ko
BashNow, if you look at the kernel logs(dmesg), you will find something like this:
Received address from user space: ffffffffbb99f2e0
BashNote that, you can run the user-space program automatically with the call_usermodehelper() as I said here. But this is up to you 😀
What will happen next?
Well, The noteworthy point is that we removed the regular file and handle the challenge with shared object in virtual file system in memory. But yet we did some file operations. So, in future posts I will minimize file-related operations and finally, anything that causes the disk to be involved in the address-saving routine is removed.