Hooking linux kernel FIFOs
catenjoyerHave you ever wanted to hook FIFOs before? If you are familiar to LKM rootkits, you probably have seen communications from usermode to kernelmode done using a FIFO created by proc_create. This function receives a structure with callback pointers that are called whenever a specified action is made, such as reading and writing. Today, we are going to swap these pointers to our own functions, so we can reutilize existing FIFOs for communication or even spoof data.
First of all, lets take a look at how the kernel implements proc_create.
Kernel implementation

proc_create receives a "proc_ops" structure containing all callbacks and returns a pointer to a "proc_dir_entry" structure

Our passed proc_ops is set in the newly created proc_dir_entry, we can asume that structure is where we will have to swap pointers
And for proc_dir_entry, it goes like this:

For kernels newer than 5.6.0, proc_ops structure is actually a file_operations.


That is it. Now, we have to find a way of getting a existing FIFO proc_dir_entry, which turns out to be very straightforward
Getting the proc_dir_entry
Almost every internal function that deals with proc FIFO files are unexported. Even though we could get their addresses and call them manually with kallsyms, i didn't like the idea at all.
You have probably heard the phrase "Everything is a file" someday. Although that is not completely true, this is the case for kernel FIFOS (if that wasn't obvious already). In unix filesystems, there is a concept called inodes, which are basically structures with information regarding a file or directory, like ownership, size, and file type.
The POSIX standard mandates file-system behavior that is strongly influenced by traditional UNIX file systems. An inode is denoted by the phrase "file serial number", defined as a per-file system unique identifier for a file. That file serial number, together with the device ID of the device containing the file, uniquely identify the file within the whole system.
Every single file and folder, be it fake or not, has an inode associated with it. This includes FIFOs created by proc_create. Their inode structure are contained in a structure type called proc_inode where the proc_dir_entry pointer we need sits at.

vfs_inode is the inode structure for our target FIFO
inodes are correlated with a path name using a dentry structure. We can retrieve this structure from a file path using the function kern_path, which supplies a structure "path" containing the dentry



We have everything. Time to code.
The code
For reference, __proc_dir_entry is my own redefined proc_dir_entry, as its definition has been isolated for internal use. The same goes for __proc_inode.
hook.h:

hook.c:

The kernel has unexported functions for retrieving proc_dir_entry from a inode, like so:

PDE is the function that gets the proc_dir_entry
proceeding with hook.c:

Now for the testing, we can hook any kernel FIFO of our choice. I am gonna hook /proc/kallsyms write callback as a POC.
That is what happens if you try to write to /proc/kallsyms in a normal scenario:

Unsurprisingly, write throws an EIO error, as the write callback for /proc/kallsyms is not implemented (a NULL callback pointer). We can abuse this behavior and return -EIO whenever any detection systems checks for the return value of writing to /proc/kallsyms. For that, i will check for the magic number "\x13\x37rk" inside the hook.


Now we compile and run.

Works flawlessly.
Hopefully this is helpful for somebody. With this, you can spoof data and save time from hooking getdents to hide your proc_create communication FIFO.
Update: the full source code has been uploaded to github. Check it out!