Windows內核對象簡介


內核對象只是操作系統內核分配的一個內存塊,並且只能由操作系統內核訪問。該內存塊是一種數據結構,它的成員負責維護該對象的各種信息。Windows提供一組函數創建和操作內核對象。調用一個創建內核對象的函數,函數會返回一個句柄,該句柄標識了這個內核對象,這個句柄可由當前進程中的所有線程調用。也可以通過跨進程邊界共享內核對象,讓其他的進程調用。

 

使用計數。內核對象有個使用計數數據成員,標識內核對象被多少個進程所使用。大部分情況是內核對象只被創建它的進程所有使用,當這個進程退出時,內核對象的使用計數就會減一,如果內核對象的使用計數為0時,內核對象就會自動銷毀,如果內核對象被多個進程使用時,它的生命周期就可能比創建它的進程要長。只要內核對象的使用計數不為0,它就不會銷毀,當當前進程退出時,只要有其他進程使用這個內核對象,它就不會銷毀。但是我們也不用擔心內核對象導致的內存泄露,就算進程沒有手動關閉內核對象,進程在退出的時候,會檢查自己的句柄表,如果句柄表中有使用的內核對象,操作系統會為我們關閉這些句柄,被這些句柄引用的內核對象的使用計數就會減一,如果使用計數為0,內核對象就會自動銷毀。從上面我們可以看出,內核對象並沒有和創建它的進程所綁定,創建它的進程退出了,內核對象可能還存活,內核對象的操作所有者是操作系統,而不是進程。

 

內核對象的安全性。內核對象可以用一個安全描述符來保護,安全描述符描述了誰是該對象的擁有者,那些組和用戶可以訪問或使用這些對象,這個對象是否可以被繼承等。SECURITY_ATTRIBUTES這個結構體就是用於安全描述的。其結構如下:

typedef struct _SECURITY_ATTRIBUTES {

    DWORD nLength;//表示結構體的長度

    LPVOID lpSecurityDescriptor;//指向一個安全描述符

    BOOL bInheritHandle;//表示內核對象被子進程繼承

} SECURITY_ATTRIBUTES;

區分一個對象是否是內核對象就可以看創建對象函數的參數中有沒有SECURITY_ATTRIBUTES這個參數,有的就是內核對象,沒有的就不是內核對象。有了這個結構就將內核對象保護起來了,其他對象就不能隨便訪問它,也就不能破壞它的內存結構。

 

進程內核對象句柄表。一個進程在初始化時,系統將為它分配一個句柄表。這個句柄表僅供內核對象使用,不適用於用戶對象和GDI對象,句柄表的結構大致包括:

1:索引

2:指向內核對象內存塊的指針

3:訪問掩碼

4:標志

一個進程在首次初始化的時候,其句柄表為空,即進程沒有引用任何內核對象,當進程中的一個線程調用一個創建一個內核對象的函數時,內核將會為這個內核對象分配並初始化一個內存塊,然后內核會掃描句柄表,找到一個空白的記錄項,指針成員會指向剛創建的內核對象的內存地址,訪問掩碼會設置成擁有完全訪問的權限,如果這個內核對象可以被繼承,標志成員將設為1,如果不能被繼承就是0。當調用關閉內核對象的函數Closehandle,引用該內核對象的進程中的句柄表中相應的記錄項就會被清除,當進程退出的使用句柄表中的所有記錄都會被清除,所有被使用的內核對象的使用計數都會減一,使用計數為0的內核對象就會自動銷毀。就算沒有手動關閉內核對象,進程退出了也不會出現內核對象泄露,當一個內核對象沒有被任何進程引用時會自動銷毀的。

 

跨進程邊界共享內核對象。在很多時候需要再不同的進程中共享內核對象,如信號量,互斥量和事件允許不同的進程中的線程同步執行,這時就需要共享內核對象。共享內核對象的方式有三種:

1:使用內核對象句柄繼承

2:為對象命名

3:復制對象句柄

1:使用內核對象句柄繼承。只有進程之間有父子關系時才能使用內核對象句柄繼承。若一個對象進程允許其子進程繼承它的句柄,那么它在創建可被繼承的內核對象時,創建內核對象的函數的參數SECURITY_ATTRIBUTES的成員bInheritHandle要設為true,這樣當父進程創建一個子進程時,子進程就會復制父進程的句柄表中可以被繼承的句柄到自己的句柄表中。每個進程都有自己的句柄表,父進程與子進程之間並不是共享句柄表,所以當子進程創建好了后,父進程在創建一個可以被繼承的內核對象時,只進程並不能訪問這個內核對象,因為它根本不知道這個內核對象的存在,當然子進程創建自己的內核對象時,父進程也是不知道的。因為微軟沒有寫多於的代碼來復制這些可被繼承的句柄到相應的句柄表中。所以說內核對象並不是真的繼承,而是繼承了內核對象的句柄。也就是說句柄只是內核對象的一個指針,而不是內核對象的一個成員。

我們可以通過函數SetHandleInformation來標志內核對象是否可一個被子進程繼承。如有一個可以被子進程繼承的內核對象,但你不想讓某個進程繼承,在創建這個子進程之前將該內核對象標記為不可繼承,在創建子進程,然后在調用函數SetHandleInformation將內核對象標記為可以繼承,這個其他即將要創建的子進程就可以共享這個內核對象。

SetHandleInformation(

    __in HANDLE hObject,//內核對象的句柄

    __in DWORD dwMask,//告訴函數想更改哪個或哪些標志

    __in DWORD dwFlags//是否可以被繼承

);

 

2:為對象命名。很多創建內核對象的函數都有一個參數lpName指定內核對象的名字,如以下兩個函數:

CreateMutexA(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,

    BOOL bInitialOwner,

    LPCSTR lpName

);

 

CreateEventW(

    LPSECURITY_ATTRIBUTES lpEventAttributes,

    BOOL bManualReset,

    BOOL bInitialState,

    LPCWSTR lpName

);

 

最后一個參數lpName的值就是內核對象的名稱,如果為這個參數傳入NULL,表示不想為該內核對象命名。如下面的代碼創建了一個名為CTH的互斥量內核對象。

HANDLE mutexProcessA=CreateMutex(NULL,FALSE,TEXT("CTH"));

另一個進程B也想創建一個互斥量內核對象,代碼如下:

HANDLE mutexProcessB=CreateMutex(NULL,FALSE,TEXT("CTH"));

內核並不會馬上創建一個互斥量內核對象,而是會先檢查是否存在一個名為CTH的互斥兩內核對象,若有,判斷是否有權限,若有,會將已經存在的互斥量的句柄復制到進程B的句柄表中,將互斥量的句柄返回給mutexProcessB,若不存在名為CTH的對象當然是創建一個,若是存在一個名為CTH的其他類型的內核對象那就會創建失敗,函數返回NULL。還是那句話內核對象屬於操作系統,跟是哪個進程創建它關系並不大,這樣也就很容易實現內核對象的共享了。

 

3:復制對象句柄。使用函數DuplicateHandle。

DuplicateHandle(

    HANDLE hSourceProcessHandle,

    HANDLE hSourceHandle,

    HANDLE hTargetProcessHandle,

    LPHANDLE lpTargetHandle,

    DWORD dwDesiredAccess,

    BOOL bInheritHandle,

    DWORD dwOptions

    );

這個函數獲取一個進程句柄表中的一個記錄項,然后在另一個進程的句柄表中創建該記錄項的一個副本,然后另一個進程也就能訪問這個內核對象了。

作者:陳太漢

博客:http://www.cnblogs.com/hlxs/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM