国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

這篇具有很好參考價值的文章主要介紹了[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。


前言

本章講述了Microsoft Windows操作系統(tǒng)中四種基本的機(jī)制,它們對于系統(tǒng)的管理和配置至關(guān)重要,它們是:

  • 注冊表(Registry)
  • 服務(wù)(Service)
  • 統(tǒng)一的背景進(jìn)程管理器(UBPM)
  • Windows管理設(shè)施(WMI)
  • Windows診斷基礎(chǔ)設(shè)施(WDI)

4.1 注冊表(Registry)

注冊表在Windows系統(tǒng)的配置和控制方面扮演了一個非常關(guān)鍵的角色。

  • 它既是系統(tǒng)全局設(shè)置的存儲倉庫,(HKLM等根鍵)
  • 也是每個用戶的設(shè)置信息的存儲倉庫。(HKU,HKCU等根鍵)

雖然大多數(shù)人都把注冊表想象成存儲在磁盤上的靜態(tài)數(shù)據(jù),
但是,在這一節(jié)中你將會看到,注冊表也是Windows執(zhí)行體和內(nèi)核所維護(hù)的各種內(nèi)存中數(shù)據(jù)結(jié)構(gòu)的一個窗口

我們首先從總體上介紹注冊表的結(jié)構(gòu),并討論一下它所支持的數(shù)據(jù)類型,簡要地瀏覽一下Windows在注冊表中維護(hù)的關(guān)鍵信息。然后,我們進(jìn)一步討論配置管理器( Configuration Manager)的內(nèi)部機(jī)理。

這里的配置管理器是負(fù)責(zé)實現(xiàn)注冊表數(shù)據(jù)庫的執(zhí)行體組件。我們將會涉及的話題有:

  • 注冊表在磁盤上的內(nèi)部結(jié)構(gòu)、
  • 當(dāng)一個應(yīng)用程序請求配置信息時Windows如何獲得相應(yīng)的信息
  • 以及Windows采用了哪些手段來保護(hù)這一關(guān)鍵的系統(tǒng)數(shù)據(jù)庫。

4.1.1 查看和修改注冊表

一般來說,你應(yīng)該永遠(yuǎn)不需要直接編輯注冊表:

  • 如果存儲在注冊表中的應(yīng)用程序設(shè)置和系統(tǒng)設(shè)置需要手工修改,那么,它們應(yīng)該有一個對應(yīng)的用戶界面來控制其修改。

然而,正如你在本書中已經(jīng)多次看到過的那樣,有些高級設(shè)置和調(diào)試設(shè)置并沒有相應(yīng)的編輯用戶界面。因此,Windows中包含了圖形用戶界面(GUI)和命令行工具,用于查看和修改注冊表。

Windows隨帶了一個主要的GUI工具可用于編輯注冊表,Regedit.exe,還有一些命令行注冊表工具。例如,Reg.exe具有導(dǎo)入、導(dǎo)出、備份和恢復(fù)注冊表鍵的能力,也可以比較、修改和刪除注冊表鍵和值。它也可以設(shè)置或者查詢在UAC虛擬化中所用到的各種標(biāo)記。而Regini.exe則允許你根據(jù)一個包括ASCII或Unicode配置數(shù)據(jù)的文本文件來導(dǎo)入注冊表數(shù)據(jù).

WDK (Windows DriverKit)也提供了一個可以再分發(fā)的組件:Offreg.dll,稱為離線注冊表庫。通過該庫,可以以二進(jìn)制格式來加載注冊表儲巢文件,以及在這些儲巢文件上應(yīng)用各種操作,因而繞過了Windows所要求的常規(guī)注冊表操作的邏輯:

  • 先加載、再映射的過程。

這種用法主要是為了幫助離線訪問注冊表,比如出于完整性檢查或有效性檢查的目的。如果底層的數(shù)據(jù)不必對系統(tǒng)可見的話,這種用法還可以獲得性能上的優(yōu)勢,因為對數(shù)據(jù)的訪問是通過本地文件I/O來完成,而不是通過注冊表系統(tǒng)調(diào)用來完成的。

4.1.2 注冊表用法

配置數(shù)據(jù)主要在四個時間點(diǎn)上被讀取:

  • 在初始的引導(dǎo)過程中,引導(dǎo)加載器讀入配置數(shù)據(jù)和引導(dǎo)設(shè)備驅(qū)動程序的列表,以便在初始化內(nèi)核以前將它們加載到內(nèi)存中。因為BCD(引導(dǎo)配置數(shù)據(jù)庫,BootConfiguration Database)實際上存放在注冊表儲巢中,有人可能會爭辯說,對注冊表的訪問發(fā)生在更早的時候(當(dāng)引導(dǎo)管理器顯示操作系統(tǒng)列表的時候)。
  • 在內(nèi)核引導(dǎo)過程中,內(nèi)核讀取有關(guān)的設(shè)置信息,這些設(shè)置信息指定了應(yīng)該加載哪些設(shè)備驅(qū)動程序,以及各個系統(tǒng)部件(比如內(nèi)存管理器和進(jìn)程管理器(process manager))該如何配置以及該如何調(diào)整系統(tǒng)的行為。
  • 在登錄過程中,Explorer和其他的Windows組件從注冊表中讀取到每個用戶的參數(shù)選擇,包括網(wǎng)絡(luò)驅(qū)動器字母映射、桌面墻紙、屏幕保護(hù)程序、菜單行為和圖標(biāo)的擺放,以及可能更重要的是,哪些啟動程序需要激發(fā)起來,以及哪些文件最近被訪問過。
  • 在應(yīng)用程序啟動過程中,它們讀取系統(tǒng)全局范圍的設(shè)置,比如所有可選安裝的組件列表和許可數(shù)據(jù),還讀取一些針對每個用戶的設(shè)置信息,可能包括菜單和工具欄的擺放,以及最近訪問過的文檔的列表。

然而,在其他時間點(diǎn)上也可以讀取注冊表,比如在響應(yīng)注冊表值或鍵的修改的時候。

注冊表提供了異步回調(diào)機(jī)制,這是優(yōu)先采用的接收注冊表變化通知的方法,盡管如此,仍然有些應(yīng)用程序會通過不停查詢的做法,來監(jiān)視它們存放在注冊表中的設(shè)置信息,以便自動地讓更新過的設(shè)置信息起作用。然而,一般來說,在一個空閑的系統(tǒng)上,注冊表不應(yīng)該有任何活動,顯然這樣的應(yīng)用程序違反了注冊表的最佳實踐原則。(Sysinternals的Process Monitor是一個追查這種活動和錯誤應(yīng)用程序的極佳工具。)

在下面的情形下,注冊表通常會被修改:

  • 雖然算不上是修改,但是,注冊表的初始結(jié)構(gòu)和許多默認(rèn)設(shè)置是由一個原型版本的注冊表來定義的,此原型版本隨Windows的安裝介質(zhì)一起發(fā)行,在安裝的時候被拷貝到一個新的系統(tǒng)中。
  • 應(yīng)用安裝工具創(chuàng)建了默認(rèn)的應(yīng)用程序設(shè)置,以及一些可反映出安裝配置選項的設(shè)置信息。
  • 在設(shè)備驅(qū)動程序的安裝過程中,即插即用系統(tǒng)在注冊表中創(chuàng)建了一些設(shè)置,告訴I/O管理器如何啟動此驅(qū)動程序;
    還創(chuàng)建了其他一些用于配置該驅(qū)動程序如何操作的設(shè)置(有關(guān)設(shè)備驅(qū)動程序如何被安裝到系統(tǒng)中的更多信息,參見本書下冊第8章“I/O系統(tǒng)”)。
  • 當(dāng)你通過用戶界面改變了應(yīng)用程序或者系統(tǒng)設(shè)置時,這些改變通常被保存在注冊表中。

4.1.3 注冊表數(shù)據(jù)類型

注冊表是一個數(shù)據(jù)庫,其結(jié)構(gòu)類似于磁盤卷的結(jié)構(gòu)。注冊表包含了鍵(key)和值( value)。鍵類似于磁盤的目錄,而值則好比是磁盤上的文件。鍵是一個容器,可以包含其他的鍵(子鍵)或值。而值存儲的是數(shù)據(jù)。最頂級的鍵是根鍵(root key)。在本節(jié)中,我們將相互交換著使用子鍵和鍵這兩個詞。

鍵和值兩者都借用了文件系統(tǒng)的命名規(guī)范。因此,可以用名稱“trade\mark”唯一標(biāo)識一個存儲在名為“trade”的鍵下面的、名為“mark”的值。此命名方案的一個例外是,每個鍵都有一個未命名的值。Regedit將未命名的值顯示為“(默認(rèn))”。
注冊表的值存儲了不同種類的數(shù)據(jù),它們可以是表4.1中所列出的12種類型之一。大多數(shù)注冊表值是REG_DWORD、REG_BINARY或REG_Sz。REG_DWORD類型的值可以存儲整數(shù)或者布爾值(開/關(guān)值);REG_BINARY值可以存儲超過32位的整數(shù)值,或者諸如加密后的口令之類的原始數(shù)據(jù);REG_Sz值存儲字符串(當(dāng)然是Unicode),可以表達(dá)諸如名稱、文件名、路徑和類型等元素。

REG_LINK類型特別值得注意,因為它讓一個鍵可以透明地指向另一個鍵。當(dāng)你搜索注冊表而碰到一個鏈接時,路經(jīng)搜索過程會在此鏈接的目標(biāo)處繼續(xù)進(jìn)行。例如,如果\Root1\Link是一個指向\Root2\RegKey的REG_LINK值,并且RegKey包含了值RegValue,那么,可以有兩條路徑標(biāo)識RegValue:\Root1\Link\RegValue和\Root2(RegKey\RegValue。

正如下一小節(jié)將要解釋的那樣,Windows自己也顯式地使用了注冊表鏈接:

  • 六個注冊表根鍵中有三個其實是鏈接,它們指向另三個非鏈接的根鍵中的子鍵。
    [筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.4 注冊表邏輯結(jié)構(gòu)

利用注冊表中存儲的數(shù)據(jù),你可以畫出注冊表的組織結(jié)構(gòu)。

總共有6個根鍵(你不能加入新的根鍵,也不能刪除已有的根鍵)可以存儲信息,如表4.2所示。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
為什么根鍵的名稱都以“H”開頭?因為根鍵的名稱代表了指向鍵(KEY)的Windows句柄(H)。正如第1章“”中所提及的那樣,HKLM是用于HKEY_LOCAL_MACHINE的縮寫。表4.3列出了所有的根鍵以及它們的縮寫。下面的幾小節(jié)詳細(xì)地解釋了這六個根鍵中每一個根鍵的內(nèi)容和用途。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.4.1 HKEY_CURRENT_USER

HKCU根鍵包含了與當(dāng)前本地登錄用戶的參數(shù)和軟件配置有關(guān)的數(shù)據(jù)。

它指向當(dāng)前登錄用戶的用戶輪廓,位于硬盤上的\Users\<用戶名>\Ntuser.dat中(要想知道根鍵是如何被映射到硬盤文件的,請參見本章后面的“注冊表的內(nèi)部機(jī)理”一節(jié))。無論何時當(dāng)一個用戶的輪廓信息被加載的時候(比如在登錄時候,或者當(dāng)一個服務(wù)進(jìn)程運(yùn)行在某個特定用戶名的環(huán)境中時),HKCU就會被創(chuàng)建出來,映射到HKEY_USERS下該用戶的鍵上。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

表4.4 列出了HKCU下面的一些子鍵。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.4.2 HKEY_USERS

HKU為系統(tǒng)中每個加載的用戶輪廓和用戶類注冊數(shù)據(jù)庫包含了一個子鍵。它也包含一個名為HKU\DEFAULT的子鍵;這是一個鏈接,指向該系統(tǒng)的輪廓(對于那些運(yùn)行在本地系統(tǒng)賬戶下的進(jìn)程,它們會用到該輪廓信息;本章后面的“服務(wù)”一節(jié)中更加詳細(xì)地講述了有關(guān)該輪廓的信息)。譬如,這是Winlogon使用的輪廓,所以,改變了該輪廓中的桌面背景設(shè)置,就可以在登錄屏幕上體現(xiàn)出來。當(dāng)一個用戶第一次登錄到一個系統(tǒng)中,并且他的賬戶不依賴于一個漫游域輪廓(也就是說,用戶的輪廓是在域控制器的指示下,從一個中心網(wǎng)絡(luò)位置上獲得的)的時候,系統(tǒng)以%SystemDrive%\Users\Default下存放的輪廓為基礎(chǔ),為她的賬戶創(chuàng)建一個輪廓。

系統(tǒng)將這些輪廓存儲在哪里呢?
此位置是由注冊表值HKLM\Software\Microsoft\WindowsNT\CurrentVersion\ProfileList\ProfilesDirectory來定義的,其默認(rèn)設(shè)置是%SystemDrive%\Users。ProfileList鍵也存放了一個系統(tǒng)上所有出現(xiàn)過的輪廓的列表。針對每個輪廓的信息存放在一個對應(yīng)的子鍵下面,其名稱反映了該輪廓所對應(yīng)賬戶的安全標(biāo)識符(SID,Security ldentifier)(有關(guān)SID的更多信息,參見第6章“安全性”)。在每個輪廓的鍵所存儲的數(shù)據(jù)中,sid值中存放的是該賬戶SID的二進(jìn)制表示,而在ProfilelmagePath目錄中存放的則是該輪廓所對應(yīng)的儲巢( hive,參見本章后面的“儲巢”一節(jié)中的介紹)的硬盤路徑。Windows在用戶輪廓管理對話框中顯示了一個系統(tǒng)中存儲的輪廓列表,如圖4.1所示。你只需在控制面板的System小程序的AdvancedSystem Settings中的Advanced標(biāo)簽視圖的User Profiles部分單擊Setting,就可以看到該對話框。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.4.3 HKEY_CLASSES_ROOT

HKCR包含了三種類型的信息:

  • 文件擴(kuò)展名關(guān)聯(lián)
  • COM類注冊信息
  • 以及UAC(User AccountControl)虛擬化注冊表根。(關(guān)于UAC的更多信息,請參見第6章。)

針對每個已注冊的文件擴(kuò)展名,都有一個對應(yīng)的鍵。大多數(shù)鍵中包含一個REG_Sz值,指向HKCR中的另一個鍵,此鍵中包含了與該擴(kuò)展名所代表的文件類相關(guān)聯(lián)的信息。

例如,HKCR.xls指向了在另一個鍵(比如HKCU.xls\Excel.Sheet.8)中包含的有關(guān)MicrosoftExcel文件的信息。HKCR中其他的鍵包含了已注冊到當(dāng)前系統(tǒng)上的COM對象的詳細(xì)配置信息。UAC虛擬化注冊表位于VirtualStore鍵下,它與HKCR中保存的其他種類的數(shù)據(jù)沒有關(guān)系。

HKEY_CLASSES_ROOT下的數(shù)據(jù)有以下兩個來源途徑:

  • 針對每個用戶的類注冊數(shù)據(jù):位于HKCU\SOFTWARE\Classes下(被映射至磁盤文件\Users\<username>\AppData\Local\Microsoft\Windows\Usrclass.dat)。
  • 整個系統(tǒng)范圍的類注冊數(shù)據(jù):位于HKLM\SOFTWARE\Classes下。

針對每個用戶的注冊數(shù)據(jù)為什么要與系統(tǒng)全局范圍的注冊數(shù)據(jù)分開呢?

其原因在于

  1. 這樣做以后,可漫游的輪廓就能夠包含這些定制的用戶數(shù)據(jù)。
  2. 它同時也關(guān)閉了一個安全漏洞:非特權(quán)用戶無法改變或者刪除系統(tǒng)全局版本HKEY_CLASSES_ROOT中的鍵,因此也就無法影響到系統(tǒng)中應(yīng)用程序的操作。非特權(quán)用戶和應(yīng)用程序可以讀取系統(tǒng)全局范圍的數(shù)據(jù),也可以在系統(tǒng)全局?jǐn)?shù)據(jù)中增加新的鍵和值(在它們的用戶數(shù)據(jù)中這些鍵和值也被鏡像過來了),但是它們只能修改其私有數(shù)據(jù)中已有的鍵和值。

4.1.4.4 HKEY_LOCAL_MACHINE

根鍵HKLM包含了所有系統(tǒng)全局范圍的配置子鍵:

  • BCD00000000
  • COMPONENTS(根據(jù)需要動態(tài)加載)、
  • HARDWARE
  • SAM
  • SECURITY
  • SOFTWARE和SYSTEM

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

HKLM\BCD00000000

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

HKLM\BCD00000000子鍵包含了引導(dǎo)配置數(shù)據(jù)庫(BCD)信息,這是作為一個注冊表儲巢被加載進(jìn)來的。該數(shù)據(jù)庫代替了以前在Windows Vista以前使用的Boot.ini文件,為每次安裝Windows的引導(dǎo)配置數(shù)據(jù)增加了極大的靈活性和隔離性。(有關(guān)BCD的更多信息,請參考本書下冊第13章“啟動和停機(jī)”。)

在BCD中的每一項,比如一次安裝的Windows,或者針對這一次安裝的命令行設(shè)置,都保存在Objects子鍵下,它或者是一個可通過GUID來引用的對象(針對引導(dǎo)項的情形),或者是一個稱為元素(element)的數(shù)值子鍵。這些原始元素,絕大多數(shù)在MSDN Library的BCD參考材料部分有文檔描述,它們定義了各種命令行設(shè)置或者引導(dǎo)參數(shù)。與每個元素子鍵相關(guān)聯(lián)的值對應(yīng)于相應(yīng)的命令行標(biāo)記或者引導(dǎo)參數(shù)的值。

BCDEdit命令行工具使得你可以通過元素和對象的符號名稱來修改BCD。它也提供了關(guān)于所有可用的引導(dǎo)選項的大量幫助信息;不幸的是,它只能工作在本地系統(tǒng)環(huán)境中。因為注冊表可以被遠(yuǎn)程打開,也可以從儲巢文件中導(dǎo)入進(jìn)來,所以,你可以利用注冊表編輯器來修改或讀取一臺遠(yuǎn)程計算機(jī)的BCD。下面的實驗顯示了你可以利用注冊表編輯器來打開內(nèi)核調(diào)試功能。

HKLM\COMPONENTS

子鍵HKLM\COMPONENTS包含的信息與CBS(Component Based Servicing,基于組件的服務(wù))棧有關(guān)。此CBSs棧包含了各種文件和資源,它們是Windows安裝映像(用于自動化的安裝包或者OEM預(yù)安裝包)的一部分,或者是一次活動的Windows安裝的一部分。為了服務(wù)目的而定義的CBS AP利用此注冊表鍵中的信息來標(biāo)識出已安裝的組件和它們的配置信息。無論何時當(dāng)單獨(dú)地或者成組地安裝、更新或者移除組件(分別稱為單元unit或包package)時,這些信息都會被用到。因為該鍵可能會非常大,所以,為了優(yōu)化系統(tǒng)資源,如果CBs棧在服務(wù)一個請求時,它只是被動態(tài)地加載到系統(tǒng)中,然后根據(jù)需要被卸載。

HKLM\HARDWARE

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

子鍵HKLM\HARDWARE維護(hù)了有關(guān)當(dāng)前系統(tǒng)中的遺留硬件,以及一些從硬件設(shè)備至驅(qū)動程序的映射關(guān)系的描述信息。在現(xiàn)代系統(tǒng)上,只有一些外設(shè),比如鍵盤、鼠標(biāo)和ACPIBIOS數(shù)據(jù),有可能在這里能找得到。設(shè)備管理器工具(從控制面板中運(yùn)行System小程序,然后單擊DeviceManager〉使你可以查看注冊表硬件信息,這些信息只是簡單地從HARDWARE鍵中讀取出來的值(盡管它主要使用了HKLM\SYSTEM\CurrentControlSet\Enum樹)。

HKLM\SAM

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

HKLM\SAM存放了本地賬戶和組的信息,比如用戶口令、組定義和域關(guān)聯(lián)信息。作為域控制器運(yùn)行的Windows Server系統(tǒng)將域賬戶和組的信息存放在活動目錄(Active Directory)中。所謂活動目錄,是一個用于存放域范圍的各種設(shè)置和信息的數(shù)據(jù)庫(本書并沒有介紹活動目錄)。在默認(rèn)情況下,SAM鍵上的安全描述符被配置成:即使管理員賬戶也不能訪問。

HKLM\SECURITY存放了系統(tǒng)全局范圍的安全策略和用戶權(quán)限分配。HKLM\SAM被鏈接到HKLM\SECURITY\SAM下的SECURITY子鍵。在默認(rèn)情況下,你不能查看HKLM\SECURITY或者HKLM\SAM\SAM鍵的內(nèi)容,因為這些鍵的安全設(shè)置只允許System賬戶才能訪問(本章后面將會極其詳細(xì)地討論System賬戶)。如果你想要檢查這些鍵的話,你可以改變此安全描述符,以便允許管理員組中的用戶可以讀訪問這些鍵,或者,你可以利用PsExec,在本地系統(tǒng)賬戶中運(yùn)行Regedit。然而,僅僅看一眼這些數(shù)據(jù)是不會有多大用處的,因為這些數(shù)據(jù)是沒有文檔說明的,其中的口令已經(jīng)通過一個單向映射關(guān)系加了密,也就是說,你是無法從加密形式得到一個口令的。

HKLM\SOFTWARE

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

HKLM\SOFTWARE是Windows存儲系統(tǒng)全局配置信息的地方,這些配置信息在系統(tǒng)引導(dǎo)的時候并不需要。而且,第三方應(yīng)用程序也將它們的系統(tǒng)全局范圍的設(shè)置存放在這里,比如應(yīng)用程序文件和目錄的路徑、授權(quán)許可信息和過期日期信息。

HKLM\SYSTEM

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

HKLM\SYSTEM包含了引導(dǎo)系統(tǒng)所需要的全局配置信息,比如該加載哪些設(shè)備驅(qū)動程序、該啟動哪些服務(wù)。因為這些信息對于啟動系統(tǒng)是至關(guān)重要的,所以,Windows也在該鍵下維護(hù)了這份信息的一份拷貝,稱為“l(fā)ast known good control set(最后已知的好控制集)”。由于維護(hù)了這樣一份拷貝,因此,萬一對當(dāng)前控制集做了修改以后,系統(tǒng)無法正常引導(dǎo)了,管理員可以選擇以前的一份正常工作的控制集。關(guān)于Windows何時聲明當(dāng)前控制集是“好”的,更詳細(xì)的信息請參見本章“接受當(dāng)前引導(dǎo)和最后已知的好控制集”一節(jié)。

4.1.4.5 HKEY_CURRENT_CONFIG

HKEY_CURRENT_CONFIG只是一個鏈接,指向HKLM\SYSTEM\CurrentControlSet\ HardwareProfiles\Current下的當(dāng)前硬件輪廓。Windows不再支持硬件輪廓,但是該鍵仍然存在,以便支持那些遺留下來的、可能依賴于該鍵的應(yīng)用程序。

4.1.4.6 HKEY_PERFORMANCE_DATA

在Windows上,注冊表也是訪問性能計數(shù)器值的機(jī)制,無論這些計數(shù)器是從操作系統(tǒng)組件中來的,還是從服務(wù)器應(yīng)用程序中來的。通過注冊表來訪問性能計數(shù)器,一個額外的好處是,遠(yuǎn)程性能監(jiān)視工作幾乎可以“免費(fèi)”完成,因為注冊表很容易通過常規(guī)的注冊表API就可以訪問到。
只需打開一個名為HKEY_PERFORMANCE_DATA的特殊的鍵,并且查詢該鍵下面的值,就可以直接訪問注冊表性能計數(shù)器信息。如果在注冊表編輯器中查尋此鍵,是無法找到的;這個鍵只能通過編程的方式,利用Windows注冊表函數(shù),例如RegQueryValueEx,才可以訪問到。性能信息實際上并沒有存儲在注冊表中,注冊表函數(shù)利用該鍵來獲得從性能數(shù)據(jù)提供者那里提供的信息。
也可以利用性能數(shù)據(jù)幫助器(PDH,Performance Data Helper)API(Pdh.dIl)中的性能數(shù)據(jù)幫助器函數(shù)來訪問性能計數(shù)器信息。圖4.2顯示了在訪問性能計數(shù)器信息過程中涉及的組件。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.5 事務(wù)型注冊表(TxR)

感謝內(nèi)核事務(wù)管理器(KTM,KernelTransaction Manager;更多信息參見第3章“系統(tǒng)機(jī)制”中關(guān)于KTM的章節(jié)),開發(fā)人員在執(zhí)行注冊表操作時,可以通過訪問直接的API就可以獲得魯棒的、錯誤恢復(fù)的能力,并且與非注冊表操作,比如文件或數(shù)據(jù)庫操作,鏈接起來。

有三個API支持事務(wù)方式的注冊表修改:

  • RegCreateKeyTransacted
  • RegOpenKeyTransacted
  • RegDeleteKeyTransacted

這些新的例程與對應(yīng)的非事務(wù)型例程一樣,也帶同樣的參數(shù),只是額外加入了一個新的事務(wù)句柄參數(shù)。開發(fā)人員在調(diào)用了KTM函數(shù)CreateTransaction以后就可以提供此句柄了。

在一個事務(wù)方式的創(chuàng)建或打開操作以后,所有后續(xù)的注冊表操作,比如創(chuàng)建、刪除或者修改該鍵中的值,都將是事務(wù)方式的。然而,在事務(wù)方式鍵的子鍵上執(zhí)行的操作并不是自動事務(wù)方式的,這也是為什么第三個API,RegDeleteKeyTransacted要存在的原因。它使得可以以事務(wù)方式來刪除子鍵,這是RegDeleteKeyEx在常規(guī)方式下做不到的。

如同其他的KTM操作一樣,這些事務(wù)操作的數(shù)據(jù)也通過公共的日志文件系統(tǒng)(CLFS,common loggingfile system)服務(wù)寫到了志文件中。在事務(wù)本身被提交或者回滾(這兩種情形可以通過編程來發(fā)生,也可以是電源失敗或者系統(tǒng)崩潰導(dǎo)致的后果) 以前,凡是在事務(wù)句柄上執(zhí)行的鍵、值和其他的注冊表修改對于通過非事務(wù)API來執(zhí)行操作的外部應(yīng)用程序都是不可見的。而且,事務(wù)是相互隔離的,在一個事務(wù)內(nèi)所做的修改,在該事務(wù)被提交以前,在其他的事務(wù)內(nèi)部,或者該事務(wù)外部,都是不可見的。

注:
一個非事務(wù)的寫者在沖突的情況下將使一個事務(wù)中途失敗一一例如,如果在一個事務(wù)內(nèi)部創(chuàng)建了一個值,后來,當(dāng)該事務(wù)仍然活動的時候,一個非事務(wù)的寫者試圖在同樣的鍵下創(chuàng)建一個值。非事務(wù)的操作將會成功,而在該沖突的事務(wù)內(nèi)的操作將會被丟棄。

TxR資源管理器實現(xiàn)的隔離級別(ACID中的“1”)是“讀-提交”(read-commit),這意味著在事務(wù)被提交之后其所做的修改對其他的讀者 (事務(wù)的或非事務(wù)的)立即起作用。這種機(jī)制對于那些熟悉數(shù)據(jù)庫事務(wù)的人來非常重要,因為在數(shù)據(jù)庫的事務(wù)中,隔離級別是“可預(yù)測的讀(predictable-read)”(或者游標(biāo)-穩(wěn)定的,cursor-stability,這是在數(shù)據(jù)庫的文章資料中的法)。對于“可預(yù)測的-讀”隔離級別,在你讀取了事務(wù)內(nèi)部的一個值以后,后續(xù)再讀這個值,都會取回同樣的數(shù)據(jù)?!白x-提交”并沒有這種保證。這樣的一個后果是,注冊表事務(wù)不能被用于對一個注冊表值進(jìn)行遞增或遞減操作。

為了對注冊表做永久的修改,已經(jīng)使用了事務(wù)句柄的應(yīng)用程序必須調(diào)用KTM函數(shù)CommitTransaction。(如果應(yīng)用程序決定要撤銷其所做的修改,比如在失敗的路徑上,那么,它可以調(diào)用RollbackTransaction AP1。)然后,這些修改對于常規(guī)的注冊表API也將是可見的。

注:
如果一個用CreateTransaction來創(chuàng)建的事務(wù)句柄在該事務(wù)被提交之前關(guān)閉了(并且也沒有其他的句柄指向這一事務(wù)),那么,系統(tǒng)將會回滾該事務(wù)。

TxR除了用到KTM提供的CLFS以外,還把它的內(nèi)部日志文件存儲在系統(tǒng)卷上的%SystemRoot%(System32\Config\Txr文件夾中;這些文件有一個regtrans-ms擴(kuò)展名,默認(rèn)是隱藏的。即使你的系統(tǒng)上沒有安裝第三方的應(yīng)用程序,該目錄中也會包含一些文件,因為Windows UpdateCBS(Component BasedServicing)利用TxR來以原子方式往注冊表中寫數(shù)據(jù)以避免系統(tǒng)失敗,或者在一次不兼容的系統(tǒng)更新過程中造成組件數(shù)據(jù)不一致。事實上,如果你看一看其中的某些事務(wù)文件,應(yīng)該能夠看到那些正在被執(zhí)行事務(wù)的鍵的名稱。

有一個全局的注冊表資源管理器(RM,resource manager),它服務(wù)于所有在引導(dǎo)時候被掛載上來的儲巢。對于每一個被顯式掛載的儲巢,都會對應(yīng)地創(chuàng)建一個RM。對于使用注冊表事務(wù)的應(yīng)用程序,RM的創(chuàng)建是完全透明的,因為KTM確保參與同一個事務(wù)的所有RM,在兩階段遞交/失敗協(xié)議中會協(xié)調(diào)工作。對于全局的注冊表RM,CLFS日志文件被存儲在前面所提到的System32\Config\Txr目錄中。對于其他的儲巢,日志文件存儲在與儲巢相同的目錄中。它們是隱藏的文件,遵從同樣的命名規(guī)則,結(jié)束后綴為.regtrans-ms。日志文件名稱的前綴是它們所對應(yīng)的儲巢的名稱。

4.1.6 監(jiān)視注冊表活動

因為系統(tǒng)和應(yīng)用程序如此嚴(yán)重地依賴于配置設(shè)置來指導(dǎo)它們的行為,所以,系統(tǒng)和應(yīng)用程序的失敗可能是由于注冊表數(shù)據(jù)的改變或者安全性而導(dǎo)致的。當(dāng)系統(tǒng)或者應(yīng)用程序未能讀取到它們認(rèn)為總是能夠訪問到的設(shè)置數(shù)據(jù)時,它們可能不會正常工作,顯示一些隱藏了根源的錯誤消息,甚至崩潰。如果在系統(tǒng)或應(yīng)用程序失敗以后,不理解它們是如何訪問注冊表的,那么,要想知道哪些注冊表鍵或者值被錯誤配置了,那基本上是不可能的。

在這樣的情況下, Windows sysinternals (http://technet.microsoftcom/sysinternals)的進(jìn)程監(jiān)視工具(ProcessMonitor)也許能提供答案。

進(jìn)程監(jiān)視器允許你監(jiān)視注冊表的一舉一動。對于每一次注冊表訪問,進(jìn)程監(jiān)視器都會向你顯示執(zhí)行這一次訪問的進(jìn)程:這一次訪問的時間、類型和結(jié)果:以及在執(zhí)行這次訪問時刻線程的棧。這些信息極其有助于看清楚應(yīng)用程序和系統(tǒng)是如何依賴于注冊表的,也有助于發(fā)現(xiàn)應(yīng)用程序和系統(tǒng)將配置設(shè)置信息存儲在哪里,以及可以診斷出那些“因漏掉了注冊表鍵或值而發(fā)生”的應(yīng)用程序問題。進(jìn)程監(jiān)視器包含了高級的過濾和加亮顯示功能,所以你可以快速地將注意力集中在與特定的鍵或值有關(guān)的活動上,或者與特定進(jìn)程有關(guān)的活動上。

4.1.6.1 進(jìn)程監(jiān)視器(Process Monitor)內(nèi)部機(jī)理

進(jìn)程監(jiān)視器依賴于一個設(shè)備驅(qū)動程序,該設(shè)備驅(qū)動程序是在運(yùn)行時刻從它的可執(zhí)行映像文件中提取得到、然后再啟動起來的。它第一次執(zhí)行的時候,要求當(dāng)前運(yùn)行它的賬戶具有LoadDriver特權(quán),以及Debug特權(quán);在同一次引導(dǎo)會話中,以后再執(zhí)行的時候只需要Debug特權(quán)就可以了,因為一旦該驅(qū)動程序被加載以后,它就會駐留在系統(tǒng)中。

4.1.7 進(jìn)程監(jiān)視器診斷技巧

有兩種基本的進(jìn)程監(jiān)視器診斷技巧對于發(fā)現(xiàn)與注冊表有關(guān)的應(yīng)用程序或系統(tǒng)問題特別有效,它們是:

  • 在進(jìn)程監(jiān)視器的痕跡數(shù)據(jù)中,查看應(yīng)用程序失敗以前所做的最后的事情:這個動作可能直指問題本身。
  • 將失敗了的應(yīng)用程序的進(jìn)程監(jiān)視器痕跡數(shù)據(jù),與一個正常運(yùn)行的系統(tǒng)的痕跡數(shù)據(jù)進(jìn)行比較。

按照第一種方法,先運(yùn)行進(jìn)程監(jiān)視器,再運(yùn)行應(yīng)用程序。在失敗發(fā)生的時候,回到進(jìn)程監(jiān)視器中,停止記錄數(shù)據(jù)(按下Ctrl+E)。然后,回到日志的末尾,找到該應(yīng)用程序失敗(或者崩潰,或者掛起,或者其他的失敗行為)以前最后執(zhí)行的一些操作。從最后一行開始往回查找,檢查被引用到的文件、注冊表鍵,或者兩者兼而有之一一通常,這將有助于查明問題的原因所在。
當(dāng)應(yīng)用程序在一個系統(tǒng)上可以工作,而在另一個系統(tǒng)上卻失敗的時候,考慮使用第二種方法。分別在正常工作的系統(tǒng)上和失敗的系統(tǒng)上捕獲到該應(yīng)用程序的痕跡數(shù)據(jù),并且將這些輸出數(shù)據(jù)保存到一個日志文件中。然后用Microsoft Excel打開好的和壞的日志文件(在Import向?qū)е薪邮苣J(rèn)選項),刪除其中的前三列(如果你不刪除這前三列,比較的時候?qū)@示出每一行都不相同,因為前三列中包含了一些每次運(yùn)行都不相同的信息,比如時間和進(jìn)程ID)最后,比較結(jié)果日志文件(你也可以用WinDiff來比較,在Windows SDK中包含了該工具)。在進(jìn)程監(jiān)視器的痕跡數(shù)據(jù)中,如果在Result一列中有“NAMENOTFOUND”或者“ACCESSDENIED”的值,則這樣的記錄項應(yīng)該是你要仔細(xì)探查的。當(dāng)一個應(yīng)用程序試圖讀取一個不存在的注冊表鍵或者值的時候,NAME NOTFOUND就會被報告出來。在許多情況下,漏掉一個注冊表鍵或者值是無關(guān)緊要的,因為如果進(jìn)程未能從注冊表中讀取到它想要的設(shè)置,它只需簡單地使用默認(rèn)的值就可以。然而,在有些情況下,應(yīng)用程序期望能找到非默認(rèn)的值,如果非默認(rèn)值不存在的話,則應(yīng)用程序就會失敗。

訪問拒絕錯誤是一種常見的、與注冊表有關(guān)的應(yīng)用程序失敗的根源,當(dāng)應(yīng)用程序沒有權(quán)限訪問一個它想要的鍵時,就會發(fā)生這種錯誤。如果應(yīng)用程序不檢查注冊表操作的結(jié)果,或者不執(zhí)行恰當(dāng)?shù)腻e誤恢復(fù)過程,則應(yīng)用程序就會失敗。

一個可能值得懷疑的常見結(jié)果字符串是BUFFER OVERFLOW。它并不表明在收到此錯誤的應(yīng)用程序中有一個緩沖區(qū)溢出漏洞。相反,配置管理器利用它來通知一個應(yīng)用程序,它所指定的用于存放一個注冊表值的緩沖區(qū)太小,因而容不下該值。應(yīng)用程序開發(fā)人員通常利用這種行為,來確定一個緩沖區(qū)到底應(yīng)該分配多大才能存放一個值。他們首先用一個0長度的緩沖區(qū)來執(zhí)行一個注冊表查詢操作,結(jié)果返回一個緩沖區(qū)溢出錯誤,以及該查詢操作期望讀取的數(shù)據(jù)的長度。然后,應(yīng)用程序按照所指示的大小值分配一個緩沖區(qū),重新讀取該值。因此,你應(yīng)該可以看到,返回BUFFER OVERFLOW的操作總是伴有一次成功的重復(fù)操作。
在一個利用進(jìn)程監(jiān)視器來診斷實際問題的例子中,它使得一個用戶避免了完全重裝他的Windows系統(tǒng)。其癥狀是,如果用戶在啟動InternetExplorer之前,不手工撥號打開Internet連接的話,Internet Explorer就會在啟動的時候掛起。此Internet連接被設(shè)置成系統(tǒng)的默認(rèn)連接,所以,一啟動Internet Explorer,應(yīng)該就會引發(fā)自動撥號到Internet (因為internet Explorer被設(shè)置成:一啟動就顯示一個默認(rèn)主頁)。

在檢查Internet Explorer啟動活動的進(jìn)程監(jiān)視器日志的過程中發(fā)現(xiàn),從Internet Explorer被掛起的點(diǎn)開始往前回溯,有一個針對HKCU\SoftwarelMicrosoft\RAS Phonebook下的鍵的查詢操作。用戶報告說,在此之前他卸載了與該鍵關(guān)聯(lián)的撥號程序,并且手工創(chuàng)建了撥號連接。因為撥號連接的名稱與卸載的撥號程序的名稱不相符,所以,看起來似乎是撥號卸載程序并沒有刪除該鍵,因而引起InternetExplorer被掛起。在刪除了該鍵以后,InternetExplorer又正常工作。

4.1.8 在非特權(quán)賬戶下或者登錄/注銷過程中記錄活動

一種常見的應(yīng)用程序失敗的情形是,應(yīng)用程序在一個具有Administrative組成員資格的賬戶下運(yùn)行時可以正常工作,但是在一個非特權(quán)用戶的賬戶下運(yùn)行時卻不能正常工作。正如前面所介紹的,為了執(zhí)行進(jìn)程監(jiān)視器,需要一些在正常情況下未分配給標(biāo)準(zhǔn)用戶賬戶的安全特權(quán),但是,利用Runas命令,在一個管理員賬戶下執(zhí)行進(jìn)程監(jiān)視器,你就可以捕獲到在非特權(quán)用戶的登錄會話中執(zhí)行的應(yīng)用程序的痕跡數(shù)據(jù)。

如果一個注冊表問題與賬戶的登錄或注銷有關(guān),那么,你也可以采取特別的步驟,以便利用進(jìn)程監(jiān)視器來捕獲一個登錄會話中有關(guān)這些階段的痕跡數(shù)據(jù)。當(dāng)一個用戶注銷的時候,在本地系統(tǒng)賬戶下運(yùn)行的應(yīng)用程序并不會終止,所以,你可以利用這一事實,讓進(jìn)程監(jiān)視器在一個先注銷隨后又登錄的過程中一直保持運(yùn)行。你或者利用Windows內(nèi)置的At命令并指定/interactive標(biāo)志,或者利用Sysinternals的PsExec工具,就可以在本地系統(tǒng)賬戶下啟動進(jìn)程監(jiān)視器。

PsExec工具的使用例子如下:

psexec -i0 -s -d c:\procmon.exe

-i0開關(guān)指示PsExec,讓進(jìn)程監(jiān)視器的窗口出現(xiàn)在會話0的交互式控制臺上:-s開關(guān)讓PSExec在本地系統(tǒng)賬戶下運(yùn)行進(jìn)程監(jiān)視器:-d開關(guān)讓PSExec激發(fā)進(jìn)程監(jiān)視器,并且無須等待它終止就可以退出。當(dāng)你執(zhí)行了該命令時,它執(zhí)行起來的進(jìn)程監(jiān)視器實例在你注銷以后仍然還存活著,當(dāng)你登錄回來時,此進(jìn)程監(jiān)視器實例仍然出現(xiàn)在桌面上,因此它可以捕獲到注銷和登錄這兩個動作的注冊表活動。

在登錄、注銷、引導(dǎo)或者停機(jī)過程中監(jiān)視注冊表的另一種辦法是利用進(jìn)程監(jiān)視器的“記錄引導(dǎo)”特性,你只要在Options菜單中選擇“Log Boot”命令就可以打開該特性。下一次當(dāng)你引導(dǎo)系統(tǒng)的時候,進(jìn)程監(jiān)視器的設(shè)備驅(qū)動程序就會從系統(tǒng)引導(dǎo)的早期開始,將注冊表的活動記錄到%SystemRoot\Procmon.pml中。它會一直在該文件中記錄注冊表的活動,直到磁盤空間耗盡、系統(tǒng)停機(jī)或者你運(yùn)行進(jìn)程監(jiān)視器為止。在Windows系統(tǒng)上,一個存儲了啟動、登錄、注銷和停機(jī)的注冊表痕跡的日志文件,通常情況下在50MB到150MB之間。

4.1.9 注冊表的內(nèi)部機(jī)理

在這一小節(jié)中,你將會了解到,配置管理器(即實現(xiàn)了注冊表的執(zhí)行體子系統(tǒng))是如何組織注冊表的磁盤文件的。我們將會檢查:當(dāng)應(yīng)用程序或者其他的操作系統(tǒng)組件讀取或者改變注冊表鍵和值的時候,配置管理器是如何管理注冊表的。我們還將討論一些注冊表管理機(jī)制,配置管理器試圖利用這些機(jī)制來確保注冊表總是處于一種可恢復(fù)的狀態(tài),即使當(dāng)注冊表被修改時系統(tǒng)崩潰了也可以被恢復(fù)。

4.1.9.1 儲巢(Hive)

在磁盤上,注冊表并不是簡單的一個大文件,而是一組稱為儲巢(hive)的獨(dú)立文件。每個儲巢包含了一棵注冊表樹,有一個鍵作為該樹的根,或者作為該樹的起點(diǎn)。子鍵和它們的值存儲在根的下面。你可能會認(rèn)為,注冊表編輯器工具所顯示的根鍵與儲巢中的根鍵一定是相互關(guān)聯(lián)的,但實際情況并不是這樣的。表4.5列出了注冊表儲巢,以及它們對應(yīng)的磁盤文件名。除了用戶輪廓的儲巢以外,其他所有儲巢的路徑名都被編碼進(jìn)了配置管理器中。當(dāng)配置管理器加載儲巢的時候,包括系統(tǒng)輪廓的儲巢,它都會在HKLM\SYSTEM\CurrentControlSet\Control\Hivelist子鍵下的注冊表值中記錄下每個儲巢的路徑,如果儲巢被卸載的話,則刪除對應(yīng)的路徑。它創(chuàng)建了根鍵,并且將這些儲巢鏈接起來,以便建立起你所熟悉的、注冊表編輯器所顯示的注冊表結(jié)構(gòu)。

你會注意到,表4.5中列出的某些儲巢是易變的,它們沒有對應(yīng)的關(guān)聯(lián)文件。系統(tǒng)完全在內(nèi)存中創(chuàng)建和管理這些儲巢,因此這些儲巢是臨時的。系統(tǒng)在每次引導(dǎo)時創(chuàng)建這些易失的儲巢。易失儲巢的一個例子是HKLM\HARDWARE儲巢,它保存了有關(guān)物理設(shè)備和這些設(shè)備分配到的資源的信息。系統(tǒng)每次引導(dǎo)時,就會進(jìn)行資源分配和硬件檢測,所以,不在硬盤上存儲這些數(shù)據(jù)是合理的。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.1.9.2 儲巢的大小限制

在有些情況下,儲巢的大小是有限制的。例如,Windows對于HKLM\SYSTEM儲巢的大小是有限制的。它之所以這樣做,是因為,在引導(dǎo)過程開始之初,當(dāng)虛擬內(nèi)存換頁機(jī)制尚未啟用時,Winload要將整個HKLM\SYSTEM儲巢讀入到物理內(nèi)存中。Winload還要將Noskrnl和引導(dǎo)設(shè)備驅(qū)動程序加載到物理內(nèi)存中,所以,它必須要對分配給HKLMSYSTEM的物理內(nèi)存數(shù)量進(jìn)行限制 (關(guān)于Winload在啟動過程中所扮演的角色的更多信息,請參見本書下冊第13章)。在32位系統(tǒng)上,Winload允許該儲巢可以達(dá)到最高400MB或者該系統(tǒng)物理內(nèi)存數(shù)量的二分之一,取決于哪個更小一點(diǎn)。在x64系統(tǒng)上,低限是1.5GB。在itanium系統(tǒng)上,低限是32MB。

4.1.9.3 注冊表符號鏈接

一種被稱為注冊表符號鏈接(symbolic link)的特殊鍵使得配置管理器有可能將鍵鏈接起來,從而將注冊表組織起來。符號鏈接是一個鍵,它指導(dǎo)配置管理器到達(dá)另一個鍵。因此,HKLM\SAM鍵是一個符號鏈接,它連接到SAM儲巢的根所在的鍵上。符號鏈接是通過在RegCreateKey或RegCreateKeyEx調(diào)用中指定REG CREATE LINK參數(shù)而創(chuàng)建出來的。在內(nèi)部,配置管理器創(chuàng)建了一個名為SymbolicLinkValue的REG_LINK值,其中包含目標(biāo)鍵的路徑。因為該值的類型是REG LINK而不是REG SZ,所以,它對于Regedit是不可見的一一然而,它仍然是磁盤上注冊表儲巢的一部分。

4.1.9.4 儲巢結(jié)構(gòu)

配置管理器從邏輯上將一個儲巢分成一些稱為塊 (block)的分配單元,其方式類似于文件系統(tǒng)將一個磁盤分成簇。

根據(jù)定義,注冊表塊的大小為4096字節(jié) (4KB)。當(dāng)新的數(shù)據(jù)要擴(kuò)展一個儲巢時,該儲巢總是按照塊的粒度來增加。一個儲巢的第一個塊是基本塊(base block)。

基本塊包含了有關(guān)該儲巢的全局信息,包括:

  • 一個特征簽名regf (將該文件標(biāo)識成儲巢)、
  • 更新的序列號、
  • 一個時間戳(顯示了該儲巢上最后一個寫操作發(fā)生的時間)、
  • 有關(guān)Winload修復(fù)或恢復(fù)注冊表的信息、
  • 儲巢格式版本號、
  • 校驗和,
  • 以及該儲巢文件的內(nèi)部文件名(例如,DevicelHarddiskVolume1\WINDOWS\SYSTEM32\CONFIGSAM)。

當(dāng)我們講述如何將數(shù)據(jù)寫到個儲巢文件中的時候,我們將會說明更新的序列號和時間戳的重要性。

儲巢格式版本號指明了該儲巢內(nèi)部的數(shù)據(jù)格式。為了與Windows 2000的漫游輪廓兼容除了System和Software,對于所有其他的儲巢,配置管理器使用儲巢格式版本1.3(該版本將稱的前4個字符緩存在巢室索引結(jié)構(gòu)內(nèi)部,以便于快速查找,以此來提升搜索的性能);而對于system和Software儲巢,它使用版本1.5,因為新格式對于大的值(支持超過1MB的大值)和搜索(不再緩存一個名稱的前4個字符,而是使用整個名稱的散列值來降低沖突)作了特別的優(yōu)化。

Windows將一個儲巢所存儲的注冊表數(shù)據(jù)組織在一種稱為巢室 (cell)的容器中。一個巢室可以容納一個鍵、一個值、一個安全描述符、一列子鍵,或者一列鍵值。在巢室數(shù)據(jù)開始處的一個4字節(jié)字符標(biāo)記描述了該巢室數(shù)據(jù)的類型,作為一個特征簽名。表4.6詳細(xì)列出了每個巢室數(shù)據(jù)類型。巢室的頭是一個指定了該巢室大小的域(作為1的補(bǔ)數(shù),不出現(xiàn)在CM 結(jié)構(gòu)中)。當(dāng)一個巢室加入到一個儲巢中,而且該儲巢必須進(jìn)行擴(kuò)展才能包含該巢室時,系統(tǒng)創(chuàng)建一個稱為巢箱 (bin)的分配單元。

一個巢箱是新巢室正好擴(kuò)展到下一個塊或頁面邊界的大小。系統(tǒng)將巢室的尾部和巢箱的尾部之間的任何空間都看成是空閑的空間,因而可以將它再分配給其他的巢室。巢箱也有頭部,其中包含了一個特征簽名hbin、一個記錄了該巢箱在儲巢文件中偏移量的域,以及該巢箱的大小。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
通過使用巢箱,而不是巢室,來跟蹤注冊表的活動部分,Windows可以最小化其管理雜務(wù)。例如,在通常情況下系統(tǒng)分配和釋放巢箱的頻率,比分配和釋放巢室的頻率要低得多,這使得配置管理器可以更加有效地管理內(nèi)存。當(dāng)配置管理器將一個注冊表儲巢讀進(jìn)內(nèi)存的時候,它讀入整個儲巢,包括空的巢箱,但是它可以選擇在后面將它們丟棄掉。當(dāng)系統(tǒng)在儲巢中加入或者刪除巢室的時候,儲巢可能會包含空的巢箱,并且它們散布在活動的巢箱之間。這種情形類似于磁盤碎片,系統(tǒng)在磁盤上創(chuàng)建和刪除文件的時候,就會產(chǎn)生磁盤碎片。當(dāng)一個巢箱變成空的時候,配置管理器將任何相鄰的空巢箱也加入到此空巢箱中,從而形成一個盡可能大的連續(xù)空巢箱。配置管理器也將相鄰的已被刪除的巢室連接起來,以便形成更大的空閑巢室(配置管理器只有當(dāng)一個儲巢尾部的儲箱變成空閑的時候才會縮短該儲巢。你可以通過Windows的RegSaveKey和RegReplaceKey函數(shù),先備份注冊表,再恢復(fù)注冊表,從而達(dá)到壓縮注冊表的目的;Windows Backup工具就使用了這些函數(shù))。
儲巢的結(jié)構(gòu)是通過一些鏈接建立起來的,這些鏈接稱為巢室索引 (cellindex)。每個巢室索引是一個巢室在儲巢文件中的偏移,再減去基本塊的大小。因此,巢室索引就像是一個指針,從一個巢室指向另一個巢室,配置管理器將巢室索引解釋成相對于儲巢起始處的偏移。例如,正如你在表4.6中所看到的,用于描述一個鍵的鍵巢室,它所包含的一個域指定了其父鍵的巢室索引;子鍵的巢室索引還指定了另一個巢室,它描述了那些隸屬于該子鍵的子鍵。子鍵列表巢室包含了一列巢室索引,這些巢室索引指向其子鍵的鍵巢室。因此,假如你想要找到子鍵A的鍵巢室,并且A的父鍵是B,那么,你必須首先利用鍵B的巢室中的子鍵列表巢室索引,找到包含鍵B所有子鍵列表的那個巢室,然后利用該子鍵列表巢室中的巢室索引列表找到鍵B的每個子鍵巢室。對于每個子鍵巢室,檢查該子鍵的名稱(鍵巢室存儲了鍵的名稱)是否符合你想要找的那個子鍵,在這個例子中即子鍵A。
巢室、巢箱和塊之間的區(qū)別可能很容易讓人混淆,所以,我們現(xiàn)在來看一個簡單的注冊表儲巢的布局示例,以幫助澄清它們之間的區(qū)別。在圖4.3中,示例性的注冊表儲巢文件包含了一個基本塊和兩個巢箱。第一個巢箱是空的,第二個巢箱包含了幾個巢室。從邏輯上講,該儲巢只有兩個鍵:一個是根鍵Root;另一個是Root的子鍵,即Sub Key。Root有兩個值:Val1和Va2。通過一個子鍵列表巢室,可以定位到根鍵的子鍵;通過一個值列表巢室,可以定位到根鍵的值。第二個巢箱中的空閑空間是空的巢室。圖4.3并沒有顯示這兩個鍵的安全巢室,它們也應(yīng)該出現(xiàn)在一個儲巢中。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
為了優(yōu)化對于值和子鍵的搜索過程,配置管理器按照字母表順序來存儲子鍵列表巢室。然后,當(dāng)配置管理器要在一個子鍵列表中查找一個子鍵時,它可以執(zhí)行二分搜索。配置管理器在列表的中間檢查要查找的子鍵,如果按照字母表的順序,配置管理器正在查找的子鍵的名稱位于中間子鍵名稱的前面,則配置管理器知道該子鍵位于子鍵列表的前半部分:否則該子鍵位于子鍵列表的后半部分。這樣的切分過程一直進(jìn)行下去,直到配置管理器找到了該子鍵,或者沒有找到匹配的子鍵。然而,值列表巢室并非是排序的,所以,新的值總是被加到列表的尾部。

4.1.9.4 巢室映射表

如果儲巢從來不增長,那么,配置管理器就可以在一個儲巢的內(nèi)存版本中執(zhí)行所有的注冊表管理工作,就好像該儲巢是一個文件一樣。給定一個巢室索引,配置管理器只需簡單地將巢室索引 (即在儲巢文件中的偏移) 加到內(nèi)存中儲巢映像的基地址上,就可以計算出該巢室在內(nèi)存中的位置。

在系統(tǒng)引導(dǎo)的早期Winload對于SYSTEM儲巢正是這樣處理的: Winload將整個SYSTEM儲巢作為一個只讀儲巢讀入到內(nèi)存中,并且將巢室索引加上內(nèi)存中儲巢映像的基地址,從而可以定位到這些巢室。不幸的是,隨著儲巢中加入新的鍵和值,儲巢會隨之增長,這意味著系統(tǒng)必須申請換頁池的內(nèi)存來存儲新的巢箱,在這些巢箱中包含新加入的鍵和值。因此,在內(nèi)存中存儲注冊表數(shù)據(jù)的換頁池不必是連續(xù)的。

為了處理內(nèi)存中存放儲巢數(shù)據(jù)的非連續(xù)內(nèi)存地址,配置管理器采用了一種類似于Windows內(nèi)存管理器用來將虛擬內(nèi)存地址映射為物理內(nèi)存地址的策略。配置管理器采用一種兩層方案,如圖4.4所示,它接受一個巢室索引 (即儲巢文件中的一個偏移)作為輸入,返回該巢室索引所在的塊的內(nèi)存地址,以及該巢室所在的塊的內(nèi)存地址。前面提到過,一個巢箱可以包含一個或者多個塊,儲巢以巢箱為粒度進(jìn)行增長,所以,Windows總是用一塊連續(xù)區(qū)域的內(nèi)存來代表一個巢箱。因此,一個巢箱內(nèi)部的所有塊都出現(xiàn)在同一個緩存管理器視圖的內(nèi)部。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
為了實現(xiàn)這種映射,配置管理器將一個巢室索引從邏輯上分成多個域,就如同內(nèi)存管理器將一個虛擬地址分成多個域一樣。Windows將一個巢室索引的第一個域解釋成一個儲巢的巢室映射表目錄中的一個索引。巢室映射表目錄包含1024個項目,每個項目指向一張巢室映射表,每張巢室映射表包含512個表項。在巢室映射表中的表項是由巢室索引中的第二個域來指定的。該表項指定了此巢室的巢箱內(nèi)存地址和塊內(nèi)存地址。并不是所有的巢箱都必須映射到內(nèi)存中,如果查找一個巢室得到的結(jié)果地址為0,則配置管理器將該巢箱映射到內(nèi)存中,如果有必要的話,將它維護(hù)的LRU表中的另一個巢箱解除映射。
在轉(zhuǎn)譯過程的最后一步,配置管理器將巢室索引的最后一個域解釋成上述指定塊中的一個偏移量,以便精確地定位到內(nèi)存中的巢室。當(dāng)一個儲巢初始化的時候,配置管理器動態(tài)地創(chuàng)建這些映射表,為儲巢中的每一個塊指定一個表項:當(dāng)儲巢的大小有必要改變時,它也會從巢室目錄中刪除和增加映射表。

4.1.9.5 注冊表名字空間和操作

配置管理器定義了一個“鍵對象 (keyobject)”的對象類型,從而將注冊表的名字空間與內(nèi)核的總名字空間整合在一起。配置管理器在Windows名字空間的根上插入了一個名為“注冊表(Registry)”的鍵對象,用作指向注冊表的基本入口點(diǎn)。

Regedit 按照HKEY_LOCAL_MACHINESYSTEM\CurrentControlSet這樣的形式來顯示鍵的名稱,但是,Windows子系統(tǒng)將這樣的名稱轉(zhuǎn)譯成相應(yīng)的對象名字空間的形式(比如Registry\MachinelSystem\CurrentControlSet)當(dāng)Windows對象管理器解析此名稱時,它首先研到Registry名稱的鍵對象,然后將名稱中剩下的部分交給配置管理器。配置管理器接管了名稱解析過程,它查找自己內(nèi)部的儲巢樹,以便找到期望的鍵或值。在我們介紹典型的注冊表操作的控制流程以前,我們首先來討論鍵對象和鍵控制塊 (key controlblock)。無論何時,當(dāng)一個應(yīng)用程序打開或者創(chuàng)建一個注冊表鍵的時候,對象管理器會給應(yīng)用程序一個句柄,讓它通過此句柄來引用該鍵。對應(yīng)于一個鍵對象的句柄是配置管理器在對象管理器的幫助下分配的。通過對象管理器的對象支持,配置管理器得以充分利用對象管理器提供的安全性和引用計數(shù)功能。

對于每個打開的注冊表鍵,配置管理器也分配一個鍵控制塊。鍵控制塊保存了該鍵的名稱,包含了該控制塊所引用的鍵節(jié)點(diǎn)的巢室索引,還包含一個標(biāo)志。該標(biāo)志指明,當(dāng)該鍵最后一個句柄關(guān)閉的時候,配置管理器是否需要刪除該鍵控制塊所引用的鍵巢室。Windows把所有的鍵控制塊都放在一張散列表中,從而可以快速地按照名稱來搜索已有的鍵控制塊。鍵對象指向它所對應(yīng)的鍵控制塊,所以,如果兩個應(yīng)用程序打開同一個注冊表鍵的話,則每個應(yīng)用程序都會收到一個鍵對象,并且這兩個鍵對象指向一個公共的鍵控制塊。

當(dāng)一個應(yīng)用程序打開一個已有的注冊表鍵的時候,控制流程從應(yīng)用程序在一個注冊表API中指定了該鍵的名稱開始(該注冊表API調(diào)用了對象管理器的名稱解析例程)對象管理器碰到了在名字空間中屬于配置管理器的注冊表鍵對象以后,將路徑名稱交給配置管理器。配置管理器在鍵控制塊散列表中執(zhí)行一次查找。如果找到了相應(yīng)的鍵控制塊,則無須再進(jìn)一步工作:否則,這次查找的結(jié)果也讓配置管理器獲得了與所查找的鍵最相近的鍵控制塊,它繼續(xù)利用內(nèi)存中的儲巢數(shù)據(jù)結(jié)構(gòu)來搜索鍵和子鍵,以便找到指定的鍵。如果配置管理器找到了該鍵巢室,它就會搜索鍵控制塊樹,以確定該鍵是否是打開的(被同一個應(yīng)用程序或者另一個應(yīng)用程序打開)。此搜索過程已經(jīng)作了優(yōu)化,以便總是從最近的,并且已經(jīng)有一個打開的鍵控制塊的祖先開始找起。例如,如果一個應(yīng)用程序要打開\Registry\Machine\Key1\Subkey2,并且\Registry\Machine已經(jīng)打開了,那么,解析例程使用\Registry\Machine的鍵控制塊作為起點(diǎn)。如果該鍵是打開的,則配置管理器增加已有的鍵控制塊的引用計數(shù)。如果該鍵尚未被打開,則配置管理器分配一個新的鍵控制塊,并且將它插入到樹中。然后,配置管理器分配一個鍵對象,使該鍵對象指向此鍵控制塊,再將控制返回給對象管理器,對象管理器則返回一個句柄給應(yīng)用程序。
當(dāng)一個應(yīng)用程序創(chuàng)建一個新的注冊表鍵時,配置管理器首先找到此新鍵的父鍵的鍵巢室然后,配置管理器對于此新鍵將要存入的儲巢中的空閑巢室列表進(jìn)行搜索,以確定是否有足夠大的巢室來容納此新鍵巢室。如果沒有足夠大的空閑巢室,則配置管理器分配一個新的巢箱,并將它用于新鍵巢室,然后將巢箱尾部的任何空間也放到空閑巢室列表中。新鍵巢室中填充了各種有關(guān)的信息,包括該鍵的名稱,配置管理器將該鍵巢室加入到其父鍵的子鍵列表巢室的子鍵列表中。最后,系統(tǒng)也將父巢室的巢室索引保存在新子鍵的鍵巢室中。
配置管理器利用鍵控制塊的引用計數(shù)來決定何時應(yīng)該刪除鍵控制塊。如果有多個句柄引用了一個鍵控制塊中的鍵,則當(dāng)所有這些句柄都關(guān)閉時,鍵控制塊中的引用計數(shù)變?yōu)?,這表明該鍵控制塊不再被需要了。如果一個應(yīng)用程序調(diào)用一個API來刪除該鍵,并設(shè)置了刪除標(biāo)志那么,配置管理器可以從該鍵的儲巢中刪除對應(yīng)的鍵,因為它知道沒有其他的應(yīng)用程序?qū)⒃撴I保持為打開的狀態(tài)。

