call *sys_call_table(0, %eax, 4) //Calling sys_call_table[eax]
movl %eax,PT_EAX(%esp) //Storing of return value
系統調用例程核心。eax寄存器存放系統調用号,即sys_call_table數組索引值,指向
某個特定系統調用函數。函數傳回值将存放到棧中的eax寄存器中,傳回給使用者空間。盡管
系統調用例程核心很适合劫持,但我們不利用此位置,因為系統調用表指針是目前各種掃描
工具首要的檢測點。
cli //Clear Interrupts
movl TI_flags(%ebp), %ecx //Copy process flags in ecx
testw $_TIF_ALLWORK_MASK, %cx //Is needed extra work?
jne syscallexitwork //If so, do extra work
當系統調用函數傳回時,系統會屏蔽所有中斷,程序測試是否有額外的工作(确信不丢
失需要排程或發信号挂起的中斷)。上面的代碼片段提供了另一個劫持機會。movl和testw
指令共8位元組大小,足以存放JMP指令,而且這兩條指令完全獨立不在任一标簽體内,可複制。
RESTORE_REGS //CPU’s registers restoration
addl $4, %esp //Clearing up system call number from stack
iret //Return from interrupt
調用例程實作體尾部即準備從系統态切回到使用者态。用暫存在棧上的值恢複所有CPU寄
存器值,清除系統調用号,觸發iret指令。尾部指令也可作為劫持位置,但問題是當程序被
跟蹤時,并不會使用這些代碼片段。
3.2 改變系統調用例程中的程式控制流
通過3.1 小節分析,我們已經在系統調用例程中确定了兩個合适位置可劫持系統控制流
程。在 2.6核心的所有版本都相容這種方式的更改,具備很好的相容性和可用性。我們的目
的是修改系統調用函數的傳回值,即在原函數調用後,再劫持轉入到 hack 函數執行。故,
我們将确定選擇第二個位置實作劫持,如圖2 所示。否則,若在函數還未調用之前,轉入攻
擊者設定的hack函數時,執行完hack 函數後,再調用原函數,則無法實作hack函數目的,
如隐藏檔案效果等,而且若不再調用原函數,顯然也不合乎邏輯,否則上層應用程式明明是
調用 fork 函數建立一個子程序,卻沒有實作此功能,很容易被使用者感覺到存在核心鈎子程
序。
我們重寫了movl TI_flags(%ebp), %ecx 和testw $_TIF_ALLWORK_MASK, %cx
兩條指令,并跳轉到trampoline()函數中重新執行,但在重新執行上面兩條指令之前,事先
儲存了棧頂位址值,以確定能準确通路到系統調用結果,而後調用hack函數,修改原系統調
用的結果資訊,如過濾待隐藏的木馬檔案、惡意程序等。執行完此函數後,再對此函數的棧
痕迹進行清理,最後重新jmp到原位置繼續執行。
實驗
為了驗證前面提出的劫持方法,筆者基于此方法開發了兩款rootkits,分别為MoleKit
和 Powerkit。MoleKit能夠借助/dev/mem檔案有效滲透到系統。Molekit可提供一些基本的
服務,如程序、目錄隐藏等。因為這種攻擊方式涉及到很多啟發式搜尋,如在記憶體中尋找代
碼片段特征值等,但這也是一種很好的思路,可以在不借助可加載子產品支援前提下,實作系
統滲透;Powerkit則是使用核心子產品滲透到核心,其主要好處是可友善通路核心API,并實
現 keylogging或權限提升等。
小結
本文給出了劫持Linux核心的一個新方法,此類攻擊在2.6核心的所有版本上均驗證可
行,并給出了兩款基于此技術的Rootkit,且目前并沒有很好的Anti-Rootkit或掃描器等工
具能夠檢測此類攻擊。