Windows核心編程之核心總結(第三章 核心對象)(2018.6.2)

來源:互聯網
上載者:User

標籤:Windows核心編程之核心總結

學習目標

第三章核心對象的概念較為抽象,理解起來著實不易,我不斷上網找資料和看視頻,才基本理解了核心對象的概念和特性,其實整本書給我的感覺就是完整代碼太少了,沒有多少實踐的代碼對內容的實現,而且書本給的源碼例子,有太多我們不知道的知識,並且這些知識對本章主要內容來說是多餘的,所以我們理解起來也非常困難。為了更好的學習這章,我補充了一些輔助性內容。這一章的學習目標:
1.Windows會話和安全機制
2.什麼是核心對象?
3.使用計數和安全性描述元
4.核心物件控點表
5.建立核心對象
6.關閉核心對象
7.跨進程邊界共用核心對象-使用物件控點繼承
8.跨進程邊界共用核心對象-為對象命名
9.防止運行一個應用程式的多個執行個體
10.終端服務命名空間和專有命名空間
11.結合專有命名空間實現防止運行一個應用程式的多個執行個體
12.跨進程邊界共用核心對象-複製物件控點

Windows會話和安全機制

Vista系統開始,Windows就建立了session(會話)的概念。Windows系統啟動後就建立session0(會話0),將公用服務載入session0(會話0)中,例如:通常將一些與硬體緊密相關的模組(如:中斷處理常式等)、各種常用裝置的驅動程式(音效卡驅動、印表機驅動、顯卡驅動)以及運行頻率較高的模組(如:時鐘管理、進程調度和許多模組所公用的一些基本操作),這些都放在記憶體,稱為作業系統核心。系統啟動後,第一個使用者登陸了該系統,就建立起了session1(會話1),那麼當你運行所有的應用程式,魔獸啊,飛車啊,絕地求生啊,這些應用程式都是在session1(會話1)下運行。會話下運行多個程式會涉及多任務,例如:多個進程並發執行,那麼就通過進程管理隔離實現多任務。當有另一個使用者遠程登陸,那麼建立起session2(會話2),session2(會話2)也有專屬的應用程式---進程。如果依次有使用者登入該系統,那麼就順序依次建立起新的會話,也依次擁有專屬的應用程式---進程。採用這個設計後,系統核心組件就可以更好地與使用者不慎啟動的惡意軟體隔離。不同使用者的進程通過會話進行隔離,這就是多使用者的過程,多使用者是依靠會話進行隔離而實現使用者之間相互獨立,互不影響。這裡引出了會話Session的概念後,就要考慮安全機制問題。假如張三登陸了這個系統,建立起了會話1,然後系統就會給張三一個會話令牌,這個令牌包含了它的使用者資訊,還有該使用者訪問的許可權、屬於什麼組等資訊。在會話1下, 張三運行了一個程式,系統會給這個程式分配一個令牌,這個程式令牌就是繼承於會話建立時獲得的會話令牌,然後這個程式想要開啟一個檔案(核心對象),這個檔案(核心對象)就會有一個安全性描述元(SD),系統會根據程式令牌和檔案的安全性描述元相互匹配,安全性描述元含有建立的使用者、哪些使用者或組允許訪問此對象,哪些使用者或組拒絕訪問此對象。匹配之後發現這個使用者屬於安全性描述元的拒絕訪問名單內,那麼這個程式就無法開啟該檔案,否則能夠開啟檔案。

什麼是核心對象?

在系統和我們寫的應用程式中,核心對象用於管理進程、線程和檔案等諸多種類的大量資源。作為Windows開發人員,我們經常都要建立、開啟和處理核心對象。當我們在會話中啟動一個應用程式,那麼當應用程式載入記憶體,就會產生一個進程(這是一個主調進程)。每個進程對應都有一個虛擬位址空間,然後由記憶體管理程式對虛擬位址空間和物理地址空間的轉換。進程的虛擬記憶體空間分為核心層和應用程式層,每個核心對象,其實就是一塊記憶體塊,這個記憶體塊位於作業系統的核心地址空間(核心層),而使用者的應用程式運行在應用程式層,注意:這裡都是說明在虛擬位址空間,然後會映射到真正的物理地址空間中。而核心對象是由作業系統核心分配的,並只能由作業系統核心訪問。因此,應用程式不能直接操作核心對象,需要用Windows系統給定的函數來操作。每一個核心對象都有特定的建立函數和操作函數。所以,當一個主調進程裡調用了建立一個核心對象函數,那麼這個核心對象(記憶體塊)就會在進程的虛擬記憶體空間的核心層裡,實際映射到實體記憶體的作業系統核心地區。核心對象這個記憶體塊是一個資料結構,其成員維護著與對象相關的資訊。