4.1.9.6 穩(wěn)定可靠的存儲

為了確保非易失性的注冊表儲巢(每個都是一個磁盤文件)總是處于一種可恢復(fù)的狀態(tài)配置管理器使用了日志儲巢 (log hive)。每個非易失性儲巢都有一個關(guān)聯(lián)的日志儲巢,它是一個隱藏文件,與對應(yīng)的儲巢有同樣的基本文件名,擴(kuò)展名為logN。為了確??偰芟蚯斑M(jìn)行,配置管理器使用了一種雙日志方案??赡軙袃蓚€日志文件:.og1和.log2。如果出于某種原因,.log1已經(jīng)被寫入了,但是在將臟數(shù)據(jù)寫到主日志文件的時候發(fā)生了失敗,那么,下一次發(fā)生刷新操作時,就會切換到.og2,累積起來的臟數(shù)據(jù)也會寫到.log2。如果那也失敗了,則累積的臟數(shù)據(jù).og1中的數(shù)據(jù),以及在此期間被弄臟的數(shù)據(jù))被保存在.og2中。因此,下一次還會再次使用.log1,直到成功地寫入到主日志文件中。如果期間不發(fā)生失敗,則只使用.log1。

例如,如果你檢查你的系統(tǒng)中的%SystemRoot%(System32\Config目錄(將“Show HiddenFiles And Folders”文件夾選項選中),你將會看到System.log1、Sam.log1以及其他的log1和log2文件。當(dāng)一個儲巢初始化的時候,配置管理器分配一個位陣列,其中每一個位代表了該儲巢中一個512字節(jié)大小的部分,或者稱為扇區(qū)(sector)。這個陣列稱為臟扇區(qū)陣列 (dirty sectorarray),因為陣列中的“on”位表示系統(tǒng)已經(jīng)修改了內(nèi)存里該儲巢中的對應(yīng)扇區(qū),因此系統(tǒng)必須將該扇區(qū)寫回到儲巢文件中(“off”位表示對應(yīng)的扇區(qū)是最新的,與內(nèi)存中儲巢的內(nèi)容一致)。

當(dāng)創(chuàng)建一個新的鍵或值,或者修改一個已有的鍵或值的時候,配置管理器在該儲巢的臟扇區(qū)陣列中對于所改變的扇區(qū)做上標(biāo)記。然后,配置管理器調(diào)度一個延遲的寫操作,或者稱為儲巢同步(hive sync)。執(zhí)行該儲巢延遲的寫操作的系統(tǒng)線程在儲巢同步請求之后5秒鐘被喚醒,它把所有儲巢的臟儲巢扇區(qū)從內(nèi)存中寫到磁盤上的儲巢文件中。因此,系統(tǒng)同時也會刷新“在發(fā)出儲巢同步請求的時間點(diǎn)和真正執(zhí)行儲巢同步的時間點(diǎn)”之間的這段時間內(nèi)的所有注冊表修改操作。當(dāng)一個儲巢同步發(fā)生時,下一個儲巢同步至少要等到5秒鐘以后。

注:
API函數(shù)RegFlushKey的名稱指明了,該函數(shù)只是將某個鍵的已修改數(shù)據(jù)刷新到磁盤上,但是它實際上也會觸發(fā)一次完全的注冊表刷新動作,這對系統(tǒng)會有顯著的性能影響。出于這一原因以及注冊表的機(jī)制自動可以保證已修改的數(shù)據(jù)在幾秒鐘之內(nèi)會進(jìn)入到穩(wěn)定可靠的存儲體中所以,應(yīng)用程序員應(yīng)該避免使用這一API。

如果延遲寫出器(lazy writer)只是簡單地把一個儲巢的所有臟扇區(qū)寫到儲巢文件中,并且在寫操作過程中系統(tǒng)崩潰了,那么,儲巢文件將處于一種不一致(破壞的)和不可恢復(fù)的狀態(tài)。為了避免這樣的情形發(fā)生,延遲寫出器首先將儲巢的臟扇區(qū)陣列和所有的臟扇區(qū)寫到儲巢的日志文件中,如果有必要的話可以增加日志文件的大小。然后,延遲寫出器更新該儲巢基本塊中的一個序列號,再將臟扇區(qū)寫到儲巢中。當(dāng)延遲寫出器結(jié)束時,它更新該基本塊中的另一個序列號。因此,如果在寫儲巢操作的過程中系統(tǒng)崩潰的話,則下一次重新引導(dǎo)時,配置管理器將會注意到,儲巢的基本塊中的兩個序列號不匹配。于是,配置管理器可以用儲巢的日志文件中的臟扇區(qū)來更新該儲巢,從而使儲巢向前滾過去。然后,該儲巢被更新,并且其狀態(tài)是一致的。

Windows的引導(dǎo)加載器(Boot Loader)也包含了一些與注冊表可靠性有關(guān)的代碼。例如它可以在內(nèi)核被加載到系統(tǒng)中以前解析Svstem.iog文件,并執(zhí)行一些修復(fù)工作來維持注冊表的一致性。而且,在某些特定的儲巢破壞情形下(比如,儲巢的基本塊、巢箱或者巢室中包含的數(shù)據(jù)未能通過一致性檢查),配置管理器可以重新對破壞的數(shù)據(jù)結(jié)構(gòu)進(jìn)行初始化,在此過程中可能會刪除一些子鍵,然后繼續(xù)正常的操作。如果它不得不求助于自治愈操作的話,則它會彈出一個系統(tǒng)錯誤對話框來通知用戶。

4.1.9.7 注冊表過濾

Windows內(nèi)核中的配置管理器實現(xiàn)了一個強(qiáng)大的注冊表過濾模型,使得像進(jìn)程監(jiān)視器(Process Monitor)這樣的工具可以方便地監(jiān)視注冊表的活動。當(dāng)一個驅(qū)動程序使用回調(diào)機(jī)制時,它可以向配置管理器注冊一個回調(diào)函數(shù)。配置管理器在執(zhí)行注冊表系統(tǒng)服務(wù)之前或者之后,調(diào)用該驅(qū)動程序的回調(diào)函數(shù),所以,該驅(qū)動程序可以完全地看到并且控制對注冊表的訪問?;卣{(diào)機(jī)制還有其他一些用途,比如反病毒軟件產(chǎn)品掃描注冊表數(shù)據(jù)以檢查病毒,或者防止未授權(quán)的進(jìn)程修改注冊表。
注冊表回調(diào)也跟高位值 (altitude)的概念有關(guān)系。高位值是一種控制方法,它針對不同廠商在注冊表過濾棧上注冊一個“高度”,所以,系統(tǒng)調(diào)用每個回調(diào)例程的順序是確定的、正確的。這可以避免這樣一種情形:反病毒軟件產(chǎn)品在加密軟件產(chǎn)品運(yùn)行其回調(diào)函數(shù)來解密注冊表鍵數(shù)據(jù)之前對這些鍵進(jìn)行掃描。按照Windows的注冊表回調(diào)模型,這兩種類型的軟件工具都被分配了一個基本的高位值,分別對應(yīng)于它們所做的過濾任務(wù)的類型 – 在這個例子中,分別是加密和掃描。其次,開發(fā)這些類型軟件工具的公司,必須向Microsoft登記,以便在它們自己的組內(nèi),它們不會與相似的或者競爭的產(chǎn)品發(fā)生沖突。
注冊表過濾模型也包含這樣的能力:完全接管注冊表操作的處理過程(從而繞過配置管理器,不讓它處理相應(yīng)的注冊表請求),或者將一個操作重定向為另一個操作(比如Wow64的注冊表重定向)。此外,修改一個注冊表操作的輸出參數(shù)或者返回值,這也是完全能做到的.最后,驅(qū)動程序也可以根據(jù)自己的目的,針對一個鍵或者一個操作,分配或標(biāo)記上該驅(qū)動程序特有的信息。一個驅(qū)動程序可以在一個創(chuàng)建操作或者打開操作過程中,創(chuàng)建并分配這樣的環(huán)境數(shù)據(jù):配置管理器將在該鍵的每次后續(xù)操作過程中,記住并返回此環(huán)境數(shù)據(jù)。

4.1.9.8 注冊表優(yōu)化

配置管理器作了一些非常顯著的性能優(yōu)化。首先,實際上每一個注冊表鍵都有一個安全描述符,它可以起到保護(hù)該鍵訪問的作用。然而,為儲巢中的每一個鍵都保存一個唯一的安全描述符拷貝將是十分低效的,因為同樣的安全設(shè)置往往應(yīng)用在注冊表的整棵子樹上。當(dāng)系統(tǒng)將新的安全性作用到一個鍵上時,配置管理器檢查該鍵所在的儲巢中的安全描述符池(其中包含了該儲巢中用到的獨(dú)一無二的安全描述符):它為該鍵共用任何已有的描述符,從而確保在一個儲巢中任何一個獨(dú)一無二的安全描述符至多只有一份拷貝。

配置管理器也對一個儲巢中鍵或者值的名稱的存儲方式做了優(yōu)化。盡管注冊表具備完全的Unicode能力,它使用Unicode編碼方式來表述所有的名稱,但是,如果一個名稱中只包含ASCI字符,那么,配置管理器在該儲巢中,按照ASCII形式來存儲該名稱。當(dāng)配置管理器讀入此名稱時(比如當(dāng)執(zhí)行名稱查詢時),它在內(nèi)存中將該名稱轉(zhuǎn)換成Unicode形式。按照ASCI形式來存儲名稱,可以顯著地降低一個儲巢的大小。

為了使內(nèi)存使用量盡可能地減到最小,鍵控制塊中并沒有存儲完整的鍵路徑名。相反,它們只是引用了一個鍵的名稱。例如,一個引用了\Registry\System\Control的鍵控制塊只是引用了名稱Control,而不是全路徑名。進(jìn)一步的內(nèi)存優(yōu)化是,配置管理器使用鍵名稱控制塊來存儲鍵的名稱,對于所有具有同樣名稱的鍵,它們的鍵控制塊共用同樣的鍵名稱控制塊。為了優(yōu)化性能,配置管理器將鍵控制塊名稱存儲在一個散列表中,以便于快速查詢。

為了能夠快速地訪問鍵控制塊,配置管理器將頻繁被訪問的鍵控制塊存儲在一個緩存表中,該表也被配置成一張散列表。當(dāng)配置管理器需要查找一個鍵控制塊時,它首先檢查此緩存表。最后,配置管理器還有另一個緩存:延遲的關(guān)閉表。它存儲的鍵控制塊是應(yīng)用程序關(guān)閉的,所以,一個應(yīng)用程序可以很快地重新打開一個剛剛被關(guān)閉的鍵。為了優(yōu)化查詢操作,這些緩存表是針對每個儲巢來存儲的。當(dāng)配置管理器將最近被關(guān)閉的鍵控制塊加入到延遲的關(guān)閉表中時,它也移除掉該表中那些最老的鍵控制塊。

4.2 服務(wù)(Service)

幾乎每一個操作系統(tǒng)都有一種在系統(tǒng)啟動時刻啟動進(jìn)程的機(jī)制,這些進(jìn)程提供了一些不依賴于任何交互式用戶的服務(wù)。在Windows中,這樣的進(jìn)程稱為服務(wù) (service)或者Windows服務(wù)(Windows Service),因為它們依賴于WindowsAPI與系統(tǒng)進(jìn)行交互。Windows服務(wù)類似于UNIX的守護(hù)進(jìn)程,它們通常實現(xiàn)了客戶/服務(wù)器應(yīng)用的服務(wù)器一方。

類比linux 使用service/systemctl enable命令查看的服務(wù)一樣的 開機(jī)自動啟動。

Windows服務(wù)的一個例子可能是Web服務(wù)器,因為不管是否有人登錄到機(jī)器上,它必須保持運(yùn)行:當(dāng)系統(tǒng)啟動的時候,它必須開始運(yùn)行,這樣,管理員就不用總是記著,也不用待在機(jī)器跟前,將服務(wù)啟動起來。

Windows服務(wù)是由三個組件構(gòu)成的:

  • 服務(wù)應(yīng)用
  • 服務(wù)控制程序(SCP,service controlprogram),
  • 以及服務(wù)控制管理器(SCM,service controlmanager)。

首先,我們講述服務(wù)應(yīng)用、服務(wù)賬號,以及SCM的操作。然后我們將解釋,那些自動啟動的服務(wù)是如何在系統(tǒng)引導(dǎo)的過程中被啟動起來的。我們還將介紹當(dāng)一個服務(wù)在啟動過程中失敗時SCM所采取的步驟,以及SCM停掉服務(wù)的方法。

4.2.1 服務(wù)應(yīng)用

像web服務(wù)器這樣的服務(wù)應(yīng)用至少包含一個作為Windows服務(wù)而運(yùn)行的可執(zhí)行程序。若用戶想要啟動、停止或者配置一個服務(wù),他可以使用SCP。雖然Windows內(nèi)置的SCP提供了一般性的啟動、停止、暫停和繼續(xù)功能,但是,有些服務(wù)應(yīng)用包含了它們自己的SCP,管理員通過這些SCP,可以指定一些特定于他們所管理的服務(wù)的特殊設(shè)置。

服務(wù)應(yīng)用也只是簡單的Windows可執(zhí)行程序(GUI風(fēng)格或者控制臺風(fēng)格)加上一些代碼來接收SCM的命令,以及將應(yīng)用的狀態(tài)反饋回SCM。因為大多數(shù)服務(wù)沒有用戶界面,所以它們都是按照控制臺程序來創(chuàng)建的。

當(dāng)你安裝一個包含有服務(wù)的應(yīng)用時,該應(yīng)用的安裝程序必須向系統(tǒng)注冊它的服務(wù)。為了注冊該服務(wù),安裝程序調(diào)用Windows的CreateService函數(shù),這是一個在Advapi32.dll(%SystemRoot%(System32\Advapi32.dll)中實現(xiàn)的、與服務(wù)有關(guān)的函數(shù)。Advapi32,即“高級API(AdvancedAPI)”DLL,實現(xiàn)了所有的客戶端SCMAPI。

當(dāng)一個安裝程序通過調(diào)用CreateService來注冊服務(wù)時,就會發(fā)送一個消息給該服務(wù)將要駐留的機(jī)器上的SCM。然后,SCM為該服務(wù)在HKLM\SYSTEM\CurrentControlSet\Services下創(chuàng)建一個注冊表鍵。Service鍵是SCM數(shù)據(jù)庫的非易失部分。針對每個服務(wù)的鍵定義了該服務(wù)所在的可執(zhí)行映像文件的路徑,以及一些參數(shù)和配置選項。

在創(chuàng)建了一個服務(wù)以后,一個安裝程序或者管理應(yīng)用程序可以通過StartService函數(shù)來啟動該服務(wù)。因為有些基于服務(wù)的應(yīng)用也必須在引導(dǎo)過程中進(jìn)行初始化才能工作,所以,像下面這樣的情形也就不足為奇了:安裝程序?qū)⒁粋€服務(wù)注冊成一個自動啟動的服務(wù),并且請用戶重新引導(dǎo)整個系統(tǒng),以便完成安裝過程,并且讓SCM在系統(tǒng)引導(dǎo)過程中啟動此服務(wù)。

當(dāng)一個程序調(diào)用CreateService時,它必須指定許多參數(shù),由這些參數(shù)來描述該服務(wù)的特征這樣的特征包括該服務(wù)的類型(它是運(yùn)行在自已獨(dú)立的進(jìn)程中,還是與其他的服務(wù)共享一個進(jìn)程)、該服務(wù)可執(zhí)行映像文件的位置、一個可選的顯示名、一個可選的賬戶名和口令(用于在特定賬戶的安全環(huán)境中啟動該服務(wù))、一個啟動類型(指定該服務(wù)是在系統(tǒng)引導(dǎo)時自動啟動,還是在SCP的指示下手工啟動)、一個錯誤代碼(指示了如果該服務(wù)在啟動時檢測到錯誤的話該如何反應(yīng)),以及一些可選的信息 (如果該服務(wù)自動啟動的話),這些信息指定了該服務(wù)相對于其他的服務(wù)該何時啟動。

SCM將每一個特征存儲為該服務(wù)的注冊表鍵下的一個值。圖4.5顯示了一個服務(wù)的注冊表鍵的例子。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

請注意,Type值包含了三個可適用于設(shè)備驅(qū)動程序的值:設(shè)備驅(qū)動程序、文件系統(tǒng)驅(qū)動程序,以及文件系統(tǒng)識別器。這些值是Windows設(shè)備驅(qū)動程序使用的,這些設(shè)備驅(qū)動程序也將它們的參數(shù)作為注冊表數(shù)據(jù)存儲在Services鍵中,SCM負(fù)責(zé)啟動那些Stat值為SERVICE_AUTO START或 SERVICE DEMAND_START的驅(qū)動程序所以SCM數(shù)據(jù)庫中包含了驅(qū)動程序的信息,這也就不足為奇了。Windows 服務(wù)使用其他的類型:SERVICE WIN320WNPROCESS和SERVICE WIN32 SHARE PROCESS這兩者是斥的。可容納多個服務(wù)的可執(zhí)行程序指定的是SERVICE_WIN32SHARE_PROCESS類型。

讓一個進(jìn)程運(yùn)行多個服務(wù)的好處是,可以節(jié)省下在單獨(dú)進(jìn)程中運(yùn)行每一個服務(wù)所需要的系統(tǒng)資源。一個潛在的缺點(diǎn)是,如果在同一個進(jìn)程中運(yùn)行的一組服務(wù)中的任何一個發(fā)生了錯誤,并因此而終止了該進(jìn)程,那么,該進(jìn)程的所有服務(wù)也隨之而終止。而且,另一個限制是,所有的服務(wù)必須運(yùn)行在同一個賬戶下(然而,如果一個服務(wù)利用了服務(wù)安全圍護(hù)機(jī)制 (servicehardening),那么它可以限制在某些情況下自己出現(xiàn)在共享進(jìn)程中不會遭受惡意攻擊)。

當(dāng)SCM啟動一個服務(wù)進(jìn)程時,該進(jìn)程必須立即調(diào)用SartServiceCtrlDispatcher函數(shù)StartServiceCtrlDispatcher接受一個入口點(diǎn)列表,每個入口點(diǎn)對應(yīng)于該進(jìn)程中的一個服務(wù)。每個入口點(diǎn)是由它所對應(yīng)的服務(wù)的名稱來標(biāo)識的StartServiceCtrlDispatcher創(chuàng)建了一個命名管道來跟SCM進(jìn)行通信,在建立了該通信管道以后,它等待SCM通過該管道發(fā)送過來的命令。每次SCM啟動一個屬于該進(jìn)程的服務(wù)時,它發(fā)送一個“服務(wù)啟動”命令。StartServiceCtrlDispatcher函數(shù)對于所接收到的每個啟動命令,創(chuàng)建一個線程(稱為服務(wù)線程),由該線程來調(diào)用所啟動服務(wù)的入口點(diǎn)函數(shù),并實現(xiàn)該服務(wù)的命令循環(huán)。StartServiceCtrlDispatcher一直在等待來自SCM的命令,只有當(dāng)該進(jìn)程的所有服務(wù)都停止時它才會將控制返回至該進(jìn)程的main函數(shù),以便服務(wù)進(jìn)程在退出以前做一些資源清理工作。

每個服務(wù)的入口點(diǎn)的第一個動作是調(diào)用RegisterServiceCtrlHandler函數(shù)。該函數(shù)接收一個指向某個函數(shù)的指針,并且將該指針保存起來,此函數(shù)指針稱為控制處理器(controlhandler)。該服務(wù)實現(xiàn)此函數(shù)以便處理各種來自SCM的命令。RegisterServiceCtrlHandler并不與SCM通信但它為StartServiceCtrlDispatcher函數(shù)將此函數(shù)存儲在本地進(jìn)程內(nèi)存中服務(wù)入口點(diǎn)維續(xù)初始化該服務(wù),包括分配內(nèi)存、創(chuàng)建通信端點(diǎn),以及從注冊表中讀入私有的配置數(shù)據(jù)。正如前面所解釋過的,大多數(shù)服務(wù)都遵從的一個慣例是,將服務(wù)的參數(shù)保存在其服務(wù)注冊表鍵下的名為Parameters的子鍵下面。

在入口點(diǎn)初始化該服務(wù)的過程中,它必須利用SetServiceStatus函數(shù)來定期地給SCM發(fā)送狀態(tài)消息,以指明該服務(wù)的啟動過程正在如何進(jìn)行。當(dāng)入口點(diǎn)完成了初始化以后,服務(wù)線程通常進(jìn)入一個循環(huán),等待來自客戶應(yīng)用的請求。例如,Web服務(wù)器會初始化一個TCP監(jiān)聽套接字,等待進(jìn)來的HTTP連接請求。

一個服務(wù)進(jìn)程的主線程,即在StartServiceCtrlDispatcher函數(shù)中執(zhí)行的線程,接收到針對該進(jìn)程中各個服務(wù)的SCM命令,再調(diào)用目標(biāo)服務(wù)的控制處理器函數(shù)(由RegisterServiceCtrlHandler 保存在該進(jìn)程中)。SCM命令包括停止、暫停、恢復(fù)、詢問和停機(jī),或者應(yīng)用程序定義的命令圖4.6顯示了一個服務(wù)進(jìn)程的內(nèi)部組織結(jié)構(gòu)。圖中的進(jìn)程宿納了一個服務(wù),它包含兩個線程:主線程和服務(wù)線程。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.2.2 服務(wù)賬戶

對于服務(wù)開發(fā)人員和管理員來說,一個服務(wù)的安全環(huán)境是一個非常重要的考慮,因為它規(guī)定了該服務(wù)進(jìn)程可以訪問哪些資源。除非一個服務(wù)安裝程序或者管理員指定了特殊的設(shè)置,否則,絕大多數(shù)服務(wù)運(yùn)行在本地系統(tǒng)賬戶 (localsystem account)的安全環(huán)境下(該賬戶有時候被顯示成SYSTEM,而其他時候則被顯示成LocaSystem)。另有兩個內(nèi)置的賬戶是:網(wǎng)絡(luò)服務(wù)(networkservice)和本地服務(wù) (localservice)賬戶。從安全角度而言,這兩個新的賬戶比本地系統(tǒng)賬戶具有的能力要少一些:那些不要求本地系統(tǒng)賬戶能力的Windows內(nèi)置服務(wù)都運(yùn)行在適當(dāng)?shù)姆?wù)賬戶之下。下面的小節(jié)介紹了這些賬戶的一些特殊之處。

