> Windows陷阱機制簡介
陷阱(Trap)是Windows系統中一種不可缺少的系統機制
當系統中發生中斷(硬件中斷或軟件中斷)
異常時
處理器會捕捉這個動作
並將系統的控制轉移到一個固定的處理程序處
進行相應的操作處理
在處理器開始處理發生的中斷或異常前
必須保存一些處理器環境參數到堆棧中以備系統還原時使用
系統是通過一種稱為陷阱幀(Trap Frame)的方式來實現的
它將系統中全部線程的環境數據保存到內核堆棧(Kernel Stack)中
在執行完後通過堆棧的出棧機制來恢復系統控制流程中的執行點
內核中的陷阱機制分為中斷和異常
中斷是系統中隨即發生的異步事件
與當前系統的處理器狀態無關
同時系統中的中斷可分為可屏蔽中斷和不可屏蔽中斷
而異常則是一種同步事件
在特定情況下異常可以重現
而中斷不可以
中斷又可以分為硬件中斷和軟件中斷
很明顯硬件中斷是與硬件相關的
比如I/O設備執行的某些操作
處理器時鐘或硬件端口上的處理等
軟件中斷則是通過中斷指令int xx引入的
它往往是應用程序在用戶模式執行後進入操作系統的代碼
這時系統為用戶提供了各種各樣的系統服務
比如我們上次提到的系統服務調用(System Service Call)
在Windows NT/
下就是通過軟件中斷int
x
e(System Service Interrupt)來實現的
雖然在Windows XP/
下微軟使用了一種稱為
快速系統調用接口
來為用戶提供系統服務
不過大量的中斷服務仍然存在與系統之中的
> 中斷處理及其相關流程
此處我們討論的是與特定處理器相關的數據結構
所以會有一些移植方面的問題
本文僅針對Intel的x
Family處理器
並且本文附帶的程序也只支持在Intel x
處理器上正常執行
何為IDT?IDT(Interrupt Descriptor Table)稱為中斷描述符表
它是可容納
個單元的數組
數組中的每個成員是稱之為
門
的長度為
字節的段描述符
在IDT中門可分為三種
中斷門(Interrrupt Gate)
陷阱門(Trap Gate)和任務門(Task Gate)
但主要的是中斷門和陷阱門
而它們兩者之間也只有少許差別
我們在此只關心IDT中的中斷門
如果您對這方面比較感興趣
請查閱Intel處理器的相關文檔《Intel Architecture Software Developer
s Manual
Volume
》
同時
在系統中存在一個中斷描述符表寄存器(IDTR)
它包含了系統中斷描述符表的基地址和IDT的限制信息
它於一條匯編指令sidt息息相關
在下文中我們將看到它是我們實現各種中斷描述符表擴展的基礎和關鍵!還有一點是需要注意的
在Windows系統中引入了分頁
分段和虛擬存儲機制後
就存在這一種調度機制
將需要執行的代碼和數據調入內存
將不需要的數據調到外存(輔助存儲器
如硬盤等)
如果我們在執行某些代碼時發現了我們需要的數據不在內存中時
就會發出一個
缺頁中斷
這時系統就會在IDT中搜尋這個中斷的ISR(Interrupt Service Routine
中斷服務例程)
執行相應的調入工作
大家可以想象如果我們的中斷描述符表被調出到外存後會是什麼樣的結果?那時系統將無法定位
缺頁中斷
的服務例程
至此系統將會崩潰掉!
在中斷描述符表中
我們剛才提到了一個感興趣的寄存器IDTR
當然我們更關心對我們來說更直接的數據
IDT中的代碼段選擇器(Code Segment Selector)
中斷執行代碼的偏移量(Offset)和中斷描述符的權限等級(Descriptor Privilege Level)參數
下面我們看看中斷指令的執行流程
我們應該知道應用程序執行在用戶模式(Ring
)下
而中斷描述符表則是存在於內核模式(Ring
)才可以訪問的系統地址空間內的
在軟件中斷發生後
也就是應用程序調用了某條軟件中斷指令後
處理器首先在IDT中檢索傳入的中斷號參數
找到響應的入口單元後就檢查中斷門的權限等級參數
看是否允許Ring
下的應用程序調用
這樣操作系統就為我們保留了對軟件中斷調用控制的權力
然而硬件中斷和異常是不會關注權限方面的信息
如果當前權限等級(Current Privilge Level
CPL)數值大於中斷門描述符需要的權限(Descriptor Privilege Level)
也就是權限不夠時會引發一個通用保護故障(General Protection Fault)
反之則進行處理器的切換從用戶堆棧到內核堆棧
現在是保存線程環境的時候了
處理器將在用戶模式下的堆棧指針(SS:ESP)和標准的中斷幀(EFLAGS和CS:EIP)壓入堆棧
之後處理器進入我們的中斷服務例程
執行相關的代碼處理後通過匯編指令iretd返回到調用的應用程序
在指令iretd執行時
系統將存儲在堆棧中的線程環境數據出棧還原
待系統恢復中斷指令執行前的環境後就接著執行應用程序的後續代碼
> 中斷相關數據結構
首先我們介紹一下前面我們提到的一條關鍵匯編指令sidt的相關數據結構
在執行指令sidt後
系統將會把中斷描述符表的基地址和限制(總共長六字節)保存在指令中指向的變量指針中
這就是我們進行IDT操作的入門口
typedef struct _idtr
{
//定義中斷描述符表的限制
長度兩字節
short IDTLimit;
//定義中斷描述服表的基址
長度四字節
unsigned int IDTBase;
}IDTR
*PIDTR;
當我們獲得了IDT的入口後
就會在中斷描述符表中檢索我們需要處理的中斷號對應的IDT單元
單元中包含了很多我們需要注意的數據結構
其中我們最為關心的是代碼段選擇器
中斷代碼執行的偏移量和特權等級等
那好我們先給出它的定義
在下文中我們將詳細討論它們的具體應用
typedef struct _idtentry
{
//中斷執行代碼偏移量的底
位
unsigned short OffsetLow;
//選擇器
也就是寄存器
unsigned short Selector;
//保留位
始終為零
unsigned char Reserved;
//IDT中的門的類型
包括中斷門
陷阱門和任務門
unsigned char Type:
;
//段標識位
unsigned char SegmentFlag:
;
//中斷門的權限等級
表示內核級
表示用戶級
unsigned char DPL:
;
//呈現標志位
unsigned char Present:
;
//中斷執行代碼偏移量的高
位
unsigned short OffsetHigh;
}IDTENTRY
*PIDTENTRY;
> 創建軟件中斷鉤子的作用
作為普通的Windows程序員
或許您需要的是熟悉對系統基本功能的操作
以及對通用程序開發的熟練掌握
但對於一個有想法的Windows內核級分析開發人員來說
對系統底層的深入了解是非常必要的
同時也是非常重要的
Hook為我們創造了一個絕好的機會
它使我們了解系統內部運行機制的想法成為了一種可能
同時
書寫一個系統相關的監視程序可以自動的對系統內部操作進行記錄與分析
當然我們不能局限於對系統的了解
我們更渴望實施對系統的修改與擴展
改變系統原有的操作特性
注入我們需要的功能組件
讓系統做更適合我們自己
也是我們最希望看到的操作
前面我們曾經談到了創建系統服務調用的鉤子來截獲系統服務調用
同樣在Windows
下
系統服務是通過系統服務中斷(System Service Interrupt
int
x
e)來實現的
通過截獲軟件中斷同樣可以達到監視並修改系統服務調用的功能
在此我們主要討論的是為軟件中斷創建鉤子
不過對於硬件中斷和異常也同樣不例外
我們同樣可以將本文提到的方法應用於硬件中斷和異常
比如我們也可以通過截獲鍵盤驅動的中斷調用來書寫內核級的鍵盤記錄器
它可以直接對每次擊鍵和釋放進行操作
效果是非常的明顯
不過這還需要使用到一些微軟為我們提供的與硬件中斷鉤子相關的函數
> 如何創建軟件中斷鉤子?
其實創建軟件中斷鉤子的過程應該是比較明顯了
下面我們將先簡要介紹一下創建Hook的過程
然後以實際代碼進行具體的講解
首先我們通過匯編指令sidt(sidt: Store Interrupt Descriptor Table Register
lidt: Load Interrupt Descriptor Table Register)來獲取IDT的基地址IDTBase
然後我們在中斷描述符表中搜尋我們需要HOOK的中斷號HOOKINTID
它應該是在
內的一個整數
雖然最新的Intel處理器聲稱支持
個中斷描述符單元
但由於某些限制原因
仍然只能處理前
個中斷描述門
在找到我們需要Hook的中斷描述門後
將它原本的中斷執行代碼偏移量(
位)保存到一個全局變量OldISR中
以備我們在執行中斷處理或恢復IDT時使用
這樣新的IDT中對應中斷號的執行代碼偏移量就指向了我們自己的處理代碼了
在我們的處理代碼NewISR中
注意先要保存一些線程環境
在處理完我們額外添加的執行程序(Monitor
監視注冊表相關的
個系統服務調用)後
恢復現場並執行中斷門以前指向的程序代碼
這樣
對外就看不出我們對中斷門做了什麼額外的處理
感覺和以前沒什麼兩樣!如果我們只是處理了我們添加的代碼而沒有繼續執行中斷門對應的以前的程序代碼
那麼系統必將混亂甚至崩潰!同樣在我們卸載我們的軟件中斷鉤子時
就是進行了一個逆向工作
先獲取IDT的基地址
然後將保存在全局變量中的舊的執行代碼地址偏移量賦給對應中斷號的偏移量單元(OffsetLow/OffsetHigh)
大概過程講得差不多了
相關程序為T
HookInt
我們再看看代碼吧!
VOID
HookInt(VOID)
{
//保存IDT入口的基地址和限制信息的數據結構
IDTR idtr;
//記錄IDT數組的指針
通過它可以查找到我們需要Hook中斷號對應的中斷門
PIDTENTRY IdtEntry;
//匯編指令sidt
獲取IDT入口信息
__asm sidt idtr;
//賦予IDT基地址值
IdtEntry = (PIDTENTRY)idtr
IDTBase;
//保存中斷號HOOKINTID對應
From:http://tw.wingwit.com/Article/os/youhua/201311/10802.html