Linux操作系統下的高級隱藏技術詳解

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  本文深入分析了Linux環境下文件、進程及模塊的高級隱藏技術,其中包括:Linux可卸載模塊編程技術、修改內存映象直接對系統調用進行修改技術,通過虛擬文件系統proc隱藏特定進程的技術.

  隱藏技術在計算機系統安全中應用十分廣泛,尤其是在網路攻擊中,當攻擊者成功侵入一個系統后,有效隱藏攻擊者的文件、進程及其載入的模塊變得尤為重要.本文將討論Linux系統中文件、進程及模塊的高級隱藏技術,這些技術有的已經被廣泛應用到各種後門或安全檢測程序之中,而有一些則剛剛起步,仍然處在討論階段,應用很少.

  1.隱藏技術

  1.1.Linux下的中斷控制及系統調用

  Intel x86系列微機支持256種中斷,為了使處理器比較容易地識別每種中斷源,把它們從0~256編號,即賦予一個中斷類型碼n,Intel把它稱作中斷向量.

  Linux用一個中斷向量(128或者0x80)來實現系統調用,所有的系統調用都通過唯一的入口system_call來進入內核,當用戶動態進程執行一條int 0x80彙編指令時,CPU就切換到內核態,並開始執行system_call函數,system_call函數再通過系統調用表sys_call_table來取得相應系統調用的地址進行執行.系統調用表sys_call_table中存放所有系統調用函數的地址,每個地址可以用系統調用號來進行索引,例如sys_call_table[NR_fork]索引到的就是系統調用sys_fork()的地址.

  Linux用中斷描述符(8位元組)來表示每個中斷的相關信息,其格式如下:

  偏移量31….16  一些標誌、類型碼及保留位

  段選擇符      偏移量15….0

  所有的中斷描述符存放在一片連續的地址空間中,這個連續的地址空間稱作中斷描述符表(IDT),其起始地址存放在中斷描述符表寄存器(IDTR)中,其格式如下:

  32位基址值  界限

  其中各個結構的相應聯繫可以如下表示:

  通過上面的說明可以得出通過IDTR寄存器來找到system_call函數地址的方法:根據IDTR寄存器找到中斷描述符表,中斷描述符表的第0x80項即是system_call函數的地址,這個地址將在後面的討論中應用到.

  1.2.Linux 的LKM(可裝載內核模塊)技術



  為了使內核保持較小的體積並能夠方便的進行功能擴展,Linux系統提供了模塊機制.模塊是內核的一部分,但並沒有被編譯進內核,它們被編譯成目標文件,在運行過程中根據需要動態的插入內核或者從內核中移除.模塊在插入后是作為Linux內核的一部分來運行的,模塊編程實際上就是內核編程,因此可以在模塊中使用一些由內核導出的資源,例如Linux2.4.18版以前的內核導出系統調用表(sys_call_table)的地址,這樣就可以根據該地址直接修改系統調用的入口,從而改變系統調用.在模塊編程中存在初始化函數及清除函數,一般情況下,這兩個函數默認為init_module()以及clearup_module(),從2.3.13內核版本開始,用戶也可以給這兩個函數重新命名,初始化函數在模塊被插入系統時調用,在其中可以進行一些函數及符號的註冊工作,清除函數則在模塊移除系統時進行調用,一些恢復工作通常在該函數中完成.

  1.3.Linux下的內存映像

  /dev/kmem是一個字元設備,是計算機主存的映像,通過它可以測試甚至修改系統,當內核不導出sys_call_table地址或者不允許插入模塊時可以通過該映像修改系統調用,從而實現隱藏文件、進程或者模塊的目的.

  1.4.proc 文件系統

  proc文件系統是一個虛擬的文件系統,它通過文件系統的介面實現,用於輸出系統運行狀態.它以文件系統的形式,為操作系統本身和應用進程之間的通信提供了一個界面,使應用程序能夠安全、方便地獲得系統當前的運行狀況何內核的內部數據信息,並可以修改某些系統的配置信息.proc以文件系統的介面實現,因此可以象訪問普通文件一樣訪問它,但它只存在於內存之中.

  2.技術分析

  2.1 隱藏文件

  Linux系統中用來查詢文件信息的系統調用是sys_getdents,這一點可以通過strace來觀察到,例如strace ls 將列出命令ls用到的系統調用,從中可以發現ls是通過sys_getedents來執行操作的.當查詢文件或者目錄的相關信息時,Linux系統用sys_getedents來執行相應的查詢操作,並把得到的信息傳遞給用戶空間運行的程序,如果修改該系統調用,去掉結果中與某些特定文件的相關信息,那麼所有利用該系統調用的程序將看不見該文件,從而達到了隱藏的目的.介紹一下原來的系統調用,其原型為:



  int sys_getdents(unsigned int fd, struct dirent *dirp,unsigned int count)

  其中fd為指向目錄文件的文件描述符,該函數根據fd所指向的目錄文件讀取相應dirent結構,並放入dirp中,其中count為dirp中返回的數據量,正確時該函數返回值為填充到dirp的位元組數.下圖是修改後的系統調用hacked_getdents執行流程.

  hacked_getdents函數實際上就是先調用原來的系統調用,然後從得到的dirent結構中去除與特定文件名相關的文件信息,從而應用程序從該系統調用返回后將看不到該文件的存在.

  應該注意的是,一些較新的版本中是通過sys_getdents64來查詢文件信息的,但其實現原理與sys_getdents基本相同,在這些版本中仍然可以用與上面類似的方法來修改該系統調用,隱藏文件.

  2.2 隱藏模塊

  上面分析了如何修改系統調用以隱藏特定名字的文件,在實際的處理中,經常會用模塊來達到修改系統調用的目的,但是當插入一個模塊時,若不採取任何隱藏措施,很容易被對方發現,一旦對方發現並卸載了所插入的模塊,那麼所有利用該模塊來隱藏的文件就暴露了,應繼續分析如何來隱藏特定名字的模塊.Linux中用來查詢模塊信息的系統調用是sys_query_module,可以通過修改該系統調用達到隱藏特定模塊的目的.解釋一下原來的系統調用,原來系統調用的原型為:

  int sys_query_module(const char *name, int which, void *buf, size_t bufsize , size_t *ret)

  如果參數name不空,則訪問特定的模塊,否則訪問的是內核模塊,參數which說明查詢的類型,當which=QM_MODULES時,返回所有當前已插入的模塊名稱,存入buff, 並且在ret中存放模塊的個數,buffsize是buf緩衝區的大小.在模塊隱藏的過程中只需要對which=QM_MODULES的情況進行處理就可以達到目的.修改後的系統調用工作過程如下:

  1)調用原來的系統調用,出錯則返回錯誤代碼;

  2)如果which不等於QM_MODULES,則不需要處理,直接返回.

  3)從buf的開始位置進行處理,如果存在特定的名字,則將後面的模塊名稱向前覆蓋該名字.

  4)重複3),直到處理處理完所有的名字,正確返回.



  2.3 隱藏進程

  在Linux中不存在直接查詢進程信息的系統調用,類似於ps這樣查詢進程信息的命令是通過查詢proc文件系統來實現的,在背景知識中已經介紹過proc文件系統,它應用文件系統的介面實現,因此同樣可以用隱藏文件的方法來隱藏proc文件系統中的文件,只需要在上面的hacked_getdents中加入對於proc文件系統的判斷即可.proc是特殊的文件系統,只存在於內存之中,不存在於任何實際設備之上,Linux內核分配給它一個特定的主設備號0以及一個特定的次設備號1,除此之外,在外存上沒有與之對應的i節點,系統也分配給它一個特殊的節點號PROC_ROOT_INO(值為1),而設備上的1號索引節點是保留不用的.通過上面的分析,可以得出判斷一個文件是否屬於proc文件系統的方法:

  1)得到該文件對應的inode結構dinode;

  2)if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev) && MINOR(dinode->i _dev) == 1) {該文件屬於proc文件系統}

  通過上面的分析,給出隱藏特定進程的偽代碼表示:

  hacket_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)

  {

  調用原來的系統調用;

  得到fd所對應的節點;

  if(該文件屬於proc文件系統&&該文件名需要隱藏)

  {從dirp中去掉該文件相關信息}

  }



  2.4 修改系統調用的方法

  現在已經解決了如何修改系統調用來達到隱藏的目的,那麼如何用修改後的系統調用來替換原來的呢?這個問題在實際應用中往往是最關鍵的,下面將討論在不同的情況下如何做到這一點.

  (1)當系統導出sys_call_table,並且支持動態的插入模塊的情況下:

  在Linux內核2.4.18版以前,這種內核配置是非常普遍的.這種情況下修改系統調用非常容易,只需要修改相應的sys_call_table表項,使其指向新的系統調用即可.下面是相應的代碼:

  int orig_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)

   int init_module(void) 

    /*初始化模塊*/

     {

     orig_getdents=sys_call_table[SYS_getdents];    //保存原來的系統調用



     orig_query_module=sys_call_table[SYS_query_module]

     sys_call_table[SYS_getdents]=hacked_getdents;  //設置新的系統調用

     sys_call_table[SYS_query_module]=hacked_query_module;

     return 0; //返回0表示成功

     }

     void cleanup_module(void)

     /*卸載模塊*/

     {

     sys_call_table[SYS_getdents]=orig_getdents;    //恢復原來的系統調用

     sys_call_table[SYS_query_module]=orig_query_module;

     }

  (2)在系統並不導出sys_call_table的情況下:

  linux內核在2.4.18以後為了安全起見不再導出sys_call_table符號,從而無法直接獲得系統調用表的地址,那麼就找到其他的辦法來得到這個地址.在背景知識中提到了/dev/kmem是系統主存的映像,可以通過查詢該文件來找到sys_call_table的地址,並對其進行修改,來使用新的系統調用.那麼如何在系統映像中找到sys_call_table的地址呢?讓我們先看看system_call的源代碼是如何來實現系統調用的(代碼見/arch/i386/kernel/entry.S):

  ENTRY(system_call)

     pushl





[火星人 ] Linux操作系統下的高級隱藏技術詳解已經有471次圍觀

http://coctec.com/docs/linux/show-post-51252.html