Windows Via C/C++:vista下的應用程序權限控制

when adminstrator runs as a standard user

Windows Vista採用了一些新技術提高了終端用戶使用系統時的安全性。其中對開發人員而言最爲重要的應該是用戶賬戶控制(User Account Control - UAC)。

在Windows系統中,許多用戶都是以管理員賬號登錄的,由於管理員賬號被賦於極高的權限,因此以該賬號登錄的用戶幾乎可以不受限制的訪問所有系統資源。用戶以管理員身份登錄Vista之前的系統時,系統會爲其創建一個安全令牌(security token)用於訪問受保護的系統資源。安全令牌將和用戶啓動的所有新進程相關聯,首先是Windows Explorer(資源管理器,登錄後系統自動創建,用戶創建的所有進程幾乎都是其子進程),然後是Windows Explorer的所有後代進程。在這種情形下,用戶從網上下載或來自郵件的惡意腳本進程會繼承管理員用戶的所有權限,因此它可以隨心所欲的對系統進行更改或者創建擁有同樣權限的惡意進程。

當用戶登錄Vista時,除了和該用戶關聯的安全令牌,系統會創建一個只擁有標準用戶權限的過濾令牌(filtered token)並將其與當前用戶關聯。該過濾令牌將和用戶啓動的所有新進程(包括Windows Explorer)關聯,也就是說,當前用戶使用的實際上是隻擁有標準用戶權限的過濾令牌。那麼,與過濾令牌關聯的進程如何訪問需要更高權限的受限資源呢?答應是:沒有任何方法可以做到這一點,除了使用用戶賬戶控制(UAC),本節下面的內容將討論開發人員如何利用UAC實現用戶權限的提升。

首先,你可以請求操作系統提升權限,但這隻能發生在進程邊界。也就是說,進程開始運行時會默認和當前用戶的過濾令牌關聯。如果要提升進程權限,在進程開始運行之前,你需要告訴操作系統獲取用戶對此次權限提升操作的確認信息。終端用戶可以使用右鍵菜單中的“以管理員身份運行”(Run as Administrator)命令提升應用的權限,如下圖所示:

如果當前用戶以管理員身份登錄,執行Run as Administrator命令時Windows會彈出一個確認對話框,要求用戶確認此次權限提升操作。確認對話框的種類有三種。如果要提升權限的應用是操作系統的一部分,會出現下面的對話框:

如果應用是經過簽名的,會出現下面的對話框:

如果應用是未簽名的,會出現下面的對話框:

如果當前用戶是以標準用戶身份登錄的,在執行Run as Administrator命令時,還會彈出對話框要求用戶輸入管理員賬號和密碼,允許標準用戶在某些情況下執行需要更高權限的任務,這種機制被稱爲“越肩登錄”(over-the-shoulder logon)。 

除了Windows Explorer在其右鍵菜單中提供的Run as Administrator命令,我相信你應該注意到Vista中的許多鏈接和按鈕旁邊都有一個盾形圖標,這意味着單擊鏈接/按鈕時將進行權限提升動作,通常會彈出上面列出的確認對話框。本章最後的例子The process information展示瞭如何在你自己的應用程序按鈕上添加盾形圖標。

讓我們來看一個簡單的例子,當打開任務管理器時,你會發現盾形圖標顯示在“顯示所有用戶進程”按鈕上:

記下此時任務管理器進程(taskmgr.exe)的進程ID,然後點擊“顯示所有用戶進程”並確認,這時任務管理器會消失一會兒並重新顯示,並且一個複選框代替了原來的盾形圖標:

現在再來觀察任務管理器(taskmgr.exe)的進程ID,我們會發現它和權限提升操作之前的ID並不相同。這是否說明權限提升之後的任務管理器並非是原先的任務管理器進程,而是一個新的實例呢?是的,答案永遠是“是的”:Windows只會在進程邊界提升進程的權限,進程一旦開始執行,就沒有任何方法可以獲得更高權限了。然而,權限較低的進程可以產生一個擁有較高權限的子進程,子進程中含有COM服務端,這樣父子進程之間就可以進行進程間通信,而不用重新啓動應用的新實例。

自動提升進程權限

