天天看點

linux核心hack-運作中動态添加系統調用

LINUX中每次添加一個系統調用都要完成重新編譯核心,然後制作initrd等工作,不得不說這是一件繁重的工作,很多人本來已經構思好了自己的一個系統調用,要添加到核心,然後卻被這些工作所中斷,毫不誇張的說,制作initrd就很麻煩,雖然基于cpio的initrd可以利用幾條指令完成,然而隻要有一個錯誤,你就不得不重新開機系統。

     我們都知道,核心子產品運作在核心态,可以通路所有的記憶體空間,那麼能不能在系統運作期間,不用重新編譯核心,而運用核心子產品機制實作添加系統調用呢?答案是肯定的,因為一個基本原理:核心态能讀寫所有的記憶體,記憶體全部在我們手中,沒有什麼事情做不到, 那麼怎麼做到呢?

     如果我們了解了系統調用執行的過程,那麼我們就知道在執行一個系統調用之前,有兩個限制,第一就是系統調用數量在編譯時定死,系統調用号必須在這個被允許的系統調用号區間内,第二就是系統調用表的位址未導出。 由于系統調用入口也是一種中斷/異常,是以我們看一下代碼,看個究竟,基于i386的核心代碼在arch/i386/kernel/entry.S(基于2.6.8核心,雖然有點老,但能說明問題),我們看一下系統調用的入口

通過以上的代碼可以看出,如果我們想添加一個新的系統調用,第一,它的系統調用号不能超過nr_syscalls,第二,它的位址必須在系統調用表位址加上系統調用号*4的位置處, 也就是說,滿足這兩個條件,核心邏輯就會将執行流路由到我們調用的系統調用服務程式。

     如果我們希望在系統運作期動态添加一個系統調用,那麼就必須破除上述的兩個神話。第一,我們必須修改nr_syscalls的值,第二,我們必須修改sys_call_table的值,并且新的sys_call_table内容值必須保留原有的sys_call_table内容值。 這個需求怎麼做到呢?很簡單,掃面二進制機器碼!記住,記憶體在我們手中,還要記住,我們的這次行為不是攻擊行為,隻是為了調試一個新增加的系統調用而不想重新編譯核心...!!! 

     到底怎麼做到呢?首先,我們隻需要修改掉system_call中的cmpl $(nr_syscalls), %eax語句中的nr_syscalls,我們首先從/proc/kallsym中取得system_call的位址,然後在其下面搜尋cmpl $(nr_syscalls), %eax的機器碼,搜到後将nr_syscalls修改;其次我們在/boot/Sysmap中取得sys_call_table的位址,然後在system_call的下面搜尋這個位址的機器碼,取得後我們将其修改為新的系統調用表的位址,需要指出的是,新的系統調用表的前nr_syscalls個字段必須和原始的系統調用表sys_call_table的相同。另外,如何獲得sys_call_table的位址,這個技術值得商榷,其實有很多方法,本文使用最直接的方式,那就是從/boot/Sysmap中擷取。

     有一個細節,那就是如何在system_call附近尋找比對檢測系統調用号範圍的機器碼呢?最簡單的方式莫過于模拟,那就是寫一個程式,調用cmpl $(nr_syscalls), %eax,其中nr_syscalls在2.6.8中為284,那麼我們就寫下面的代碼:

編譯後使用objdump得到了3d 1c 01 00 00 cmp    $0x11c,%eax這樣的機器碼/彙編碼,于是我們可以斷定,機器特征碼為3d 1c 01 00 00 。類似的,sys_call_table的位址也可以這樣來求得,不過記住,沒有必要,既然你有root權限了,還有必要這樣嗎?

     那麼到底怎麼做到的呢?請看子產品dynamic_add_syscall:

編譯上述子產品,然後加載:

insmod dynamic_add_syscall.ko entry_addr=0xc01061d8 table_addr=0xc02dad1c nowa_num=0x11c want_num=0x21c 

上述的位址資訊都是從/boot/Sysmap以及/proc/kallsym以及核心源碼中得到的。現在我們希望增加一個系統調用,該怎麼辦呢?

     很簡單,再寫一個子產品newsyscall:

編譯此子產品,然後加載,為了測試我們新增加的系統調用sys_force,特寫一個使用者态程式:

編譯為test,當我們執行test 284 123之後,用dmesg看一下,會發現最後一行列印:new sys----:123,表明成功,我們成功增加了一個系統調用,可喜!

後記: 

還是那句話,既然徹底了解了原理,那就沒有什麼是不可能的,我們不必為了實作一個系統調用而重新編譯核心,完全沒有必要,我們可以先使用上述的方式将新系統調用調試通過,然後最終再通過重新編譯核心的方式添加進核心,OK?

         雖然修改記憶體機器碼并不是可取的方式,不是正常的方式,然而再次重申,我們這不是在進行攻擊,而是為了讓自己添加和調試新的系統調用更友善,隻要達到目的,有什麼不可以呢?難道僅僅因為LKML上以及教科書上建議就非要重新編譯核心嗎?适合自己的才是最好的,我們需要的是提高工作效率! 

     還是那些人,然而還是那些人!有那麼重要嗎?信口開河!人要生活,而不僅僅是生存!

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271019

繼續閱讀