Totally Awesome FreeBSD Kernel Coding!

Ok, so we receive hundreds upon millions of thousands of requests from all you budding FreeBSD Kernel hackers out there asking "oh wise int13h chaps, how do I interface with my FreeBSD kernel from a userland application?". Gosh, we love these types of challenges. Informing the masses in a way that only the int13h massive can.

So, we want to write a kernel module and then talk to it, hmm. Ok, step 1.. load up Google and do a search... "Writing a FreeBSD Kernel Module". Looks like many others have already beaten us to this one. Probably because this is how I read about doing it a few months ago when the original article was posted on osnews.com but lets not let that get in the way of us actually writing some content for a change.

Let's just ignore what everyone else has done because lets face it, if it's been found on a Google search then more than likely its just been copy and pasted out of the systems documentation/example files (man syscall and /usr/share/examples/kld/syscall/module/syscall.c) with the functions renamed.

Great, now that we have our basis for an article let's go ahead and mess it up, int13h style!

First of all we need a name for our awesome kernel module. I think we have our answer for that : Awesome++!

-------------------------- Awesome KLD! --------------------------

We'll start with the Kernal Module side of things as without this we won't actually know if it's our shoddy userland code that is failing or not. Whip our your favourite text editor/ide and we'll start a new file: awesome.c

/***************************************************************
*  awesome.c                                       #######     *
*  Awesome++ int13h 2008                           #.....#     *
*                                                  #..@..#     *
*                                                  #.....#     *
*  Awesome Kernel FreeBSD Module                   #######     *
*                                                              *
***************************************************************/

Excellent, now that we've doodled about and made a header, we are at least 80% of the way to achiving our goal. Though if we want to do better than this we'll need to include some system headers as I'll be damned if I am going go as hardcore as talking to the system without it's help. I'm not a masochist.

So the headers that we have found from the man pages and examples are..

/*
############
#..Globals.#
############
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h> 

Groovy, that should keep the compiler happy for now. Let's move on to setting up what we want the module to do when we call it. Let's stick with our naming convention and name the function something obvious:

/*
##############
#..awesome().#
##############
*/

static int awesome (struct thread *td, void *arg) { 
	uprintf("Awesome! You have received some Awesome\n"); 
	return 0; 
} 

static struct sysent awesome_sysent = { 
	0,			/* sy_narg */
	awesome		/* sy_call */
}; 

/* Find an offset*/
static int offset = NO_SYSCALL;

Hey, whoa... hold up, whats all this, did we just turn two pages at once. Yup, welcome to the fast moving world of cut and paste. Please try to keep up. Of course if you have any c coding experience you'll know by now that the first bit of code here is our function, the awesome function! You'll notice that it passes in two arguments, both of which we aren't using in this article thankfully so I won't have to look up what they mean or are supposed to do.

Our function is shockingly simple, it prints a bit of text and returns, note that we are using the uprintf() function, this tells the kernel module to send the output to the process that called it instead of sending it to the dmesg/system log. Nothing fancy but hey, I'm not supposed to be writing your app for you, just showing you the way... via the example file!

So following our function is a struct, nice and simple, it lists our functions so that the userland knows what to call. Then we have an offset, which thankfully we won't need to work out ourselves, with the handy NO_SYSCALL macro the OS will give us the offset. Don't know what an offset is? well it's the place in the kernel where our syscall is stored.

Fantastic, so we are all done for the kernel side eh? not quite, we have to tell it what to do when its loaded. Nice and simple, we set up a cunning switch() statement to handle the loading nd unloading of our module.

/*
###########
#..load().#
###########
*/
static int load(struct module *module, int cmd, void *arg) {
	int error=0;
	switch (cmd) {
		case MOD_LOAD:
			uprintf("Awesome Kernel Module Loaded at offset %d \n",offset);
			break;
		case MOD_UNLOAD:
			uprintf("Awesome Kernel Module Unloaded (http://www.int13h.com)\n");
			break;
		default:
			error = EOPNOTSUPP;
			break;
	}
	return(error);
}

Ok, simple enough, more random arguments passed into a function. The only one we care about is the 2nd arg (int cmd), this is an enumerated int that tells us what the system is doing with our awesome kernel module.

Now the only thing left for us to do in the kernel module is to tell the rest of the system that it exists and how to talk to it. Simple enough...

/*
############
#..Install.#
############
*/
SYSCALL_MODULE(awesome, &offset, &awesome_sysent, load, NULL);

Handy macro here that sets it all up for us. You should be able to see how this ties the rest of our code up. No? hmm, ok.. well broken down we have the 1st argument which is the name of our module, in this case "awesome". Then we tell it where it is (the offset) and then we pass in our list of functions that we have in here. Next is the name of the function that will handle our module's loading and unloading stuffs and finally the last one we aren't using is for passing arguments into our loading function so ignore that and send NULL.

Awesome! We have our kernel module, so lets bang out a quick Makefile to round it off. Edit Makefile!

#/***************************************************************
#*  Makefile                                        #######     *
#*  Awesome int13h 2008                             #.....#     *
#*                                                  #..@..#     *
#*  Build Awesome Kernel Module                     #.....#     *
#*                                                  #######     *
#*                                                              *
#***************************************************************/

