歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux 內核級後門的原理和簡單實戰

Linux是一個具有保護模式的操作系統。它一直工作在i386 cpu的保護模式之下。

內存被分為兩個單元: 內核區域和用戶區域。(譯者注:我覺得還是這樣叫比較順口)內核區域存放並運行著核心代碼,當然,顧名思義,用戶區域也存放並運行用戶程序。當然,作為用戶進程來講它是不能訪問內核區域內存空間以及其他用戶進程的地址空間的。

不幸地是, 核心進程也有同樣的情況。核心代碼也同樣不能訪問用戶區地地址空間。
那麼,這樣做到底有什麼意義呢?好, 我們假設當一個硬件驅動試圖去寫數據到一個用戶內存空間的程序裡的時候, 它是不可以直接去完成的, 但是它可以利用一些特殊的核心函數來間接完成。同樣, 當參數需要傳遞地址到核心函數中時,核心函數也不能直接的來讀取該參數。同樣的,它可以利用一些特殊的核心函數來傳遞參數。

這裡有一些比較有用的核心函數用來作為內核區與用戶區相互傳遞參數用。

#include <asm/segment.h>

get_user(ptr)
從用戶內存獲取給定的字節, 字,或者長整形。這只是一個宏(在核心代碼裡面有此宏的詳細定義),並且它依據參數類型來確定傳輸數量。所以你必須巧妙地利用它。

put_user(ptr)和get_user()非常相似, 但,它不是從用戶內存讀取數據,而是想用戶內存寫數據。

memcpy_fromfs(void *to, const void *from,unsigned long n)
從用戶內存中的*from拷貝n個字節到指向核心內存的指針*to。

memcpy_tofs(void *to,const *from,unsigned long n)
從核心內存中的*from拷貝n個字節數據到用戶內存中的*to。

/*譯者注:這四個函數足以在2.0.x中解決內核和用戶區的參數傳遞問題,在2.0.x以上
的版本有新的實現,即copy_user_to(...)以及copy_user_from(...)根據內核版本這些
特殊函數會有不同,請關注核心代碼的實現方法。*/

系統調用

大部分的c函數庫的調用都依賴於系統調用, 就是一些使用戶程序可以調用的簡單核心包裝函數。 這些系統調用運行在內核本身或者在可加載內核模塊中, 就是一些可動態的加載卸載的核心代碼。

就象MS-DOS和其他許多系統一樣, linux中的系統調用依賴一個給定的中斷來調用多個系統調用。linux系統中,這個中斷就是int 0x80。當調用'int 0x80'中斷的時候,控制權就轉交給了內核(或者,我們確切點地說, 交給_system_call()這個函數), 並且實際上是一個正在進行的單處理過程。

* _system_call()是如何工作的 ?

首先, 所有的寄存器被保存並且%eax寄存器全面檢查系統調用表, 這張表列舉了所有的系統調用和他們的地址信息。它可以通過extern void *sys_call_table[]來被訪問到。 該表中的每個定義的數值和內存地址都對應每個系統調用。大家可以在/usr/include/sys/syscall.h這個頭中找到系統調用的標示數。他們對應相應的SYS_systemcall名。假如一個系統調用不存在, 那麼它在sys_call_table中相應的標示就為0, 並且返回一個出錯信息。否則,系統調用存在並在表裡相應的入口為系統調用代碼的內存地址。

這兒是一個有問題的系統調用例程:

[root@ www.linuxidc.com kernel]# cat no1.c
#include <linux/errno.h>
#include <sys/syscall.h>
#include <errno.h>

extern void *sys_call_table[];

sc()
{ // 165這個系統調用號是不存在的。
    __asm__(
        "movl $165,%eax
             int $0x80");
}

main()
{
    errno = -sc();
    perror("test of invalid syscall");
}
[root@ www.linuxidc.com kernel]# gcc no1.c
[root@ www.linuxidc.com kernel]# ./a.out
test of invalid syscall: Function not implemented
[root@ www.linuxidc.com kernel]# exit

系統控制權就會轉向真正的系統調用, 用來完成你的請求並返回。 然後_system_call()調用_ret_from_sys_call()來檢查不同的返回值, 並且最後返回到用戶內存。

Copyright © Linux教程網 All Rights Reserved