1 - 介紹 2 - Linux的keyboard驅動是如何工作的 3 - 基於內核的鍵盤紀錄的原理 3.1 - 中斷句柄 3.2 - 函數劫持 3.2.1 - 劫持handle_scancode 3.2.2 - 劫持put_queue 3.2.3 - 劫持receive_buf 3.2.4 - 劫持tty_read 3.2.5 - 劫持syserials_read/syserials_write 4 - vlogger 4.1 - 工作原理 4.2 - 功能及特點 4.3 - 如何使用 5 - 感謝 6 - 參考資料 7 - Keylogger源代碼 --[ 1 - 介紹 本文分成兩個部分。第一部分給出了linux鍵盤驅動的工作原理,並且討論了建立一個基於 內核的鍵盤紀錄器的方法。這部分內容對那些想寫一個基於內核的鍵盤紀錄器,或者寫一個 自己鍵盤驅動的朋友會有幫助。 第二部分詳細描述了vlogger的每個細節,vlogger是一個強大的基於內核的linux鍵盤紀錄器, 以及如何來使用它。這向技術可以運用在蜜罐系統中,也可以做成一些很有意思的hacker game, 主要用來分析和采集hacker的攻擊手法。我們都知道,一些大家熟知的鍵盤紀錄器,如iob, uberkey,unixkeylogger等,它們是基於用戶層的。這裡介紹的是基於內核層的鍵盤紀錄器。 最早期的基於內核的鍵盤紀錄器是linspy,它發表在phrack雜志第50期。而現代的kkeylogger( 後面我們將用kkeylogger來表示基於內核的鍵盤紀錄器)廣泛采用的手法是中斷syserials_read或者 syserials_write系統調用來對用戶的擊鍵進行記錄。 顯然,這種方法是很不穩定的並且會明顯的降低系統的速度,因為我們中斷的恰恰是系統使用最 頻繁的兩個系統調用syserials_read,syserials_write;syserials_read在每個進程需要讀寫設備的時候都會用到。 在vlogger裡,我用了一個更好的方法,就是劫持tty buffer進程函數,下面會介紹到。 我假定讀者熟悉linux的可加載模塊的原理和運作過程,如果不熟悉,推薦大家首先閱讀我以前寫 過的linux kernel simple hacking,或者linux tty hijack,(在http://e4gle.org有下載), 參閱《linux驅動程序設計》來獲得相關的理論基礎知識。 --[ 2 - linux鍵盤驅動的工作原理 首先讓我們通過以下的結構圖來了解一下用戶從終端的擊鍵是如何工作的: _____________ _________ _________ / \ put_queue receive_buf tty_read /handle_scancode\-------->tty_queue---------->tty_ldisc-------> \ / buffer \_____________/ _________ _________ _________ ____________ syserials_read --->/dev/ttyX------->user process _________ ____________ Figure 1 首先,當你輸入一個鍵盤值的時候,鍵盤將會發送相應的scancodes給鍵盤驅動。一個獨立的 擊鍵可以產生一個六個scancodes的隊列。 鍵盤驅動中的handle_scancode()函數解析scancodes流並通過kdb_translate()函數裡的 轉換表(translation-table)將擊鍵事件和鍵的釋放事件(key release events)轉換成連 續的keycode。 比如,'a'的keycode是30。擊鍵’a'的時候便會產生keycode 30。釋放a鍵的時候會產生 keycode 158(128+30)。 然後,這些keycode通過對keymap的查詢被轉換成相應key符號。這步是一個相當 復雜的過程。 以上操作之後,獲得的字符被送入raw tty隊列--tty_flip_buffer。 receive_buf()函數周期性的從tty_flip_buffer中獲得字符,然後把這些字符送入 tty read隊列。 當用戶進程需要得到用戶的輸入的時候,它會在進程的標准輸入(stdin)調用read()函數。 syserials_read()函數調用定義在相應的tty設備(如/dev/tty0)的file_operations結構 中指向tty_read的read()函數來讀取字符並且返回給用戶進程。 /*e4gle add file_operations是文件操作結構,定義了文件操作行為的成員,結構如下,很容易理解: strUCt file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *);