0x01 句柄,句柄表概念數組
任意進程,只要每打開一個對象,就會得到一個句柄,這個句柄用來標誌對某個對象的一次打開,經過句柄,能夠直接找到對應的內核對象。句柄自己是進程的句柄表中的一個結構體,用來描述一次打開操做。句柄值則能夠簡單看作句柄表中的索引,並不影響理解。HANDLE的值能夠簡單的看作一個整形索引值。函數
每一個進程都有一個句柄表,用來記錄本進程打開的全部內核對象。句柄表能夠簡單看作爲一個一維數組,每一個表項就是一個句柄,一個結構體,一個句柄描述符,其結構體定義以下:spa
typedef struct _HANDLE_TABLE_ENTRY //句柄描述符 { union { PVOID Object;//關鍵字段。該句柄指向的內核對象(注意是其頭部) ULONG_PTR ObAttributes;//關鍵字段。該句柄的屬性 PHANDLE_TABLE_ENTRY_INFO InfoTable; ULONG_PTR Value;//值(可見值自己是一個複合體),最低3位表示該句柄的屬性(Value= Object | ObAttributes) }; union { ULONG GrantedAccess;//關鍵字段。該句柄的訪問權限 struct { USHORT GrantedAccessIndex; USHORT CreatorBackTraceIndex; }; LONG NextFreeTableEntry;//當本句柄是一個空閒表項時,用來連接到句柄表中下一個空閒表項 }; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
一 個 進 程 可 能 同 時 打 開 許 多 對 象 ,跟 許 多 對 象 建 立 連 接 , 還 可 以 跟 同 一 個 對 象 建 立 起 多 個 連 接 。 所 以 每 個 程 都 需 要 有 個 句 柄 表 用 來 記 錄 , 維 持 這 些 連 接 。 所 以 , 句 柄 表 最 基 本 的 做
用 就 是 一 張 句 柄 與 目 標 對 象 之 間 的 對 照 表 , 而 句 柄 表 中 的 每 個 表 項 , 則 代 表 着 一 個 具 體 的 連 接 。 所 以 , 在 "進 程 控 制 控 制 *"EPROCESS#「中有 指 針 ObjectTable, 用 來指 向 本 進 程 向 柄 表。線程
句柄表結構體定義:指針
typedef struct _HANDLE_TABLE //句柄表描述符 { ULONG TableCode; //表的地址|表的層數(該字段的最後兩位表示表的層數) PHANDLE_TABLE_ENTRY **Table; PEPROCESS QuotaProcess;//所屬進程 PVOID UniqueProcessId; //所屬進程的PID EX_PUSH_LOCK HandleTableLock[4]; LIST_ENTRY HandleTableList;//用來掛入全局的句柄錶鏈表(間接給出了系統中的進程列表) EX_PUSH_LOCK HandleContentionEvent; ERESOURCE HandleLock; LIST_ENTRY HandleTableList; KEVENT HandleContentionEvent; PHANDLE_TRACE_DEBUG_INFO DebugInfo; LONG ExtraInfoPages; ULONG FirstFree;//第一個空閒表項的索引位置 ULONG LastFree;//最後一個空閒表項的索引位置 ULONG NextHandleNeedingPool;//本句柄表自己佔用的內存頁數 LONG HandleCount;//表中的有效句柄總數 union { ULONG Flags; UCHAR StrictFIFO:1; }; } HANDLE_TABLE, *PHANDLE_TABLE;
每當建立或打開了一個對象,要爲之建立句柄並插入句柄表的時候,就爲其準備一個臨時的HANDLE_TABLE_ENTRY結構,使其指向這個對象的頭部,而後經過EXCreateHandle將其「安裝」句柄表中。而EXCreateHandle返回句柄,句柄的值代表了安裝的位置。對象
ExCreateHandle函數原型(關鍵代碼):blog
HANDLE ExCreateHandle(PHANDLE_TABLE HandleTable, PHANDLE_TABLE_ENTRY HandleTableEntry) { EXHANDLE Handle; NewEntry = ExpAllocateHandleTableEntry(HandleTable,&Handle);//在句柄表中找到一個空閒表項
... *NewEntry = *HandleTableEntry;//複製句柄表項
... return Handle.GenericHandleOverlay;//返回句柄值(也即空閒表項的索引位置) }
ObpCreateHandle函數打開對象,得到句柄:繼承
NTSTATUS
ObpCreateHandle(IN OB_OPEN_REASON OpenReason,//4種打開時機
IN PVOID Object, //要打開的對象
IN PACCESS_STATE AccessState, //句柄的訪問權限
IN ULONG HandleAttributes, //句柄的屬性
IN KPROCESSOR_MODE AccessMode,
OUT PHANDLE ReturnedHandle) //返回的句柄值
{
BOOLEAN AttachedToProcess = FALSE, KernelHandle = FALSE;
NewEntry.Object = ObjectHeader;//關鍵。將該句柄指向對應的對象頭
if (HandleAttributes & OBJ_KERNEL_HANDLE)//若是用戶要求建立一個全局型的內核句柄
{
HandleTable = ObpKernelHandleTable;//改用內核句柄表
KernelHandle = TRUE;
//將當前線程掛靠到system進程,也即修改當前的CR3,將頁表換成system進程的頁表
if (PsGetCurrentProcess() != PsInitialSystemProcess)
{
KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
AttachedToProcess = TRUE;
}
}
else
HandleTable = PsGetCurrentProcess()->ObjectTable;//使用當前進程的句柄表
//檢查是否能夠獨佔打開,檢查權限,若各項檢查經過纔打開對象,遞增句柄計數,調用對象的OpenProcedure等等工做
Status = ObpIncrementHandleCount(Object,
AccessState,
AccessMode,
HandleAttributes,
PsGetCurrentProcess(),
OpenReason);
if (!NT_SUCCESS(Status))
return Status;
NewEntry.ObAttributes |= (HandleAttributes & OBJ_HANDLE_ATTRIBUTES);//填上句柄的屬性
DesiredAccess =AccessState->RemainingDesiredAccess|AccessState->PreviouslyGrantedAccess;
GrantedAccess = DesiredAccess &(ObjectType->TypeInfo.ValidAccessMask);
NewEntry.GrantedAccess = GrantedAccess;//填上句柄的屬性
Handle = ExCreateHandle(HandleTable, &NewEntry);//將句柄插入到句柄表中
if (Handle)//if 插入成功
{
if (KernelHandle)
Handle = ObMarkHandleAsKernelHandle(Handle);//將句柄值的最高位設爲1,標記爲內核句柄
*ReturnedHandle = Handle;
if (AttachedToProcess)
KeUnstackDetachProcess(&ApcState);//撤銷掛靠
return STATUS_SUCCESS;
}
Else
{
…
return STATUS_INSUFFICIENT_RESOURCES;
}
}
打開對象,以獲得一個訪問句柄。有四種打開時機:索引
一、 建立對象時就打開,如CreateFile在建立一個新文件時,就同時打開了那個文件對象進程
二、 顯式打開,如OpenFile,OpenMutex,OpenProcess顯式打開某個對象
三、 DuplicateHandle這個API間接打開對象,得到句柄
四、 子進程繼承父進程句柄表中的句柄,也可看作是一種打開
在這四種狀況下,都會調用這個函數來打開對象,獲得一個句柄。OpenReason參數就是指打開緣由、時機
注意句柄值的最高位爲1,就表示這是一個內核全局句柄,能夠在各個進程中通用。不然,通常的句柄,只能在對應的進程中有意義。
另外有兩個特殊的僞句柄,他們並不表示‘索引’,而是一個簡單的代號值
GetCurrentProcessHandle 返回的句柄值是-1
GetCurrentThreadHandle 返回的句柄值是-2
對這兩個句柄要特殊處理。
句柄不光含有指向對象的指針,每一個句柄都還有本身的訪問權限與屬性,這也是很是重要的。訪問權限表示本次打開操做要求的、申請的而且最終獲得的權限。句柄屬性則表示本句柄是否能夠繼承,是不是獨佔打開的,是不是一個內核句柄等屬性。