本地系統(tǒng)賬戶

本地系統(tǒng)賬戶也正是核心的Windows用戶模式操作系統(tǒng)組件運(yùn)行時所在的賬戶,這樣的組件包括會話管理器(%SystemRoot%\System32\Smss.exe)、Windows子系統(tǒng)進(jìn)程(Csrss.exe)本地安全權(quán)威進(jìn)程 (%SystemRoot%\System32\Lsass.exe )和Logon進(jìn)程 (%SystemRoot%\System32\Winlogon.exe)。關(guān)于后兩個進(jìn)程的更多信息,請參見第6章。

從安全的角度來看,本地系統(tǒng)賬戶有非常強(qiáng)大的能力一一當(dāng)涉及本地系統(tǒng)上的安全能力時,它比任何本地的或者域的賬戶要強(qiáng)大得多。該賬戶有以下一些特征。

它是本地管理員組中的一個成員。表4.8顯示了本地系統(tǒng)賬戶所屬的那些組(有關(guān)在對象訪問檢查時如何利用組成員關(guān)系的更多信息,請參見第6章)。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

  • 它有權(quán)利賦予幾乎每一種特權(quán)(甚至包括那些通常并不賦予本地管理員賬戶的特權(quán)比如創(chuàng)建安全令牌的特權(quán))。表4.9列出了分配給本地系統(tǒng)賬戶的特權(quán) (第6章講述了每一種特權(quán)的用途)。
  • 絕大多數(shù)文件和注冊表鍵都賦予本地系統(tǒng)賬戶完全的訪問權(quán)限(即使它們沒有賦予全部的訪問權(quán)限,在本地系統(tǒng)賬戶下運(yùn)行的進(jìn)程也可以利用“接管所有權(quán) (take-ownership)”的特權(quán)來獲得訪問權(quán)限)。
    在本地系統(tǒng)賬戶下運(yùn)行的進(jìn)程按照默認(rèn)的用戶輪廓(HKU\DEFAULT)來運(yùn)行。因此它們不能訪問存儲在其他賬戶的用戶輪廓中的配置信息。
  • 當(dāng)一個系統(tǒng)是Windows域中的一個成員時,本地系統(tǒng)賬戶包含了一個服務(wù)進(jìn)程運(yùn)行時所在計算機(jī)的機(jī)器安全標(biāo)識符(SID)。因此,一個運(yùn)行在本地系統(tǒng)賬戶中的服務(wù)通過利用它的計算機(jī)賬戶,就可以自動地在同一個域林中的其他機(jī)器上得到身份認(rèn)證(一個林 (forest) 是指一組域)。
  • 除非該機(jī)器賬戶被特別賦予了對某些資源(比如網(wǎng)絡(luò)共享體、命名管道,等等)的訪問權(quán)限,否則,一個進(jìn)程只能訪問那些允許空會話(也即,不需要安全憑證的連接)訪問的網(wǎng)絡(luò)資源。你可以在特定的允許空會話的計算機(jī)上,通過HKLM)SYSTEM\CurrentControlSet(Services\lanmanserver parameters下的 NullSessionPipes和NullSessionShares注冊表值來指定這些共享體和管道

網(wǎng)絡(luò)服務(wù)賬戶

網(wǎng)絡(luò)服務(wù)賬戶的用途是,供那些“既希望利用計算機(jī)賬戶來向網(wǎng)絡(luò)上其他的機(jī)器認(rèn)證身份(就好像本地系統(tǒng)賬戶所具有的認(rèn)證能力那樣),但是又不需要管理員組的成員所屬權(quán),或者也不需要用到那些分配給本地系統(tǒng)賬戶的諸多特權(quán)”的服務(wù)來使用的。因為網(wǎng)絡(luò)服務(wù)賬戶并不屬于管理員組,所以,運(yùn)行在網(wǎng)絡(luò)服務(wù)賬戶中的服務(wù)在默認(rèn)情況下,比起運(yùn)行在本地系統(tǒng)賬戶下的服務(wù),只能訪問很少量的注冊表鍵,以及文件系統(tǒng)中的文件夾和文件。而且,只被賦予少量的特權(quán),也限制了一個被攻破的網(wǎng)絡(luò)服務(wù)進(jìn)程的能力范圍。例如,一個運(yùn)行在網(wǎng)絡(luò)服務(wù)賬戶下的進(jìn)程不可能加載一個設(shè)備驅(qū)動程序或者打開任意的進(jìn)程。

網(wǎng)絡(luò)服務(wù)賬戶和本地系統(tǒng)賬戶之間的另一個差別是,運(yùn)行在網(wǎng)絡(luò)服務(wù)賬戶中的進(jìn)程使用了網(wǎng)絡(luò)服務(wù)賬戶的輪廓。

網(wǎng)絡(luò)服務(wù)輪廓的注冊表部分被加載在HKU\S-1-5-20下面,
而構(gòu)成此注冊表部分的文件和目錄則位于%SystemRoot%\ServiceProfiles\NetworkService中。

一個運(yùn)行在網(wǎng)絡(luò)服務(wù)賬戶下的服務(wù)是DNS客戶,它負(fù)責(zé)解析DNS名稱,也負(fù)責(zé)找到域控制器。

本地服務(wù)賬戶

本地服務(wù)賬戶幾乎等同于網(wǎng)絡(luò)服務(wù)賬戶,兩者重要的區(qū)別在于,前者只能訪問那些允許匿名訪問的網(wǎng)絡(luò)資源。表4.9顯示了網(wǎng)絡(luò)服務(wù)賬戶與本地服務(wù)賬戶具有同樣的特權(quán);表4.8顯示了兩者屬于同樣的組,唯一的例外是網(wǎng)絡(luò)服務(wù)賬戶屬于NetworkService組,而不是LocalService組。運(yùn)行在本地服務(wù)賬戶中的進(jìn)程所使用的輪廓被加載到HKU\S-1-5-19中,以及存儲在%SystemRoot%\ServiceProfiles\LocalService中。

運(yùn)行在本地服務(wù)賬戶中的服務(wù)例子包括: 允許遠(yuǎn)程訪問本地系統(tǒng)注冊表的遠(yuǎn)程注冊表服務(wù)(Remote Registry Service),和執(zhí)行NetBIOS名稱解析任務(wù)的LmHosts服務(wù)。

在哪個賬戶中運(yùn)行服務(wù)

由于剛才提到的這些限制,有些服務(wù)需要使用一個用戶賬戶的安全憑證來運(yùn)行。你可以在創(chuàng)建一個服務(wù)的時候,對該服務(wù)進(jìn)行配置,讓它在選定的賬戶下運(yùn)行;你也可以利用Windows Services MMC加載件 (snap-in)來指定一個賬戶和口令,讓該服務(wù)運(yùn)行在此賬戶下在Services加載件中,在一個服務(wù)上右鍵單擊,再選擇Properties,并單擊Logn標(biāo)簽,然后選擇ThisAccount選項,如圖4.7所示。

以最小特權(quán)來運(yùn)行

服務(wù)通常會受制于“要么全部、要么什么也沒有 (all-or-nothing)”的模型,這意味著,對于服務(wù)進(jìn)程運(yùn)行時所在的賬戶,所有可使用的特權(quán)對于該進(jìn)程中運(yùn)行的服務(wù)都是可用的,而該服務(wù)實際上可能只要求這些特權(quán)中的一個子集。

為了更好地遵從最小特權(quán)原則,即windows只給服務(wù)分配它們真正需要的特權(quán),開發(fā)人員可以為他們的服務(wù)指定所需要的特權(quán),這樣SCM創(chuàng)建一個安全令牌,其中只包含這些特權(quán)。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

注: 為一個服務(wù)指定的特權(quán)必須是它運(yùn)行時所在服務(wù)賬戶可以使用的特權(quán)的子集。

服務(wù)開發(fā)人員使用changeServiceConfig2API來指明他們期望的特權(quán)列表。此API將這一信息保存在注冊表中該服務(wù)的Parameters鍵中。當(dāng)服務(wù)啟動的時候,SCM讀取該鍵,把這些特權(quán)加入到該服務(wù)運(yùn)行時所在進(jìn)程的令牌中。

如果有一個RequiredPrivileges值,并且該服務(wù)是一個獨(dú)立的服務(wù)(作為一個專有進(jìn)程來運(yùn)行),那么,SCM創(chuàng)建一個令牌,僅包含該服務(wù)所需要的特權(quán)。

若一個服務(wù)只是一個多服務(wù)的服務(wù)進(jìn)程中的一部分(絕大多數(shù)服務(wù)是Windows的一部分),并且指定了所要求的特權(quán),那么,SCM計算出這些特權(quán)的并集,將它們組合起來構(gòu)成服務(wù)宿主進(jìn)程的令牌。換句話講,只有那些沒有被服務(wù)組中任何一個服務(wù)指定的特權(quán)將會被刪除。如果此注冊表值不存在,那么,SCM假定該服務(wù)與最小特權(quán)原則不兼容,或者要求所有的特權(quán)才可以工作,除此之外別無選擇。在這種情況下,SCM創(chuàng)建一個完全的令牌,其中包含所有的特權(quán),這樣的模型也不會提供額外的安全性。要想剝離幾乎所有的特權(quán),服務(wù)只需指定Change Notify特權(quán)。

服務(wù)隔離

雖然限定一個服務(wù)能夠訪問的特權(quán)可以有助于削弱一個被攻破的服務(wù)進(jìn)程危害其他進(jìn)程的能力,但是,它并沒有做任何努力,將該服務(wù)與它運(yùn)行所在賬戶在通常條件下可以訪問的資源隔離開來。正如前面所提到的,本地系統(tǒng)賬戶對關(guān)鍵的系統(tǒng)文件、注冊表鍵和系統(tǒng)中其他一些受保護(hù)的對象具有完全的訪問權(quán),因為它們的訪問控制列表(ACL)把訪問權(quán)授予此賬戶了。
有時候,訪問這其中某些資源對于一個服務(wù)的操作確實是至關(guān)重要的,但其他的對象應(yīng)該要針對該服務(wù)保護(hù)起來。以前的做法是,通過在本地系統(tǒng)賬戶中運(yùn)行以獲得所需要的資源為了避免這樣做,一個服務(wù)將運(yùn)行在一個標(biāo)準(zhǔn)用戶的賬戶之下,同時把系統(tǒng)對象加入到ACL中這大大增加了惡意代碼攻擊系統(tǒng)的風(fēng)險。另一種方案是,創(chuàng)建專門的服務(wù)賬戶,并且為每個賬戶(與某個服務(wù)關(guān)聯(lián)) 設(shè)置專門的ACL,但這種做法很容易形成一個管理瓶頸。

Windows現(xiàn)在將這兩種做法結(jié)合到一個更加可管理的方案之中:它允許服務(wù)運(yùn)行在一個非特權(quán)的賬戶中,但是仍然可以訪問特定的特權(quán)資源,又不降低這些對象的安全性。以一種類似于Windows Vista之前的第二種方案,一個對象的ACL現(xiàn)在可以直接為一個服務(wù)設(shè)置許可而不要求專門的賬戶。相反地,SCM生成一個服務(wù)SID來代表一個服務(wù),此SID可以用于為像注冊表鍵和文件這樣的資源設(shè)置許可。服務(wù)SID是在宿納服務(wù)的進(jìn)程令牌的組SID中實現(xiàn)的。它們是在系統(tǒng)啟動過程中,為每個通過ChangeServiceConfig2API來請求服務(wù)ID的服務(wù)而生成的。在服務(wù)宿主進(jìn)程(包含多個服務(wù)的進(jìn)程) 的情形下,該進(jìn)程的令牌將包含所有服務(wù)的服務(wù)SID,這些服務(wù)都是與該進(jìn)程關(guān)聯(lián)的服務(wù)組的一部分,也包含那些尚未啟動的服務(wù),因為在一個令牌被創(chuàng)建以后就無法再增加新的SID。

作為對象訪問的細(xì)粒度控制方法,為每個服務(wù)有一個SID,其有用性超出了僅僅有能力為系統(tǒng)上的各個對象增加ACL項和許可設(shè)置。我們最初討論的情形是,系統(tǒng)上可被某個給定賬戶訪問的特定對象,必須要保護(hù)起來,以免被同樣賬戶中運(yùn)行的服務(wù)訪問。隨著我們講述到現(xiàn)在這個程度,通過服務(wù)SID,只需把與服務(wù)SID相關(guān)聯(lián)的Deny項放在每個需要被保護(hù)的對象上,就可以阻止這一問題。這顯然不是一個可容易管理的方法。

為了避免使用Deny訪問控制項(ACE)作為防止服務(wù)訪問那些該服務(wù)運(yùn)行所在賬戶具有訪問權(quán)的資源另外還有兩種類型的服務(wù)SID:受限制的服務(wù)SID(SERVICE SID TYPE_RESTRICTED)和非受限的服務(wù)SID (SERVICE SID TYPE UNRESTRICTED),后者是默認(rèn)的,我們現(xiàn)在來看一看它的使用情形。

非受限的服務(wù)SID是作為默認(rèn)啟用的組所有者SID而創(chuàng)建的,進(jìn)程令牌也被賦予一個新的ACE:為服務(wù)登錄SID賦予完全的權(quán)限許可,這就使得該服務(wù)可以繼續(xù)跟SCM進(jìn)行通信。(一個主要的用途是,在服務(wù)啟動或停止過程中,允許或禁止該進(jìn)程內(nèi)部的服務(wù)SID。)

另一方面,受限制的服務(wù)SID,將服務(wù)宿主進(jìn)程的令牌轉(zhuǎn)變成一個寫限制的令牌(關(guān)于令牌的更多信息,參見第6章),這意味著,只有給服務(wù)SID賦予顯式寫訪問權(quán)的對象才對該服務(wù)是可寫的,不管該服務(wù)運(yùn)行在哪個賬戶下。由于這個原因,運(yùn)行在該進(jìn)程中的所有服務(wù)(屬于同一個服務(wù)組)必須具有受限制的SID類型;否則,具有受限制SID類型的服務(wù)將不能啟動。出于兼容性的原因,一旦此令牌變成寫限制的,就會加入三個額外的SID:

  • 全局SID加入進(jìn)來,以便允許對通常情況下任何人無論如何(最重要的是,加載路徑中的特定DLL)都可以訪問的對象進(jìn)行寫訪問。
  • 服務(wù)登錄SID加入進(jìn)來,以便允許該服務(wù)可以與SCM進(jìn)行通信。
  • 寫限制的SID加入進(jìn)來,以便允許對象可以顯式地允許任何寫限制的服務(wù)對它們進(jìn)行寫訪問。例如,ETW (Event Tracing for Windows)在它的對象上使用此SID,以便允許任何寫限制的服務(wù)能夠生成事件。

圖4.8顯示了一個服務(wù)宿主進(jìn)程的例子,該進(jìn)程包含的服務(wù)已經(jīng)被標(biāo)記為具有受限制的服務(wù)SID。例如,BFE (Base FilteringEngine)負(fù)責(zé)應(yīng)用Windows的防火墻過濾規(guī)則,它是該服務(wù)的一部分,因為這些規(guī)則被存放在注冊表鍵中,而這些注冊表鍵是必須要保護(hù)的,以避免在服務(wù)被攻破的情況下它們被惡意寫訪問。(例如,這可能會導(dǎo)致一個服務(wù)攻擊手法,禁止向外流量的防火墻規(guī)則,從而與攻擊者建立起雙向通信。)

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
對于一個服務(wù)本來可以寫訪問的那些對象 (通過繼承進(jìn)程賬戶所具有的訪問許可來獲得寫訪問權(quán)限),在阻止了對它們的寫訪問以后,受限制的服務(wù)SID解決了最初我們展示過的那個問題的另一個方面,因為用戶無須做任何事情就可以防止一個服務(wù)在特權(quán)賬戶下運(yùn)行,避免對關(guān)鍵的系統(tǒng)文件、注冊表鍵或其他對象的寫訪問,從而限制了這些服務(wù)可能被攻破之后暴露出來的進(jìn)一步攻擊。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
Windows也允許在防火墻規(guī)則中引用服務(wù)SID,將它們鏈接到表4.10所述的三種行為之一。

交互式服務(wù)和會話0隔離

對于在Windows中常見的,在本地系統(tǒng)賬戶、本地服務(wù)賬戶和網(wǎng)絡(luò)服務(wù)賬戶下運(yùn)行的服務(wù)一個限制是,它們不能(如果不在MessageBox函數(shù)中使用一個特殊標(biāo)志的話,稍后會討論這一標(biāo)志)在交互式用戶的桌面上顯示對話框或者窗口。這一限制并非是由于它們運(yùn)行在這些賬戶下所導(dǎo)致的直接結(jié)果,而是由于Windows子系統(tǒng)將服務(wù)進(jìn)程分配給窗口站的做法所導(dǎo)致的結(jié)果。利用會話的概念,在一個稱為會話零隔離 (Session Zero lsolation)的模型中,這一限制被進(jìn)一步得到了增強(qiáng):此模型的一個結(jié)果是,這些服務(wù)不能直接與用戶的桌面進(jìn)行交互。Windows子系統(tǒng)將每個Windows進(jìn)程與一個窗口站(windowstation)關(guān)聯(lián)起來。窗口站包含了桌面,桌面包含了窗口。在一個控制臺上只有一個窗口站是可見的,它可以接收用戶的鼠標(biāo)和鍵盤的輸入。在終端服務(wù)環(huán)境中,每個會話有一個窗口站是可見的,但是所有的服務(wù)都作為控制臺會話的一部分來運(yùn)行。Windows將可見的窗口站命名為WinSta0,所有的交互式進(jìn)程都訪問WinSta0。

除非另行指定,否則,Windows子系統(tǒng)都將運(yùn)行在本地系統(tǒng)賬戶下的服務(wù)跟一個名為Service-0x0-3e7S的非可見窗口站關(guān)聯(lián)起來,所有的非交互式服務(wù)都共享此窗口站。此名稱中的數(shù)字3e7,代表了LSASS(本地安全權(quán)威進(jìn)程)分配給登錄會話的登錄會話標(biāo)識符:對于運(yùn)行在本地系統(tǒng)賬戶下的非交互式服務(wù),SCM都使用此登錄會話。
凡是配置了要運(yùn)行在某個用戶賬戶(也就是說,不是本地系統(tǒng)賬戶)下的服務(wù),都運(yùn)行在一個不同的非可見窗口站中,此窗口站是用LSASS分配給該服務(wù)的登錄會話的登錄標(biāo)識符來命名的。圖4.9顯示了Sysinternals Winobj工具的一個輸出示例,它顯示的是Windows專門放置窗口站對象的對象管理器目錄。從其中可以看到交互式窗口站(WinSta0)非交互式系統(tǒng)服務(wù)窗口站 (Service-0x0-3e7S)。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
不管一個服務(wù)是運(yùn)行在用戶賬戶下,或是運(yùn)行在本地系統(tǒng)賬戶下,或是本地服務(wù)賬戶下,還是運(yùn)行在網(wǎng)絡(luò)服務(wù)賬戶下,只要它沒有運(yùn)行在可見的窗口站中,就不能接收來自用戶的輸入,或者在控制臺上顯示窗口。實際上,如果一個服務(wù)要在該窗口站上彈出一個普通的對話框,那么,該服務(wù)就好像掛起了一樣,因為沒有用戶能看到該對話框,當(dāng)然,用戶也就無法提供鍵盤或者鼠標(biāo)的輸入來解除對話框,從而無法讓服務(wù)繼續(xù)執(zhí)行下去。

注: 在過去,即使一個服務(wù)被標(biāo)記為非交互式的,它也有可能在MessageBox API中使用特殊的標(biāo)志MBSERVICENOTIFICATION或MB DEFAULT DESKTOPONLY,從而在交互式窗口站上顯示消息?,F(xiàn)在,由于會話隔離的原因,任何使用此標(biāo)志的服務(wù)將會立即接收到IDOK返回值,但消息框卻永遠(yuǎn)不會顯示出來。

在很少的情況下,一個服務(wù)可以有充分的理由通過對話框或窗口來與用戶進(jìn)行交互。為了將一個服務(wù)配置成有權(quán)利與用戶進(jìn)行交互,SERVICE INTERACTIVEPROCESS修飾符必須出現(xiàn)在該服務(wù)的注冊表鍵的Tvpe參數(shù)中(注意,凡是被配置成在一個用戶賬戶下運(yùn)行的服務(wù),不能被標(biāo)記為交互式的)。當(dāng)SCM啟動一個被標(biāo)記為交互式的服務(wù)時,它在本地系統(tǒng)賬戶的安全環(huán)境中激發(fā)起該服務(wù)的進(jìn)程,但是將該服務(wù)與WinSta0連接起來,而不是與非交互式的服務(wù)窗口站連接在一起。

用戶進(jìn)程也將與服務(wù)運(yùn)行在同一個會話中的,服務(wù)進(jìn)程與WinStao的連接使得該服務(wù)可以在控制臺上顯示對話框和窗口,也允許這些窗口可以響應(yīng)用戶的輸入,因為它們與交互式服務(wù)共享窗口站。然而,只有屬于系統(tǒng)的進(jìn)程和Windows服務(wù)才在會話0中運(yùn)行,所有其他的登錄會話,包括控制臺用戶的登錄會話,都運(yùn)行在其他不同的會話中。因此,會話0中的進(jìn)程所顯示的窗口對用戶是不可見的。

這一額外的邊界有助于抵擋住粉碎攻擊(shatter attack),在這種攻擊中,一個低特權(quán)的應(yīng)用程序向同一個窗口站上可見的窗口發(fā)送窗口消息,以便挖掘出擁有該窗口的高特權(quán)進(jìn)程中的軟件錯誤,從而使得在高特權(quán)進(jìn)程中執(zhí)行代碼。
為了與那些依賴于用戶輸入的服務(wù)之間保持兼容性,Windows包含一個服務(wù),用于當(dāng)一個服務(wù)顯示一個窗口時可以通知用戶。交互式服務(wù)檢測(UIODetect)服務(wù)尋找會話0的WinStao窗口站的主桌面上的可見窗口,并且在控制臺用戶的桌面上顯示一個通知對話框,使用戶可以切換到會話0,并且查看該服務(wù)的UI。(這類似于連接到一個本地終端服務(wù)會話,或者切換不同的用戶。
注交互式服務(wù)檢測機(jī)制純粹是為了應(yīng)用兼容性而設(shè)計。強(qiáng)烈建議開發(fā)人員不要使用交互式服務(wù)而使用一個二級的、非特權(quán)的輔助應(yīng)用與用戶進(jìn)行通信和交互。當(dāng)接收到UI輸入以后,在這一輔助應(yīng)用與配置目的的服務(wù)之間可以使用本地RPC或COM。
圖4.10中顯示的對話框例子包含了進(jìn)程名稱、當(dāng)UI消息顯示時的時間,以及要被顯示的窗口的標(biāo)題。一旦用戶連接到會話0,一個類似的對話框也會出現(xiàn),以提供一個回到用戶會話的入口。在圖中,顯示窗口的服務(wù)是Microsoft Paint,它是由Sysinternals的PsExec工具顯式地啟動起來的,在PsExec工具的參數(shù)選項中指定了在會話0中運(yùn)行畫圖程序。你可以自己利用下面的命令來試一試:

psexec -s -i 0 -d mspaint.exe

此命令告訴PsExec,在會話0中 (0)以系統(tǒng)進(jìn)程的形式(-s來運(yùn)行畫圖程序,并且立即返回,而不是等待該進(jìn)程結(jié)束(-d)。

如果單擊“ViewThe Message”,可以切換到會話0的控制臺(在控制臺上通過一個類似的窗口再切換回來)。

4.3.3 服務(wù)控制管理器

SCM的可執(zhí)行文件是%SvstemRoot%\System32\Servicesexe,如同大多數(shù)服務(wù)進(jìn)程一樣,它也是作為一個Windows控制臺程序來運(yùn)行的。Wininit進(jìn)程在系統(tǒng)引導(dǎo)的早期將SCM啟動起來(有關(guān)引導(dǎo)過程的細(xì)節(jié),請參見本書下冊第13章)。SCM的啟動函數(shù),即SvcCtrlMain,會有條不紊地將那些被配置成自動啟動的服務(wù)激發(fā)起來。
SvcCtrlMain首先創(chuàng)建一個名為SvcctrlStartEvent A3752DX的同步事件,該事件被初始化成無信號狀態(tài)。只有當(dāng)SCM完成了必要的步驟來準(zhǔn)備接收SCP的命令以后,它才將該事件設(shè)置成有信號狀態(tài)。SCP用來與SCM建立對話的函數(shù)是OpenSCManager。OpenSCManager等待SvcctrlStartEvent_A3752DX事件變成有信號狀態(tài),因而,SCP在SCM完成初始化以前,不必總是試圖與SCM聯(lián)絡(luò)。

接下來,SvcCtrlMain開始做它自己的事情,并調(diào)用ScGenerateServiceDB,該函數(shù)負(fù)責(zé)建立起SCM的內(nèi)部服務(wù)數(shù)據(jù)庫。ScGenerateServiceDB 讀取并存儲 HKLMSYSTEM)CurrentControlSet\ControlServiceGroupOrder\List的內(nèi)容,這是一個REGMULTI SZ值,它列出了定義好的服務(wù)組的名稱和順序。如果一個服務(wù)或者設(shè)備驅(qū)動程序需要控制其相對于其他組中的服務(wù)的啟動順序,那么,該服務(wù)的注冊表鍵包含一個可選的Group值。例如,Windows網(wǎng)絡(luò)棧是由底向上建立起來的,所以,涉及網(wǎng)絡(luò)的Windows服務(wù)必須指定Group值,以便在啟動序列中將它們放在網(wǎng)絡(luò)設(shè)備驅(qū)動程序的后面。SCM內(nèi)部創(chuàng)建了一個組列表,此列表保留了SCM從注冊表中讀入的組順序。這些組包括(但不限于)NDIS、TDI、Primary Disk、Keyboard Port和Keyboard Class。加載件和第三方應(yīng)用程序甚至可以定義它們自己的組,并且將這些組加入到列表中。例如,Microsoft Transaction Server添加了一個名為MS Transactions的組。

然后,ScGenerateServiceDB掃描HKLM\SYSTEM\CurrentControlSet\Services中的內(nèi)容,并且在服務(wù)數(shù)據(jù)庫中為它所碰到的每一個鍵創(chuàng)建一個條目。數(shù)據(jù)庫條目包含了所有為一個服務(wù)而定義的、與服務(wù)有關(guān)的參數(shù),以及一些記錄了該服務(wù)狀態(tài)的域。SCM既為設(shè)備驅(qū)動程序添加條目,也為服務(wù)添加條目,因為SCM將所有標(biāo)記為自動-啟動的服務(wù)和驅(qū)動程序都啟動起來,并且對于那些標(biāo)記為引導(dǎo)-啟動和系統(tǒng)-啟動的驅(qū)動程序,檢測其啟動失敗的情況。它還提供了一種方法,讓應(yīng)用程序可以查詢驅(qū)動程序的狀態(tài)。在任何用戶模式進(jìn)程執(zhí)行起來以前,I/O管理器將那些標(biāo)記為引導(dǎo)-啟動和系統(tǒng)-啟動的驅(qū)動程序加載到系統(tǒng)中,因此,凡是這些啟動類型的驅(qū)動程序,都在SCM啟動之前被加載到系統(tǒng)中。

ScGenerateServiceDB讀取一個服務(wù)的Group值,以確定它是否是一個組的成員,并且將這個值與先前創(chuàng)建的組列表中該組的條目關(guān)聯(lián)起來。此函數(shù)也通過查詢該服務(wù)的DependOnGroup和DependOnService注冊表值,讀取到該服務(wù)的組相依性和服務(wù)相依性,并將它們記錄到數(shù)據(jù)庫中。圖4.11顯示了SCM如何組織服務(wù)條目和組順序列表。注意,服務(wù)列表是按字母順序來排列的。此列表之所以按字母序來排列,是因為,SCM根據(jù)Services注冊表鍵來創(chuàng)建此列表,而windows按字母順序來存儲注冊表鍵。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
在服務(wù)啟動過程中,SCM調(diào)用LSASS(比如,在一個非本地系統(tǒng)賬戶中登錄一個服務(wù)),所以SCM等待LSASS發(fā)出LSA RPC SERVER ACTIVE同步事件,當(dāng)LSASS完成了初始化時它就會發(fā)出此信號。Wininit也啟動LSASS進(jìn)程,所以,LSASS的初始化與SCM的初始化是同時進(jìn)行的,LSASS和SCM誰先完成初始化的順序是不確定的。

然后,SvcCtrlMain 調(diào)用ScGetBootAndSystemDriverState來掃描服務(wù)數(shù)據(jù)庫,以尋找那些自動-啟動的和系統(tǒng)-啟動的設(shè)備驅(qū)動程序條目。

ScGetBootAndSystemDriverState確定一個驅(qū)動程序是否已成功啟動,它的做法是,在名為Driver的對象管理器名字空間目錄中查找該驅(qū)動程序的名稱。當(dāng)一個設(shè)備驅(qū)動程序被成功地加載時,I/O管理器將該驅(qū)動程序的對象插入到名字空間中該目錄下面,所以,如果它的名稱沒有出現(xiàn)的話,則它還沒有被加載進(jìn)來。在圖4.12中,Winobj顯示了Driver目錄的內(nèi)容。SvcCtrMain將所有尚未啟動的、包含在當(dāng)前輪廓中的驅(qū)動程序的名稱記錄在一個名為ScFailedDrivers的列表中。

在啟動那些“自動-啟動”的服務(wù)以前,SCM還要執(zhí)行其他的一些步驟。它創(chuàng)建一個名為PipelNtsvcs的遠(yuǎn)過程調(diào)用(RPC)命名管道,然后,RPC激發(fā)一個線程,在該管道上監(jiān)聽來自SCP的進(jìn)入消息。然后,SCM將它的初始化完成事件SvcctrlStartEvent A3752DX設(shè)置成有信號狀態(tài)。注冊一個控制臺應(yīng)用程序停機(jī)事件處理器,以及通過RegisterServiceProcess向Windows子系統(tǒng)進(jìn)程進(jìn)行注冊,這些都為SCM做好了系統(tǒng)停機(jī)的準(zhǔn)備。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

網(wǎng)絡(luò)驅(qū)動器字母
SCM除了作為服務(wù)接口的角色以外,它還有另一項完全不相關(guān)的責(zé)任:無論何時當(dāng)系統(tǒng)創(chuàng)建或者刪除一個網(wǎng)絡(luò)驅(qū)動器字母連接時,它都要通知系統(tǒng)中的GUI應(yīng)用程序。SCM等待MPR(Multiple Provider Router)用信號通知一個命名的事件 BaseNamedObjects ScNetDrvMsg,當(dāng)應(yīng)用程序?qū)⒁粋€驅(qū)動器字母分配給一個遠(yuǎn)程網(wǎng)絡(luò)共享文件夾,或者刪除了一個遠(yuǎn)程共享文件夾的驅(qū)動器字母分配時,MPR就會用信號通知此事件(有關(guān)MPR的更多信息,請參見第7章“網(wǎng)絡(luò)”)。
當(dāng)MPR用信號通知該事件時,SCM調(diào)用Windows函數(shù)GetDriveType來查詢已連接的網(wǎng)絡(luò)驅(qū)動器字母的列表。如果在事件信號之間該列表改變了的話,則SCM發(fā)送一個類型為WMDEVICECHANGE的Windows廣播消息。SCM使用DBT DEVICEREMOVECOMPLETE或DBT DEVICEARRIVAL作為該消息的子類型。此消息主要是給WindowsExplorer使用的,所以,它可以更新任何已打開的Computer窗口,顯示出網(wǎng)絡(luò)驅(qū)動器字母的存在與否。

4.2.4 服務(wù)啟動

SvcCtrlMain調(diào)用SCM函數(shù)ScAutoStartServices來啟動所有已被指定為“自動-啟動”(在Start值中)的服務(wù)(除了那些延遲的自動-啟動服務(wù))。ScAutoStartServices也會將“自動-啟動”的設(shè)備驅(qū)動程序啟動起來。為了避免混淆,你應(yīng)該將術(shù)語“服務(wù)”看成是“服務(wù)和驅(qū)動程序”除非另行指定。ScAutoStartServices中按照正確順序來啟動服務(wù)的算法分階段進(jìn)行處理,每個階段對應(yīng)于一個組,這些階段按照HKLM\SYSTEMCurrentControlSetlControNServiceGroupOrdelList注冊表值中存儲的組順序所定義的序列進(jìn)行處理。如圖4.13所示,List值包含了組的名稱,其順序正是SCM啟動這些組的順序。因此,將一個服務(wù)分配給一個組,這對于屬于其他組的其他服務(wù),在啟動順序上沒有任何影響

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
當(dāng)一個階段開始時,ScAutoStartServices將所有屬于此階段的組的那些服務(wù)條目標(biāo)記出來以便啟動它們。然后,ScAutoStartServices循環(huán)檢查這些標(biāo)記出來的服務(wù),看它是否能將每-個服務(wù)啟動起來。

作為檢查過程的一部分,它要看這個服務(wù)是否被標(biāo)記為延遲的自動-啟動若是的話,SCM將在后面的步驟中啟動該服務(wù)。(延遲的自動-啟動服務(wù)必須不屬于任何一個組).作為檢查過程的另一部分,它根據(jù)這個服務(wù)的注冊表鍵中的DependOnGroup值是否存在,來確定這個服務(wù)是否依賴于其他的組。如果存在這種相依性,則它所依賴的那個組必須已經(jīng)初始化,該組中至少有一個服務(wù)已經(jīng)成功啟動起來。如果在組啟動序列中,該服務(wù)所依賴的那個組,比該服務(wù)所屬的組還要遲,那么,SCM記錄下該服務(wù)有一個“循環(huán)相依性”錯誤。如果ScAutoStartServices正在檢查的是一個Windows服務(wù),或者一個“自動啟動”的設(shè)備驅(qū)動程序那么,它接下來檢查該服務(wù)是否依賴于其他一個或者多個服務(wù),如果是的話,它查看這些服務(wù)是否已經(jīng)啟動了。服務(wù)相依性是由一個服務(wù)的注冊表鍵中的DependOnService值來指定的如果一個服務(wù)依賴于其他一些服務(wù),并且這些服務(wù)所屬的組位于ServiceGrouporderl List的后面,那么,SCM也會產(chǎn)生一個“循環(huán)相依性”錯誤,并且不會啟動該服務(wù)。如果該服務(wù)依賴于同一個組中的任何其他尚未啟動的服務(wù),則該服務(wù)將被跳過去。

當(dāng)一個服務(wù)的相依性已經(jīng)被驗證通過以后,ScAutoStartServices在啟動該服務(wù)之前還要做最后的檢查,看該服務(wù)是否是當(dāng)前引導(dǎo)配置的一部分。當(dāng)系統(tǒng)以安全模式來引導(dǎo)時,SCM要確保:在適當(dāng)?shù)陌踩龑?dǎo)注冊表鍵中,該服務(wù)或者是按名稱來標(biāo)識的,或者是按組來標(biāo)識的。注冊表中有兩個安全引導(dǎo)鍵:Minimal和Network,它們位于HKLM\SYSTEMCurrentControlSetControlSafeBoot的下面;SCM到底檢查哪一個,取決于用戶引導(dǎo)的是哪一種安全模式。如果用戶在專門的引導(dǎo)菜單(在引導(dǎo)過程中,按下F8就可以看到該菜單)中選擇的是Safe Mode或者Safe Mode With Command Prompt,則SCM使用的是Minimal鍵;如果用戶選擇的是Safe ModeWith Networking,則SCM使用的是Network鍵。若SafeBoot鍵下存在一個名為Option的字符串值,則不僅表明了當(dāng)前系統(tǒng)是按安全模式引導(dǎo)起來的,而且也指出了用戶選擇的安全模式類型。有關(guān)安全引導(dǎo)的更多信息,請參見本書下冊第13章中的“安全模式”一節(jié)。