# Name of Module
KMOD    =  awesome
# Source File
SRCS    =  awesome.c
# Output Name
KO = ${KMOD}.ko 

# System Header lovin'
.include <bsd.kmod.mk>

Simple enough? If I have to explain this any more than you shouldn't be here =P

Now, once we have that all saved and spangly we should have two files :

Makefile
awesome.c

Run our make file (using bsd's make, not GNUmake).

> make

Warning: Object directory not changed from original /myawesomeprojects/awesome

cc -O2 -fno-strict-aliasing -pipe  -D_KERNEL -DKLD_MODULE -std=c99 -nostdinc   -I. -I@ -I@/contrib/altq -finline-limit=8000 --param inline-unit-growth=100 --param large-function-growth=1000 -fno-common  -mno-align-long-strings -mpreferred-stack-boundary=2  -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 -ffreestanding -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions -c awesome.c

ld  -d -warn-common -r -d -o awesome.kld awesome.o

:> export_syms

awk -f /sys/conf/kmod_syms.awk awesome.kld  export_syms | xargs -J% objcopy % awesome.kld

ld -Bshareable  -d -warn-common -o awesome.ko awesome.kld

objcopy --strip-debug awesome.ko

Whoa, whats going on here, it's alright, don't panic. This is normal and if you haven't done anything stupid we should have no errors and a nice awesome.ko file in our directory. Now lets load it..

ok, hold up, as awesome a 'cut and paster' as I am, I really can't take any credit for hosing your system if this all goes tits up. Loading Kernel modules requires you to run as a superuser, which generally means you can trash the system with your typing skills. Not that there is any malicious code here, or really anything risky being done but still we can't take responsibility. So by reading this you are taking full responsibility for breaking your system. Not us... again... your fault if it breaks, not ours. I'm glad we cleared that up..

Let's load our KLD!

> sudo kldload ./awesome.ko

Awesome Kernel Module Loaded at offset 210

If you can see this than we're still alive and all is good. Let's quickly check that it's there..

> kldstat

Id Refs Address    Size     Name

[...]

11    1 0xc5c4a000 2000     awesome.ko

Just to make sure the rest of it good, let's unload it too:

> sudo kldunload awesome

Awesome Kernel Module Unloaded (http://www.int13h.com)

All looks good, if you haven't got all that go back and try again.

-------------------------- Awesome App! --------------------------

Wow, so we've made it this far. I suppose now would be a good time to see if we can talk to it using a non-super user application. Let's get our text editors out again... awesomeapp.c

/***************************************************************
*  awesome.c                                       #######     *
*  Awesome++ int13h 2008                           #.....#     *
*                                                  #..@..#     *
*                                                  #.....#     *
*  Awesome App                                     #######     *
*                                                              *
***************************************************************/

/*
############
#..Globals.#
############
*/

#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h> 

/*
###########
#..main().#
###########
*/

int main(int argc, char **argv) { 
	int err;
	struct module_stat stat;
	stat.version = sizeof(stat); 

	// Get kernel object info
	if((err = modstat(modfind("sys/awesome"), &stat)) != 0){
		printf("Exiting : Module not found.\n");
		return -1;
	}

	printf("Found %s [id %d at slot %d]\n",
		stat.name,stat.id,stat.data.intval);

	syscall(stat.data.intval);
	printf("Awesome syscall fired\n");

	return 0;
}

Is that it? yes it is, nothing fancy really is it. So, we have our usual #includes and the standard main() function to get things rolling. Now if you strip out the basic error checking stuff we have a few new functions to look at :modstat(), modfind() and syscall()

Simple enough, modstat() will get some information about our kernel module into the module_stat struct so that we can find our a bit about it. modfind() allows us to simply pass in the name of the kernel module we want to use and syscall() allows us to call a function from it. When we look at stat.data.intval, we are given the id of our function in the module, firing that into syscall() will run that function for us. Joy!

Exciting! let's compile it!

gcc -o awesomeapp awesomeapp.c
No errors? rock on. Let's run it.
> ./awesomeapp 
Exiting : Module not found.

Uh oh, what's going on here? it's exited out, well thankfully our errorhandling skills have saved us some drive space for the core dump file. Looks like we ran the app without having the kernel module loaded. Duh. Fire up that kernel module again and re-run it:

> ./awesomeapp
Found awesome [id 267 at slot 210]
Awesome! You have received some Awesome
Awesome syscall fired

There we have it, the 2nd line you see is coming from our kernel loaded module. Phew, we made it all this way without PANIC'ing our system or nuking our root partition, pat yourself on the back and go get yourself a well deserved Coffee.

Your regular int13h broadcasting will resume shortly. Thanks for watching, we've been great.

2 Comments

Note: This is broken in FreeBSD 8.1.

modules loaded with SYSCALL_MODULE need to be prefixed with "sys/" in the modfind function

ie...

modstat(modfind("sys/awesome"), &stat);

By Rainey |

Thanks for the heads up, not only was that broke but so was the article when we moved over to the new layout =P

By Nightmare |


Heavy Engine Console
--------------------------------------------------------------------------------
Loading Page... /1061-Totally-Awesome-FreeBSD-Kernel-Coding
Console: