句柄與句柄表(數據結構,源碼分析)

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

對這兩個句柄要特殊處理。

 

句柄不光含有指向對象的指針,每一個句柄都還有本身的訪問權限與屬性,這也是很是重要的。訪問權限表示本次打開操做要求的、申請的而且最終獲得的權限。句柄屬性則表示本句柄是否能夠繼承,是不是獨佔打開的,是不是一個內核句柄等屬性。

相關文章
相關標籤/搜索