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上以及教科書上建議就非要重新編譯核心嗎?適合自己的才是最好的,我們需要的是提高工作效率!
還是那些人,然而還是那些人!有那麼重要嗎?信口開河!人要生活,而不僅僅是生存!
linux核心載入可執行檔案
使用gdb跟蹤分析乙個execve系統呼叫核心處理函式 sys execve 驗證您對linux系統 載入可執行程式所需處理過程的理解 登陸實驗樓虛擬機器 增加 s s啟動引數開啟除錯模式 qemu kernel linux 3.18.6 arch x86 boot bzimage initrd r...
從0開始執行主線Linux核心
本部落格以xunlong orangepi zero為例,執行最新linux 4.11.0 rc4核心。轉殖u boot倉庫 git clone git 編譯配置 make orangepi zero defconfig 交叉編譯 make v s j8 arch arm cross compile...
Linux將執行緒繫結到CPU核心執行
先介紹三個函式 一 pthread setafftinity np 在linux上,我們可以使用pthread特定的pthread setafftinity np函式。通過設定其親和性將每個執行緒固定到單個cpu 設定cpu親和度,為0表示設定成功,非0值表示失敗 int rc pthread se...