3.1 什麼是核心對象
核心對象就是核心中的一塊記憶體,是一個結構,並且只能由核心對象訪問,應用程式只能通過調用Windows提供的函數來操作核心對象。每個核心對象都有相同的部分比如安全屬性和使用計數器。
3.1.1 核心對象的使用計數
核心對象中的使用計數和進程無關,當進程第一次建立某個核心對象時候使用計數變為1,當另一個進程也調用此核心對象時計數變為2。當進程釋放時或者關閉核心對象時(CloseHandle),核心的使用計數減去1,如果使用計數不為0的話,核心不會釋放此核心對象。
3.2.2 安全性
核心對象能夠得到安全性描述元的保護,安全性描述元定義了誰能夠建立,訪問和使用該對象,一般在伺服器代碼中使用,用戶端可以忽略。
所有建立核心對象的函數的參數都有一個指向SECURITY_ATTRIBUTES結構的指標。
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle;} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
只有lpSecurityDescriptor成員和安全屬性有關。一般此參數傳遞NULL,表示預設的安全描述。
如果需要:
SECURITY_ATTRIBUTES sa;sa.nLength = sizeof(sa);sa.lpSecurityDescriptor = NULL;sa.bInheritHandle = FALSE;HANDLE h = CreateMutex(&sa, FALSE, "XI");
其餘進程可用用OpenMutex函數開啟,如果許可權可以就返回控制代碼,如果失敗返回NULL,GetLastError被設定為ERROR_ACCESS_DENIED。Windows除了核心對象之外還有GDI和使用者物件,區分它們的簡單辦法就是,建立函數中帶有安全性描述元參數的就是核心對象。 3.2 進程的核心物件控點表
索引 核心對象記憶體塊得指標 訪問屏蔽(標誌位的DWORD) 標誌(標誌位的DWORD)
1 0x???????? 0x???????? 0x???????? 2 0x???????? 0x???????? 0x????????… … … …
3.2.1 建立核心對象
調用Create&函數族來建立相應的核心對象,返回的是核心對象的控制代碼(也有個說法就是控制代碼表的索引),如果建立失敗一般會返回0(NULL),也有的會返回INVALID_HANDLE_VALUE=-1,比如CreateFile失敗後會返回後者,失敗的原因有可能是記憶體不足或者是安全問題等等。其他對核心操作的函數都需要此控制代碼值作為參數傳遞進去,如果傳遞一個無效的控制代碼進去,那麼GetLastError函數的值將被置為6(ERROR_INVALID_HANDLE)。
3.2.2 關閉核心對象
BOOL CloseHandle(HANDLE hobj);
調用此函數,系統會了清理進程的控制代碼表中的對應項目,如果使用計數器為0,核心釋放該核心對象的資源,如果使用計數器不為0,說明其他進程還在使用此核心對象,則不釋放資源。
當進程忘記調用CloseHandle函數,可能造成記憶體泄露,但是當進程結束的時候資源一樣會被釋放。
如果傳遞的參數無效,則函數返回FALSE,並且GetLastError函數的值被設定成ERROR_INVALID_HANDLE。如果是DEBUG階段,則返回錯誤資訊。
3.3 跨越進程邊界共用核心對象3.3.1 物件控點的繼承性
- 在父進程建立子進程的時候,將參數SECURITY_ATTRIBUTES結構的Inherithandle欄位設定為TRUE的話,再父進程控制代碼表中標示該核心對象的項的標誌位的值將會變成TRUE,標示該核心對象是可以讓子進程繼承的,具有可繼承性(僅僅標示 該控制代碼值具有可繼承性,而核心對象沒有可繼承性)。
- 將CreateProcess的參數bInherithandle參數的值設定為TRUE,標示建立的進程可以繼承有繼承性的父進程控制代碼。
- 子進程建立後不會先載入程式,它先搜尋父進程的控制代碼表將有繼承性的項目原封不動的拷貝給自己(索引也沒有變,所以控制代碼值也不變)。
- 父進程可以有3種方式將控制代碼值傳遞給子進程,參數傳遞,處理序間通訊和環境變數(GetEnvironmentVariavle函數解析)。
BOOLWINAPICreateProcess( __in_opt LPCSTR lpApplicationName, __inout_opt LPSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCSTR lpCurrentDirectory, __in LPSTARTUPINFOA lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation );
注意:
子進程再建立其子進程,如果滿足上面方式,可以繼續繼承。
如果父進程再建立子進程後,再建立控制代碼,子進程不會被繼承。
3.3.2 改變控制代碼標誌
BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);改變控制代碼的標誌,目前可改變的標誌有兩種#define HANDLE_FLAG_INHERIT 0x00000001 // 繼承標誌#define HANDLE_FLAG_PROJECT_FROM_CLOSE 0x00000001 // 保護不允許關閉控制代碼標誌可以用OR操作同時設定2個標誌。第一個參數是要設定的控制代碼值,第二個就是要改變的標誌,第三個參數是將標誌改編成什麼值。BOOL GetHandleInformation(HANDLE hObkect, PDWORD pdwFlags);擷取當前控制代碼的標誌的值。
// 設定控制代碼值可繼承:SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);// 設定控制代碼不可繼承:SetHandleInformation(hObject, HANDLE_FLAG_INHERIT, 0);// 設定控制代碼值不可關閉,受保護:SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, HANDLE_FLAG_PROJECT_FROM_CLOSE);// 設定控制代碼值可關閉,不受保護:SetHandleInformation(hObject, HANDLE_FLAG_PROJECT_FROM_CLOSE, 0);
3.3.3 命名物件建立核心對象函數族Create&中的最後一個參數是pszName,該參數是如果傳遞NULL,表示是匿名核心對象,可以通過其他倆種方式來使用其他進程的核心對象。當pszName參數傳遞以’/0’(最多長度為MAX_PATH 260字元)結尾的字串時,表示啟用命名物件,比如進程A調用CreateMutex(NULL, FALSE, “XI”)的時候,他將建立核心對象名字為“XI”,之後某一時刻如果進程B也調用CreateMutex(NULL, FALSE, “XI”)函數他將經過以下幾步:
- 判斷核心對象名稱是否相同。
- 判斷核心物件類型是否相同,如果名字相同但是類型不相同則Create&函數族返回NULL,GetLastError函數值為6(ERROR_INVALID_HANDLE)。
- 判斷安全性,返回同2步,GetLastError值同2步。
- 如果驗證通過則返回控制代碼(返回控制代碼的值和該核心對象其他控制代碼的值不一定相同),GetLastError的值等於ERROR_ALREADY_EXISTS。
也可以用Open&函數族來開啟已經建立的控制代碼,成功後GetLastError也不會被設定。具體如下
HANDLE Open&(DWORD, BOOL, PCSTR);
第一個參數:表示存取權限。
第二個參數:表示新建立的控制代碼是否有繼承性(注意不是核心對象!)。
第三個參數:不能傳遞NULL。如果該控制代碼不存在則返回NULL,GetLastError被設定為2(ERROR_FILE_NOT_FOUND)。
3.3.4 終端伺服器的名字空間
Globad,Local,Session程式保留關鍵字,具體的沒弄明白,理解的就是說當伺服器的時候,用戶端可以訪問以這些名字開頭的核心對象。
3.3.5 複製物件控點
BOOL DuplicateHandle(HANDLE hSourceProcessHandle,HANDLE hSourceHandle,HANDLE TargetProcessHandle,PHANDLE phTargetHandle,DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwOptions);
執行DuplicateHandle函數的進程為ProcessC,原進程為ProcessS,目標進程為ProcessT。則hSourceProcessHandle為進程ProcessS的進程控制代碼,TargetProcessHandle為進程ProcessT的進程控制代碼,ProcessC將控制代碼hSourceHandle從ProcessC拷貝到ProcessT中,值存在phTargetHandle中,dwDesiredAccess新控制代碼的反問許可權,bInheritHandle新控制代碼的繼承性,參數dwOptions有兩種類型分別是:
DUPLICATE_SAME_ACCESS忽略參數dwDesiredAccess,新控制代碼和原進程控制代碼具有相同的反問許可權。
DUPLICATE_CLOSE_SOURCE關閉ProcessS中的拷貝控制代碼,核心對象的計數不變。
HANDLE hObjProcessS = CreateMutex(NULL, FALSE, NULL);HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT);HANDLE hObjProcessT;DuplicateHandle(GetCurrentProcess(), hObjProcessS, hProcessT , &hObjProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);CloseHandle(hObjProcessS);CloseHandle(hProcessT);
注意:
一般DuplicateHandle函數沒有在三個進程中使用,因為很難知道原進程的控制代碼值。
要使用IPC機制通知目標進程,新控制代碼已經拷貝過去。