內核對象


     

  1. 每一個內核對象都只是一個內存塊,它由操做系統內核分配,並只能由操做系統內核訪問。這個內存塊是一個數據結構,其成員維護着與對象相關的信息。少數成員(安全描述符和使用計數)是全部對象都有的,但其餘大多數成員都是不一樣類型的對象特有的數組

  2. 因爲內核對象的數據結構只能由操做系統內核訪問,因此應用程序不能在內存中定位這些數據結構並直接更改其內容。正由於有這個限制,因此微軟能自由地添加、刪除或修改這些數據結構中的成員,同時不會干擾任何應用程序的正常運行安全

  3. 爲了加強系統的可靠性,內核對象的句柄是與進程相關的。若是進程A將一個屬於A進程的內核對象句柄傳送給進程B,那麼進程B中的線程用進 程A傳入的句柄發出調用時,就有可能失敗;更糟糕的狀況是,進程B會根據傳入的句柄值在進程B的句柄表中引用另外一個徹底不一樣的內核對象,將致使沒法預測的 後果session

  4. 內核對象的全部者是操做系統內核,而非進程數據結構

  5. 使用計數:函數

    • 操做系統內核知道有多少個進程正在使用一個特定的內核對象,由於每一個對象都包含一個使用計數(usage count)spa

    • 使用計數是全部內核對象類型都有的一個數據成員操作系統

    • 初次建立一個對象的時候,其使用計數被設爲1。另外一個進程得到對現有內核對象的訪問後,使用計數就會遞增。進程終止運行後,操做系統內核將自動遞減此進程仍然打開的全部內核對象的使用計數.net

    • 若是一旦對象的使用計數遞減爲0,操做系統就會銷燬該對象命令行

  6. 內核對象能夠用一個安全描述符(security descriptor, SD)來保護。安全描述符描述了誰擁有對象;哪些組和用戶被容許訪問或使用此對象;那些組和用戶被拒絕訪問此對象線程

  7. 進程內核對象句柄表(後文簡稱句柄表):

    • 一個進程在初始化時,系統將爲它分配一個句柄表(handle table)。這個句柄表僅供內核對象使用,不適用於用戶對象或GDI對象

    • 句柄表是一個由數據結構組成的數組。每一個結構都包含一個內核對象指針、一個訪問掩碼和可繼承標誌,如:

    • 一 個進程首次初始化的時候,其句柄表爲空。當進程內的一個線程調用一個會建立內核對象的函數,內核將爲這個對象分配並初始化一個內存塊。而後,內核掃描進程 的句柄表,查找一項空白的記錄項,並對其進行初始化(指針成員會被設置成內核對象的數據結構的內部內存地址,訪問掩碼將被設置成擁有徹底訪問權限,繼承標 志也會被設置)

    • 用於建立內核對象的任何函數都會返回一個與進程相關的句柄,這個句柄可由同一個進程中運行的全部線程使用。系統用索引來表示內核對象的信息保存在進程句柄表中的具體位置,句柄值除以四便是實際的索引值

    • 調用一個函數時,若是它接受一個內核對象句柄做爲參數,就必須把Create*函數返回的值傳給它。在內部,這個函數會查找進程的句柄表,得到目標內核對象的地址,而後以一種恰當的方式來操縱對象的數據結構

  8. 關閉內核對象(CloseHandle):

    • 該函數首先檢查調用進程的句柄表,驗證「傳給函數的句柄值」標識的是「進程確實有權訪問的一個對象」。若是句柄是有效的,系統將得到內核對象的數據結構的地址,並將結構中的「使用計數」成員遞減。若是使用計數變爲0,內核對象將被銷燬,並從內存中除去

    • 就在CloseHandle函數返回以前,它會清楚進程句柄表中對應的記錄項——這個句柄如今對咱們的進程來講是無效的,不要再試圖用 它。不管內核對象當前是否銷燬,這個清除過程都會發生!一旦調用CloseHandle,咱們的進程就不能訪問那個內核對象;可是若是對象的使用計數還沒 遞減至0,它就不會被銷燬

    • 調用CloseHandle後,應將保存句柄值的變量置NULL。若是不當心使用了未被置NULL的已被CloseHandle的值,將會發生兩種狀況:

    1. 因爲此變量所引用的句柄表記錄項已被清除,調用函數調用失敗

    2. 建立一個新的內核對象時,Windows會在句柄表中查找空白記錄。因此,未被置NULL的變量極有可能匹配到新建立的內核對象。函數 調用時,一旦錯誤地用這個還沒有置NULL的變量,就可能定位到一個錯誤類型的內核對象(這種狀況會報錯)。跟糟糕的是,可能會定位到一個類型(和已經關閉 的內核對象)相同的內核對象(這種狀況不會報錯)。這種狀況下,應用程序的狀態將損壞,沒有任何辦法能夠恢復

  9. 當進程終止運行,操做系統會確保此進程使用的全部資源(內核對象、資源、內存塊等)都被釋放,系統會確保進程不會留下任何東西。對於內核 對象,操做系統執行如下操做:進程終止時,系統自動掃描該進程的句柄表。若是這個表中的任何有效記錄項(進程終止前沒有關閉的對象),操做系統會關閉這些 對象的句柄。若某對象的使用計數遞減爲0,內核就會銷燬對象

  10. 使用對象句柄繼承:

    • 只有在進程之間有一個父——子關係的時候,纔可使用對象句柄繼承

    • 步驟:

    • 當爲CreateProcess函數的bInheritHandles傳遞TRUE時,操做建立新的子進程,但不容許子進程當即執行它 的代碼。系統會爲子進程建立一個新的、空白的進程句柄表——就像它爲任何一個新進程全部的那樣。系統還會多作一件事:它會遍歷父進程的句柄表,對它的每一 個及錄像進行檢查。凡是包含一個有效的「可繼承的句柄」的項,都會被完成地複製到子進程的句柄表。在子進程的句柄表中,複製項的位置與它在父進程句柄表中 的位置是徹底同樣的。這意味着:在父進程和子進程中,對一個內核對象進行標識的句柄值是徹底同樣的

    • 對象句柄的繼承只會在生成子進程的時候發生。假如父進程後來又建立了新的內核對象,並一樣將它們的句柄設爲可繼承的句柄。那麼正在運行的子進程是不會繼承這些新句柄的

    • 對象句柄的繼承還有一個很是奇怪的特徵:子進程並不知道本身繼承了任何句柄。爲了使子進程獲得它想要的一個內核對象的句柄值,最經常使用的方式是將句柄值做爲命令行參數傳遞給子進程。能夠這樣作的緣由是,父子進程對一個內核對象進行標識的句柄值是徹底同樣的

    • 能夠調用SetHandleInformation來改變內核對象句柄的繼承標誌,這樣父進程就能夠控制哪些子進程能繼承內核對象句柄了

    1. 當父進程建立一個內核對象時,父進程必須向系統指出它但願這個對象的句柄是能夠繼承的。注意,只有句柄是能夠繼承的,對象自己是不能繼 承的。爲了建立一個可繼承的句柄,父進程必須分配並初始化一個SECURITY_ATTRIBUTES結構,將 SECURITY_ATTRIBUTES.bInheritHandle設爲TRUE,並將這個結構的地址傳給具體的Create函數

    2. 建立子進程,調用CreateProcess來完成。若是參數bInheritHandles被設置爲TRUE,子進程就會繼承父進程的「可繼承句柄」的值

  11. 爲對象命名:

    • 全部進程的全部內核對象都共享同一個命名空間,即便它們的類型並不相同

    • 進程A建立了一個名爲「TTMutex」的互斥量內核對象,以後,進程B發生如下調用

        HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, TEXT("TTMutex"));
    • 用於建立內核對象的函數Create*老是返回具備徹底訪問權限的句柄。若是想限制一個句柄的訪問權限,可使用Create*ex

    • 在調用Create*以後,能夠當即調用GetLastError,若返回ERROR_ALREADY_EXIST則代表僅僅打開了一個現有的對象,而並不是建立了一個新的

    • 微軟沒有提供任何專門的機制來保證咱們建立獨一無二的對象名

    • 能夠利用命名對象來防止運行一個應用程序的多個實例

    1. 當進程B調用CreateMutex時,系統首先會查看是否存在一個名爲「TTMutex」的內核對象。因爲已存在,因此內核接着檢查 對象的類型。因爲試圖建立一個互斥對象,而名爲「TTMutex」的對象也是一個互斥對象,因此係統接着執行一次安全檢查,驗證調用者是否擁有對該對象的 徹底訪問權限。若是答案是確定的,系統就會在進程B的句柄表中查找一個空白記錄項,並將其初始化爲指向現有的內核對象。若是類型不匹配,或調用者被拒絕訪 問,CreateMutex就會失敗(返回NULL)

    2. 進程B調用CreateMutex時,它會向函數傳遞安全屬性信息和第二個參數,若是已經存在一個指定名稱的對象,這些參數就會被忽略

  12. 終端服務命名空間:

    HANDLE h = CreateEvent(NULL, FALSE, FALSE, TEXT("Global\\MyName"));

    也能夠顯示把一個內核對象放入當前會話的命名空間,只要在名稱前加上「Local\」便可:

    HANDLE h = CreateEvent(NULL, FALSE, FALSE, TEXT("Local\\MyName"));
    • 在正在運行終端服務的計算機中,有多個用於內核對象的命名空間。其中一個是全局命名空間,全部客戶端都能訪問的內核對象要放在這個命名空間中。這個命名空間主要由服務使用。此外,每一個客戶端會話(client session)都有一個本身的命名空間,這樣不會相互干擾

    • 服務的命名內核對象始終位於全局命名空間中。默認狀況下,在終端服務中,應用程序本身的命名內核對象在會話的命名空間內。能夠在其名稱前加上「Global\」前綴來強制把一個命名對象放入全局命名空間:

  13. 複製對象句柄:

        BOOL DuplicateHandle(HANDLE hSourceProcessHandle,    ///< 源進程進程內核對象句柄
                             HANDLE hSourceHandle,            ///< 要複製的源內核對象句柄
                             HANDLE hTargetProcessHandle,    ///< 目標進程進程內核對象句柄
                             LPHANDLE lpTargetHandle,        ///< 用來接收穫得的句柄值
                             DWORD dwDesiredAccess,            ///< 訪問掩碼
                             BOOL bInheritHandle,              ///< 繼承標誌
                             DWORD dwOptions);                ///< 句柄表項,若爲DUPLICATE_SAME_ACCESS,函數會忽略dwDesiredAccess;若爲DUPLICATE_CLOSE_SOURCE,內核對象的使用計數不變
    • 這個函數得到一個進程的句柄表中的一個記錄項,而後在另外一個進程的句柄表中建立這個記錄項的一個副本

    • 調用函數以後,目標進程不知道它如今能訪問一個新的內核對象,必須使用進程間通訊方法來通知目標進程(命令行和環境變量均行不通,由於進程早就存在,需考慮其餘方法)

    • 假設一個進程擁有對一個文件映射對象的讀寫權限,可使用該函數爲現有的對象建立一個新句柄,並確保這個新句柄只有只讀權限,這樣就能夠保證新句柄不會有意外的寫入操做,這是該函數經常使用的方法之一

    • 函數原型

相關文章
相關標籤/搜索