一旦SCM決定要啟動一個服務(wù),它就調(diào)用ScStartService,該函數(shù)對于服務(wù)和設(shè)備驅(qū)動程序分別采取不同的步驟。當(dāng)ScStartService啟動一個Windows服務(wù)時,它首先讀取該服務(wù)的注冊表鍵中的ImagePath值,以確定該服務(wù)進(jìn)程的映像文件名。然后,它檢查該服務(wù)的Type值,如果此值是SERVICE WINDOWS SHARE PROCESS (0x20),那么,SCM保證: 該服務(wù)運(yùn)行所在的進(jìn)程如果已經(jīng)啟動了的話,其登錄的賬戶一定與該服務(wù)的指定啟動賬戶相同。(這也確保了,該服務(wù)沒有被配置成錯誤的賬戶,比如LocalService賬戶,但映像路徑卻指向一個正在運(yùn)行的5vchost,比如netsvcs,它以LocalSystem來運(yùn)行。)一個服務(wù)的ObjectName注冊表值存儲了該服務(wù)應(yīng)該在哪個賬戶下運(yùn)行。如果一個服務(wù)沒有ObjectName值,或者它的ObjectName值為LocalSystem,則該服務(wù)運(yùn)行在本地系統(tǒng)賬戶中。

SCM在一個稱為映像數(shù)據(jù)庫(image database)的內(nèi)部數(shù)據(jù)庫中,檢查是否有針對該服務(wù)的ImagePath值的條目,以便驗證該服務(wù)的進(jìn)程尚未在其他的賬戶下被啟動起來。如果在映像數(shù)據(jù)庫中沒有找到此ImagePath值的條目,則SCM創(chuàng)建一個這樣的條目。當(dāng)SCM創(chuàng)建一個新的條目時,它還將該服務(wù)的登錄賬戶名,以及該服務(wù)的ImagePath值中的數(shù)據(jù)也存儲起來。SCM要求Windows服務(wù)有一個magePath值。如果一個服務(wù)沒有ImagePath值,則SCM報告一個錯誤,表示它不能找到該服務(wù)的路徑,從而無法啟動該服務(wù)。如果SCM在映像數(shù)據(jù)庫中找到了一個與該服務(wù)的ImagePath數(shù)據(jù)相匹配的條目,那么,它要保證:它當(dāng)前正在啟動的服務(wù)的用戶賬戶信息與數(shù)據(jù)庫條目中存儲的信息是相同的一一一個進(jìn)程只能以一個賬戶的身份來登錄,所以,當(dāng)一個服務(wù)指定的賬戶名與同一個進(jìn)程中已經(jīng)啟動起來的其他服務(wù)的賬戶名不相同時,SCM會報告一個錯誤。

