2.6內核裡劫持系統調用 幾乎所有的空用戶間操作在系統內核裡都是通過系統裡的系統調用來實現的,前面也寫了一篇有關系統調用的文章,這裡再簡單的介紹一下大至過程,當用戶執行某一操作時,比如果打開文件的操作,打開文件的程序會調用到glib庫中的open函數,而open函數最終在內核的實現就是open系統調用,當用戶執行到open函數時,會由用戶空間切換到內核內間,通過int 80進行切換,進入內核空間後會找到一個sys_call_table的符號,sys_call_table是一個指向系統調用號列表的指針,再在 sys_call_table裡找到想應的系統調用號,再通過系統調用號找到內核的系統調用,再執行系統調用。 劫持系統調用就是我們自己構造一個系統調用,想辦法使sys_call_table相應的系統調用號指向我們自己構造的函數。在原來的2.2內核裡,可以將sys_call_table直接導出,這樣我們就很容易拿到sys_call_table的控制權來實現系統調用的劫持,但是從2.4的版本以後,考慮到安全問題不允許將sys_call_table再導出,這樣就加大了取得sys_call_table控制權的難度,後面有人從/dev/kmem裡讀取sys_call_table的地址,再通過sys_call_table地址來實現系統調用的劫持,後來找到了一種更好的辦法,可以不通過 /dev/kmem來直接找到sys_call_table的地址。這裡重點討論一下這種方法。 這種方法的原理比較簡單,但思路很好,實現過程是自已構建一個內核模塊,在這個模塊中導出一個sys_call_table,因為內核的 sys_call_table不允許導出,所以我們在自已編寫的內核模塊裡實現sys_call_table的導出。我們在模塊裡找到int 80對sys_call_table的引用,找出sys_call_table的地址,再將我們將要導出的sys_call_table指向這個地址,這樣就是使到導出的sys_call_table和真實的sys_call_table沒區別。 實現代碼如下: getsyscall.c文件 代碼:: #ifndef __SYSCALL_INCLUDE__ # define __SYSCALL_INCLUDE__ #endif #ifdef MODVERSIONS #include #endif #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("xinhe "); MODULE_DESCRIPTION("eXPort the sys_call_table"); #if !defined(symname) #error symname not defined #endif #define CALLOFF 100 unsigned symname; /* #define */ strUCt { unsigned short limit; unsigned int base; } __attribute__ ((packed)) idtr; struct { unsigned short off1; unsigned short sel; unsigned char none, flags; unsigned short off2; } __attribute__ ((packed)) * idt; void set_symbol_addr(unsigned old_value, unsigned new_value) { struct module *mod; struct kernel_symbol *s; int i; for (mod = THIS_MODULE, s = mod->syms, i = 0; i num_syms; ++i, ++s) if (s->value == old_value) { s->value = new_value; return; } /*遍歷本模塊的符號表,把本模塊的一個也叫sys_call_table的符號的地址 設置為系統真正的sys_call_table的實際地址。 */ } char * findoffset(char *start) { char *p; for (p = start; p off2 off1; /*查找int 80的入口地址*/ if ((p = findoffset((char *) sys_call_off))) { sct = *(unsigned *) (p + 3); set_symbol_addr((unsigned) &symname, sct); } EXPORT_SYMBOL(sys_call_table); return 0; } static void __exit fini(void) { } module_init(init); module_exit(fini); getsyscall的makefile 代碼:: obj-m :=getsyscall.o EXTRA_CFLAGS := -Dsymname=sys_call_table KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: $(RM) -rf .*.cmd *.mod.c *.o *.ko .tmp* 為了證明sys_call_table確實導出來,我們再來寫一個系統調用劫持的模塊來證明一下 getmkdir.c文件 代碼:: #ifndef __GETMKDIR_INCLUDE__ # define __GETMKDIR_INCLUDE__ #endif #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("xinhe "); MODULE_DESCRIPTION("export the sys_call_table"); extern void *sys_call_table[]; int (*orig_mkdir)(const char *path);/*指向系統的mkdir*/ /*構造自己的mkdir*/ int hacked_mkdir(const char *path) { printk("this is a test"); return 0; } static int __init init(void) { orig_mkdir=sys_call_table[__NR_mkdir]; sys_call_table[__NR_mkdir]=hacked_mkdir; /*將sys_call_table的mkdir指向我們自己構造的hacked_mkdir*/ } static void __exit fini(void) { sys_call_table[__NR_mkdir]=orig_mkdir; /*做恢復工作*/ } module_init(init); module_exit(fini); 對應的makefile文件 代碼:: obj-m :=getmkdir.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: $(RM) -rf .*.cmd *.mod.c *.o *.ko .tmp* 加載這個兩模塊後,再用mkdir創建目錄,發現已經不能創建目錄了 注:以上代碼全在2.6.9中測試通過 存在的問題: 在測試的過程中也發現了一些問題. 當我在2.6.11.4中編譯沒問題,加載getsyscall沒問題,當加載getmkdir時就出錯了,而且內核出現異常,很多gnome的程序都出現問題,當執mkdir命令時也出來段錯誤,希望有高手來說明一下.