原文地址:http://www.codeproject.com/KB/vista-security/VistaElevator.aspx
有空就回復一個哈
源代碼分享在CSDN上的鏈接:http://download.csdn.net/detail/wsyjz/3934006 C++代碼
演示程序分享在CSDN上的鏈接:http://download.csdn.net/detail/wsyjz/3934004
若是積分不夠,可以在原文地址中找到下載鏈接。
另外添加2個相關文章(只有提權,沒有提到降低權限):
UAC 編程入門 1:進程Mandatory Level檢查和自我提升權限 C#代碼,英文 -->VB.Net代碼
[C#]Enable UAC Shield icons and run as administrator 比較簡短的中文說明,C#代碼
歡迎轉載,但最好請注明 Jero 翻譯。
- 已提權、已經提升權限的進程——可以理解為使用管理員權限運行的。
- 未提權、沒有提升權限的進程——可以理解為使用 非 管理員權限(既普通用戶權限)運行的。
UAC機制是由Vista引出,並且由於Windows 7的內核與Vista框架相同,所以本文中提到的Vista的UAC權限相關對於Windows 7完全適用。
正文中斜體都是我添加的說明。
正文
當你為Windows Vista開發程序的時候,你常常遇到一個問題:如何使用編程技巧來控制一個應用程序的執行權限(是否從普通用戶提權到管理員,或是從管理員降級到普通用戶)。當用戶運行一個程序的時候,它的執行權限是由應用程序清單(manifest)中的requestedExecutionLevel屬性值所決定的,以及Vista/7的用戶帳戶控制(UAC)根絕需要來采取適當的行為(例如彈出UAC提權確認的窗口,等)。然而,如果一個程序需要執行一個新的不同權限的程序應該如何?
例如:
- 一個程序普通運行的時候是采用非管理員的權限(沒有經過提升權限,普通用戶的權限),然后在運行過程中檢測到了程序有新版本可用。為了能夠讓自身更新,它需要啟動一個單獨的進程來提升自身的權限,這樣才能正確執行升級。在這種情況下,一個普通用戶的權限需要建立一個新的提升到至少是管理員權限的程序。
- 大多數安裝程序讓用戶選擇在安裝結束以后運行其程序。安裝程序是以提權過后的進程運行的,但是新的程序需要以普通的,未經過提升的權限來執行。
譯者注:簡單理解就是以普通用戶的權限運行了A程序,然后用A程序運行B程序,但是B程序被提升到管理員權限,而不是繼承A程序的權限(如果打開UAC的話,這個提權過程系統會彈出窗口進行確認的)
或是:C程序是通過管理員權限執行的,然后用C程序運行D程序,但是D程序不繼承C程序的管理員權限,而是降級到一個普通用戶的權限。(對於降級,系統是不會提示的)
微軟已經提供了一個相對簡單的方法來完成上面提到的第一個任務(即A程序使B程序提升權限),通過指定ShellExecuteEx API的一個參數為“runas”。但是,由於某些原因,微軟並沒有提供一個相似的方法來執行一個相反的過程:從一個已提權的程序來啟動一個未提權的程序(即C程序使D程序降級)。這篇文章中,我將會展示如何解決這個問題,以及相關的問題。
檢測當前程序的執行權限
首先,如何檢測一個程序當前的執行權限?源碼中的 VistaTools.cxx 文件包含的兩個函數對這個問題得到了答案。
第一個函數是 GetElevationType(),它使用了 Win32 API GetTokenInformation() 來獲得當前程序其令牌(token)的權限類型,它所可能返回的值為:
- TokenElevationTypeDefault - 用戶沒有使用一個分隔的令牌(權限機制)。這個值說明了UAC已經禁用,或者是程序是有一個非管理員組的普通用戶運行的。
- TokenElevationTypeFull - 程序已經獲得提權(至少是管理員權限的)
- TokenElevationTypeLimited - 程序沒有經過提權(普通權限運行的)
注意:只有當UAC開啟的時候,后面2個值才能返回,而且當前用戶是管理員組中的一員(就是這個用戶有一個分隔的令牌 <既是當前用戶是管理員,可以執行普通用戶權限的程序,也可以運行管理員權限的程序>)
第二個函數是 IsElevated() 它也調用了 GetTokenInformation() API,但是它獲取的是TokenElevation 類的信息。它只能返回下面兩項之一:
- S_OK - 當前進程已經提權過。這個數值表明了UAC已經開啟,而且當前程序是由管理員提權的;或者UAC已經禁用了,但是當前用戶是管理員組中的一員。
- S_FALSE - 當前進程沒有經過提權(受限的)。這個數值表明了UAC已經開啟了,而且當前進程只是普通的執行,沒有經過提權;或是UAC已經禁用了,進程只是由一個普通用戶執行。
使用這兩個函數,一個程序可以確定它執行的(權限的)確切情況。
運行一個提權的程序
如果一個未提權的程序需要運行一個提權的程序,所有它需要做的是調用 ShellExecuteEx() API ,然后指示一個參數為"runas",源碼中的函數 RunElevated() 就是這樣達到提權目的的:
BOOL RunElevated( HWND hwnd, LPCTSTR pszPath, LPCTSTR pszParameters = NULL, LPCTSTR pszDirectory = NULL ) { SHELLEXECUTEINFO shex; memset( &shex, 0, sizeof( shex) ); shex.cbSize = sizeof( SHELLEXECUTEINFO ); shex.fMask = 0; shex.hwnd = hwnd; shex.lpVerb = _T("runas"); shex.lpFile = pszPath; shex.lpParameters = pszParameters; shex.lpDirectory = pszDirectory; shex.nShow = SW_NORMAL; return ::ShellExecuteEx( &shex ); }
從一個已提權的進程來執行一個沒有提權的進程
從相反的方向(從一個已經提權的進程來執行一個沒有提權的進程)變得非常復雜。如果父進程已經提權了的,那么它所執行的所有子程序都將繼承它的已經提權的權限,而且無論子程序的清單(manifest)中的 requestedExecutionLevel 屬性值是如何指定的。由於某些原因,微軟並沒有提供一個API來直接降低進程的權限,所以我們需要想出一個間接的辦法來達到目的。
這個訣竅是使用Windows Vista自帶的任務計划程序(Task Scheduler)來建立以低權限執行的任務,並且要求這個任務建立以后應當立即執行。最終的結果是相同的,猶如進程直接啟動一樣。
本文的源碼包含的函數 RunAsStdUser() 正是這么做的。它是基於MSDN樣本“Registration Trigger Example(注冊任務以后立即執行的例子)”,而且它涉及了十幾個COM接口與任務計划程序交流,以及設立一個任務通過普通(未提權)的權限。我沒有將這些源碼的函數包含在這里,因為那是相當乏味的;你可以在 VistaTools.cxx 文件中找到它。
通過事實來證明以上
這個演示程序(VistaElevator)說明了如何通過編程方法來執行提權和降權。當你運行它的時候,它顯示了一個對話框來展示進程運行權限的相關信息,通過 GetElevationType() 和 IsElevated() 這兩個函數來獲取(函數說明看上面)。它也提供了你兩個如何重啟進程的選擇,提權或是取消。基於你的選擇,VistaElevator 調用 RunElevated() 或是 RunAsStdUser() 函數 (依舊看上面) 來重啟自身,並在要求的權限下。
作者:Andrei Belogortseff