SCM調(diào)用ScLogonAndStartlmage來登錄一個服務(wù)(如果該服務(wù)的配置中指定了的話),并啟動該服務(wù)的進(jìn)程。SCM通過調(diào)用LSASS函數(shù)LogonUserEx來登錄那些并非運(yùn)行在系統(tǒng)賬戶下的服務(wù)。LogonUserEx在通常情況下要求一個口令,但是SCM告訴LSASS,該口令保存在注冊表中HKLM\SECURITYPolicySecrets的下面,是該服務(wù)的LSASS“秘密”(記住,涉及安全的內(nèi)容通常是不可見的,因為它的默認(rèn)安全設(shè)置只允許系統(tǒng)賬戶訪問)。當(dāng)SCM調(diào)用LogonUserEx時,它指定了登錄類型參數(shù)為服務(wù)登錄,所以,LSASS在Secrets子鍵的名為_SC服務(wù)名>的子鍵下查找口令。

當(dāng)SCP配置一個服務(wù)的登錄信息時,SCM利用LsaStorePrivateData函數(shù)來指示LSASS將一個登錄口令保存到Secrets子鍵下。在登錄成功以后,LoonUserEx給調(diào)用者返回一個句柄,指向個訪問令牌。Windows使用訪問令牌來代表一個用戶的安全環(huán)境,以后,SCM將該訪問令牌與實現(xiàn)此服務(wù)的進(jìn)程關(guān)聯(lián)起來。

在成功登錄以后,如果此賬戶的輪廓信息還沒有被加載的話,SCM通過調(diào)用UserEnv DLL(%SystemRoot%\System32\Userenvdl)的LoadUserProfile函數(shù)將此賬戶的輪廓信息加載進(jìn)來HKLM\SOFTWARE\Microsoft(Windows NT\CurrentVersio\Profilelist<用戶輪鍵>ProfileImagePath值包含了LoadUserProfile需要加載的注冊表儲巢在磁盤上的位置,從而使得該儲巢中的信息成為該服務(wù)的HKEYCURRENTUSER鍵。

交互式的服務(wù)必須打開Winta0窗口站,但是,ScLogonAndStartlmage在允許一個交互式服務(wù)訪問WinSta0以前,它要查 HKLMSYSTEMCurrentControlSet\ControWindowsNolnteractiveServices值是否已被設(shè)置。管理員設(shè)置此值以后,可以防止那些被標(biāo)記為交互式的服務(wù)在控制臺上顯示窗口。在無人值守的服務(wù)器環(huán)境中,此選項是非常合適的,因為在這種環(huán)境中,交互式服務(wù)的會話o UI Discovery通知是沒有人來響應(yīng)的。

下一個步驟是,如果該服務(wù)的進(jìn)程尚未被啟動(例如,為了另一個服務(wù)),則ScLogonAndStartlmage為該服務(wù)激發(fā)一個進(jìn)程。SCM通過Windows函數(shù)CreateProcessAsUser來啟動此進(jìn)程,并且將該進(jìn)程的狀態(tài)設(shè)置為掛起狀態(tài)。接下來,SCM創(chuàng)建一個命名管道,以后它通過該管道與服務(wù)進(jìn)程進(jìn)行通信,它分配給管道的名稱為\Pipe\Net\NtControlPipeX,這里X是一個數(shù)字,每次SCM創(chuàng)建一個管道,該數(shù)字就會遞增。然后,SCM通過ResumeThread函數(shù)來恢復(fù)服務(wù)進(jìn)程的執(zhí)行,并且等待該服務(wù)連接到它的SCM管道上。如果注冊表值HKLM\SYSTEM\CurrentControlSet\ControServicesPipeTimeout存在的話,則它決定了SCM等待一個服務(wù)調(diào)用StartServiceCtrlDispatcher并連接過來的時間長度,如果在這么長時間里沒有等到則SCM就會放棄,終止該進(jìn)程,并得出結(jié)論:該服務(wù)未能啟動。如果ServicesPipeTimeout不存在,則SCM使用默認(rèn)的30秒作為超時間隔值。SCM對于它所有的服務(wù)通信都使用同樣的超時間隔值。

當(dāng)一個服務(wù)通過它的管道連接到SCM時,SCM向該服務(wù)發(fā)送一個啟動命令。如果該服務(wù)未能在超時間隔內(nèi)肯定地響應(yīng)此啟動命令,SCM就會放棄,并且轉(zhuǎn)移到啟動下一個服務(wù)。當(dāng)一個服務(wù)沒有對啟動請求作出響應(yīng)時,SCM并不會像一個服務(wù)在超時間隔內(nèi)沒有調(diào)用StartServiceCtrlDispatcher的情形那樣終止該進(jìn)程,相反,它會在系統(tǒng)的事件日志(Event Log)中記錄一個錯誤,指明該服務(wù)未能及時地啟動起來。

如果SCM調(diào)用ScStartService啟動的服務(wù)有一個Type注冊表值為SERVICE KERNEL DRIVER或SERVICE_FILE_SYSTEM_DRIVER,那么,該服務(wù)確實是一個設(shè)備驅(qū)動程序,所以,ScStartService調(diào)用ScLoadDeviceDriver來加載該驅(qū)動程序。ScLoadDeviceDriver首先使SCM進(jìn)程具有加載驅(qū)動程序的安全特權(quán),然后調(diào)用內(nèi)核服務(wù)NtLoadDriver,將該驅(qū)動程序的注冊表鍵中的ImagePath值的數(shù)據(jù)傳遞過去。與Windows服務(wù)不同的是,驅(qū)動程序不需要指定lmagePath值:如果該值不存在的話,SCM通過將驅(qū)動程序的名稱附加在字符串%SystemRoot%(System32\Drivers\的后面就可以構(gòu)造一個映像文件路徑。

ScAutoStartServices繼續(xù)循環(huán)處理同屬于一個組的服務(wù),直到所有這些服務(wù)要么被啟動起來,要么產(chǎn)生相依性錯誤。這種循環(huán)處理方式是SCM根據(jù)一個組中的服務(wù)的DependonService相依性來自動對它們進(jìn)行順序處理的。SCM在較早的循環(huán)中啟動那些被其他服務(wù)依賴的服務(wù),跳過那些依賴于其他服務(wù)的服務(wù),而在后續(xù)的循環(huán)中再啟動這些服務(wù)。注意:SCM忽略了Windows服務(wù)的Tag值,你可能會在HKLM\SYSTEM\CurrentControlSet\Services鍵的子鍵下面看到這些值:I/0管理器利用Ta值來排列一個組內(nèi)的引導(dǎo)-啟動(boot-start)和系統(tǒng)-啟動 (systemstart)的設(shè)備驅(qū)動程序的啟動順序。一旦SCM完成了ServiceGroupOrder\List值中列出的所有組。

的啟動階段,它再為那些屬于其他組(未列在該值中)的服務(wù)執(zhí)行一個單獨(dú)的階段,最后,再
為那些不屬于任何一個組的服務(wù)執(zhí)行一個階段。SCM在處理了自動-啟動的服務(wù)以后,調(diào)用ScinitDelayStart,該函數(shù)將一個延遲的工作項目加入隊列中,該工作項目與一個專門的輔助線程相關(guān)聯(lián),它負(fù)責(zé)處理所有由于被標(biāo)記為“延遲的自動-啟動”而被ScAutoStartServices忽略掉的服務(wù)。此輔助線程將在一段時間的延遲之后執(zhí)行,默認(rèn)的延遲是120秒,但是通過在HTML\SYSTEM\CurrentControlSet\Control中創(chuàng)建一個AutoStartDelay值,可以覆蓋這一默認(rèn)值。就如同非延遲的自動-啟動服務(wù)一樣,SCM在這些延遲的自動-啟動服務(wù)的啟動過程中也執(zhí)行同樣的動作。

延遲的自動-啟動服務(wù)
延遲的自動-啟動服務(wù)使得Windows可以處理越來越多的當(dāng)用戶登錄時候需要啟動的服務(wù)這些服務(wù)數(shù)量大了以后,使得引導(dǎo)過程陷于停頓,用戶需要等待很長時間才能從桌面得到響應(yīng)。自動-啟動服務(wù)的設(shè)計目標(biāo)主要是為那些在引導(dǎo)過程早期必需的服務(wù),因為其他的服務(wù)要依賴它們。一個很好的例子是RPC服務(wù),所有其他的服務(wù)都要依賴它。另一個用途是,允許一個服務(wù)不被留意到就可以啟動起來,比如Windows Update服務(wù)。因為許多自動-啟動服務(wù)都落在第二類別中,所以,將它們標(biāo)記為延遲的自動-啟動可以讓關(guān)鍵的服務(wù)啟動得更快,而且在引導(dǎo)以后當(dāng)用戶登錄時可以更快地準(zhǔn)備好用戶的桌面。此外,這些服務(wù)在后臺模式下運(yùn)行,這也降低了它們的線程、I/O和內(nèi)存的優(yōu)先級。把一個服務(wù)配置成延遲的自動-啟動,要求調(diào)用ChangeServiceConfig2AP1。你也可以使用scexe的qc位選項來檢查一個服務(wù)的這一標(biāo)志狀態(tài)

注如果一個非延遲的自動-啟動服務(wù)有一個延遲的自動-啟動服務(wù)作為它的依賴服務(wù)之一,那么這一延遲的自動-啟動標(biāo)志將被忽略,該服務(wù)將被立即啟動,以滿足相依性要求。

當(dāng)SCM完成了啟動所有這些自動啟動的服務(wù)和驅(qū)動程序,以及設(shè)置了延遲的自動-啟動的工作項目時,它發(fā)信號通知\BaseNamedObjects(SC_AutoStartComplete事件。這一事件被Windows Setup程序用于在安裝過程中衡量啟動過程。

4.2.5 啟動錯誤

如果一個驅(qū)動程序或者服務(wù)報告一個錯誤,作為對SCM啟動命令的響應(yīng),那么該服務(wù)的注冊表鍵的ErrorControl 值決定了SCM如何作出反應(yīng)。如果ErrorControl值是SERVICE_ERROR_IGNORE0),或者ErrorControl值沒有被指定,那么,SCM只是簡單地忽略該錯誤,繼續(xù)處理它的服務(wù)啟動工作即可。如果ErrorControl值是SERVICE ERROR NORMAL(1),那么,SCM向系統(tǒng)事件日志中寫入一個事件,它這樣說:“由于以下錯誤,故<服務(wù)名>服務(wù)未能啟動 (The service failed to start due to the following error):”。該服務(wù)向SCM返回一個Windows錯誤代碼作為啟動失敗的原因;SCM在事件日志記錄中包含了此Windows錯誤
代碼的文本表示。圖4.14顯示了這樣的事件日志記錄,它報告了一個服務(wù)啟動錯誤。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
如果一個服務(wù)報告的ErrorControl值是SERVICE_ERROR SEVERE(2)或SERVICE_ERRORCRITICAL(3),那么,SCM會在事件日志中加入一條記錄,然后調(diào)用內(nèi)部函數(shù)ScRevertToLastKnownGood。此函數(shù)將系統(tǒng)的注冊表配置切換到一個名為“最后已知的好控制集 (last known good)”的版本,此版本是該系統(tǒng)最后一次成功引導(dǎo)的注冊表配置。然后,它利用執(zhí)行體中實現(xiàn)的NtShutdownSystem系統(tǒng)服務(wù)來重新啟動該系統(tǒng)。如果該系統(tǒng)當(dāng)前已經(jīng)是在利用“最后已知的好控制集”來引導(dǎo)了,那么,該系統(tǒng)僅僅重新啟動而已。

4.2.6 接受當(dāng)前引導(dǎo)和“最后已知的好控制集"

除了啟動服務(wù)以外,系統(tǒng)也要讓SCM來決定:應(yīng)該何時將系統(tǒng)的注冊表配置HKLMSYSTEM\CurrentControlSet保存為“最后已知的好控制集”。CurrentControlSet鍵包含了Services鍵,作為它的一個子鍵,所以,CurrentControlSet包含了SCM數(shù)據(jù)庫的注冊表表示形式。它也包含了Control鍵,其中保存了許多內(nèi)核模式和用戶模式的子系統(tǒng)配置信息。在默認(rèn)情況下,一次成功的引導(dǎo)是由“所有自動-啟動服務(wù)的成功啟動”和“一次成功的用戶登錄”構(gòu)成的。如果由于一個設(shè)備驅(qū)動程序在引導(dǎo)過程中使系統(tǒng)崩潰了,或者由于一個自動-啟動的服務(wù)報告了一個啟動錯誤(其ErrorControl值為SERVICE ERROR SEVERE或SERVICE ERROR CRITICAL)因而導(dǎo)致系統(tǒng)停止,則引導(dǎo)就失敗了。

很顯然。SCM知道什么時候它已經(jīng)完成了所有自動-啟動服務(wù)的成功啟動,但是Winlogon(%SystemRoot%\System32\Winlogon.exe)必須通知它什么時候有了一次成功的登錄。當(dāng)一個用戶登錄的時候,Winlogon調(diào)用Notlfy8ootConfigStatus函數(shù),而NotifyBootConfigStatus則會向SCM發(fā)送一個消息。在所有自動-啟動的服務(wù)都成功啟動以后,或者CM接收到了NotifyBootConfigStatus發(fā)送過來的消息以后(看哪個來得更晚一些),SCM訓(xùn)用系統(tǒng)函數(shù)NtlnitializeRegistry,將當(dāng)?shù)淖员淼膯优渲帽4嫦聛怼?/p>

第三方軟件開發(fā)人員可以用他們自己的定文來代替Winlogon對于一次成功登錄的定文。例如,運(yùn)行Microsoft SQL Server的系統(tǒng)可能要等到SQL erver能夠接受或者處理數(shù)據(jù)庫事務(wù)之后才認(rèn)為引導(dǎo)成功了。開發(fā)人員只要編寫一個引導(dǎo)確認(rèn)程序并且安裝該程序,就可以強(qiáng)加上他們自己的對于一次成功引導(dǎo)的定義,安裝方法很簡單,只要讓注冊表鍵HKLM\SYSTEM\CurrentControlSet\ControNBootVerificationProgram中存儲的值指向它在磁盤上的位置即可。而且,安裝的引導(dǎo)確認(rèn)程序必須禁止Winlogon對于NotifyBootConfi5tatus的調(diào)用其做法是,將HKLM\SOFTWARE\Microsoft)Windows NTCurrentVersion\Winlogon\ReportBootk設(shè)置為0。在一個引導(dǎo)確認(rèn)程序被安裝到系統(tǒng)中以后,SCM在完成了所有自動-啟動的服務(wù)以后會激發(fā)該程序,然后等待它調(diào)用NotlfybootConfigStatus,再保存“最后已知的好控制集”Windows維護(hù)了CurrentControlSet的幾份持貝,CurrentControlSet實際上是一個注冊表符號鏈接,它指向其中的一份拷貝,這些控制集有著形如HKLM\SYSTEMYControlSetnnn這樣的名稱,其中nnn是諸如001或者002這樣的數(shù)值。HKLM\SYSTEM\Select鍵中包含的值指出了每一份控制集的角色。例如,若CurrentControlSet 指向ControlSet001,那么Select下的Current值就是1.Select下的LastKnownGood值包含了“最后已知的好控制集”的數(shù)值,這是最后一次用來成功引導(dǎo)的控制集。在你的系統(tǒng)上另一個可能出現(xiàn)在Select鍵下的值是Failed,它指向最后一次引導(dǎo)不成功并且即使在“最后已知的好控制集”的幫助下也未能成功引導(dǎo)而不得不退出的控制集。圖4.15顯示了一個系統(tǒng)的控制集和Select值。