使用計數和安全性描述元

我們上節說到,核心對象這個記憶體塊實際是一個資料結構,核心對象的結構分為兩個部分:公用部分(安全性描述元(security descriptor,SD),使用計數)和特有部分。其中特有部分,例如:進程核心對象有一個進程ID、一個基本的優先順序和一個結束代碼。使用計數是每一個核心對象都有的一個資料成員,當有一個核心對象被建立時,使用計數被設為1,當另一個進程獲得對現有核心對象的訪問後,使用計數就會遞增,進程終止運行後,作業系統核心將自動遞減此進程仍然開啟的所有核心對象的使用計數,如果一旦核心對象的使用計數為0,作業系統核心就會銷毀該核心對象。安全性描述元描述了誰擁有核心對象,哪些組和使用者被允許訪問或使用此對象,哪些組和使用者被拒絕訪問或使用此對象。用於建立核心對象的所有函數幾乎都有一個指向SECURITY_ATTRIBUTES結構的指標作為參數。下面給出這個結構的簽名:

        typedef struct _SECURITY_ATTRIBUTES {            DWORD  nLength;//結構的大小            LPVOID lpSecurityDescriptor;//安全性描述元             BOOL   bInheritHandle;//表示所建立的核心對象是否可被繼承,一般是具有父子關係的進程才可以繼承            } SECURITY_ATTRIBUTES;

如果想對我們建立的核心對象加以訪問限制,就必須建立一個安全性描述元。在Windows核心編程有這一核心對象的概念,而Windows程式設計又有著GDI對象(例如畫筆,視窗,畫刷,位元影像。)的概念,我們能知道的只有核心對象在核心層,而使用者物件(例如:GDI對象)在應用程式層。那麼我們要怎麼區分一個對象是核心對象還是非核心對象?剛剛我們學習了安全性描述元,每一個核心對象的建立函數基本都有一個SECURITY_ATTRIBUTES屬性作為參數,所以很明顯了。我們可以看建立對象的函數,如果建立對象的函數有安全性描述元參數,那麼這個函數建立的對象就是核心對象。

核心物件控點表

一個進程在初始化時,系統為進程分配了一個核心物件控點表。顯示了一個進程的控制代碼表。可以看出核心物件控點表是一個由資料結構組成的數組,每個結構都包含索引、指向一個核心對象記憶體塊的指標、一個存取遮罩和一些標誌(例如:是否可被繼承的標誌,在建立核心對象就被指定了)。

建立核心對象

一個進程首次初始化的時候,其核心物件控點表為空白。然後,當進程中的線程調用建立核心對象的函數時,比如CreateFileMapping,作業系統核心就為該對象分配一個記憶體塊,並對它初始化。這時,作業系統核心對進程的控制代碼表進行掃描,找出一個空項。作業系統核心找到索引1位置上的結構並對它進行初始化。該指標成員將被設定為核心對象的資料結構的記憶體位址,訪問屏蔽設定為全部訪問權,同時,各個標誌也作了設定。我列舉以下部分建立核心對象的函數簽名:

HANDLE CreateThread(   PSECURITY_ATTRIBUTES psa,   size_t dwStackSize,   LPTHREAD_START_ROUTINE pfnStartAddress,   PVOID pvParam,   DWORD dwCreationFlags,   PDWORD pdwThreadId);HANDLE CreateFile(   PCTSTR pszFileName,   DWORD dwDesiredAccess,   DWORD dwShareMode,   PSECURITY_ATTRIBUTES psa,   DWORD dwCreationDisposition,   DWORD dwFlagsAndAttributes,   HANDLE hTemplateFile);HANDLE CreateFileMapping(   HANDLE hFile,   PSECURITY_ATTRIBUTES psa,   DWORD flProtect,   DWORD dwMaximumSizeHigh,   DWORD dwMaximumSizeLow,   PCTSTR pszName);HANDLE CreateSemaphore(   PSECURITY_ATTRIBUTES psa,   LONG lInitialCount,   LONG lMaximumCount,   PCTSTR pszName);

我們可以看到這些建立核心對象的函數簽名,參數都有一個SECURITY_ATTRIBUTES結構參數,然後返回一個核心物件控點,這個控制代碼值其實就是作為核心物件控點表的索引來使用的,所以這些控制代碼是與當前這個進程相關的,無法供其他進程使用,如果我們真的在其他進程中使用它,那麼實際引用的只是那個進程的控制代碼表中位於同一個索引的核心對象----只是索引值相同而已。那麼要得到實際的控制代碼表的索引值,核心物件控點值應該除以4才得到索引值。

關閉核心對象

無論怎樣建立核心對象,都要向系統指明將通過調用CloseHandle函數來結束對該對象的操作,下面給出該函數的簽名:

HRESULT CloseHandle(    HANDLE hHandle  );

調用這個函數,函數內部會先檢查主調進程的控制代碼表,看下主調進程對這個核心物件控點是否有權訪問。如果核心物件控點是有效,系統將獲得核心對象的資料結構的地址,並將結構中的使用計數成員遞減。如果使用計數變成0,核心對象將被銷毀,並且清除對應核心物件控點表中對應的記錄項;如果使用計數遞減後不為0,說明其他進程還在使用該核心對象,那麼只清除對應核心物件控點表中對應的記錄項,不銷毀核心對象。講了這麼多,有什麼方法可以看見進程有多少個核心對象嗎?當然有,微軟提供了一個小工具:Process Explorer,下面圖顯示了我自己的應用程式,裡面建立了一個名為"ydm"的互斥量核心對象,我將會關閉這個核心對象。下方的mutant類型這一行,就是我在內部建立的互斥量核心對象。

跨進程邊界共用核心對象-使用物件控點繼承

在每個進程中都有一個核心控制代碼表,這也就說明同一個核心對象其在不同的進程中其核心物件控點值可能是不一樣的。但是核心對象的作用很大程度上就在於能夠在進程間共同訪問,即跨進程邊界共用核心對象。那我們怎麼實現不同進程間共用同一個核心對象呢?Windows核心編程這本書給我們提供了三個方法實現這個功能。這一小節先講使用物件控點繼承來實現跨進程邊界共用核心對象。
只有在進程之間有一個父子關係時,才可以使用物件控點繼承。為了使子進程繼承父進程的核心物件控點表,必須執行以下幾個步驟:
1.當父進程建立一個核心對象時,父進程必須向系統指出它希望這個對象的控制代碼是可繼承的。注意,這裡說的繼承是指繼承核心物件控點,而非核心對象。為了建立一個可以繼承的核心物件控點,父進程必須分配並初始化一個SECURITY_ATTRIBUTES結構,並將這個結構的地址傳遞給具體的Create*建立核心對象函數。下面舉個鮮明的例子:

SECURITY_ATTRIBUTES sa;//安全屬性結構sa.nLength=sizeof(sa);//結構大小sa.lpSecurityDescriptor=NULL;//安全性描述元sa.bInheritHandle=TRUE;//指定核心對象是否可被繼承HANDLE hMutex=CreateMutex(&sa,FALSE,NULL);//建立一個互斥量核心對象

我們都知道核心物件控點表的每個記錄項含有索引、指向核心對象記憶體塊的地址、存取遮罩和標誌,其中標誌就是指是否可以繼承。如果在建立核心對象的時候將NULL作為PSECURITY_ATTRIBUTES參數傳入,則返回的控制代碼將是不可繼承的,這個標誌也會被設為0,如果bInheritHandle成員設為TRUE,則這個標誌被設為1.
2.父進程產生子進程,通過在主調進程內調用CreateProcess函數完成。下面給出CreateProcess的函數簽名:

BOOL WINAPI CreateProcess(  _In_opt_    LPCTSTR               lpApplicationName,  _Inout_opt_ LPTSTR                lpCommandLine,  _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,  _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,  _In_        BOOL                  bInheritHandles,  _In_        DWORD                 dwCreationFlags,  _In_opt_    LPVOID                lpEnvironment,  _In_opt_    LPCTSTR               lpCurrentDirectory,  _In_        LPSTARTUPINFO         lpStartupInfo,  _Out_       LPPROCESS_INFORMATION lpProcessInformation);

注意參數bInheritHandles,如果設為TRUE,子進程就會繼承父進程的“可繼承的核心物件控點”的值,注意:如果是父進程的“不可繼承的核心物件控點”,那麼子進程就不會繼承到。我說過每個進程都有一個核心物件控點表,子進程也不例外。系統在建立子進程後會分配一個新的、空白的核心物件控點表。總的執行流程如下:系統會先遍曆父進程的核心物件控點表,對它的每一個記錄項進行檢查,凡是包含一個有效“可繼承的核心物件控點”的項,都會被完整地複製到子進程的核心物件控點表,在子進程的核心物件控點表中,複製項的位置與它在父進程控制代碼表中的位置完全一樣,這一特性意味著:在父進程和子進程中,對每一個核心對象進行標識的核心物件控點值是完全一樣的。除了複製核心控制代碼表的記錄項,系統還會遞增核心對象的使用計數,因為兩個進程現在都在使用這個核心對象。記住一個要點:核心物件控點的繼承只會在產生子進程的時候發生,假如父進程後來又建立了新的核心對象,並同樣將它們的控制代碼設為可繼承的控制代碼,那麼正在啟動並執行子進程是不會繼承這些新控制代碼的。前面都是先建立一個父進程的可繼承的核心對象,父進程調用CreateProcess函數建立第一個子進程,然後系統自動將父進程可繼承的核心物件控點複製到子進程的核心物件控點表中,然後建立第二個子進程,過程還是依然如此。但是我希望在建立第二個子進程時繼承不到父進程的這一核心對象。簡單來說,就是我們想控制哪些子進程能繼承核心物件控點,可以調用SetHandleInformation函數來改變已經建立好了的核心物件控點的繼承標誌。那麼只要在調用CreateProcess函數產生第二個子進程前調用SetHandleInformation函數關閉核心對象的繼承標誌,就可以實現我們目的啦。這個函數簽名如下:

BOOL SetHandleInformation(   HANDLE hObject,//標識了一個有效核心物件控點,為什麼有效?因為還是需要主調進程有存取權限。   DWORD dwMask,//告訴函數我們想更改哪個或者哪些標誌   DWORD dwFlags);//指出把標誌設為什麼

下面給出參數2,dwMask的兩種取值:

HANDLE_FLAG_INHERIT 0x00000001  If this flag is set, a child process created with the bInheritHandles parameter of CreateProcess set to TRUE will inherit the object handle. HANDLE_FLAG_PROTECT_FROM_CLOSE  0x00000002  If this flag is set, calling the CloseHandle function will not close the object handle. 

1.如果要開啟一個核心物件控點的繼承標誌,可以這樣寫:

SetHandleInformation(hObj,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);

2.要關閉這個標誌,可以這樣寫:

SetHandleInformation(hObj,HANDLE_FLAG_INHERIT,0);

3.HANDLE_FLAG_PROTECT_FROM_CLOSE標誌是告訴系統不允許關閉核心物件控點:

SetHandleInformation(hObj,HANDLE_FLAG_PROTECT_FROM_CLOSE,HANDLE_FLAG_PROTECT_FROM_CLOSE);//如果在這個函數之後調用CloseHandle關閉這個控制代碼就會報錯

4.如果需要告訴系統允許關閉核心物件控點,我們可以這樣寫:

SetHandleInformation(hObj,HANDLE_FLAG_PROTECT_FROM_CLOSE,0);//這時候在這個函數調用之後調用CloseHandle函數關閉核心物件控點不會報錯,成功關閉

5.我們可以通過GetHandleInformation函數擷取指定核心物件控點的當前標誌。如果要檢查一個核心物件控點是否可以被繼承,我們可以這樣寫:

DWORD dwFlags;GetHandleInformation(hObj,&dwFlags);BOOL fHandleIsInheritable=(0!=(dwFlags & HANDLE_FLAG_INHERIT));

後面內容晚點補充...

Windows核心編程之核心總結(第三章 核心對象)(2018.6.2)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.