“快說,你是不是安裝程序?”操作系統問。
“我不是啊,長官。我雖然長得像,但是我真的不是安裝程序啊!”,一個應用程序扮出一副可憐相,膽膽怯怯的回答道。
“不是?那爲什麼你的程序名中含有Install?”,操作系統以懷疑的眼光盯着他,“所有程序名中含有Install的應用程序都是安裝程序,都必須在執行的時候都向用戶請求管理員權限!”
“是是是,長官!”應用程序心中暗喜,操作系統主動給我機會讓我請求管理員權限,我求之不得呢,用戶早就厭煩UAC了,肯定直接點擊“YES”了事啊,有了管理員權限,我就可以爲所欲爲啦,偷偷修改個首頁先,哈哈哈哈~~~
UAC又惹禍了
隨着Windows Vista引入UAC(User Access Control)機制,默認情況下,應用程序都運行在普通用戶權限下。雖然微軟出於良好的願望而在Vista中引入UAC機制, 但是在Vista操作系統中,只要系統稍作改變,它就會頻繁彈出對話框來尋求用戶的許可,因此它成爲了Vista中最受痛恨的一個功能。 雖然如此,Windows 7還是繼承了這一機制並根據用戶的反饋做了相應的改進。爲了降低計算機系統的風險,UAC機制將執行應用程序的用戶權限降低了,這就爲那些在UAC機制出現之前所設計的應用程序的執行帶來了兼容性的麻煩。這些舊有應用程序通常都假設以管理員權限運行,在Windows 7上,因爲UAC的存在,這一假設不成立了,最終導致應用程序無法正常運行。一些應用程序確實是需要管理員權限纔可以正常運行的,尤其是安裝程序,他們需要向一些需要特殊權限的區域,比如“Program Files”或者是註冊表的HKEY_LOCAL_MACHINE寫入內容,這種情況它們會遇到訪問拒絕的錯誤,或者是數據被UAC Virtualization重定向到其他位置而無法正確執行。
爲了解決這個問題,“聰明”的雷德蒙程序員們想出了一個辦法:安裝程序檢測。從Windows Vista開始,當然也包括Windows 7,操作系統將採用一些啓發式算法來判斷應用程序是不是一個安裝程序,也就是在執行的時候這個程序是否需要請求管理員權限,如果操作系統判斷應用程序是一個安裝程序,就會讓它在執行的時候向用戶請求獲取管理員權限以便讓應用程序正確的執行。
操作系統是如何檢測的?
所有在Windows Vista之前開發的沒有manifest(包括外部的和內部的)的32位應用程序都會進行這種啓發式的安裝程序檢測。操作系統會假設這些應用程序是舊有的,他們都需要進行安裝程序檢測以確定這些應用程序是否管理員權限才能正常運行。面對這樣的應用程序,操作系統的啓發式安裝檢測通常會通過以下這些途徑來判斷一個32位應用程序是不是安裝程序:
• 文件名包含關鍵字:”install”, “setup”和”update”等等。
• 在版本資源的以下字段內包含關鍵字:廠商(Vendor)、公司名(CompanyName)、產品名(ProductName)、文件說明(File Description)、初始文件名(Original Filename)、內部文件名(Internal Name)、導出名(Export Name)。
• 在可執行文件的manifest內包含關鍵字。
• 在鏈接到可執行文件的特定StringTable中包含關鍵字。
• 在鏈接到可執行文件的資源文件數據包含關鍵屬性。
• 可執行文件包含特定的字節序列。
如果找到了,操作系統會認爲它需要管理員權限纔可以正常運行。一個UAC保護盾的圖標會覆蓋在應用程序圖標上,這就表示應用程序在啓動的時候會請求管理員權限以便它可以正確執行。
開好車的就一定是好人嗎?
雖然這種啓發式安裝程序檢測可以讓一箇舊有的安裝程序能夠直接在Windows Vista或者Windows 7上正常運行。但是,它本身也帶來很多問題。一方面,一些不需要管理員權限就可以正常運行的應用程序,就因爲文件名中含有了“setup”等相關的文字,比如,StockUpdater.exe,就被強行在運作的時候向用戶詢問請求管理員權限。這不僅給用戶帶來不便,同時也無形中降低了系統的安全性。甚至用戶會因爲擔心繫統受到損害而取消應用程序的運行。另一方面,一些惡意軟件也會憑藉這種機制的漏洞,將自己僞裝成安裝程序,從而輕鬆地,正大光明地獲取管理員權限而給系統安全帶來隱患。
與此相反的是,很多自定義的安裝程序,並不使用Microsoft Windows Installer(MSI)技術,並且沒有按照啓發式安裝程序檢測的規則命名,這樣,操作系統就不會認爲它是一個安裝程序。由於啓發式安裝程序檢測不承認它是安裝程序,所以用戶在運行這些應用程序的時候,它並不會自動請求管理員權限運行,這就可能導致應用程序在向一些敏感位置寫入數據時遇到拒絕訪問錯誤,安裝過程半途而廢。
就像電影中的臺詞,“開好車的就一定是好人嗎?”同樣的,“姓Setup的就一定是安裝程序嗎?”
想要你就說嘛
既然微軟的啓發式安裝程序檢測這麼弱智,那麼我們只好自己麻煩一下,簡單地爲應用程序添加一個Manifest文件,告訴操作系統我們到底是不是安裝程序,是否需要請求管理員權限。如果應用程序的源代碼無法獲得,我們只需要在應用程序的同一目錄下爲應用程序添加一個外部Manifest文件。這個Manifest文件的名字應該是應用程序文件名加上“.manifest”後綴。例如,StockUpdater.exe的Manifest文件的文件名應該是StockUpdater.exe.manifest。這個Manifest文件應該指明此應用程序在運行的時候是否需要申請管理員權限,或者是否在普通用戶權限下就可以正常運行。
一個典型的外部Manifest文件如下所示:
<asmv1:assemblymanifestVersion="1.0"xmlns="urn:schemas-microsoft-com:asm.v1"xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentityversion="1.0.0.0"name="MyApplication.app"/>
<trustInfoxmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivilegesxmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest 選項
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
-->
<requestedExecutionLevellevel="asInvoker"uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
其中,requestedExecutionLevel屬性就表示我們的應用程序正確執行所需要的權限。它有這樣幾個可選值:
• asInvoker – 它表示應用程序需要跟創建者相同的權限運行。也就是跟Windows Explorer相同的權限運行,通常就是普通用戶權限。這個應用程序不是安裝程序並且不會被啓發式安裝程序檢測錯誤地標記。
• requireAdministrator – 它表示這個應用程序需要管理員權限才能正常運行。(它可能是一個安裝程序。)
• highestAvailable – 它表示這個應用程序應該以儘可能高的權限運行。如果當前用戶是一個管理員用戶,那麼它就等同於requireAdministrator 。如果當前用戶是普通用戶,那麼它會在運行的時候請求管理員權限。
當我們爲應用程序添加外部Manifest文件後,操作系統會根據Manifest文件中的定義爲應用程序制定相應的UAC規則。例如,我們可以通過外部Manifest文件,爲一個自定義的安裝程序在執行時請求管理員權限。
圖2 自定義安裝程序
嵌入式Manifest
如果我們可以獲得應用程序的源代碼,我們可以在Visual Studio 2010中爲應用程序添加嵌入式的Manifest來完成相同的工作。在項目屬性中,我們設置“Linker->Manifest File->UAC Execution Level”,就可以指定應用程序執行所需要的權限。
圖3 設置UAC執行權限等級
同時,我們可以將Manifest文件作爲資源嵌入到應用程序中,這樣我們就只需要一個單獨應用程序就可以完成所有工作了,而無需額外的一個Manifest文件。同樣的,在項目屬性“Manifest Tool->Input and Output->Embed Manifest”中設置就可以將Manifest文件嵌入到應用程序中。
圖4 嵌入式Manifest
在默認情況下,Visual Studio 2010所創建的新項目,已經含有了嵌入式的Manifest。
有了Manifest文件,應用程序就像有了身份證一樣,操作系統不會再冤枉好人了。