Friday 21 June 2013

A Linux rootkit tutorial - Makefile and symbols

We're going to use the classic method of loading our rootkit as a device driver.  The reason this is used so often is that it's an established method of granting ring-0 access to code.  However - we don't really want to be a device driver (indeed for this reason I'm going to refer to the rootkit as a module from now on), and indeed loading ourselves as one will create various issues and could leave behind an irremovable audit trail (see load_module in kernel/module.c in the kernel source code for more).  Later we'll look at other methods we could use - which assume we can get ring-0 some other way - normally due to a device driver bug.

To start with however, we're going to go through the method required to build the rootkit at https://github.com/nnewson/km.git and why it's set-up that way.  I'm not however, going to go in-depth into how a basic module is built and how it works.  You can get that here - http://lwn.net/Kernel/LDD3/ - in the second chapter.  It does a much better job that I could.

One thing I will quickly discuss is that the Makefile we use will actually call the kernel's Makefile, which will set up the environment, and then call our Makefile to retrieve some variables.  It's using these variables that the kernel's Makefile builds the module for us.

These means we're somewhat limited by what we can do without Makefile - as it's almost like it's a include file rather than a proper Makefile.  For instance, most module Makefiles on the Internet explicitly name each source file - mainly because the wildcard pattern doesn't work when the kernel Makefile calls back into our one.  I get around this by explicitly using the ls shell command to find all the .c files.  It saves us having to edit the file every time we add a new compilation unit.

The Makefile has one real build option (that calls the kernel Makefile) and two phony ones.  The first is the standard 'clean' entry - which again calls the kernel Makefile to handle the actual clean.  The second is more interesting - it's called symbols.

Building Symbols
Before I discuss what building symbols does, it's important to describe why we need to do this at all. We're going to be using a lot of functions and global variables from the kernel - and most of the time these will be exported.  This means we refer to them by name (i.e. symbol) in our built module, and when it's loaded these are mapped to the actual addresses of the function or global variable by looking up these symbols.

Not all functions will be exported though.  Static ones certainly won't - indeed by default the kernel build system will only export symbols which are explicitly registered as such - using the EXPORT_SYMBOL/EXPORT_SYMBOL_GPL define.  So anything that isn't registered as such isn't going to be available to us - either our module will fail to build, or it simply won't load.

However - these unexported symbols are available to us - via the System.map file (stored in your /boot directory).  This file details the location and name of all the function and global variables (amongst other things) that the kernel uses.  As such we can grep through this file to discover memory address as a function.  For instance if we use the following command (which we need to sudo because we want to read from /boot):

sudo grep -w sysfs_mutex /boot/System.map-$(uname -r)

We get the following:

ffffffff81c5d940 D sysfs_mutex

This means the sysfs_mutex global variable is actually at memory address 0xffffffff81c5d940.  Of course this is only true for this kernel build - if you update or rebuild your kernel, you'd have to perform this check again.  We don't need to rebuild this for changes to our module however.

What this means is that using awk we can extract that memory address into a Makefile variable - which you see here:

SYSFS_MUTEX_SYMBOL = $(shell grep -w sysfs_mutex /boot/System.map-$(shell uname -r) | awk '{print $$1}')

This variable can then be placed in a file to be used by the code as the location of the sysfs_mutex variable.  So after looking up a number of various symbols, the code used sed to process a symbols_template.h file, and replace specific tokens with the addresses we looked up.  So building symbols should give us a symbols.h file in our src directory.

sudo make symbols

With this done you should now see the resulting entry in the symbols.h file as:

#define      c_sysfs_mutex_symbol  ffffffff81c5d940

This can now be assigned to a pointer - meaning we now have a pointer to the sysfs_mutex variable - and can access it as though it were exported.

static mutex*   g_sysfs_mutex = ( mutex* ) c_sysfs_mutex_symbol;

So, to finalise, to build the module the first time you need two Make commands:

sudo make symbols
make

No comments:

Post a Comment