NtinitializeRegistry取出“最后已知的好控制集”中的內(nèi)容,并且與CurrentControlSet鍵樹中的控制集進(jìn)行同步。如果這是系統(tǒng)第一次成功引導(dǎo),則“最后已知的好控制集”并不存在,于是系統(tǒng)將創(chuàng)建一個新的控制集,如果“最后已知的好控制集”已經(jīng)存在了,則系統(tǒng)只是簡單地
用它與CurrentControSet之間的差值對它進(jìn)行更新?!白詈笠阎暮每刂萍睂τ谝駽urrentControl5et的一個變化而引起隨后引導(dǎo)失敗的情形是非常有幫助的,比如在HKLM\SYSTEM)Control下面有一個調(diào)節(jié)系統(tǒng)性能的值被修改了,或者新增加了一個服務(wù)或設(shè)備驅(qū)動程序。用戶可以在引導(dǎo)過程的早期按下F8,得到一個菜單,然后在菜單中直接選擇使用“最后已知的好控制集”,將系統(tǒng)的注冊表配置回滾到最后一次系統(tǒng)成功引導(dǎo)時的配置。本書下冊第13章將會更加詳細(xì)地討論如何使用“最后已知的好控制集”和其他的恢復(fù)機(jī)制來診斷系統(tǒng)啟動時出現(xiàn)的問圈。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.2.7 服務(wù)失敗

在一個服務(wù)的注冊表鍵中,可以有可選的FailureActions和FailureCommand值,這是SCM在該服務(wù)的啟動過程中記錄下來的。由于SCM向系統(tǒng)進(jìn)行了登記,因而當(dāng)服務(wù)進(jìn)程退出時系統(tǒng)可以發(fā)信號通知SCM。當(dāng)一個服務(wù)進(jìn)程意外終止時,SCM確定該進(jìn)程中運(yùn)行的是哪些服務(wù),并且根據(jù)其與失敗有關(guān)的注冊表值中指定的恢復(fù)步驟來采取行動。而且,一個服務(wù)能夠為在崩潰或意外服務(wù)終止過程中請求的失敗動作也是受限制的,因為有些其他的問題,比如內(nèi)存泄漏,也可能會導(dǎo)致服務(wù)失敗。
如果一個服務(wù)進(jìn)入SERVICE STOPPED狀態(tài),并且返回給SCM的錯誤代碼不是ERROR SUCCESS,那么,SCM將檢查該服務(wù)是否設(shè)置了FailureActionsOnNonCrashFailures標(biāo)志,并執(zhí)行同樣的恢復(fù)動作就好像該服務(wù)崩潰了一樣。為了使用這一功能,該服務(wù)必須通過ChangeServiceConfig2API來進(jìn)行配置,將FailureActionsOnNonCrashFailures設(shè)置為1:或者系統(tǒng)管理員使用Scexe工具的Failureflag參數(shù),將FailureActionsOnNonCrashFailures設(shè)置為1。默認(rèn)的值為0,SCM將會繼續(xù)保持windows早期版本中為所有其他服務(wù)那樣的行為。

一個服務(wù)可以為SCM配置的動作包括重新啟動該服務(wù)、運(yùn)行一個程序,以及重新引導(dǎo)計算機(jī)。而且,一個服務(wù)可以指定當(dāng)它第一次服務(wù)進(jìn)程失敗、第二次失敗,以及后續(xù)失敗時可以采取的失敗動作,它也可以指定:如果該服務(wù)要求重新啟動的話,那么,SCM在重啟該服務(wù)之前要等待多長時間。IISAdmin ervice的服務(wù)失敗動作是導(dǎo)致SCM運(yùn)行ISReset應(yīng)用程序,該程序會執(zhí)行一些清理工作,然后重新啟動該服務(wù)。你利用Services MMC加載件中一個服務(wù)的Properties對話框中的Recovery標(biāo)簽頁面,就能很容易地管理該服務(wù)的恢復(fù)動作,如圖4.16所示。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.2.8 服務(wù)停機(jī)

當(dāng)Winlogon調(diào)用Windows的ExitWindowsEx函數(shù)時,ExitWindowsEx向Windows子系統(tǒng)進(jìn)程Csrss發(fā)送一個消息,以便調(diào)用srss的停機(jī)例程。Csrss對所有活動的進(jìn)程進(jìn)行循環(huán),通知它們系統(tǒng)正在停機(jī)。對于除了SCM以外的每一個系統(tǒng)進(jìn)程,Csrss都要等待到由HKUNDEFAULT\ControPaneDesktolWaitToKillAppTimeout指定的秒數(shù)(默認(rèn)值為20秒),以便讓該進(jìn)程退出,然后再轉(zhuǎn)移到下一個進(jìn)程。當(dāng)Csrss碰到SCM進(jìn)程時,它也會通知SCM進(jìn)程系統(tǒng)正在停機(jī),但是使用一個專門針對SCM的超時間隔值。Csrss使用進(jìn)程ID來識別SCM,當(dāng)SCM在系統(tǒng)初始化過程中利用RegisterServicesProcess函數(shù)向Csrss登記時,Csrss將SCM的進(jìn)程ID保存下來了。SCM的超時間隔值之所以不同于其他的進(jìn)程,是因為,Csrss知道,SCM與那些在停機(jī)時需要執(zhí)行清理工作的服務(wù)進(jìn)行通信,所以,管理員可能需要單獨(dú)調(diào)節(jié)SCM的超時間隔值。SCM的超時間隔值位于HKLM\SYSTEM\CurrentControlSet\ControlWaitTokilServiceTimeout注冊表值中,其默認(rèn)值是12秒。
凡是在初始化時請求SCM發(fā)送停機(jī)通知的Windows服務(wù),SCM的停機(jī)處理器 (shutdownhandler)負(fù)責(zé)給它們發(fā)送停機(jī)通知。SCM函數(shù)ScShutdownAlIServices在SCM服務(wù)數(shù)據(jù)庫中進(jìn)行循環(huán),找到那些希望得到停機(jī)通知的服務(wù),并且給每一個這樣的服務(wù)發(fā)送一個停機(jī)命令。對于發(fā)送了停機(jī)命令的每一個服務(wù),SCM記錄下該服務(wù)的“等待提示(waithint)”值,這是該服務(wù)在向SCM登記時指定的一個值。SCM跟蹤記錄了它所接收到的最大的等待提示值。在發(fā)送了停機(jī)消息以后,SCM開始等待,要么等到它所通知的服務(wù)有一個已經(jīng)退出,或者等到指定的最大等待提示值已經(jīng)過去。
如果等待提示值已經(jīng)到期,仍然沒有哪個服務(wù)退出,那么,SCM確定是否它所等待退出的一個或者多個服務(wù)已經(jīng)向SCM發(fā)送了一個消息,告訴SCM該服務(wù)正在進(jìn)行停機(jī)處理。如果至少有一個服務(wù)正在進(jìn)行之中,那么,SCM再等待一個等待提示值周期。SCM繼續(xù)執(zhí)行此等待循環(huán)直至要么所有的服務(wù)都退出,要么在等待提示值周期內(nèi),它所等待的服務(wù)全都沒有通知它停機(jī)的進(jìn)度。
當(dāng)SCM忙于告訴服務(wù)停機(jī)并等待它們退出的過程中,Csrss也在等待SCM退出。如果srss的等待結(jié)束了(即WaitToKillServiceTimeout時間到期),但SCM還沒有退出,那么,Csrss殺掉SCM,繼續(xù)它的停機(jī)過程。因此,未能及時停機(jī)的服務(wù)將被殺掉。這一邏輯可以讓系統(tǒng)在面對那些由于不正確設(shè)計而永遠(yuǎn)無法完成停機(jī)過程的服務(wù)的情況下,也可以正常停機(jī),但這也意味著,那些要超過20秒才能完成停機(jī)的服務(wù)將無法完成其停機(jī)操作。
而且,因為停機(jī)順序是不確定的,所以,那些可能依賴于其他服務(wù)先停機(jī)的服務(wù)(稱為停機(jī)相依性)無法將這種停機(jī)相依性報告給SCM,可能永遠(yuǎn)也沒有機(jī)會來完成清理工作。為了滿足這些需求,Windows實現(xiàn)了預(yù)停機(jī)通知和停機(jī)順序來應(yīng)對這兩種情形引發(fā)的問題。對于通過SetServiceStatus API來請求預(yù)停機(jī)通知的服務(wù),將使用與停機(jī)通知同樣的機(jī)制來發(fā)送預(yù)停機(jī)通知,并且SCM將等待這些預(yù)停機(jī)通知被確認(rèn)。
這些通知背后的思路是,將那些可能要花較長時間來完成清理工作的服務(wù)(比如數(shù)據(jù)庫服務(wù)器服務(wù))做好標(biāo)志,并且給它們更多的時間來完成其工作。SCM將發(fā)送一個進(jìn)度查詢請求,并等待3分鐘,以便讓一個服務(wù)來響應(yīng)此通知。如果該服務(wù)沒有在此時間間隔內(nèi)作出響應(yīng),它將在停機(jī)過程中被殺掉;否則,它可以根據(jù)其需要一直運(yùn)行,只要它繼續(xù)響應(yīng)SCM即可。
參與預(yù)停機(jī)的服務(wù)也可以指定一個相對于其他預(yù)停機(jī)服務(wù)的停機(jī)順序。依賴于其他服務(wù)先停機(jī)的服務(wù)(比如,組策略服務(wù)需要等待Windows Update先結(jié)束)可以在HKLMSYSTEM\CurrentControlSet\ControPreshutdownOrder注冊表值中指定它們的停機(jī)依賴性。

4.2.9 共享的服務(wù)進(jìn)程

在單獨(dú)的進(jìn)程中運(yùn)行每一個服務(wù),而不是在可能的情況下讓多個服務(wù)共享同一個進(jìn)程,必然會浪費(fèi)系統(tǒng)資源。然而,共享進(jìn)程也意味著,如果該進(jìn)程中的任何一個服務(wù)出現(xiàn)了錯誤因而導(dǎo)致進(jìn)程退出,則該進(jìn)程中所有的服務(wù)都終止了。
在Windows的內(nèi)置服務(wù)中,有些運(yùn)行在它們自己的進(jìn)程中,而有些則與其他的服務(wù)共享一個進(jìn)程。例如,LSASS進(jìn)程包含了與安全相關(guān)的一些服務(wù),比如安全賬戶管理器(SamSs)服務(wù)、網(wǎng)絡(luò)登錄(Netlogon)服務(wù),以及Crypto Next Generation (CNG) Keylsolation (Keylso)服務(wù)。

有一個名為“服務(wù)宿主(Service Host)”的“通用”進(jìn)程(SvcHost,%SystemRoot%(System32Svchost.exe)可以包含多個服務(wù)。SvcHost的多個實例可以運(yùn)行在不同的進(jìn)程中。在SvcHost進(jìn)程中運(yùn)行的服務(wù)有電話 (TapiSrv) 服務(wù)、遠(yuǎn)過程調(diào)用 (RpSs)服務(wù)和遠(yuǎn)程訪問連接管理器(RasMan)服務(wù)。運(yùn)行在SvcHost中的服務(wù)被實現(xiàn)為DLL,Windows在該服務(wù)的注冊表鍵中包含了一個形如“%SystemRoot%System32\svchost.exe -k netsvcs”這樣的ImagePath定義。該服務(wù)的注冊表鍵還必須有一個名為ServiceDll的注冊表值,它位于Parameters子鍵下面,其中指向該服務(wù)的DLL文件。
共享同一個SvcHost進(jìn)程的所有服務(wù)必須指定同樣的參數(shù)(比如,在上一段例子中的“-knetsvcs”),所以,它們在SCM的映像數(shù)據(jù)中只有一個記錄。當(dāng)SCM在服務(wù)啟動過程中碰到第個ImagePath為SvcHost且?guī)в刑囟▍?shù)的服務(wù)時,它創(chuàng)建一個新的映像數(shù)據(jù)庫記錄,并且用此參數(shù)激發(fā)一個SvcHost進(jìn)程。新的SvcHost進(jìn)程帶有此參數(shù),它在HKLMSOFTWARE)Microsoft\Windows NT\CurrentVersion(Svchost下查找一個以此參數(shù)作為名稱的注冊表值。SvcHost讀取該值的內(nèi)容,并且將它解釋成一個服務(wù)名稱列表:當(dāng)SvcHost向SCM登記時,它通知SCM,自己宿納著這些服務(wù)。
當(dāng)SCM在服務(wù)啟動過程中碰到一個SvcHost服務(wù),并且其ImagePath與映像數(shù)據(jù)庫中已有的記錄相匹配時,它不會再激發(fā)第二個進(jìn)程,而是向早先為了該lmagePath值而啟動起來的SvcHost發(fā)送一個啟動命令,讓它啟動該服務(wù)。原先的SvcHost進(jìn)程讀入該服務(wù)注冊表鍵中的ServiceDII參數(shù),將該DLL加載到它的進(jìn)程中,以便啟動此服務(wù)。
表4.11列出了Windows上所有默認(rèn)的服務(wù)組,有些服務(wù)向每一個組都進(jìn)行了注冊。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.2.9 服務(wù)標(biāo)記

使用服務(wù)宿主進(jìn)程的一個缺點(diǎn)是,要計量某個特定服務(wù)的CPU時間和使用率,以及各種資源的使用率,都比較困難,因為每個服務(wù)共享了內(nèi)存地址空間、句柄表,針對每個進(jìn)程的CPU計量值也是與同一個服務(wù)組中其他的服務(wù)共享的。雖然在服務(wù)宿主進(jìn)程內(nèi)部總存在一個線程屬于一個特定的服務(wù),但是,這種關(guān)聯(lián)性并不總是很容易建立起來。例如,一個服務(wù)有可能使用輔助線程來完成它的操作,或者該線程的啟動地址和棧并沒有暴露出該服務(wù)的DLL名稱,這就使得很難確切地判斷出一個線程在做哪種工作,以及它可能屬于哪個服務(wù)。

Windows實現(xiàn)了一個稱為服務(wù)標(biāo)記的服務(wù)屬性,當(dāng)一個服務(wù)被創(chuàng)建的時候,或者當(dāng)在系統(tǒng)引導(dǎo)過程中服務(wù)數(shù)據(jù)庫被生成的時候,SCM調(diào)用ScGenerateServiceTag來生成服務(wù)標(biāo)記。這一屬性只是一個簡單的索引,它標(biāo)識了相應(yīng)的服務(wù)。此服務(wù)標(biāo)記被保存在每個線程的線程環(huán)境塊(TEB)的SubProcessTag域中(關(guān)于TEB的更多信息,請參見第5章“進(jìn)程和線程”),它也會被傳播到由主服務(wù)線程創(chuàng)建的所有其他線程中(除了那些通過線程池API間接創(chuàng)建的線程以外)。雖然服務(wù)標(biāo)記被保持在SCM的內(nèi)部,但是有幾個Windows工具,像Netstat.exe(你可以用此工具來顯示哪些程序打開了哪些網(wǎng)絡(luò)端口),利用未文檔化的API來查詢服務(wù)標(biāo)記,并且將它們映射到相應(yīng)的服務(wù)名稱上。因為TCP/IP棧保存了創(chuàng)建TCP/IP端點(diǎn)的線程的服務(wù)標(biāo)記,所以,當(dāng)你通過-b參數(shù)來運(yùn)行Netstat時,Netstat可以報告出由服務(wù)創(chuàng)建的端點(diǎn)的服務(wù)名稱。另一個你可以用來查看服務(wù)標(biāo)記的工具是ScTagQuery (來自 Winsider Seminars & Solutions Inc.www.winsidersscom/tools/sctagquery/sctagquery.htm)。它可以向SCM查詢每個服務(wù)標(biāo)記的映射關(guān)系,并且按照全系統(tǒng)范圍或每個進(jìn)程的角度來顯示它們。它也可以向你顯示,一個服務(wù)宿主進(jìn)程內(nèi)部的所有線程屬于哪些服務(wù)。(這是有條件的:這些線程必須有一個適當(dāng)?shù)姆?wù)標(biāo)記與它們相關(guān)聯(lián)。)通過這種方法,如果你有一個失控的服務(wù)正在消耗大量的CPU時間,那么,即使線程的啟動地址或者棧沒有顯然的服務(wù)DLL與之關(guān)聯(lián),你也可以標(biāo)識出罪魁服務(wù)。

4.3 統(tǒng)一的后臺進(jìn)程管理器(UBPM)

傳統(tǒng)上,隨著Windows操作系統(tǒng)在各種特性上復(fù)雜性的增加,各種各樣的Windows組件負(fù)責(zé)管理所宿納的任務(wù)或者后臺任務(wù):

  • 從前面講述的服務(wù)控制管理器
  • (SCM)到任務(wù)調(diào)度器(TaskScheduler),
  • DCOM服務(wù)器激發(fā)器 (DCOM Server Launcher),
  • 以及WMI提供者一一所有這些組件都在負(fù)責(zé)其宿納的跨越進(jìn)程的代碼的執(zhí)行。

今天,Windows實現(xiàn)了一個統(tǒng)一的后臺進(jìn)程管理器(UBPM,Unified Background Process Manager),它處理這些機(jī)制中的兩種(至少兩種,到目前為止):

  • SCM
  • 任務(wù)調(diào)度器

為這些組件提供了訪問UBPM功能的能力。

UBPM是在Servicesexe中實現(xiàn)的,與SCM在同樣的位置上,但是它作為一個獨(dú)立的庫,在RPC基礎(chǔ)上提供了它自己的接口(類似于即插即用管理器,它也運(yùn)行在Services.exe中,但是是一個獨(dú)立的組件)。它通過一個公開的導(dǎo)出DLL (Ubpm.dll)來提供對此接口的訪問。Ubpm.dll通過新的觸發(fā)器API(TriggerAPI,已經(jīng)被加入到SCM中了)暴露給第三方服務(wù)開發(fā)人員。然后SCM加載一個自定義的SCM擴(kuò)展DLL (Scextdll),它再調(diào)用進(jìn)入Ubpm.dli中。這一層間接性是為了支持MinWin而需要的,在MinWin中Scextdll并未被加載,SCM只提供了最小的功能。圖4.17描述了這一體系結(jié)構(gòu)。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.3.1 初始化

UBPM是由SCM初始化的,這發(fā)生在當(dāng)它的Ubpminitialize導(dǎo)出函數(shù)被SCM擴(kuò)展DLL中的SzExtInitializeTerminateUbpm調(diào)用的時候。同樣地,UBPM被實現(xiàn)成一個DLL,運(yùn)行在SCM的環(huán)培而不是它自己的獨(dú)立進(jìn)程。

UBPM首先建立起它的內(nèi)部工具庫,作為初始化的開始。通過利用Windows新版本中諸多的改進(jìn)措施,UBPM使用一個線程池來處理許多進(jìn)來的事件(后面我們將會看到),這使得UBPM可以有很好的伸縮性:從單個輔助線程,到1000個輔助線程(根據(jù)最多處理10,000個消費(fèi)者)。

接下來,UBPM初始化它的內(nèi)部跟蹤支持,這可以在HKLM\SOFTWARE\MicrosoftlWindowsNT\CurrentVersion\Tracing\UBPM\Regular鍵中用一些標(biāo)志值進(jìn)行配置。這對于使用WPP跟蹤機(jī)制(在Windows Driver Kit中有介紹)來調(diào)試和監(jiān)視UBPM的行為很有幫助。

