Getting and Setting Values for Mac Generic Kernel Extension

Lets extend our generic kext created in previous post to to accept values for setting values from Kext as well as to get the values of those.

Lets start our example to implement a sysctl node with two variable : an integer and a string.We will call the new node mysysctlnode. and it will have the following properties.

  • It will be a subcategory of the existing top-level sysctl node called debug.In other words , new node’s MIB-style name would be debug.mysysctlnode
  • One of its two children will be called uint32, which will hold a 32-bit unsigned integer. The integer’s value will be readable and writable by any user.
  • The second of its children will be called string, which will hold a string up to 16 characters in length ( including the terminating NULL character).The string’s value will be read by anyone but writable only by the root user.

When our syctl kext is loaded , the kernel’s sysctl hierarchy would look like the one shown in figure , with possibly other top-level categories depending on the kernel version.

| – debug

| |

| | – mysysctlnode

| | |

| | | — uint32

| | | — string

The most general way to create a sysctl variable is to use the SYSCTL_PROC() macro , which allows a handler function to be specified for the sysctl.The handler is called when the variable is accessed for reading and writing. There exist data-type-specific macros such as SYSCTL_UINT() for unsigned integers and SYSCTL_STRING() for strings. These sysctls defined using these macros are served by predefined type-specific functions such as sysctl_handle_int() and sysctl_handle_string().We will use SYSCTL_PROC() to define our sysctl variables , with our own handler functions , although we will simply call the predefined handlers from our handlers.

Lets update the source code to include these sysctls declarations.

#include <sys/systm.h>
#include <sys/types.h>
#include <sys/sysctl.h>

static u_int32_t k_uint32 = 0; // Contents of debug.mysysctlnode.uint32
static u_int8_t k_string[16] = { 0 }; // Contents of debug.mysysctlnode.string

//Construct a node { debug.mysysctlnode } from which other sysctl objects can hang
SYSCTL_NODE(_debug, // our parent
OID_AUTO , // automatically assign us an object ID
mysysctlnode , // our name
CTLFLAG_RW, // we wil be creating children therefore , read/write
0, // Handler function ( none selected )
“demo sysctl hierarchy “) ;

//Prototypes for read/write handling functions for our sysctl nodes
static int sysctl_mysysctlnode_uint32 SYSCTL_HANDLER_ARGS;
static int sysctl_mysysctlnode_string SYSCTL_HANDLER_ARGS;

// We can directly use SYSCTL_INT() , in which sysctl_handle_int()
// will be assigned as the handling function.We use SYSCTL_PROC() and
// specify our own handler sysctl_mysysctlnode_unint32()

SYSCTL_PROC ( _debug_mysysctlnode, //our parent
OID_AUTO , // automaticall assign us an object ID
uint32, // our name
( CTLTYPE_INT | // type flag
CTLFLAG_RW | CTLFLAG_ANYBODY), //access flag ( read/write by anybody )
&k_uint32, // location of our data
0, //argument passed to our handler
sysctl_mysysctlnode_uint32, //our handler function
“IU”, // our data type ( unsigned integer )
“32-bit unsigned integer” // our description
);

// We can directly use SYSCTL_STRING() , in which sysctl_handle_string()
// will be assigned as the handling function.We use SYSCTL_PROC() and
// specify our own handler sysctl_mysysctlnode_string()

SYSCTL_PROC ( _debug_mysysctlnode, //our parent
OID_AUTO , // automaticall assign us an object ID
string, // our name
( CTLTYPE_STRING | // type flag
CTLFLAG_RW), //access flag ( read/write by root )
&k_string, // location of our data
16, //maximum allowable length of the string
sysctl_mysysctlnode_string, //our handler function
“A”, // our data type ( string )
“16-byte string” // our description
);

static int sysctl_mysysctlnode_uint32 SYSCTL_HANDLER_ARGS {

// Do some processing of our own , if neccesary
return sysctl_handle_int( oidp, oidp->oid_arg1 , oidp->oid_arg2 , req );
}

static int sysctl_mysysctlnode_string SYSCTL_HANDLER_ARGS {

// Do some processing of our own , if neccesary
return sysctl_handle_string( oidp, oidp->oid_arg1 , oidp->oid_arg2 , req );

}

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

printf(“DummySysctl_start \n”);

// Register Sysctl entries.
sysctl_register_oid(&sysctl__debug_mysysctlnode);
sysctl_register_oid(&sysctl__debug_mysysctlnode_uint32);
sysctl_register_oid(&sysctl__debug_mysysctlnode_string);

printf(“Registered Sysctl entries \n”);

return KERN_SUCCESS;
}

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

// Unregister Sysctl entries.
sysctl_unregister_oid(&sysctl__debug_mysysctlnode);
sysctl_unregister_oid(&sysctl__debug_mysysctlnode_uint32);
sysctl_unregister_oid(&sysctl__debug_mysysctlnode_string);

printf(“Registered Sysctl entries \n”);

return KERN_SUCCESS;
}

Lets compile and load the kext to test it. ( Use previous blog post steps to load the built kext ).

Once its loaded use sysctl command to get and set the values of sysctl variables.

$ sysctl debug
……….

……….

debug.mysysctlnode.uint32: 0
debug.mysysctlnode.string:

$ sysctl -w debug.mysysctlnode.uint32=56
debug.mysysctlnode.uint32:  -> 56

$ sysctl debug.mysysctlnode.uint32
debug.mysysctlnode.uint32: 56

$ sysctl -w debug.mysysctlnode.string=mykernel
debug.mysysctlnode.string:
sysctl: debug.mysysctlnode.string: Operation not permitted

$ sudo sysctl -w debug.mysysctlnode.string=mykernel
debug.mysysctlnode.string:  -> mykernel

$ sysctl debug.mysysctlnode.string
debug.mysysctlnode.string: mykernel

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: