Creating a proc entry in kernels version < 4.7 for reading and writing

Posted by in Linux, on April 9, 2017

The linux kernel is constantly evolving and I was reminded of that recently when I was looking into writing a rootkit. I found myself searching but did not find much in terms of any useful examples for the more recent kernel versions. Then I got kind of side tracked or maybe overwhelmed and thought maybe, just maybe, I need to take a few baby steps before I start this again.

I decided that I would create a proc entry that would allow for reading and writing. Something fairly basic but insightful enough for understanding some of the kernel data structures.

Since the examples that I found were mostly outdated, I ended up using the linux 4.7.2 kernel source code as a reference point.

First thing I needed to do was to create a directory in proc and a file that would allow me to read and write to it.

I want to write data to my file from the command line.
$ echo “foobar” > /proc/test/hello

And I want to read that data from the command line.
$ cat /proc/test/hello
foobar

Using the /proc interface I can create the root directory (“test”) and the file (“hello”).

To create a /proc directory which is equivalent to mkdir /proc/test, I used the following:
proc_mkdir(“test”,NULL);

This will create a directory called test in the /proc directory.

Next, I create a file to read and write to under the test directory and call it “hello”.
proc_create(“test/hello”,S_IRUGO | S_IWUGO,NULL,&test_hello_proc_fops);

The proc_create function is a wrapper around proc_create_data which was introduced to resolve some issues that I am not making any attempt to explain since it is outside the scope of what I’m doing. But I mention this on behalf of the astute reader that may question why I am using proc_create instead of the more recent proc_create_data.

I have now created a proc entry called /test/hello with read and write permissions. The access permissions identified by S_IRUGO and S_IWUGO set the “owner, group and others” fields to read and write.

Next up is to create the file_operations structure to map the read and write proc entry functions. The &test_hello_proc_fops is a function pointer to the following file_operations structure:

static const struct file_operations test_hello_proc_fops = {
.open = hello_proc_open,
.read = seq_read,
.write = hello_proc_write,
};

The file operations are rather intuitive. In order to do anything with the proc entry, you will need to open the file before you can read or write to it. This pretty much goes without saying in linux since the “everything is a file” philosophy applies.

To help manage the /proc filesystem there is a seq_file API that provides some basic helper functionality for file_operations. I have utilized this API for handling the functions within the file_operations structure.

The hello_proc_open is a wrapper around the single_open function. This function sets up iterating through the virtual file. The magic behind the seq_file API is well documented online. The single_open() gets a parameter that is the “show” function for the data that is to be written to /proc. The “show” function does everything that is needed to write the data, it’s basically an “all in one” type of function call.

static int hello_proc_open(struct inode *inode_ptr, struct file *fp)
{
return single_open(fp,test_hello_proc_show, PDE_DATA(inode_ptr));
}

The test_hello_proc_show parameter interprets the iterator passed to it and generates output strings to be displayed when a user reads the corresponding /proc file. This method makes use of the helper function seq_printf() which works similar to printk to write output to the /proc file (“hello”).

static int test_hello_proc_show(struct seq_file *m, void *v)
{
seq_printf(m,”%s\n”,proc_data);
}

That pretty much sums up creating a new proc entry. I have uploaded the code to github and you can find it here.

I have left comments in the source code for clarity.

Leave a Reply