之后,建立起事件管理器,它將被UBPM后來的組件用于報告內(nèi)部事件狀態(tài)。事件管理器注冊了一個GUID:TASKCHED,將來可通過此GUID來使用ETW事件,事件管理器把它的狀態(tài)記錄到一個TaskScheduler.log文件中。

下一個步驟對UBPM至關(guān)重要,它要初始化它自己的實時ETW消費(fèi)者,這也是UBPM完成其任務(wù)的中心機(jī)制,因為它接收到的幾乎所有數(shù)據(jù)都是通過ETW事件過來的。UBPM啟動一個安全模式下的ETW實時會話,這意味著它將是唯一能夠接收其事件的進(jìn)程:它把此會話的名稱命名為UBPM。為了接收與時間變化相關(guān)的通知,它也啟用了第一個內(nèi)置的提供者(由內(nèi)核所擁有)。
然后它將一個事件回調(diào)函數(shù)UbpmpEventCallback與進(jìn)來的事件關(guān)聯(lián)起來,并創(chuàng)建一個消費(fèi)者線程UbpmpConsumeEvents,該線程等待SCM用于通知“自動-啟動事件已完成”的事件(此前已經(jīng)提到過)。一旦等待完成,消費(fèi)者線程調(diào)用ProcessTrace,該函數(shù)調(diào)用進(jìn)入到ETW,并阻塞線程,直到ETW痕跡數(shù)據(jù)完成(通常,只有UBPM退出)。另一方面,隨著每一個ETW事件的到來,事件回調(diào)函數(shù)會接收每個事件,并根據(jù)下一小節(jié)將要介紹的算法來處理該事件。

ETW自動回放所有在ProcessTrace被調(diào)用之前漏掉的事件,這意味著在引導(dǎo)過程中的內(nèi)核事件也將會立即進(jìn)來,并正確地進(jìn)行處理。UBPM也在SCM的自動-啟動事件上等待,這可以確保當(dāng)這些事件進(jìn)來的時候,至少有很多服務(wù)已經(jīng)注冊過要消費(fèi)這些事件:否則,太早地啟動痕跡跟蹤將會導(dǎo)致有些事件還沒有已注冊的消費(fèi)者,從而這些事件被丟掉。

最后,UBPM建立一個與TaskHost通信的本地RPC接口,這里TaskHost是UBPM的第二個組件稍后將進(jìn)一步介紹。UBPM也建立起它自己的本地RPC接口,這一接口暴露的API可以讓各種服務(wù)使用UBPM的功能(比如注冊觸發(fā)器提供者、生成觸發(fā)器和通知,等等)。這些API是在Ubpm.dll庫中實現(xiàn)的,它們使用RPC與Services.exe中UBPM代碼的RPC接口進(jìn)行通信。

當(dāng)UBPM退出的時候,相反的動作按照相反的順序執(zhí)行,以便將系統(tǒng)重置到它以前的狀態(tài)。

4.3.2 UBPM API

UBPM讓W(xué)indows服務(wù)使用UBPMAPI,它提供下面的機(jī)制讓它們使用:

  • 注冊或者注銷一個觸發(fā)器提供者,以及打開和關(guān)閉一個指向此提供者的句柄。
  • 生成一個通知或觸發(fā)器。
  • 設(shè)置和查詢一個觸發(fā)器提供者的配置
  • 向一個觸發(fā)器提供者發(fā)送控制命令。

4.3.3 提供者注冊

提供者是通過SCM擴(kuò)展DLL來注冊的,它使用ScExtpRegisterProvider函數(shù),此函數(shù)通過ScExtGenerationNotification 來調(diào)用。這打開一個指向UBPM的句,并調(diào)用UbmpRegisterTriggerProviderAPI。當(dāng)一個服務(wù)注冊一個提供者時,它必須為這個提供者定義-個唯一的名稱和GUID,以及一些必要的標(biāo)志來定義此提供者(例如,使用ETW提供者標(biāo)志)。

而且,提供者也可以有一個友好名稱以及一段描述。一旦注冊完成,該提供者被插入到UBPM的提供者鏈表中,提供者的總數(shù)隨之遞增;如果這是一個設(shè)置了disabled標(biāo)志而尚未啟動的ETW提供者,那么該提供者的GUID將在UBPM在初始化階段激活的實時ETW痕跡中被啟用。另外也創(chuàng)建一個提供者塊,其中包含了所有在注冊過程中獲取到的有關(guān)提供者的信息。
既然提供者已經(jīng)被注冊了,就可以使用打開和關(guān)閉API來遞增該提供者的引用計數(shù),并返回它的提供者塊。而且,如果此提供者并未被注冊成禁用的狀態(tài),那么,這也是第一個指向它的引用,它的GUID在實時ETW痕跡中被啟用。
類似地,注銷一個提供者將禁用它的GUID,并且將它從提供者鏈表中解除鏈接關(guān)系:一旦所有的引用都關(guān)閉了,則提供者塊也相應(yīng)地被刪除。

4.3.4 消費(fèi)者注冊

服務(wù)消費(fèi)者的注冊工作最初是由ScExtRegisterTriggerConsumer回調(diào)暴露出來的,此回調(diào)函數(shù)是SCM擴(kuò)展DLL提供的。它的任務(wù)是接收所有由SCM格式化的觸發(fā)器信息(由服務(wù)開發(fā)者提供,根據(jù)MSDNAPI文檔“Service TriggerEvents”,可以查閱MSDN獲得),并且將這些信息轉(zhuǎn)換成UBPM內(nèi)部使用的原始數(shù)據(jù)結(jié)構(gòu)。一旦所有的處理都已經(jīng)完成,SCM擴(kuò)展DLL將這一觸發(fā)器包裝起來,將它與兩個動作關(guān)聯(lián)起來: UBPM Start Service和UBPM Stop Service。

ScheduledTasks服務(wù)利用了UBPM的能力,它通過一個內(nèi)部的UBPM單體類(UBPM SingletonClass,調(diào)用進(jìn)入Ubpmdll)提供了類似的功能。它允許其內(nèi)部的RegisterTaskAP!也可以注冊觸發(fā)器的消費(fèi):它也對其內(nèi)部的數(shù)據(jù)進(jìn)行類似的處理,不同之處在于它使用了UBPM StatEXE動作。接下來,為了真正地完成注冊操作,也為了打開一個指向UBPM的句柄,要檢查一下是否該消費(fèi)者已經(jīng)注冊過了(不允許改變已有的消費(fèi)者),最后通過UbpmRegisterTriggerConsumerAPI注冊消費(fèi)者。

觸發(fā)器消費(fèi)者的注冊是由UbpmTriggerProviderRegister完成的,它驗證此注冊請求,把提供者的GUID加入到提供者鏈表中,并且啟用此提供者,使得ETW痕跡會話現(xiàn)在也可以接收關(guān)于此提供者的事件。

4.3.5 TaskHost

TaskHost接收SCM中運(yùn)行的UBPM發(fā)過來的命令。在初始化時刻,它打開UBPM在初始化時候創(chuàng)建的本地RPC接口,然后無限循環(huán),等待此通道中過來的命令。

當(dāng)前支持四種命令,它們可通過TaskHostSendResponseReceiveCommand RPCAPI發(fā)送過來:

  • 停止宿主
  • 啟動一個任務(wù)。
  • 停止一個任務(wù)
  • 終止一個任務(wù)。

而且,所宿納的任務(wù)是通過TaskHostReportTaskStatus RPCAPI來提供的,該API使得這些任務(wù)可以在調(diào)用UbpmReportTaskStatus的時候?qū)⑺鼈兊漠?dāng)前執(zhí)行狀態(tài)通知UBPM。

所有基于任務(wù)的命令實際上在內(nèi)部都是由一個通用的COM任務(wù)庫來實現(xiàn)的,它們實質(zhì)上都會導(dǎo)致創(chuàng)建或銷毀相應(yīng)的COM組件。

4.3.6 服務(wù)控制程序

服務(wù)控制程序是標(biāo)準(zhǔn)的Windows應(yīng)用程序,它用到了SCM服務(wù)管理函數(shù),包括CreateServiceOpenService、StartService、ControlService、QueryServiceStatus和DeleteService。為了使用這些SCM函數(shù),SCP必須首先調(diào)用OpenSCManager函數(shù),以打開一個通向SCM的通信通道。在調(diào)用該打開函數(shù)的時候,SCP必須指定它想要執(zhí)行的動作的類型。例如,如果一個SCP只是簡單地想要枚舉并顯示SCM數(shù)據(jù)庫中出現(xiàn)的服務(wù),那么,它在調(diào)用OpenSCManager時請求“枚舉服務(wù)”訪問許可。SCM在初始化過程中,創(chuàng)建了一個代表SCM數(shù)據(jù)庫的內(nèi)部對象,并且利用Windows的安全功能,通過一個安全描述符來保護(hù)該對象。此安全描述符指定了哪些賬戶可以用什么樣的訪問許可來打開該對象。例如,此安全描述符指示Authenticated Users組可以以“枚舉服務(wù)”的訪問許可來打開該SCM對象。然而,只有管理員組才可以請求以創(chuàng)建或刪除服務(wù)的訪問方式來打開該對象。

如同SCM數(shù)據(jù)庫對象一樣,SCM也為這些服務(wù)本身實現(xiàn)了安全功能。當(dāng)一個SCP利用CreateService函數(shù)來創(chuàng)建一個服務(wù)時,它指定一個安全描述符,SCM在內(nèi)部將此安全描述符與該服務(wù)在服務(wù)數(shù)據(jù)庫中的記錄關(guān)聯(lián)起來。SCM將此安全描述符保存在該服務(wù)注冊表鍵的Security值中,SCM在初始化過程中掃描注冊表的Services鍵時,讀入此Security值,所以,即使機(jī)器重新引導(dǎo)以后,安全設(shè)置也會一直有效。如同SCP必須要在OpenSCManager調(diào)用中指定它將要以什么類型的方式來訪問SCM數(shù)據(jù)庫一樣,SCP也必須在OpenService調(diào)用中告訴SCM,它想要怎樣訪問一個服務(wù)。一個SCP可以請求的訪問方式有:能夠查詢一個服務(wù)的狀態(tài),可以配置、停止和啟動一個服務(wù)。

你可能最熟悉的SCP是Windows 自帶的Services MMC加載件,位于%SystemRoot%\System32\Filemgmtdll中。Windows也包含了一個命令行服務(wù)控制程序Sc.exe(Service Controller tool,服務(wù)控制器工具),前面我們已經(jīng)多次提到過。

有時候,SCP在SCM實現(xiàn)的服務(wù)策略之上又疊加了一層服務(wù)策略。一個很好的例子是,當(dāng)一個服務(wù)被手工啟動時,Services MMC加載件實現(xiàn)了超時的策略。該加載件顯示一個進(jìn)度條表示一個服務(wù)正在啟動過程中。服務(wù)在響應(yīng)SCM命令(比如啟動命令)時,通過設(shè)置它們的配置狀態(tài)來反映它們的進(jìn)度情況,從而可以間接地與SCP進(jìn)行交互。SCP通過QueryServiceStatus函數(shù)來查詢此狀態(tài)。它們可以辨別何時一個服務(wù)主動更新了此狀態(tài),何時一個服務(wù)像是被掛起了,因此,SCM可以采取適當(dāng)?shù)膭幼鱽硗ㄖ粋€用戶該服務(wù)當(dāng)前的狀況。

4.4 Windows管理設(shè)施(WMI)

Windows管理設(shè)施(WMl,Windows ManagementInstrumentation)是WBEM(Web-BasedEnterprise Management)的一個實現(xiàn),而WBEM則是DMTF(Distributed Management Task Force,一個工業(yè)界聯(lián)盟)定義的一個標(biāo)準(zhǔn)。WBEM標(biāo)準(zhǔn)包含了一套可擴(kuò)展的、針對企業(yè)的數(shù)據(jù)采集和數(shù)據(jù)管理設(shè)施的設(shè)計方案,此設(shè)計方案具有很強(qiáng)的靈活性和擴(kuò)展性;為了管理具有任意多個組件的本地和遠(yuǎn)程系統(tǒng),這樣的靈活性和擴(kuò)展性是非常必要的。

4.4.1 WMI體系結(jié)構(gòu)

WMI是由四個主要的部件構(gòu)成的,如圖4.18所示。這四個部件是管理應(yīng)用程序、WMI基礎(chǔ)設(shè)施、提供者和被管理的對象。管理應(yīng)用程序是Windows應(yīng)用程序,它們訪問有關(guān)被管理對象的數(shù)據(jù),并且顯示這些數(shù)據(jù),或者對它們進(jìn)行處理。管理應(yīng)用程序的一個簡單例子是,利用WMI而不是性能API來獲得有關(guān)性能信息的性能工具。另一個稍微復(fù)雜一點(diǎn)的例子是一個企業(yè)管理工具,它使得管理員可以自動完成計算機(jī)設(shè)備的清查工作,即將他們企業(yè)中每臺計算機(jī)的軟硬件配置信息收集到指定的地點(diǎn)。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
開發(fā)人員通常將管理應(yīng)用程序的目標(biāo)定義為采集有關(guān)特定對象的數(shù)據(jù),以及管理這些對象。一個對象可能代表了一個組件,比如網(wǎng)絡(luò)適配器設(shè)備,也可能代表了一組組件,比如一臺計算機(jī)(計算機(jī)對象可能包含了網(wǎng)絡(luò)適配器對象)。提供者需要為管理應(yīng)用程序可能感興趣的對象,定義和導(dǎo)出它們的表示形式。例如,網(wǎng)絡(luò)適配器的廠商可能想要在Windows包含的網(wǎng)絡(luò)適配器的WMI支持中,再加入一些與他們的適配器有關(guān)的屬性,從而允許管理應(yīng)用程序可以直接查詢和設(shè)置適配器的狀態(tài)和行為。在有些情況下(比如,針對設(shè)備驅(qū)動程序),Microsoft提供了一個具有專屬API的提供者,以幫助開發(fā)人員用最小的編程努力為他們自己管理的對象實現(xiàn)一個提供者。

WMI基礎(chǔ)設(shè)施是把管理應(yīng)用程序和提供者綁定在一起的黏合劑,其核心是CIM對象管理器(CIMOM,Common Information Model Object Manager)(本章后面將會講述CIM)。此基礎(chǔ)設(shè)施也被用作對象-類的存儲體,在許多情況下,還被用作永久對象屬性的存儲管理器。WMI將此存儲體,或者說倉庫,實現(xiàn)為磁盤上的數(shù)據(jù)庫,稱為CIMOM對象倉庫。作為此基礎(chǔ)設(shè)施的一部分,WMI支持幾個API; 管理應(yīng)用程序通過這些API可以訪問對象的數(shù)據(jù),而提供者則通過這些API來提供數(shù)據(jù)和類的定義。

Windows程序和腳本(比如Windows PowerShell)使用WMICOMAPI來直接與WMI打交道這是最為基本的管理API。其他的API建立在此COMAPI基礎(chǔ)之上其中包括針對Microsoft Access數(shù)據(jù)庫應(yīng)用程序的ODBC (Open Database Connectivity)配接接口。數(shù)據(jù)庫開發(fā)人員使用WMIODBC配接接口,來嵌入對于他們自己數(shù)據(jù)庫中的對象數(shù)據(jù)的引用。然后,開發(fā)人員可以通過數(shù)據(jù)庫查詢,來得到基于WM的數(shù)據(jù),從而很容易生成相應(yīng)的數(shù)據(jù)報告。WMI Activex控件支持另一層API。Web開發(fā)人員使用Activex控件,來為WMI數(shù)據(jù)構(gòu)造出基于Web的界面。另一個管理API是WMI腳本API,主要為了在基于腳本的應(yīng)用程序和Microsoft Visual Basic程序中使用。wMI的腳本支持也是為了所有的Microsoft程序設(shè)計語言技術(shù)而存在的。

如同針對管理應(yīng)用程序的情形一樣,WMICOM接口也是提供者最為基本的API。然而,不像管理應(yīng)用程序是COM客戶,提供者是COM或者分布式COM (DCOM)服務(wù)器(也就是說,提供者實現(xiàn)了COM對象,而WMI與這些COM對象打交道)。WMI提供者的可能實現(xiàn)方式有,被加載到WMI管理器進(jìn)程中的DLL、單獨(dú)的Windows應(yīng)用程序,或者Windows服務(wù)。Microsoft包含了許多內(nèi)置的提供者,它們供應(yīng)的數(shù)據(jù)來自于大家熟悉的各種數(shù)據(jù)源,比如性能API、注冊表事件管理器、活動目錄、SNMP和現(xiàn)代的設(shè)備驅(qū)動程序。WMISDK讓開發(fā)人員可以開發(fā)第三方WMI提供者。

4.4.2 提供者

WBEM的核心是DMTF設(shè)計的CIM規(guī)范。CIM規(guī)定了管理系統(tǒng)如何從系統(tǒng)管理的角度出發(fā)將一臺計算機(jī)的方方面面展示給一臺計算機(jī)上的應(yīng)用程序或者設(shè)備。提供者的開發(fā)人員使用CIM來表達(dá)那些“屬于他們想要管理的應(yīng)用中的各種部件”。開發(fā)人員使用“可管理對象的格式”(MOF,Managed Object Format)語言來實現(xiàn)CIM表示。

提供者除了定義一些類來表達(dá)對象以外,還必須作為接口將WMI與這些對象連接起來。WMI根據(jù)這些提供者所支持的接口特性對它們進(jìn)行分類。表4.12列出了WMI提供者的分類。注意,每個提供者可以實現(xiàn)一個或者多個特性,因此,舉例來說,一個提供者既可以是一個類,也可以是一個事件提供者。為了清楚地闡述表4.12中給出的特性定義,我們來看一個實現(xiàn)了其中多個特性的提供者。事件日志(Event Log)提供者支持幾個對象,包括事件日志計算機(jī)(EventLog Computer)、事件日志記錄 (Event Log Record)和事件日志文件 (Event Log File)。事件日志(Event Log)是一個實例 (Instance)提供者,因為它可以為它的類中的某幾個類定義多個實例。其中一個可以定義多個實例的類是事件日志文件類(Win32_NTEventlogFile);事件日志提供者為系統(tǒng)中的每一個事件日志 (即系統(tǒng)事件日志(System Event Log)、應(yīng)用事件日志Application Event Log]或安全事件日志(Security Event Log))定義該類的一個實例.

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
事件日志提供者定義了實例數(shù)據(jù),讓管理應(yīng)用程序可以枚舉它的記錄。為了使管理應(yīng)用程序可以使用WMI來備份和恢復(fù)事件日志文件,事件日志提供者為事件日志文件對象實現(xiàn)了備份和恢復(fù)方法。這樣做使得事件日志提供者成了一個方法(Method)提供者。最后,管理應(yīng)用程序可以注冊成:無論何時當(dāng)一條新的記錄被寫入到任一個事件日志中時它可以接收到通知。因此,當(dāng)事件日志提供者使用WMI事件通知機(jī)制來告訴WMI事件日志記錄已經(jīng)到達(dá)時,它就成了一個事件(Event)提供者。

4.4.3 公共信息模型(CIM)和可管理對象的格式語言

CIM也遵從像C++和Java這樣的面向?qū)ο笳Z言的步驟,在這些語言中,它們用類來表示問題中的數(shù)據(jù)。由于采用了類的概念,因而開發(fā)人員可以使用強(qiáng)大的建模技術(shù),包括繼承和復(fù)合。子類可以繼承父類的屬性,也可以增加自己的特征,還可以覆蓋從父類繼承過來的特征。如果一個類從另外一個類繼承了屬性或特征,則稱前一個類派生自后一個類。類也能夠復(fù)合起來:開發(fā)人員可以構(gòu)造一個包含了多個其他類的類。

DMTF提供了多個類,這些類作為WBEM標(biāo)準(zhǔn)的一部分。這些類是CIM的基本語言,它們表達(dá)了可適用于所有管理領(lǐng)域的對象。這些類是CIM核心模型的一部分。核心類的一個例子是CIM_ManagedSystemElement。該類包含少數(shù)幾個基本屬性,這些屬性標(biāo)識了像硬件設(shè)備這樣的物理組件,以及像進(jìn)程和文件這樣的邏輯組件。這樣的屬性有標(biāo)題、說明、安裝日期和狀態(tài)。所以,CIM_LogicalElement和CIM_PhysicalElement這兩個類都承了CIM_ManagedSystemElement類的屬性。這兩個類也是CIM核心模型的一部分。WBEM標(biāo)準(zhǔn)將這些類稱為抽象類,因為它們存在的唯一理由是為了讓其他的類繼承它們(也就是說,抽象類不存在對象實例)。因此,你也可以把抽象類想象成專門用于為其他類定義屬性的模板。

第二種類別的類表達(dá)了那些特定于管理領(lǐng)域但是與具體實現(xiàn)無關(guān)的對象。這些類構(gòu)成了公共模型 (common model),它們被認(rèn)為是核心模型的一個擴(kuò)展。公共模型類的一個例子是CIM_FileSystem類,它繼承了CIM_LogicalElement的屬性。因為幾乎每一個操作系統(tǒng),包括Windows、Linux和其他的UNIX變種,都依賴于基于文件系統(tǒng)的結(jié)構(gòu)化存儲,所以,CIM FileSystem類是公共模型中一個很恰當(dāng)?shù)慕M成部分。

最后一種類別的類是擴(kuò)展模型 (extended model),它是由一些附加于公共模型之上的與特定技術(shù)相關(guān)的類組成的。Windows定義了一大堆這種類來表達(dá)那些特定于Windows環(huán)境的對象。因為所有的操作系統(tǒng)都將數(shù)據(jù)存儲在文件中,所以,CIM公共模型包含了CIM_LogicalFile類。CIM_DataFile類繼承了CIM_LogicalFile類,另外,Windows針對其頁面文件和快捷文件這兩種文件類型分別加入了Win32PageFile和Win32 ShortcutFile文件類。

事件日志 (Event Log)提供者大量地使用了繼承技術(shù)。圖4.19顯示了WMI CIM Studio的一個視圖,這是隨WMIAdministrative Tools一起帶的類瀏覽器(你可以從Microsoft Web站點(diǎn)的Microsoft下載中心獲得WMIAdministrative Tools)。你可以看到,事件日志提供者依賴于繼承技術(shù),該提供者的Win32_NTEventlogFile類派生自CIM_DataFile。事件日志文件是具有額外特性的數(shù)據(jù)文件,這些額外的特性與事件日志有關(guān),比如日志文件名(LogfileName)、該文件所包含的記錄的數(shù)量 (NumberOfRecords)。類瀏覽器中顯示的樹反映了Win32 NTEventlogFile具有多層繼承性:CIM_DataFile派生自CIM_LogicalFile,而CIM_LogicalFile又派生自CIM_LogicalElement.而CIM LogicalElement又派生自CIM_ManagedSystemElement。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
正如前面所述,WMI提供者的開發(fā)人員使用MOF語言來編寫他們的類。下面的輸出顯示了事件日志提供者的Win32_NTEventlogFile類的定義,圖4.19中選中的正是其父類CIM_DataFile.請注意觀察,圖4.19中右邊窗格中列出的屬性,和下面的MOF文件中這些屬性的定義,兩者是怎樣關(guān)聯(lián)起來的。CIM Studio使用黃色的箭頭來標(biāo)記那些因繼承而得來的屬性。因此,你在Win32_NTEventlogFile的定義中看不到這些特定的屬性。

dynamic: ToInstanceprovider(“MS NT EVENTLOC PROVIDER”),Locale(1033),UUID(“[8502C57B-5FBB-11D2-AAC1-006008C78BC73”)]class Win32_NTEventlogFile:CIM DataFile
[read] string LogfileName;[read,write] uint32 MaxFileSize;
[read] uint32 NumberOfRecords;volatie,VaTueMap[“0”“1…365"“4294967295]] string OverWritePolicy;read,write,Units(“Days"),Range(“0-365 4294967295”)] uint32 0verwrite0utDated;read.[read] string Sources[];[implemented,Privileges{“SeSecurityPrivilege”,“SeBackupPrivilege”]] uint32 CTearEvent1og([in] string ArchiveFileName);[implementedPrivileges[“SeSecurityPrivilege”,“SeBackupPrivilege”]] uint32 BackupEventloa([in] string ArchiveFileName);

一個值得再看一看的術(shù)語是dynamic(動態(tài)的),這是出現(xiàn)在上面MOF文件中的、用于說明Win32_NTEventlogFile類的指示符。Dynamic意味著,當(dāng)一個管理應(yīng)用程序查詢該類的對象的屬性時,WMI基礎(chǔ)設(shè)施向WMI提供者詢問與該對象相關(guān)聯(lián)的屬性的值。Static(靜態(tài)的)類是指WMI倉庫中的類:WMI基礎(chǔ)設(shè)施引用WMI倉庫就可以得到這些值,無須向提供者詢問這些值。因為更新WMI倉庫是一個相對昂貴的操作,所以,對于那些屬性頻繁變化的對象,動態(tài)提供者更加高效。

在WMI開發(fā)人員使用MOF編寫了他們的類以后,他們可以通過幾種不同的方式將這些類定義提供給WMI。WDM驅(qū)動程序的開發(fā)人員可將一個MOF文件編譯成一個二進(jìn)制MOF(BMF)文件(這是一種比MOF文件更加緊湊的二進(jìn)制表示形式),并且可以選擇動態(tài)地將BMF文件交給WDM基礎(chǔ)設(shè)施,或者靜態(tài)地在二進(jìn)制文件中包含BMF文件。另一種方法是,提供者編譯該MOF文件,并且通過WMICOMAPI,將這些定義交給WMI基礎(chǔ)設(shè)施。最后一種做法是,提供者可以使用MOF編譯器(Mofcompexe)工具,將一個類編譯之后的表示形式直接交給WMI基礎(chǔ)設(shè)施。

4.4.6 WMI名字空間

類定義了對象的屬性,而對象則是一個系統(tǒng)上的類實例。WMI使用一個名字空間將對象組織起來,該名字空間包含了幾個子名字空間,WMI按照層次結(jié)構(gòu)來組織對象。管理應(yīng)用程序在訪問一個名字空間中的對象以前,必須先與該名字空間建立連接。

WMI將名字空間的根目錄命名為root。所有安裝的WMI都有四個預(yù)定義的名字空間,它們位于root的下面,分別是CIMV2、Default、Security和WMI。在這四個名字空間中,有的還有其他的名字空間。例如,CIMV2包含了Applications和ms_209這兩個名字空間作為自己的子名字空間。有時候提供者還定義了它們自己的名字空間。你可以在Windows中看到root下面的WMI名字空間(這是Windows設(shè)備驅(qū)動程序WMI提供者定義的名字空間)。