如果你的應用總是需要管理員級別的權限,比如安裝程序運行時如需要訪問受限資源,系統會自動提示用戶當前應用需要提升權限。那麼Windows的UAC組件依據什麼來提示用戶呢?如果應用的可執行文件中內嵌了指定的清單資源(RT_MANIFEST),系統將在其中尋找節點並解析其內容,下面是含有節點清單的例子:
level屬性的取值見下表(表4-8):

除了在可執行文件中嵌入清單資源,還可以將其存放在可執行文件所在的文件夾中,其文件名與可執行文件名相同,只是後綴改爲manifest。外部清單文件必須在進程開始執行前就配置好,否則不會起作用。同樣,可執行文件中內嵌的清單資源會屏蔽外部清單文件。

手動提升進程權限

在前面章節中討論的CreateProcess並沒有提供任何手動提升進程權限的能力,但是ShellExecuteEx可以做到這一點:

使用ShellExecuteEx提升權限時,必須將其參數的lpVerb成員設置爲“runas”,lpFile必須是要以管理員權限執行的可執行文件的路徑,下面的代碼段展示了這一點:
上面代碼執行時會彈出權限提升確認對話框,如果用戶拒絕操作,ShellExecuteEx會返回FALSE,GetLastError會返回ERROR_CANCELLED。

注意如果進程以管理員權限開始運行,它調用CreateProcess創建的所有子進程都會繼承其父進程的權限——也就是說沒有必要再次調用ShellExecuteEx提升子進程的權限了。假如進程運行時依然使用過濾令牌,當其調用CreateProcess創建需要更高權限的子進程時,CreateProcess調用會失敗並且GetLastError返回ERROR_ELEVATION_REQUIRED。

開發Windows Vista下的應用時,大多數情況下你應該將應用設計爲只需要使用標準用戶權限,如果需要提升權限,你應該在激發高權限任務的UI處添加盾形圖標。由於提升進程權限是通過執行新進程完成的,你應該把所有需要更高權限的操作都置於另一個應用中,並使用ShellExecuteEx打開它。本章最後的例子Process Information將展示這一做法。

判斷當前權限上下文

在上文任務管理器的例子中,任務管理器運行在標準用戶權限和管理員權限上下文中時,它會分別顯示一個盾形圖標和一個複選框——在不同的權限上下文中,應用程序的行爲是不同的,那麼如何確定當前權限上下文是標準用戶模式還是管理員模式呢?

下面的GetProcessElevation函數返回當前進程的原始權限類型以及是否以管理員權限運行當前進程:

代碼中用到了GetTokenInformation,其第1個參數用來標識目標令牌的句柄,第2個參數指定爲TokenElevationType時表示函數將取得目標令牌的權限信息,並寫入到pElevationType參數中,pElevationType是TOKEN_ELEVATION_TYPE的指針,TOKEN_ELEVATION_TYPE是一個枚舉類型,其可能的取值如表4-9所示:

從TOKEN_ELEVATION_TYPE的枚舉值可以知道和當前進程相關聯的令牌權限。接着我們來確定運行該進程的用戶是否是管理員(以Run as Administrator運行)。如果TOKEN_ELEVATION_TYPE的值是TokenElevationTypeFull,表示當前進程權限已被成功提升,與其關聯的令牌並不是過濾令牌,此時用IsUserAdmin函數判斷當前用戶是否爲管理員既可。如果TOKEN_ELEVATION_TYPE取值是TokenElevationTypeLimited,表示和當前進程關聯的令牌依然是過濾令牌,其權限爲標準用戶權限,此時我們需要獲取當前登錄用戶的安全令牌(向GetTokenInformation傳遞TokenLinkedToken)並據此檢查當前用戶是否屬於管理員組(使用CreateWellKnownSid和CheckTokenMembership)。

下一節的Process Information例子會用到該函數,Process Information在處理WM_INITDIALOG消息時調用了該函數,在窗口標題欄上顯示當前進程的權限類別並決定是否顯示盾形圖標。

枚舉系統中的所有進程

[因爲這個例子程序要在Vista下才跑得了,我機器用的還是xp,等有空裝了Vista驗證後再發上來]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章