310 likes | 539 Views
請以純粹黑白列印. Kernel Objects. Windows 作業系統與應用程式使用 Kernel Object 來管理許多資源, 譬如行程、執行緒、檔案. 井民全製作. What is a Kernel Object?. The data structure maintains information about the object Process Object : 紀錄了 PID, priority, exit code
E N D
請以純粹黑白列印 Kernel Objects Windows作業系統與應用程式使用 Kernel Object來管理許多資源, 譬如行程、執行緒、檔案 井民全製作
What is a Kernel Object? • The data structure maintains information about the object • Process Object: 紀錄了PID, priority, exit code • File Object:紀錄了byte offset, sharing mode, open mode 等資訊 • A memory block allocated by kernel and accessible onlyby the kernel Ex: Open for writing or Open for reading 即 application 無法直接修改 kernel object 的資料結構
修改 ex HANDLE hFile; hFile =CreateFile("myfile.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); How to access these kernel objects? CreateFileMapping(…) CreateThread(…) CrateFile(…) 某些建立 kernel object 的函式 會傳回 Handle - We use the handle to manipulate the kernel object - The kernel object handle isprocess relative 亦即相同的 kernel object 在不同的 Process 值會不一樣
Usage Counting Usage count 1 0 5 deleted Process A CreateFileMapping(…) 3 Process B CreateFileMapping(…) kernel Process C CreateFileMapping(…)
Security • Kernel Object can be protected with a security descriptor, which decrypts • Who create the object • Who can gain access to or use the object • Who is denied access • Usually used when writing server applications
Security • Almost all function that create kernel object need SECURITY_ATTRIBUTES structure ex HANDLE CreateFileMapping( HANDLE hFile, PSECURITY_ATTRIBUTES psa, DWORD flProtect, DWORD dwMaximumSizeHeight, DWORD dwMaximumSizeLow, PCTSTR pszName); NULL (default security) Full access only for 1. administrator group 2. Creator
Access to an existing kernel object • 我必須要得到 access right才能得到正確的 FileMapping handle HANDLE hFileMapping=OpenFileMapping(FILE_MAP_READ, FALSE “MyFileMapping”); 指定為讀取模式 READ op 要開啟的 File-Mapping 名稱 Security check Return:NULL fail ok Valid handle
Example: Read the registry HKEY hKey; LONG lRet; lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Office", 0, KEY_QUERY_VALUE, &hKey ); if( lRet == ERROR_SUCCESS ) MessageBox(NULL,"You have Office software","ok",MB_OK); else MessageBox(NULL,"fail","fail",MB_OK); RegCloseKey( hKey ); 系統先檢查該key 的security descriptor, 看看user是否有權限存取 完整程式範例: OperRegstryDemo
Kernel object 與其他Object • 你的程式可能會用到其他的object • 選單、視窗、滑鼠游標、筆刷、字型等 • 幾乎所有建立核心物件的函式, 都會有 Security attribute 參數, 沒有一個GDI 或 User Object 需要 security attribute 我要如何分辨 Kernel Object 或其他 object ? 例如: CreateIcon HICON CreateIcon( HINSTANCE hinst, int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE *pbANDbits, CONST BYTE *pbXORbits);
系統是如何表示 Kernel Object? • 對每一個 Process 都會配置一個 Kernel Object Handle Table 他的長相應該是像這樣: 一個用來管理 Kernel Object 的陣列
一些建立 kernel object 的函式 • HANDLE CreateThread (…) • HANDLE CreateFile(…) • HANDLE CreateFileMapping(…) • HANDLE CreateSemaphore(…) 傳回的 HANDLE 值, 就是 Handle Table 中的相關位置 可以被該 process 中的所有 thread 使用 建立 Kernel Object 時,系統的動作 系統的行為 1 當 Process 初始化時 空的 Object Handle Table 2 當 Process 中的某個 Thread 建立了 kernel object 1. Kernel 配置該 kernel object 所需記憶體 2. 在 Process 中的 Handle Table 找尋空的 位置 3. 將必要的資訊放到 Handle Table 中
使用 kernel object 的系統動作 不合法: GetLastError() == ERROR_INVALID_ HANDLE 一個 function 需要 HANDLE 當作引數時 Step 1 檢查該 Handle是否合法 Step 2 例如 利用傳入的 HANDLE 由 HANDLE TABLE 找出 kernel Object 的 所在位置 BOOLWriteFile(HANDLE hFile, LPCVOIDlpBuffer, DWORDnNumberOfBytesToWrite, LPDWORDlpNumberOfBytesWritten, LPOVERLAPPEDlpOverlapped ); Step 3 依照規定處理該 Object 的資料結構 系統的行為
這個判斷式有問題: 因為 CreateMutex 失敗時, 傳回 NULL HANDLE hMutex = CreateMutex(...); if (hMutex = = INVALID_HANDLE_VALUE) { } HANDLE hFile = CreateFile(...); if (hFile = = NULL) { // We will never execute this code because CreateFile // returns INVALID_HANDLE_VALUE(-1) if it fails. } 這個判斷式有問題: 因為 CreateFile 失敗時, 傳回 -1 當你建立 kernel object 失敗時 • 大部分會傳回 NULL 可是有些 FUNCTION 卻會傳回 -1 (INVALID_HANDLE_VALUE ) 注意情況一: 注意情況二:
當你不再使用 kernel object 時 系統的行為 • 請使用 CloseHandle Step 1 檢查該 Handle是否合法 BOOL CloseHandle(HANDLE hobj); 不合法時, CloseHandle 會傳回 FALSE Step 2 查閱 Handle Table 以取得 Kernel Object 的位址 如果我忘記 CloseHandle 會不會造成 Memory Leak的問題? Step 3 遞減該 kernel object 的 Usage count 若 = 0 刪除該 kernel object Step 4 將 Handle Table 中相對應的 entry 清除掉 答案是: 當 process terminated 時 會釋放該 process 所有的 resource
增加說明 MemoryMapFile Mailslots & named piples Mutexes, semaphores, and events Sharing Kernel Objects Across Process • 由前面可知, 我們的 kernel object 可以在 thread 之間共享 • 但是, 其他的 process 就不能存取我們的 kernel object 只要我們知道該物件的 Handle 我們會希望 1. 不同的 processes 共享資料 2. 不同機器的 process 之間, 共享資料 3. 不同 processes 中的 thread 同步處理協調執行工作
Parent Process Object Handle Inheritance-- 描述parent-child kernel object 之間的handle 使用關係 在建立時,就指定這個 Object 是可以被繼承的 I am a inheritable mutex object Kernel Objects SECURITY_ATTRIBUTES sa; sa.nLength=sizeof(sa); sa.lpSecurityDescriptor=NULL; sa.bInheritHandle=TRUE; HANDLE hMutex=CreateMutex(&sa, FALSE, NULL); 設定 security 屬性 Spawn Child Process Can I access? SECURITY_ATTRIBUTESthat the object handle should be inheritable!
增加說明 The process’s handle table entry 預設 security 屬性 建立第一個 Mutex 作業系統核心的列表 HANDLE hMutex1=CreateMutex(NULL, FALSE, NULL); 自訂 security 屬性: 指定可以被繼承 SECURITY_ATTRIBUTES sa; sa.nLength=sizeof(sa); sa.lpSecurityDescriptor=NULL; sa.bInheritHandle=TRUE; HANDLE hMutex2=CreateMutex(&sa, FALSE, NULL); Process handle table 建立第二個 Mutex
新 process 的 security 屬性: 限定系統其他使用者是否可以存取該 process Make your object to inheritance 新 thread 的設 security 屬性 Let your child process to inherit all inheritable handles Two Steps BOOL CreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOLbInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); bInheritHandles FALSE: 不希望這個 child process 能夠存取 parent 的 kernel object TRUE: 這個 child process 可以自由的存取 parent 中的 inheritable kernel objects
Parent Process How about the child process’s handle table? Parent’s handle table inheritable Spawn Child Process 複製到child process (注意: Index也一樣) Child’s handle table
美化圖示 實際的看看例子吧 範例的進行流程 Parent主程式 建立一個 Event Kernel Object 傳遞 Object 的 handle 建立 Child Process並將 Event Object 傳給 Child 等待 Event Signal 才繼續執行 Child 程式 Step 1: 被主程式呼叫並且被建立 Step 2: 將Command line字串轉換成 HANDLE Step 4: 將 Kernel Object Handle Table 中對應的欄位清掉 Step 3: 利用該 HANDLE 將 Kernel Object 設定為 signal CloseHandle(hsecondHandle);
因為 HANDLE 被定義為 void*, 所以我們使用 %p #include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; // Step 1: 被主程式呼叫並且被建立 int _tmain(int argc, _TCHAR* argv[]) { // Step 2: 將Command line字串轉換成 HANDLE HANDLE hsecondHandle=NULL; sscanf(argv[0],"%p",&hsecondHandle); // Step 3: 利用該 HANDLE 將 Kernel Object 設定為 signal BOOL bSuccess=SetEvent(hsecondHandle); if(bSuccess) cout <<"成功的得到 parent 的 handle"<<endl; else cout <<"失敗"<< endl; // Step 4: 將 Kernel Object Handle Table 中對應的欄位清掉 CloseHandle(hsecondHandle); return 0; } 先看 Child Child 程式 關鍵片段 執行完這行,主程式才能繼續執行
Parent主程式 (片段) void main(){ // Step 1: 建立一個可以被繼承的 Kernel Object (目前用 Event 表示) SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // << -- 設定可繼承 HANDLE hmyEvent=CreateEvent(&sa,TRUE,FALSE,NULL); // Step 2: 將該 Kernel Object 的 HANDLE 轉換成字串 char buffer[200]; sprintf( buffer,"%p",hmyEvent); // Step 3: 建立 Child process 並將 HANDLE 字串以 CommandLine 的方式 // 傳給 Child CreateProcess(…); // Step 4: 等待 Child process 利用該 HANDLE 將 Kernel Object signal WaitForSingleObject(hmyEvent, INFINITE ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); CloseHandle(hmyEvent); return 0; } 必須等待 Childe 執行 SetEvent
Named Object • Sharing kernel object • Not all kernel object can be named
增加說明 How to sharing a object Process A HANDLE hMutexProcessA=CreateMutex(NULL,FALSE, ”JeffMutex” ); 此時, 另一個 process B 的 thread 又建立相同名稱的 object HANDLE hMutexProcessA=CreateMutex(NULL,FALSE, ”JeffMutex” ); Process B 1. 配置新的Handle Table 2. Point to the existing kernel object 3. Increase the usage count 通過 檢查該 kernel Object Name是否 已經存在 檢查新建 kernel Object的 Type 是否一樣 檢查是否可以存取 Yes fail NULL
萬一,我不想 share 了怎麼辦? • 情況是: parent 只想給其中一個 child 繼承,該怎麼辦 ? • 使用 SetHandleInformation(…) 目標 kernel object 設定為可以繼承 針對繼承屬性 SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT 設定為不可繼承 針對繼承屬性 SetHandleInformation(hobj, HANDLE_FLAG_INHERIT, 0
增加說明 如何禁止一個Kernel Object 被 Close ? • 情況是: 防止 “子process”在尚未建立”孫process”前,就先 close 掉某些Handle 剛好這些 Handle 是 孫 process 要繼承使用的 導致這個 孫 process 無法藉由 這些 Handle 與 母行程溝通 針對不可關閉屬性 SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE , HANDLE_FLAG_PROTECT_FROM_CLOSE ) CloseHandle(hobj); 設定不可關閉 執意關掉,會發生 exception 當然, 如果 子process 執行 那當然就可以 CloseHandle SetHandleInformation(hobj, HANDLE_FLAG_PROTECT_FROM_CLOSE , 0 ) 關閉功能取消
我要如何知道一個 object 是否有繼承屬性? • 使用 GetHandleInformation 取得 Handle 的資料 判斷是否具有繼承的範例 DWORD dwFlags; GetHandleInformation (hObj, &dwFlags); BOOL fHandleIsInheritable = (0 != (dwFlags & HANDLE_FLAG_INHERIT)); 若 dwFlags 相對應的 bit 欄位 =1 1 否則 0 運算結果 ≠ 0 時, TRUE
Prevent multiple instances • 我們可以用 named kernel object #include "stdafx.h" #include <windows.h> int _tmain(int argc, _TCHAR* argv[]) { HANDLE h = CreateEvent(NULL,FALSE,FALSE,"Baby"); if (GetLastError( )==ERROR_ALREADY_EXISTS) { MessageBox(NULL,"系統中已經有重複的程式正在執行","Error",MB_OK); return(0); } getchar(); // Before exiting, close the object. CloseHandle(h); return(0); } 建立一個名為 Baby的 kernel object 當你嘗試在系統中重複建立兩個相同名稱的 kernel object, 會造成失敗
你的系統中剛好有人使用 Baby 這個名字 ? Universally Unique Identifier (UUID) • 使用 VS 的工具 UUIDGEN.EXE 產生GUID(Globally Unique Identifier) 來當作名稱 填入 GUID HANDLE h = CreateEvent(NULL,FALSE,FALSE, "d00fead7-d497-4765-a5ef-3d10f9d23023");
End UUIDGEN.EXE 是 VC 提供的 tool 如果你想使用程式自動產生, 請使用 CoCreateGuid請參考課程網頁範例 操作範例: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html /_sample_mfc_GUIDGEN.asp