不像文件系統(tǒng)的名字空間是由目錄和文件的層次結(jié)構(gòu)構(gòu)成的,WMI名字空間只有一層的深度。不像文件系統(tǒng)那樣使用名稱來標(biāo)識文件或目錄,WMI使用對象的屬性來標(biāo)識對象,它把這些屬性定義成鍵(key)。管理應(yīng)用程序指定類名和鍵名來找到一個名字空間內(nèi)部的特定對象。因此,一個類的每個實例必須由它的鍵值唯一標(biāo)識。例如,事件日志提供者使用Win32_NTLogEvent類來表達(dá)事件日志中的記錄。該類有兩個鍵:一個是字符串Logfile,另一個是無符號整數(shù)RecordNumber。如果一個管理應(yīng)用程序要向WMI查詢事件日志記錄的實例,則它可以利用能標(biāo)識出這些記錄的鍵對來獲得它們。應(yīng)用程序利用特定的語法來引用一條記錄,在下面的例子對象路徑名中你可以看到這種語法:\DARYL\root\CIMV2:Win32_NTLogEvent.Logfile="Application"RecordNumber=“1”
該名稱中的第一個部件(\DARYL)標(biāo)識了該對象所在的計算機(jī),第二個部件(\root\CIMV2)是該對象所處的名字空間。類名緊跟在冒號的后面,鍵名和對應(yīng)的值跟在句點(diǎn)的后面。逗號把兩個鍵值分隔開。

WMI提供了一些接口讓應(yīng)用程序可以枚舉一個特定類中所有的對象,也可以發(fā)出一些查詢請求以獲得某個類中符合查詢條件的那些實例。

4.4.7 類關(guān)聯(lián)

許多對象類型彼此之間以某種方式聯(lián)系在一起。例如,一個計算機(jī)對象可以有一個處理器、軟件、一個操作系統(tǒng)、活動的進(jìn)程,等等。WMI允許提供者構(gòu)造一個關(guān)聯(lián)類 (associationclass)來表達(dá)兩個不同的類之間的邏輯連接。關(guān)聯(lián)類將一個類與另一個類關(guān)聯(lián)起來,所以,這樣的類僅有兩個屬性:一個類名和Ref修飾符。

下面的輸出顯示了一種關(guān)聯(lián):

  • 事件日志提供者的MOF文件將Win32_NTLogEvent類與Win32_ComputerSystem類關(guān)聯(lián)起來。給定一個對象,管理應(yīng)用程序可以查詢與其關(guān)聯(lián)的對象。按照這種方式,提供者可以定義出一個對象層次結(jié)構(gòu)。

[dynamic: ToInstanceprovider(“MS_NT_EVENTLOG_PROVIDER”): ToInstance EnumPrivilegeslSeSecurityPrivilege"}:ToSubCTass,Locale(1033): ToInstance,UUID"[8502C57F-5FBB-11D2-AAC1-006008C78BC7}"):ToInstance,Association: DisableOverride ToInstance ToSubClass]class Win32_NTLogEventComputer
ToSubCTass] Win32 ComputerSystem ref Computer;[key,read:[key,read: ToSubClass] Win32_NTLogEvent ref Record;

圖4.20 顯示了WMI對象瀏覽器(WMIAdministrativeTools包含的另一個工具),它顯示的是CIMV2名字空間中的內(nèi)容。Windows的系統(tǒng)組件往往把它們的對象放在CIMV2名字空間中。對象瀏覽器首先找到Win32 ComputerSystem對象的實例ALEX-LAPTOP,這是代表整個計算機(jī)的對象。然后,對象瀏覽器獲得那些與Win32 ComputerSvstem相關(guān)聯(lián)的對象,并且將它們顯示在ALEX-LAPTOP的下面。對象瀏覽器的用戶界面利用雙箭頭文件夾圖標(biāo)來顯示關(guān)聯(lián)對象。關(guān)聯(lián)類類型的對象顯示在該文件夾的下面。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
在對象瀏覽器中可以看到,事件日志提供者的關(guān)聯(lián)類Win32_NTLogEventComputer位于ALEX-LAPTOP的下面,同時,Win32_NTLogEvent類有大量的實例。參照前面的代碼輸出,可以驗證MOF文件定義了Win32NTLogEventComputer 類,將Win32 ComputerSystem 與Win32_NTLogEvent類關(guān)聯(lián)起來。在對象瀏覽器中選擇Win32_NTLogEvent的一個實例,可以在右側(cè)窗格的Properties表單中看到該類的屬性。雖然Microsoft提供對象瀏覽器的用意是幫助WMI開發(fā)人員檢查他們的對象,但是管理應(yīng)用程序也可以執(zhí)行同樣的操作,并以一種更加易于理解的方式來顯示對象的屬性或者收集到的信息。

4.4.8 WMI實現(xiàn)

WMI服務(wù)運(yùn)行在一個共享的Svchost進(jìn)程中,它在本地系統(tǒng)賬戶下執(zhí)行。它將提供者加載到一個專門宿納提供者的進(jìn)程Wmiprvse.exe中。Wmiprvse.exe作為RPC服務(wù)進(jìn)程的一個子進(jìn)程被激發(fā)起來。WMI在本地系統(tǒng)賬戶、本地服務(wù)賬戶或者網(wǎng)絡(luò)服務(wù)賬戶下執(zhí)行Wmiprvse,具體在哪個賬戶下取決于代表此提供者實現(xiàn)的WMI Win32Provider對象實例的HostingModel屬性值。當(dāng)該提供者被從緩存中移除以后,也就是說,它接收到最后一個提供者請求以后一分鐘,Wmiprvse進(jìn)程即退出。

大多數(shù)WMI組件在默認(rèn)情況下駐留在%SystemRoot%System32和%SystemRoot%\System32Wbem下,包括Windows MOF文件、內(nèi)置的提供者DLL和管理應(yīng)用程序WMI DLL。打開%SystemRoot%(ystem32Wbem目錄,你可以找到Ntevt.mof,這是事件日志提供者的MOF文件。你還可以找到Ntevt.dll,這是事件日志提供者的DLL文件,WMI服務(wù)要用到它。

%SystemRoot%\System32 Wbem下面的目錄存放了倉庫文件日志文件和第三方的MOF文件。WMI使用一個私有版本的Microsoft JET數(shù)據(jù)庫引警來實現(xiàn)它的倉庫,即CIMOM對象倉庫此數(shù)據(jù)庫文件默認(rèn)駐留在%SystemRoot%\System32Wbem\Repository中。
WMI用到了大量的注冊表設(shè)置存放在該服務(wù)的HKLMSOFTWARE\Microsoft\WBEMCIMOM注冊表鍵中,比如某些特定參數(shù)的閥值和最大值。

設(shè)備驅(qū)動程序使用特殊的接口來向WMI提供數(shù)據(jù),以及接受來自WMI的命令(稱為WMI系統(tǒng)控制命令)。這些接口是WDM的一部分,本書下冊第8章“I/0系統(tǒng)”將會講述。因為這些接口是跨平臺的,所以它們位于\root\wMI名字空間的下面。

WMIC
Windows包含了一個工具Wmic.exe,它使得你可以從一個能感知WMI的命令行外殼中與WMI進(jìn)行交互。所有的WMI對象和它們的屬性,也包括它們的方法,都可以通過該外殼進(jìn)行訪問,這使得WMIC成為一個高級的系統(tǒng)管理控制臺。

4.4.9 WMI安全性

WMI在名字空間層次上實現(xiàn)了安全性。如果一個管理應(yīng)用程序成功地連接到一個名字空間,那么,此應(yīng)用程序可以查看和訪問該名字空間中所有對象的屬性。管理員可以使用WMIControl應(yīng)用程序來控制哪些用戶可以訪問哪個名字空間。在內(nèi)部,這一安全模型是通過ACL和安全描述符來實現(xiàn)的,這是在Windows平臺上實現(xiàn)訪問檢查的標(biāo)準(zhǔn)安全模型的一部分。(有關(guān)訪問檢查的更多信息,請參見第6章。)
為了啟動WMIControl應(yīng)用程序,你可以從Start菜單中,選擇ControlPanel,再選擇SystemAnd Maintenance,Administrative Tools,以及Computer Management。接下來,打開Services AndApplications分支,右鍵單擊WMI Control,選擇Properties,就可以激發(fā)WMI Control Properties對話框,如圖4.21所示。為了配置名字空間的安全性,單擊Security標(biāo)簽頁,選中一個名字空間,再單擊Security。通過WMIControlProperties對話框中的其他標(biāo)簽頁,你可以修改注冊表中存儲的性能和備份設(shè)置。

[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.5 Windows診斷基礎(chǔ)設(shè)施(WDI)

Windows診斷基礎(chǔ)設(shè)施 (WDl,Windows Diagnostic infrastructure)可以幫助在用戶干預(yù)最小的情況下檢測、診斷和解決一些常見的問題場景。Windows組件實現(xiàn)了一些觸發(fā)器,通過這些觸發(fā)器讓W(xué)DI激發(fā)起與特定場景有關(guān)的診斷模塊,以檢測到一個問題場景的存在。觸發(fā)器可以指示出當(dāng)前系統(tǒng)正在接近或者已經(jīng)到達(dá)了一種有問題的狀態(tài)。一旦診斷模塊識別出一個根原因(rootcause),它可以調(diào)用一個問題解決器 (problemresolver)來處理該問題。解決方案可能很簡單,比如改變一個注冊表設(shè)置,或者與用戶進(jìn)行交互以便完成恢復(fù)步驟或者配置改變。最后,WDI的主要角色是,為Windows組件提供一個統(tǒng)一的框架來完成各種與自動化的問題檢測、診斷和解決相關(guān)的任務(wù)。

4.5.1 WDI設(shè)施

Windows或者應(yīng)用程序組件必須加入專門的設(shè)施代碼,以便當(dāng)一個問題場景發(fā)生的時候可以通知WDI。這些組件可以以同步方式等待診斷的結(jié)果,或者繼續(xù)操作,讓診斷過程以異步方式進(jìn)行。WDI實現(xiàn)了兩種不同類型的設(shè)施API(instrumentation API)來支持這些模型:

基于事件的診斷,這可以用于最小侵入式的診斷設(shè)施方案,它可以被加入到一個組件中,而無須對該組件的實現(xiàn)做任何改變。WDI支持兩種基于事件的診斷:簡單的場景和啟動-停止場景。在簡單的場景中,代碼中的一個點(diǎn)對失敗負(fù)責(zé)任,并且升起(raise一個事件來觸發(fā)診斷過程。在啟動-停止場景中,整個代碼路徑都被認(rèn)為是危險的,因而被加上設(shè)施代碼以便于診斷。在場景的開始處,向一個名為DiagLog的實時ETW(Event Tracing for Windows)會話升起一個事件;同時,一個稱為場景事件映射器(SEM,Scenario Event Mapper)的內(nèi)核設(shè)施允許一組額外的ETW痕跡記錄到WDI環(huán)境日志記錄器中。第二個事件也被升起來,以標(biāo)志該診斷場景的結(jié)束,此時SEM禁止詳細(xì)的日志跟蹤。這種“即時跟蹤”機(jī)制使得詳細(xì)跟蹤的性能開銷盡可能地小,同時又保持足夠的環(huán)境信息,以便WDI在無須重現(xiàn)一個問題的情況下找到根原因(若一個失敗應(yīng)該發(fā)生的話)。
按需診斷,允許應(yīng)用程序根據(jù)它們的需要來請求診斷、參與診斷、當(dāng)診斷完成的時候接收通知,并根據(jù)診斷的結(jié)果來調(diào)整它的行為。當(dāng)診斷過程需要在特權(quán)安全環(huán)境中執(zhí)行的時候,按需診斷設(shè)施尤為有用。WDI可以在跨越信任和進(jìn)程邊界時對執(zhí)行環(huán)境的轉(zhuǎn)換加上代碼設(shè)施,它也支持在必要時候模仿調(diào)用者。

4.5.2 診斷策略服務(wù)

診斷策略服務(wù)(DPS,Diagnostic Policy Service,%SystemRoot%System32Dps.dll)實現(xiàn)了絕大多數(shù)WDI場景的后端。DPS是一個多線程的服務(wù)(運(yùn)行在vchost中),它接受按需場景的診斷請求,也監(jiān)視和守護(hù)著通過DiagLog遞交的診斷事件。(看圖4.22,它顯示了DPS和其他的WDI關(guān)鍵組件之間的關(guān)系。 作為對這些請求的響應(yīng),DPS激發(fā)起適當(dāng)?shù)脑\斷模塊,該模塊融合了特定領(lǐng)域的知識,比如如何找到一個網(wǎng)絡(luò)問題的根原因。而且,DPS使所有與該場景有關(guān)的環(huán)境信息,讓診斷模塊通過所捕獲的痕跡數(shù)據(jù)加以使用。診斷模塊對這些數(shù)據(jù)執(zhí)行一次自動化的分析,也可以請DPS激發(fā)第二個稱為解決器的模塊,由它負(fù)責(zé)解決該問題,如果可能的話悄悄地進(jìn)行。
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制
DPS也為診斷場景控制和強(qiáng)制應(yīng)用組策略設(shè)置。你可以使用組策略編輯器(Group PolicyEditor,%SystemRoot%System32\Gpedit.msc)來配置診斷選項和自動恢復(fù)選項的設(shè)置信息。你可以通過 Computer Configuration 、Administrative Templates、System 、Troubleshooting And Diagnostics來訪問這些設(shè)置,如圖4.23所示
[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制

4.5.2 診斷功能

Windows實現(xiàn)了幾個內(nèi)置的診斷場景和一些工具。下面是一些例子:

  • 磁盤診斷功能,包含了存儲類驅(qū)動程序(%SystemRoot%\System32\Driver\Classpnp.sys)0中“Self-MonitoringAnalysis and Reporting Technology(SMART)”的代碼,監(jiān)視磁盤的健康情況。當(dāng)檢測到即將發(fā)生磁盤失敗以后,WDI通知和指導(dǎo)用戶。而且,Windows監(jiān)視由關(guān)鍵系統(tǒng)文件的磁盤破壞而引起的應(yīng)用程序崩潰。此診斷功能利用Windows的文件保護(hù)機(jī)制,自動地從備份緩存(若可能的話)恢復(fù)這些已被損壞的系統(tǒng)文件有關(guān)Windows存儲管理的更多信息,請參見本書下冊第9章“存儲管理”。

  • 網(wǎng)絡(luò)診斷功能,擴(kuò)展了WDI,能處理不同種類的與網(wǎng)絡(luò)相關(guān)的問題,比如文件共享.Internet訪問、無線網(wǎng)絡(luò)、第三方防火墻和一般的網(wǎng)絡(luò)連接問題。有關(guān)網(wǎng)絡(luò)的更多信息,參見第7章“網(wǎng)絡(luò)”。

  • 資源耗盡保護(hù),包括Windows內(nèi)存泄漏診斷,以及Windows資源耗盡檢測和解決。這些診斷功能可以檢測到何時內(nèi)存的提交限制值已經(jīng)到達(dá)它的最大值,并提醒用戶出現(xiàn)了這樣的情況,包括消耗內(nèi)存和資源最多的一些消費(fèi)者。然后,用戶可以選擇終止這些應(yīng)用程序以試圖釋放一些資源。有關(guān)內(nèi)存提交限制和虛擬內(nèi)存的更多信息,參見本書下冊第10章“內(nèi)存管理”。

  • Windows內(nèi)存診斷工具,可以由用戶在啟動時候從引導(dǎo)管理器中手工執(zhí)行,或者在-次系統(tǒng)崩潰之后(分析的結(jié)果是,可能是由于RAM錯誤而導(dǎo)致崩潰)由WER(WindowsErrorReporting,Windows錯誤報告)自動推薦執(zhí)行。有關(guān)引導(dǎo)過程的更多信息,參見本書下冊第13章。

  • Windows啟動修復(fù)工具,試圖自動地修復(fù)某些常見的不能引導(dǎo)系統(tǒng)的錯誤類型,比如不正確的BCD設(shè)置、損壞的磁盤結(jié)構(gòu)(比如MBR或引導(dǎo)扇區(qū)),以及錯誤的驅(qū)動程序。當(dāng)系統(tǒng)引導(dǎo)不成功的時候,引導(dǎo)管理器自動地激發(fā)起啟動修復(fù)工具《若已經(jīng)安裝的話),它也包含手工的恢復(fù)選項,以及可以訪問命令提示環(huán)境。有關(guān)啟動修復(fù)工具的更多信息,參見本書下冊第13章。

  • Windows性能診斷功能,包括Windows引導(dǎo)性能診斷、Windows停機(jī)性能診斷、Windows待機(jī)/恢復(fù)性能診斷,以及Windows系統(tǒng)響應(yīng)性能診斷。根據(jù)特定的時間閥值和這些機(jī)制的內(nèi)部行為期望,windows可以檢測到由于很慢的性能而引起的問題并且將問題記錄在事件日志中,繼而WDI使用這些日志來提供解決方案和運(yùn)行步驟供用戶修復(fù)相應(yīng)的問題。

  • 程序兼容性助手(PCA,Program Compatibility Assistant),使遺留的應(yīng)用程序可以在新版本的Windows上執(zhí)行而不用擔(dān)心兼容性問題。PCA檢測到由于版本檢查不匹配而引起的應(yīng)用程序安裝失敗,以及由于不正確的二進(jìn)制文件和用戶賬戶控制 (UAC)設(shè)

置而引起的運(yùn)行時失敗。PCA試圖從這些失敗中恢復(fù)過來,其做法是,為應(yīng)用程序使用適當(dāng)?shù)募嫒菪栽O(shè)置,這些設(shè)置將在應(yīng)用程序下次運(yùn)行的時候生效。而且,PCA維護(hù)了一個程序數(shù)據(jù)庫,其中的程序都有已知的兼容性問題,并且,PCA在程序啟動的時候?qū)⑦@些潛在的問題通知用戶。

4.6 本章總結(jié)

到現(xiàn)在為止,我們已經(jīng)討論了Windows的總體結(jié)構(gòu),以及該結(jié)構(gòu)賴以建立起來的各種核心系統(tǒng)機(jī)制,還討論了各種核心管理機(jī)制。有了這些基礎(chǔ)作為準(zhǔn)備,我們現(xiàn)在可以更加詳細(xì)地挖掘單獨(dú)的執(zhí)行體組件,首先從進(jìn)程和線程開始這一挖掘歷程。文章來源地址http://www.zghlxwxcb.cn/news/detail-444848.html

實驗

實驗:在一個空閑的系統(tǒng)上查看注冊表的活動

實驗:使用進(jìn)程監(jiān)視器來找到應(yīng)用程序的注冊表設(shè)置

實驗:離線方式或遠(yuǎn)程編輯BCD

實驗:觀察輪廓加載和卸載

實驗:手工加載和卸載儲巢

實驗:查看儲巢句柄

實驗: 查看儲巢換頁池的使用量

實驗:查看鍵控制塊

實驗:查看服務(wù)所要求的特權(quán)

實驗:查看進(jìn)程中運(yùn)行的服務(wù)

實驗:查看UBPM觸發(fā)器提供者

實驗:查看哪些服務(wù)響應(yīng)哪些觸發(fā)器

實驗:查看WMI類的MOF定義

實驗:查看WMI名字空間

實驗:使用WMI腳本來管理系統(tǒng)

實驗:查看Wmiprvse的創(chuàng)建

總結(jié)

到了這里,關(guān)于[筆記]深入解析Windows操作系統(tǒng)《四》管理機(jī)制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Linux操作系統(tǒng)】深入探索Linux進(jìn)程:創(chuàng)建、共享與管理

    【Linux操作系統(tǒng)】深入探索Linux進(jìn)程:創(chuàng)建、共享與管理

    進(jìn)程的創(chuàng)建是Linux系統(tǒng)編程中的重要概念之一。在本節(jié)中,我們將介紹進(jìn)程的創(chuàng)建、獲取進(jìn)程ID和父進(jìn)程ID、進(jìn)程共享、exec函數(shù)族、wait和waitpid等相關(guān)內(nèi)容。 在Linux系統(tǒng)中,進(jìn)程的創(chuàng)建使用 fork() 系統(tǒng)調(diào)用。 fork() 系統(tǒng)調(diào)用會創(chuàng)建一個與當(dāng)前進(jìn)程相同的子進(jìn)程,子進(jìn)程會復(fù)制父進(jìn)

    2024年02月12日
    瀏覽(34)
  • 云原生之深入解析分布式存儲系統(tǒng)Ceph的環(huán)境部署和實戰(zhàn)操作

    云原生之深入解析分布式存儲系統(tǒng)Ceph的環(huán)境部署和實戰(zhàn)操作

    ① 什么是 Ceph ? Ceph 是當(dāng)前非常流行的開源分布式存儲系統(tǒng),具有高擴(kuò)展性、高性能、高可靠性等優(yōu)點(diǎn),同時提供塊存儲服務(wù)(rbd)、對象存儲服務(wù)(rgw)以及文件系統(tǒng)存儲服務(wù)(cephfs),Ceph 在存儲的時候充分利用存儲節(jié)點(diǎn)的計算能力,在存儲每一個數(shù)據(jù)時都會通過計算得出該數(shù)據(jù)

    2024年02月09日
    瀏覽(24)
  • Windows 操作系統(tǒng)下 Python 及其模塊的管理

    Windows 操作系統(tǒng)下 Python 及其模塊的管理

    Python 是一款解釋型語言,理論上一個.py文件可以當(dāng)成一個稍微復(fù)雜一些的字符串指令集 本文不涉及jupyter,VS,VScode,Pycharm 等集成開發(fā)環(huán)境,這不是我們這篇文章所關(guān)心的東西 這篇文章面向的是Python 的初學(xué)者? 最近沒有寫太多長文章,多寫幾篇,開學(xué)了沒時間了 首先,在Win 操

    2024年02月10日
    瀏覽(42)
  • 【W(wǎng)indows系統(tǒng)編程】01.文件與目錄操作-筆記

    本專欄從這篇文章開始做Windows系統(tǒng)編程的筆記,本章主要講解:Windows文件操作(讀寫文件,刪除文件,拷貝文件,移動文件等),目錄操作(遍歷目錄,刪除目錄等)硬盤的一點(diǎn)小知識。 參考書:Windows核心編程 上述的是沒有錯誤的操作流程 如果文件已經(jīng)存在的話,就會出

    2024年02月13日
    瀏覽(21)
  • 【一些隨筆】淺析 Linux和Windows:系統(tǒng)介紹、操作差異與使用技巧解析

    【一些隨筆】淺析 Linux和Windows:系統(tǒng)介紹、操作差異與使用技巧解析

    Linux和Windows系統(tǒng)的操作差異; Linux系統(tǒng)介紹、系統(tǒng)監(jiān)控和優(yōu)化技巧、Shell腳本編程技巧、一些命令使用技巧; Windows系統(tǒng)介紹、優(yōu)化和加速技巧、一些在Windows系統(tǒng)下常用的快捷鍵; 在使用Linux和Windows時,有一些事情可能在Linux上較為順理成章,而在Windows上可能令人費(fèi)解。比如

    2024年02月13日
    瀏覽(19)
  • 軟考學(xué)習(xí)筆記--操作系統(tǒng)-進(jìn)程管理

    軟考學(xué)習(xí)筆記--操作系統(tǒng)-進(jìn)程管理

    進(jìn)程管理是一個具有獨(dú)立功能的程序關(guān)于數(shù)據(jù)集合的一次可以并發(fā)執(zhí)行的運(yùn)行活動,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位。相對于程序,進(jìn)程是動態(tài)的概念,而程序是靜態(tài)的概念,是指令的集合。進(jìn)程具有動態(tài)性和并發(fā)性,需要一定的資源來完成任務(wù)。在大多數(shù)操作系統(tǒng)中

    2024年01月18日
    瀏覽(92)
  • 操作系統(tǒng)-筆記-第三章-內(nèi)存管理

    操作系統(tǒng)-筆記-第三章-內(nèi)存管理

    一、第一章——操作系統(tǒng)的概念 二、第二章——【進(jìn)程】 二、第二章——【線程】?編輯 二、第二章——【進(jìn)程調(diào)度】 二、第二章——【進(jìn)程同步與互斥】 二、第二章——【鎖】 三、第三章——內(nèi)存管理 四、第四章——文件管理 五、第五章——輸入輸出管理 ?????

    2024年02月11日
    瀏覽(89)
  • 操作系統(tǒng)-筆記-第五章-輸入輸出管理

    操作系統(tǒng)-筆記-第五章-輸入輸出管理

    一、第一章——操作系統(tǒng)的概念 二、第二章——【進(jìn)程】 二、第二章——【線程】?編輯 二、第二章——【進(jìn)程調(diào)度】 二、第二章——【進(jìn)程同步與互斥】 二、第二章——【鎖】 三、第三章——內(nèi)存管理 四、第四章——文件管理 五、第五章——輸入輸出管理 ???學(xué)習(xí)心

    2024年02月11日
    瀏覽(29)
  • 操作系統(tǒng)-筆記-第四章-文件管理

    操作系統(tǒng)-筆記-第四章-文件管理

    一、第一章——操作系統(tǒng)的概念 二、第二章——【進(jìn)程】 二、第二章——【線程】?編輯 二、第二章——【進(jìn)程調(diào)度】 二、第二章——【進(jìn)程同步與互斥】 二、第二章——【鎖】 三、第三章——內(nèi)存管理 四、第四章——文件管理 五、第五章——輸入輸出管理 ???學(xué)習(xí)心

    2024年02月11日
    瀏覽(19)
  • 【操作系統(tǒng)學(xué)習(xí)筆記】文件管理1.5

    參考書籍: 王道考研 視頻地址: Bilibili 邏輯結(jié)構(gòu): 從用戶角度看,由創(chuàng)建文件的用戶自己設(shè)計的 無結(jié)構(gòu)文件 有結(jié)構(gòu)文件 順序文件 順序存儲 鏈?zhǔn)酱鎯?索引文件 索引順序文件 物理結(jié)構(gòu): 從操作系統(tǒng)看,由操作系統(tǒng)決定 連續(xù)分配 鏈接分配 索引分配

    2024年03月09日
    瀏覽(105)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包