windows核心程式設計--核心物件
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其餘程序可用用OpenMutex函式開啟,如果許可權可以就返回控制代碼,如果失敗返回NULL,GetLastError被設定為ERROR_ACCESS_DENIED。 Windows除了核心物件之外還有GDI和使用者物件,區分它們的簡單辦法就是,建立函式中帶有安全描述符引數的就是核心物件。 3.2 程序的核心物件控制代碼表(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; HANDLE h = CreateMutex(&sa, FALSE, "XI");
索引 核心物件記憶體塊得指標 訪問遮蔽(標誌位的DWORD) 標誌(標誌位的DWORD)
呼叫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函式解析)。
BOOL WINAPI CreateProcess( __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機制通知目標程序,新控制代碼已經拷貝過去。