簡介 將Linux操作系統用於服務器在現在是越來越普遍了。因此,入侵Linux在今天也變得越來越有趣.目前最好的攻擊Linux的技術就是修改內核代碼.由於一種叫做可卸載內核(Loadable KernelModules(LKMs))的機制,我們有可能編寫在內核級別運行的代碼,而這種代碼可以允許我們接觸到操作系統中非常敏感的部分.在過去有一些很好的關於LKM知識的文本或者文件,他們介紹一些新的想法,方法以及一名Hacker所夢寐以求的完整的LKMs.而且也有一些很有趣的公開的討論(在新聞組,郵件列表). 然而為什麼我再重新寫這些關於LKMs的東西呢?下面是我的一些理由: 在過去的教材中常常沒有為那些初學者提供很好的解釋.而這個教材中有很大一部分的基礎章節.這是為了幫助那些初學者理解概念的.我見過很多人使用系統的缺陷或者監聽器然而卻絲毫不了解他們是如何工作的.在這篇文章中我包含了很多帶有注釋的源代碼,只是為了幫助那些認為入侵僅僅是一些工具游戲的初學者! 每一個發布的教材不過把話題集中在某個特別的地方.沒有一個完整的指導給那些關注LKMs的Hacker.這篇文章會覆蓋幾乎所有的關於LKMs的資料(甚至是病毒方面的). 這篇文章是從Hacker或者病毒的角度進行討論的,但是系統管理員或者內核的開發者也可以參考並從中學到很多東西. 以前的文章介紹一些利用LKMs進行入侵的優點或者方法,但是總是還有一些東西是我們過去從來沒有聽說過的.這篇文章會介紹一些新的想法給大家.(不是所有的新的資料,只是一些對我們有幫助的) 這篇文章會介紹一些簡單的防止LKM攻擊的方法,同時也會介紹如何通過使用一些像運行時內核補丁(Runtime Kernel Patching)這樣的方法來對付這些防御措施. 要記住這些新的想法僅僅是通過利用一些特殊的模塊來實現的.要在現實中真正使用他們還需要對他們進行改進.這篇文章的主要目的是給大家在整個LKM上一個大方向上的指導.在附錄A中,我會給大家一些實用的LKMs,並附上一些簡短的注釋(這是為那些新手的),以及如何使用他們. 整篇文章(除了第五部分)是基於 Linux 2.0.x的80x86機器的.我測試了所有的程序和代碼段.為了能夠正常使用這裡提供的絕大部分代碼,你的Linux系統必須有LKM支持.只有在第四部分會給大家一些不需要LKM支持的源代碼.本文的絕大多數想法一樣可以在Linux2.2.x上實現(也許你會需要一些小小的改動). 這篇文章會有一個特別的章節來幫助系統管理員進行系統安全防護.你(作為一名Hacker)也必須仔細閱讀這些章節.你必須要知道所有系統管理員知道的,甚至更多.你也會從中發現很多優秀的想法.這也會對你開發高級的入侵系統的LKMs有所幫助. 因此,通讀這篇文章吧.
第一部分. 基礎知識 1.1 什麼是LKMs LKMs就是可卸載的內核模塊(Loadable Kernel Modules)。這些模塊本來是Linux系統用於擴展他的功能的。使用LKMs的優點有:他們可以被動態的加載,而且不需要重新編譯內核。由於這些優點,他們常常被特殊的設備(或者文件系統),例如聲卡等使用。 每個LKM至少由兩個基本的函數組成: int init_module(void) /*用於初始化所有的數據*/ { ... } void cleanup_module(void) /*用於清除數據從而能有一個安全的退出*/ { ... } 加載一個模塊(常常只限於root能夠使用)的命令是: # insmod module.o 這個命令讓系統進行了如下工作: 加載可執行的目標文件(在這兒是module.o) 調用 create_module這個系統調用(至於什麼叫系統調用,見1.2)來分配內存. 不能解決的引用由系統調用get_kernel_syms進行查找引用. 在此之後系統調用init_module將會被調用用來初始化LKM->執行 int inti_module(void) 等等 (內核符號將會在1.3節中內核符號表中解釋) OK,到目前為止,我想我們可以寫出我們第一個小的LKM來演示一下這些基本的功能是如何工作的了. #define MODULE #include int init_module(void) { printk("Hello World\n"); return 0; } void cleanup_module(void) { printk("Bye, Bye"); } 你可能會奇怪為什麼在這裡我用printk(....)而不是printf(.....).在這裡你要明白內核編程是完全不同於普通的用戶環境下的編程的.你只能使用很有限的一些函數(見1.6)僅使用這些函數你是干不了什麼的.因此,你將會學會如何使用你在用戶級別中用的那麼多函數來幫助你入侵內核.耐心一些,在此之前我們必須做一點其他的..... 上面的那個例子可以很容易的被編譯: # gcc -c -O3 helloworld.c # insmod helloworld.o OK,現在我們的模塊已經被加載了並且給我們打印出了那句很經典的話.現在你可以通過下面這個命令來確認你的LKM確實運行在內核級別中: # lsmod Module Pages Used by helloworld 1 0 這個命令讀取在 /proc/modules 的信息來告訴你當前那個模塊正被加載.'Pages' 顯示的是內存的信息(這個模塊占了多少內存頁面).'Used by'顯示了這個模塊被系統 使用的次數(引用計數).這個模塊只有當這個計數為0時才可以被除去.在檢查過這個以後,你可以用下面的命令卸載這個模塊 # rmmod helloworld OK,這不過是我們朝LKMs邁出的很小的一步.我常常把這些LKMs於老的DOS TSR程序做比較,(是的,我知道他們之間有很多地方不一樣),那些TSR能夠常駐在內存並且截獲到我們想要的中斷.Microsoft's Win9x有一些類似的東西叫做VxD.關於這些程序的最有意思的一點在於他們都能夠掛在一些系統的功能上,在Linux中我們稱這些功能為系統調用. 1.2什麼是系統調用 我希望你能夠懂,每個操作系統在內核中都有一些最為基本的函數給系統的其他操作調用.在Linux系統中這些函數就被稱為系統調用(System Call).他們代表了一個從用戶級別到內核級別的轉換.在用戶級別中打開一個文件在內核級別中是通過sys_open這個系統調用實現的.在/usr/include/sys/syscall.h中有一個完整的系統調用列表.下面的列表是我的syscall.h #ifndef _SYS_SYSCALL_H #define _SYS_SYSCALL_H #define SYS_setup 0 /* 只被init使用,用來啟動系統的*/ #define SYS_exit 1 #define SYS_fork 2 #define SYS_read 3 #define SYS_write 4 #define SYS_open 5 #define SYS_close 6 #define SYS_waitpid 7 #define SYS_creat 8 #define SYS_link 9 #define SYS_unlink 10 #define SYS_execve 11 #define SYS_chdir 12 #define SYS_time 13 #define SYS_prev_mknod 14 #define SYS_chmod 15 #define SYS_chown 16 #define SYS_break 17 #define SYS_oldstat 18 #define SYS_lseek 19 #define SYS_getpid 20 #define SYS_mount 21 #define SYS_umount 22 #define SYS_setuid 23 #define SYS_getuid 24 #define SYS_stime 25 #define SYS_ptrace 26 #define SYS_alarm 27 #define SYS_oldfstat 28 #define SYS_pause 29 #define SYS_utime 30 #define SYS_stty 31 #define SYS_gtty 32 #define SYS_Access 33 #define SYS_nice 34 #define SYS_ftime 35 #define SYS_sync 36 #define SYS_kill 37 #define SYS_rename 38 #define SYS_mkdir 39 #define SYS_rmdir 40 #define SYS_dup 41 #define SYS_pipe 42 #define SYS_times 43 #define SYS_prof 44 #define SYS_brk 45 #define SYS_setgid 46 #define SYS_getgid 47 #define SYS_signal 48 #define SYS_geteuid 49 #define SYS_getegid 50 #define SYS_acct 51 #define SYS_phys 52 #define SYS_lock 53 #define SYS_ioctl 54 #define SYS_fcntl 55 #define SYS_mpx 56 #define SYS_setpgid 57 #define SYS_ulimit 58 #define SYS_oldolduname 59 #define SYS_umask 60 #define SYS_chroot 61 #define SYS_prev_ustat 62 #define SYS_dup2 63 #define SYS_getppid 64