簡介
將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("<1>Hello World\n"); return 0; } void cleanup_module(void) { printk("<1>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
#define SYS_getpgrp 65
#define SYS_setsid 66
#define SYS_sigaction 67
#define SYS_siggetmask 68
#define SYS_sigsetmask 69
#define SYS_setreuid 70
#define SYS_setregid 71
#define SYS_sigsuspend 72
#define SYS_sigpending 73
#define SYS_sethostname 74
#define SYS_setrlimit 75
#define SYS_getrlimit 76
#define SYS_getrusage 77
#define SYS_gettimeofday 78
#define SYS_settimeofday 79
#define SYS_getgroups 80
#define SYS_setgroups 81
#define SYS_select 82
#define SYS_symlink 83
#define SYS_oldlstat 84
#define SYS_readlink 85
#define SYS_uselib 86
#define SYS_swapon 87
#define SYS_reboot 88
#define SYS_readdir 89
#define SYS_mmap 90
#define SYS_munmap 91
#define SYS_truncate 92
#define SYS_ftruncate 93
#define SYS_fchmod 94
#define SYS_fchown 95
#define SYS_getpriority 96
#define SYS_setpriority 97
#define SYS_profil 98
#define SYS_statfs 99
#define SYS_fstatfs 100
#define SYS_ioperm 101
#define SYS_socketcall 102
#define SYS_klog 103
#define SYS_setitimer 104
#define SYS_getitimer 105
#define SYS_prev_stat 106
#define SYS_prev_lstat 107
#define SYS_prev_fstat 108
#define SYS_olduname 109
#define SYS_iopl 110
#define SYS_vhangup 111
#define SYS_idle 112
#define SYS_vm86old 113
#define SYS_wait4 114
#define SYS_swapoff 115
#define SYS_sysinfo 116
#define SYS_ipc 117
#define SYS_fsync 118
#define SYS_sigreturn 119
#define SYS_clone 120
#define SYS_setdomainname 121
#define SYS_uname 122
#define SYS_modify_ldt 123
#define SYS_adjtimex 124
#define SYS_mprotect 125
#define SYS_sigprocmask 126
#define SYS_create_module 127
#define SYS_init_module 128
#define SYS_delete_module 129
#define SYS_get_kernel_syms 130
#define SYS_quotactl 131
#define SYS_getpgid 132
#define SYS_fchdir 133
#define SYS_bdflush 134
#define SYS_sysfs 135
#define SYS_personality 136
#define SYS_afs_syscall 137
#define SYS_setfsuid 138
#define SYS_setfsgid 139
#define SYS__llseek 140
#define SYS_getdents 141
#define SYS__newselect 142
#define SYS_flock 143
#define SYS_syscall_flock SYS_flock
#define SYS_msync 144
#define SYS_readv 145
#define SYS_syscall_readv SYS_readv
#define SYS_writev 146
#define SYS_syscall_writev SYS_writev
#define SYS_getsid 147
#define SYS_fdatasync 148
#define SYS__sysctl 149
#define SYS_mlock 150
#define SYS_munlock 151
#define SYS_mlockall 152
#define SYS_munlockall 153
#define SYS_sched_setparam 154
#define SYS_sched_getparam 155
#define SYS_sched_setscheduler 156
#define SYS_sched_getscheduler 157
#define SYS_sched_yield 158
#define SYS_sched_get_priority_max 159
#define SYS_sched_get_priority_min 160
#define SYS_sched_rr_get_interval 161
#define SYS_nanosleep 162
#define SYS_mremap 163
#define SYS_setresuid 164
#define SYS_getresuid 165
#define SYS_vm86 166
#define SYS_query_module 167
#define SYS_poll 168
#define SYS_syscall_poll SYS_poll
#endif /* */
每個系統調用都有一個預定義的數字(見上表),那實際上是用來進行這些調用的.內核通過中斷0x80來控制每一個系統調用.這些系統調用的數字以及任何參數都將被放入某些寄存器(eax用來放那些代表系統調用的數字,比如說)
那些系統調用的數字是一個被稱之為sys_call_table[]的內核中的數組結構的索引值.這個結構把系統調用的數字映射到實際使用的函數.
OK,這些是繼續閱讀所必須的足夠知識了.下面的表列出了那些最有意思的系統調用以及一些簡短的注釋.相信我,為了你能夠真正的寫出有用的LKM你必須確實懂得那些系統調
用是如何工作的.
系統調用列表:
int sys_brk(unsigned long new_brk);
改變DS(數據段)的大小->這個系統調用會在1.4中討論
int sys_fork(struct pt_regs regs);
著名的fork()所用的系統調用