Creating a Simple Mac Kernel Extension… by hand

Basically, a KEXT is a bundle on Mac OS X (it’s also a “package”), which means it is a directory structure with some predefined form and an Info.plist file. Our sample KEXT will be named “MyFirstKernelExtension.kext”, and it will be in a directory structure that looks like this:

$ find MyFirstKernelExtension.kext
MyFirstKernelExtension.kext
MyFirstKernelExtension.kext/Contents
MyFirstKernelExtension.kext/Contents/Info.plist
MyFirstKernelExtension.kext/Contents/MacOS
MyFirstKernelExtension.kext/Contents/MacOS/MyFirstKernelExtension

To start off, we need to make the basic directory structure.
$ cd
$ mkdir -p MyFirstKernelExtension.kext/Contents/MacOS
$ cd MyFirstKernelExtension.kext/Contents/MacOS

Now we can start writing our code. Our code will contain two main routines: MyFirstKernelExtensionStart() and MyFirstKernelExtensionStop(), which are called when the KEXT is loaded and unloaded respectively. It will also contain some required bookkeeping code that’s needed in order to make our compiled binary proper. Our start and stop routines look like:
// File: MyFirstKernelExtension.c
#include <libkern/libkern.h>
#include <mach/mach_types.h>

kern_return_t MyFirstKernelExtensionStart(kmod_info_t *ki, void *d) {
printf(“Hello, World!\n”);
return KERN_SUCCESS;
}

kern_return_t MyFirstKernelExtensionStop(kmod_info_t *ki, void *d) {
printf(“Goodbye, World!\n”);
return KERN_SUCCESS;
}

… more to come in a minute

After these two methods (in the same MyFirstKernelExtension.c file) we need to put the required bookkeeping stuff.
extern kern_return_t _start(kmod_info_t *ki, void *data);
extern kern_return_t _stop(kmod_info_t *ki, void *data);

KMOD_EXPLICIT_DECL(net.macjunkie.kext.MyFirstKernelExtension, “1.0.0d1”, _start, _stop)
__private_extern__ kmod_start_func_t *_realmain = MyFirstKernelExtensionStart;
__private_extern__ kmod_stop_func_t *_antimain = MyFirstKernelExtensionStop;
__private_extern__ int _kext_apple_cc = __APPLE_CC__;

This stuff basically declares some needed structures, and it also sets up our routines (MyFirstKernelExtensionStart() and MyFirstKernelExtensionStop()) so that they’re called on load and unload, by assigning them to the _realmain and _antimain symbols respectively.

OK, now comes the tricky part: the compile. KEXTs are compiled statically, they can only use certain headers that are available in the kernel, and they can’t link with the standard C library. These requirements basically translate into a gcc command like the following:

$ gcc -static MyFirstKernelExtension.c -o MyFirstKernelExtension -fno-builtin -nostdlib -lkmod -r -mlong-branch -I/System/Library/Frameworks/Kernel.framework/Headers -Wall

If the planets are properly aligned, you won’t get any errors or warnings, and you’ll end up with a Mach-O object file in the current directory named MyFirstKernelExtension. This is your actual compiled KEXT. (At this point, it’s fun to inspect this file using otool. For example, otool -hV MyFirstKernelExtension, and otool -l MyFirstKernelExtension. Read the man page for otool(1) for more details here.)

Now, the last thing we need to do (before we actually load this thing up) is to give our KEXT an Info.plist. The easiest way to do this is to copy another KEXT’s Info.plist file, and change the names of a few things. For this example, I’m going to copy /System/Library/Extensions/webdav_fs.kext/Contents/Info.plist.
$ cd ..
$ pwd
/Users/janakiram/MyFirstKernelExtension.kext/Contents
$ cp /System/Library/Extensions/webdav_fs.kext/Contents/Info.plist .

Now, you’ll need to edit the file and change the value of the “CFBundleExecutable” key to MyFirstKernelExtension, and the value of “CFBundleIdentifier” to net.macjunkie.kext.MyFirstKernelExtension (or whatever you set that value to in your MyFirstKernelExtension.c file).

Okay, it’s show time. To load any KEXT, all files in the KEXT must be owned by root and be in group wheel. The files must also have certain permissions in order to load. Here’s the steps to load the KEXT.

$ cd /tmp
$ sudo -s
# cp -rp ~/MyFirstKernelExtension.kext .
# chown -R root:wheel MyFirstKernelExtension.kext
# chmod -R 0644 MyFirstKernelExtension.kext
# kextload -v MyFirstKernelExtension.kext
kextload: extension MyFirstKernelExtension.kext appears to be valid
kextload: loading extension MyFirstKernelExtension.kext
kextload: sending 1 personality to the kernel
kextload: MyFirstKernelExtension.kext loaded successfully
# tail -1 /var/log/system.log
Dec 15 20:15:47 janakiram-mac kernel[0]: Hello, World!

We can see that our MyFirstKernelExtensionStart() was called. Now let’s unload it and see what happens.

# kextunload -v MyFirstKernelExtension.kext
kextunload: unload kext MyFirstKernelExtension.kext succeededstKernelExtension.kext succeeded

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: