1、何為內核對象
每個內核對象都只是一個內存塊,它由操作系統內核分配,并只能由操作系統內核訪問。這個內存塊是一個數據結構,其成員維護著于對象相關的信息。少數成員(安全描述符何使用計數等)是所有對象都有的,但其他大多數成員都是不同類型的對象特有的。
e.g. 訪問令牌(access token)對象、事件對象、文件對象、文件映射對象、I/O完成端口對象、作業對象、郵件槽(mailsolt)對象、互斥量(mutex)對象、管道(pipe)對象等
*可利用Sysintenals的WinObj(http://technet.microsoft.com/zh-cn/sysinternals/bb896657.aspx)工具查看內核對象類型的列表。
1.1、使用計數
使用計數事所有內核對象類型都有的一個數據成員。初次創建對象事,其使用計數被設為1。如果另一進程獲得對現有內核對象的訪問后,使用計數會遞增。進程終止運行后,操作系統內核將自動遞減進程仍然打開的所有內核對象的使用計數。一旦對象的使用計數變為0,操作系統內核就會銷毀該對象。
1.2、內核對象的安全性
內核對象可以用一個安全描述(security descriptor, SD)符來保護。它描述了誰擁有對象;哪些組和用戶被允許訪問或使用此對象;哪些組何用戶被拒絕訪問此對象。
用于創建內核對象的所有函數幾乎都有指向一個SECURITY_ATTRIBUTES結構的指針
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID nLength; //Specifies the size, in bytes, of this structure,一般為sizeof(此結構變量)
LPVOID lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL lpSecurityDescriptor; //和安全性有關的成員
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
2、進程內核對象句柄表
進程的句柄表結構
索引
|
指向內核對象內存塊的指針
|
訪問掩碼(包含標志位的一個DWORD)
|
標志
|
1
|
0x?????????
|
0x?????????
|
0x?????????
|
2
|
0x?????????
|
0x?????????
|
0x?????????
|
…
|
…
|
…
|
…
|
2.1、創建一個內核對象
用于創建內核對象的任何函數都會返回一個與進程相關的句柄,這個句柄可由同一個進程中運行的所有線程使用。由于句柄值實際事作為進程句柄表的索引來使用的,索引這些句柄是與當前這個進程相關的,無法供其他進程使用。
調用函數來創建一個內核對象時,如果調用失敗,多數返回的句柄值是0(NULL),但有幾個函數會返回-1(也就是在Winbase.h中定義的INVALID_HANDLE_VALUE)。檢查它們的返回值是,務必相當仔細。
2.2、關閉內核對象
無論以什么方式創建內核對象,我們都要調用CloseHandle向系統表明我們已經結束使用對象。
BOOL CloseHandle(
HANDLE hObject // handle to object
);
在內部,該函數首先檢查主調進程的句柄表,驗證“傳給函數的句柄值”標識的是“進程確實有權訪問的一個對象”。
1)如果句柄是有效的,系統就將獲得內核對象的數據結構的地址,并將結構中的“使用計數”成員遞減,如果使用計數變成0,內核對象將被銷毀,并從內存中去除。
2)如果句柄是無效的,
如果進程是正常運行的,CloseHandle將返回FALSE,而GetLastError返回ERROR_INVALID_HANDLE。
如果進程正在被調試,那么系統將拋出0xC0000008異常(“指定了無效的句柄”)。
hObject // handle to object
);
在內部,該函數首先檢查主調進程的句柄表,驗證“傳給函數的句柄值”標識的是“進程確實有權訪問的一個對象”。
1)如果句柄是有效的,系統就將獲得內核對象的數據結構的地址,并將結構中的“使用計數”成員遞減,如果使用計數變成0,內核對象將被銷毀,并從內存中去除。
2)如果句柄是無效的,
如果進程是正常運行的,CloseHandle將返回FALSE,而GetLastError返回ERROR_INVALID_HANDLE。
如果進程正在被調試,那么系統將拋出0xC0000008異常(“指定了無效的句柄”)。
*檢測內核對象泄露:
用Windows任務管理器,選擇(查看)->(選擇列),然后選擇顯示(句柄數)。便可在進程出監視任何一個應用程序的內核對象數了。
使用Sysinternals提供的Process Explorer工具(http://technet.microsoft.com/zh-cn/sysinternals/bb896653.aspx),選擇(View)->(Select Columns),選擇(Handle),選中所有列標題。 在頂部選擇想要檢查的進程,按F5來獲得一份最新的內核對象列表。 然后啟動應用程序并開始指向一個待查的工作流。完成之后,再次按F5。在此期間生成的每個內核對象都顯示為綠色。
3、跨進程邊界共享內核對象
內核對象的句柄是與每一個進程相關的,是為確保健壯性(可靠性)和安全性。
3.1、使用對象句柄繼承
只有在進程之間有一個父-子關系的時候,才可以使用對象句柄繼承。
首先,父進程必須向系統指出它希望這個對象的句柄是可繼承的。
e.g.
SECURITY_ATTRIBUTES sa;
sa.nLength = siezof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE; //Make the returned handle inheritable

HANDLE hMutex = CreateMutex(&sa, FALSE, NULL);
然后,由父進程生成子進程,通過
CreateProcess函數完成
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles, //通常情況下設為FALSE(表明我們不希望子進程繼承父進程句柄表中的“可繼承句柄”)
//如果傳遞TRUE,子進程就會繼承父進程的“可繼承句柄”的值。
//(系統會遍歷符進程的句柄表,對它的每一個記錄項進行檢查,凡是包含一個
//有效的“可繼承句柄”的項,都會被完整地復制到子進程的句柄表)
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
除了復制句柄表的記錄項,系統還會遞增內核對象的使用計數,引擎兩個進程現在都在使用這個對象。
3.2改變句柄的標志
1)父進程想控制哪些子進程能繼承內核對象句柄。可以調用
SetHandleInformation函數來改變內核對象句柄的繼承標志。
BOOL SetHandleInformation(
HANDLE hObject, //有效句柄標志
DWORD dwMask, //想要更改那個或哪些標志
DWORD dwFlags //希望把標志設為什么
);
每個句柄都關聯了兩個標志:
#define HANDLE_FLAG_INHERIT 0x00000001
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002
如果想打開一個內核對象句柄的繼承標志
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
如果想關閉這個標志
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, 0);
如果告訴系統不允許關閉句柄
SetHandleInformation(hObj,,HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
CloseHandle(hObj); //會引發異常
2)使用
GetHandleInformation函數返回句柄標志
BOOL GetHandleInformation(
HANDLE hObject,
LPDWORD lpdwFlags
);
e.g.檢查句柄是否可繼承
DWORD dwFlags;
GetHandleInformation(hObj, &dwFlags);
BOOL fHandleIsInheritable = (0 != (dwFlags & HANDLE_FLAG_INHERIT));
3.3為對象命名
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);

HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);

HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
所有這些函數的最后一個參數都是pszName。傳入NULL,相當于向系統表明我們要創建一個未命名的(即匿名)內核對象。
要根據對象名稱來共享一個對象,我們必須為此對象指定一個名稱。傳入一個“以0為終止的名稱字符串”的地址。
//進程A
HANDLE hMutexProcessA = CreateMutex(NULL, FALSE, TEXT("JeffMutex"));

//進程B
HANDLE hMutexProcessB = CreateMutex(NULL, FALSE, TEXT("JeffMutex"));
當進程B調用CreateMutex時,系統會查看是否存在一個名為 "JeffMutex "("JeffMutex "的對象是一個互斥量對象)的內核對象。如果存在,接著檢查對象的類型,和調用者是否擁有該對象的完全訪問權限。 如果答案是肯定的,系統就會在進程B的句柄表中查找一個空白記錄項,并將其初始化為指向現有的內核對象;否則,返回NULL(失敗)。
也可以調用Open*函數:
posted on 2009-11-26 11:06
longshen 閱讀(962)
評論(0) 編輯 收藏 引用 所屬分類:
VC++