Creating a Generic Mac Kernel Extension

In this post , we will discuss how kernel extensions are created and loaded in Mac OS X.

Note : Apple urges third-party developers to avoid the programming in the kernel unless absolutely necessary.

Lets create a trivial kext – one that implements the start and stop points. Once we can compile and load the kext we will extend it to implement a couple of sysctl entries in next post. We will call our kext DummySysctl.

We begin with XCode project instantiated from the template for generic kernel extensions. Since sysctl implementation is a BSD -only endeavor, we need to specify our kext’s dependency on BSD KPI.

We will use the kernel version of printf() function to print messages in our kext. Therefore , we need libkern,which provides the print().

Configuring KEXT

The contents of the Info.plist should look like as follows

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE plist PUBLIC “-//Apple Computer//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”&gt;
<plist version=”1.0″>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>DummySysctl</string>

<key >CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.osxbook.kext.DummySysctl</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleVersion</key>
<string>1.0.0d1</string>
<key>OSBundleLibraries</key>
<dict>
    <key>com.apple.kpi.bsd</key>
    <string>8.0.0</string>

    <key>com.apple.kpi.libkern</key>
    <string>8.0.0</string>
</dict>

</dict>

</plist>

Beginning with Mac OS X 10.4 , a kext can declare dependencies either on new style kernel programing interfaces (KPI) or on compatibility interfaces. KPI dependencies are specified thru com.apple.kpi.* identifiers, where as the others are specified through com.apple.kernel.* identifiers. The Former start from version 8.0.0 ( Mac OS X 10.4 and newer ), where as latter end at version 7.9.9 ( Mac OS X 10.3 and older ) .

Lets see the source file created by XCode for us. The start function is called when the kext is loaded , and the stop function is called when its unloaded. XCode inserts the skeleton implementations of these functions in the automatically generated C file for the Generic Kernel Extension project template. We have added a printf() statement to both functions. The following shows the code of DummySysctl.c .

#include <mach/mach_types.h>

kern_return_t DummySysctl_start (kmod_info_t * ki, void * d) {

printf(“DummySysctl_start \n”);

return KERN_SUCCESS;
}

kern_return_t DummySysctl_stop (kmod_info_t * ki, void * d) {
printf(“DummySysctl_stop \n”);
return KERN_SUCCESS;
}

Lets now compile the kext. Now build the XCode project. Upon successful compilation it creates the DummySysctl.kext in the build folder of XCode.

Now Open the Terminal and move the folder where the built kext exists.

$ cd cd /Users/janakiram/KernelExtensions/DummySysctl/build/Release/

$ ls
DummySysctl.kext

Since loading a kext requires the kext bundle’s contents to have root and wheel as the owner and group , respectively, a typical compile – test – debug cycle would involve copying the kext bundle from the build directory to a temporary location – say to /tmp/ and using chown command on the copy. As we noted earlier , besides ownership, the modifiability of objects within the bundle also matters – the bundle’s contents must not be writable by any user except root.

$ cp -pr DummySysctl.kext /tmp/

$ sudo chown -R root:wheel /tmp/DummySysctl.kext

Now we can use the kextload to load the kext manually.

$ sudo kextload -v /tmp/DummySysctl.kext
kextload: extension /tmp/DummySysctl.kext appears to be valid
kextload: loading extension /tmp/DummySysctl.kext
kextload: sending 1 personality to the kernel
kextload: /tmp/DummySysctl.kext loaded successfully

If the kext fails to load , the -t option os the kextload may provide information about possible problems. For example , suppose we specified an unavailable version of the dependency – say version 7.9.9 for com.apple.kpi.libkernel , then -t option would be helpful in identifying the cause of the problem.

Besides the dependency resolution failure , other reasons for a kext’s failure to load include incorrect file permissions , a flawed bundle structure, a missing CFBundleIdentifier property in kext’s Info.plist file , and a missing or syntactically involved Info.plist file.

We can use the kextstat command to check whether our kext is currently loaded in the kernel

$ kextstat

Index Refs Address    Size       Wired      Name (Version) <Linked Against>
    1    1 0x0        0x0        0x0        com.apple.kernel (8.8.3)
    2   24 0x0        0x0        0x0        com.apple.kpi.bsd (8.8.3)
    3   41 0x0        0x0        0x0        com.apple.kpi.iokit (8.8.3)
    4   42 0x0        0x0        0x0        com.apple.kpi.libkern (8.8.3)
    5   39 0x0        0x0        0x0        com.apple.kpi.mach (8.8.3)
…..

….

 95    0 0x2f1bd000 0x2000     0x1000     com.osxbook.kext.DummySysctl (1.0.0d1) <4 2>

The value 95 in the kextstat output indicates the index at which the kext is loaded. The Kernel uses these indices for tracking the inter kext dependencies. The second value , which is 0 in this case , shows the number of references to this kext.The next value ,0x2f1bd000, is the kext ‘s load address in the kernel virtual address space. The next tow values ,memory and wired  0x2000  and 0x1000 , represent the amounts of kernel memory and wired kernel memory, respectively,used by the kext. The final value in the column is a list of indices of all other kexts that this ket references. We see that DummySysctl references two kexts : the ones loaded at indices 4(com.apple.kpi.libkern) and 2 (com.apple.kpi.bsd).

Once we are done , we can unload the kext manually using kextunload command.

$ sudo kextunload -v /tmp/DummySysctl.kext
Password:
kextunload: unload kext /tmp/DummySysctl.kext succeeded

The output from the printf() statements we inserted in our kext should appear in the /var/log/system.log

$ tail -f /var/log/system.log

Jun  9 14:38:02 administrators-computer-7 sudo: janakiram : TTY=ttyp1 ; PWD=/Users/janakiram/Kernel Extensions/DummySysctl/build/Release ; USER=root ; COMMAND=/sbin/kextload -v -t /tmp/DummySysctl.kext
Jun  9 14:38:09 administrators-computer-7 kernel[0]: DummySysctl_start
Jun  9 14:58:06 administrators-computer-7 sudo: janakiram : TTY=ttyp1 ; PWD=/Users/janakiram/Kernel Extensions/DummySysctl/build/Release ; USER=root ; COMMAND=/sbin/kextunload -v /tmp/DummySysctl.kext
Jun  9 14:58:08 administrators-computer-7 kernel[0]: DummySysctl_st

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: