談windows中的句柄

談windows中的句柄windows

 
每當一個進程打開一個對象,系統就返回一個句柄做爲憑證,由此能夠想到,句柄是依賴於具體的進程的,換句話說,句柄必定屬於某個進程,之後在訪問這個對象時就要使用這個憑證!
    因此句柄還能夠認爲是一個進程和一個對象之間創建的一種鏈接,一座橋樑,一個上下文,就像一個門同樣,已經打開。這種鏈接就持續存在,直到關閉。
一個進程可打開對個對象,就會擁有多個句柄,因此每一個進程都擁有一個句柄表,在進程控制塊EPROCESS中有個指針ObjectTable是 _HANDLE_TABLE類型,指向本進程的句柄表!
看下 _HANDLE_TABLE結構:
 1 kd> dt _handle_table
 2 nt!_HANDLE_TABLE
 3 +0x000 TableCode : Uint4B
 4 +0x004 QuotaProcess : Ptr32 _EPROCESS
 5 +0x008 UniqueProcessId : Ptr32 Void
 6 +0x00c HandleLock : _EX_PUSH_LOCK
 7 +0x010 HandleTableList : _LIST_ENTRY //句柄表雙向鏈表
 8 +0x018 HandleContentionEvent : _EX_PUSH_LOCK
 9 +0x01c DebugInfo : Ptr32 _HANDLE_TRACE_DEBUG_INFO
10 +0x020 ExtraInfoPages : Int4B
11 +0x024 Flags : Uint4B
12 +0x024 StrictFIFO : Pos 0, 1 Bit
13 +0x028 FirstFreeHandle : Uint4B
14 +0x02c LastFreeHandleEntry : Ptr32 _HANDLE_TABLE_ENTRY
15 +0x030 HandleCount : Uint4B
16 +0x034 NextHandleNeedingPool : Uint4B
17 +0x038 HandleCountHighWatermark : Uint4B

 

TableCode的低兩位被用做標誌位,用於表示當前句柄表的級數,0,1,2分別表示一級表,二級表,三級表。
一級表其實是一個_HANDLE_TABLE_ENTRY 數組,每一個_HANDLE_TABLE_ENTRY 8個字節,而一級表是一個page的大小,因此一級表能夠容納2^9個_HANDLE_TABLE_ENTRY 
 1 lkd> dt _HANDLE_TABLE_ENTRY
 2 nt!_HANDLE_TABLE_ENTRY
 3 +0x000 Object : Ptr32 Void//指向對象的一個指針
 4 +0x000 ObAttributes : Uint4B
 5 +0x000 InfoTable : Ptr32 _HANDLE_TABLE_ENTRY_INFO
 6 +0x000 Value : Uint4B
 7 +0x004 GrantedAccess : Uint4B
 8 +0x004 GrantedAccessIndex : Uint2B
 9 +0x006 CreatorBackTraceIndex : Uint2B
10 +0x004 NextFreeTableEntry : Uint4B

 

而句柄以4爲步進(注0),所以最大句柄爲0x200*4=0x800.其中可存放的最大句柄不超過0x800(最大爲0x800-4),而每一個一級表的第一個HANDLE_TABLE_ENTRY的Object老是爲0,由於咱們都知道0是一個無效的句柄,它不指向一個有效的對象。所以,每一個一級表實際存放的句柄數爲511個!
 
_HANDLE_TABLE結構中有個TableCode字段,該字段有32位,低2位做爲標記是幾級表,0爲一級,1爲2級,2爲3級。當爲0時,TableCode值就指向一個Page,此頁就是句柄表,每一個表項8個字節爲一個entry,低4個字節是對象地址,注意第一個entry不表明任何對象。
若TableCode前兩位爲1或者2,那麼TableCode&0xfffffffd就指向二級或者三級表,每一個表項四個字節,指向一個一級或者二級表,以此類推!!
注意:因爲對象體都是8字節對齊的,因此對象地址的低3位老是0,可用於標記對象的某些屬性,因此在一級句柄表中的對象地址要&0xfffffff8才表示對象頭的地址,加上0x18後獲得對象體(這裏涉及到windows內核中對象的管理,參見另外一篇關於對象的文章)!
句柄表架構如圖所示:

 

至於爲何句柄表的號都是4的倍數呢?


一個進程的句柄表包含了全部已被該進程打開的那些對象的指針。對象句柄是用來檢索句柄表的一個「僞索引」。對於句柄表機制,achillis <<Windows句柄表>>系列文章已經分析得很透徹了,只是對「句柄以4爲步進」來源不明。經查,根源以下:

typedef struct _EXHANDLE
{
 union
 {
  struct
  {
   ULONG TagBits:2;
   ULONG Index:30;
  }
  HANDLE GenericHandleOverlay;
  #define HANLE_VALUE_INC 4
  ULONG_PTR Value;
 }

}EXHANDLE,*PEXHANDLE;
此結構正是用來定義句柄類型。低2位TagBits爲標誌位Windows用於其它用途,故句柄值低2位對其做爲句柄表索引自己無心義,因此等於4的倍數。有了以上分析,天然,在用句柄值爲索引取句柄表項時,句柄值必須/4。所以程序中用到的句柄值並不能直接用來索引句柄表,也就有了「僞索引」說法。
數組

 
在windows系統中,主要分爲兩種句柄表:
一、單個進程的句柄表
二、系統全局句柄表pspCidTable
前者主要用於進程打開的各類對象,然後者用於分配全局進程PID。以任務管理器關閉某個進程爲例,若是其要關閉一個進程,首先根據進程PID打開其進程並獲取訪問這個進程的句柄,這時,PID對應在PspHandleTable中的索引,而得到的句柄對應任務管理器的句柄表中的索引,僅僅在任務管理器的進程空間中有效,一個全局、一個局部。而解析句柄和PID的過程徹底一致。
主要區別在於全局句柄表的表項指向的是對象體而不是對象頭。
相關文章
相關標籤/搜索