摘要
本文描述了一種檢測內核與用戶級rootkit的新技術
此技術利用處理器的單步執行模式
來測定系統內核與DLL中執行指令的數量
從而達到檢測rootkit和後門的目的
同時還討論了其在Win
k下的代碼實現
背景知識 一個在計算機安全領域中重要的問題是
如何判斷給定的主機是否已被入侵
由於以下兩點這項工作變的非常困難
攻擊者可以利用未知漏洞進入系統
當進入系統後
入侵者可通過安裝rootkit和後門來隱藏自身(例如
隱藏進程
通訊渠道
文件等)
本文將集中討論在Win
K系統下rootkit的檢測問題
傳統rootkit檢測技術中的一些問題 傳統的rootkit檢測程序(那些我們經常在UNIX系統中見到的)只能檢測一些已知的rootkit(這點使它變的像反病毒程序)或進行一些內核存儲的掃描
例如Linux中就有一些工具掃描內核中的syscall table
這顯然不夠好
因為已經有了很多並不更改syscall table的rootkit
而Win
k下也可開發出類似的rootkit
那檢測程序是不是還應該掃描內核代碼空間?這樣我們就有了一個運行在內核模式中的Tripwire
但這還不夠好
因為在大多數的操作系統中
我們可以寫出即不更改SST(syscall table)也不更改代碼的內核級rootkit
在系統中有很多可以勾連的函數指針(例見[
])
以我看
存儲掃描技術決不會成為rootkit檢測的終結
這主要是因為我們不能確定具體的監測存儲區域
那到底怎樣檢測出我們系統中的入侵者呢?
首先我們以rootkit中所使用的技術
將其分為兩類
*通過更改系統結構來隱藏某對象的(如運行的進程)和
*更改內核執行路徑(例
勾連那些負責枚舉活動進程的內核函數)來達到同樣目的的
更改系統數據結構的rootkit 這類的rootkit不太多
有意思的例子包括fu rootkit(見[
])
通過刪除內核中PsActiveProcessList鏈上的進程對象來隱藏進程
ZwQuerySystemInformation等函數是不能發現這些隱藏進程的
但同時
因為Windows的線程分派器(dispatcher
scheduler)使用另外的數據結構
這些
隱藏
進程仍可運行(被分配到CPU使用時間)
Windows的線程分派器使用以下三個(注
)數據結構
*pKiDispatcherReadyListHead
*pKiWaitInListHead
*pKiWaitOutListHead
後兩個是處於
等待
狀態的線程鏈頭
他們之間稍有不同
但對我們來說並不重要
從上面的信息我們可以找到一種檢測隱藏進程的方法
及讀取線程分派器使用的數據結構
而不是PsActiveProcessList
當檢測rootkit時我們應該盡可能的觸及更底層的內核數據結構
有一點應該注意
直接從線程分派器使用的鏈中刪除要隱藏的進程是不可能的
因為隱藏的進程將分配不到CPU使用時間
更改執行路徑的rootkit 這類的rootkit較為普及
他們通過修改或增加內核或系統DLL中的指令來達到目的
檢測這類rootkit的問題是
我們不知道rootkit在什麼地方做了那些修改
它可以勾連DLL中的函數
系統服務列表(System Service Table)
改變內核函數的內容或修改內核中一些奇怪的函數指針
執行路徑分析(Execution Path Analysis) EPA關注這樣一個事實
如果入侵者通過修改執行路徑隱藏了一些對象
那麼當調用一些典型的系統和庫的函數時
系統將運行一些多余的指令
舉個例子
如果入侵者為了隱藏文件和進程而修改了ZwQueryDirectoryFile()和ZwQuerySysteminformation()
那樣和干淨的系統相比
這些系統函數就會執行更多的指令
不管入侵者采用勾連或在代碼中加入jmp指令
或其他任何方法
指令增加的原因是rootkit必須進行它的任務(在這個例子中是隱藏文件和進程)
但是Windows
的內核是一個非常復雜的程序
即使在干淨的系統中
某些系統函數每次運行的指令個數也是不同的
然而我們可以用統計學來解決這個問題
但首先
我們需要一種測定指令個數的方法
指令計數器的實現 單步執行模式是Intel處理器的一個好的特性
我們可以用它來實現指令計數
當處理器處在這個模式時
每執行完一條指令
系統將產生一個除錯異常(debug exception
#DB)
要進入這個模式
需要設置EFLAGS寄存器中的TF位(詳見[
])
當執行int指令時
處理器會自動清TF位並進行權限切換
這意味著
如果要進行內核模式下的指令計數
則必須在中斷處理程序開頭設置TF位
因為我們將要測定一些系統服務中的指令個數
勾連中斷向量
x
e
也就是Windows
下的系統服務調用門會變得很有效
但是
因為存在用戶模式的rootkit
也應該測定在ring
級運行的指令個數
只要在用戶模式下設置TF位一次就可以了
從內核返回時
處理器會自動恢復這一位
以上的計數方法通過內核驅動器實現
如圖
所示
驅動加載後勾連IDT
x
和
x
e
為和用戶級程序交互
驅動勾連一個系統調用
用戶級程序通過這個特定的系統調用來開關計數器
一些測試 我們可以使用上一段中描述的方法來測定任意系統服務中執行的指令個數
例如
來檢查是否有人試圖隱藏任意文件
我們可以開始一個簡單的測試
pfStart();
FindFirstFile(
C
\\WINNT\\system
\\drivers
&FindFileData);
int res = pfStop();
如果有rootkit隱藏任意文件
則執行的指令數要比干淨的系統多
如果運行這個測試上百次
並計算執行指令數的平均值
我們會發現這個值是非常不確定的
考慮Win
k的復雜性
這一點也不讓人吃驚
但對於我們的檢測目的來說
這種現象是不能接受的
但如果我們將得到的那些值的頻率分布用條形圖表示出來
會發現圖中有一個明顯的頻率高點
如圖
和
中所表示那樣的
即使是在系統負載很大時
頻率高點所對應的數值保持不變
很難解釋這個令人吃驚的現象
可能是因為在循環中同一個系統服務被調用幾百次後
與這個系統服務相關的緩沖最後會被填入固定的值
假想現在有人安裝了隱藏文件的rootkit
如果我們重復測試並繪制相應的條形圖
就會發現頻率高點向右移了
這是因為rootkit需要進行隱藏文件的工作
在現在的代碼實現中
只進行了少量的測試
包括典型的服務如
文件系統讀取
枚舉進程
枚舉注冊表項以及Socket讀取
這些測試將有效地檢測出著名的NTRootkit(見[
])
或最近比較流行的Hacker Defender(見[
])
包括它自帶的網絡後門
當然還包括很多其他的後門
但要檢測出一些更好的後門還要加入一些新的測試
誤報和執行路徑跟蹤 雖然對頻率高點的檢測有助於我們處理系統的不確定因素
但有時會發現測試得到的值有小的差值
一般來說不大於
有時這會是一個很嚴重的問題
因為我們不能確定那些多出來的指令意味著被入侵或只是正常的誤差
為解決這個問題
我們使用了執行路徑記錄模式
和單一的EPA模式比較
系統增加了對執行路徑的記錄(包括地址和運行的指令)
首先
系統記錄下正常情況下的執行路徑
以後的每一次運行將產生diff文件(正常系統和現行系統之間的比較)
我們應該使用好的反編譯器來分析那些不一樣的地方
以此判定他們是否可疑
圖
是一個diff文件的例子
現階段的diff文件只記錄下指令的地址
以後可能將兩次測試的不同結果存為PE格式文件
並可用IDA等工具分析
檢測 offsetinthecode 的變化 想象有這樣一個rootkit
它基本和上面提到的 fu rootkit (見[
]) 一樣
但不從PsActiveProcessList中
而是從分派器使用的數據結構中移除進程
我說過那不可能
因為隱藏的進程將分配不到運行時間
然而
rootkit可以同時更改分派器代碼中所使用數據結構的地址(offset)
換句話說
就是使其使用不同的鏈表
但只有分派器使用這個
新的
鏈表
而系統其他地方還是使用
舊的
鏈表
(見圖
)
雖然這種技術不會改變執行指令的個數
我們還是能檢測到它
但需要進一步的完善現有的工具
這項功能現在還沒有實現
但應該不是很難
針對EPA的攻防 我們可以想到一些能騙過EPA類檢測工具的方法
先把它們分為兩類
針對特定工具的欺騙
對EPA類技術的通用攻擊
首先
我們考慮一下通用的攻擊方法和怎樣防止這類攻擊
接著討論針對特定工具的攻擊以及怎樣通過多態來預防
對EPA類技術的通用攻擊 首先
惡意程序可以勾連包含除錯處理程序(debug handler)地址的IDT入口
這樣將暫停記錄運行的指令數
當它完成工作時
再恢復 IDT入口
這樣rootkit中所執行的指令數不會被記錄
我們可以使用intel的除錯寄存器來防止這類的攻擊
可以使用DR
和DR
寄存器對IDT入口
進行寫保護
並且為防止rootkit向除錯處理程序的開始處寫入Jmp指令
還需對其進行讀保護
換句話說
我們不想讓rootkit發現除錯處理程序的地址
但單純對IDT入口進行讀保護是不行的
系統會藍屏
但有一簡單的解決方法
就是增加額外的一層
見圖
還有一種攻擊的方法
在rootkit運行時
其將TF位清零
並在惡意操作完成時恢復TF位
這樣檢測工具也只能發現運行的指令數和正常的系統有細微差別
另外
rootkit還能檢查TF位
如發現被跟蹤
則不進行惡意操作
這種行為並不會影響rootkit的正常工作
因為只有被檢測的進程才被設置TF位
我們可以防止這種攻擊
應該注意到的是運行每一個系統指令前
都會運行我們的除錯處理程序
以下是簡單的防預方法
如果除錯處理程序發現上一個運行指令是pushf(將EFLAGS寄存器壓入堆棧)
則運行
From:http://tw.wingwit.com/Article/os/youhua/201401/30194.html