Windows Vista IE 7保護模式開發人員生存指南

來源:互聯網
上載者:User
Windows Vista IE 7 保護模式開發人員生存指南            保護模式之簡介         Internet Explorer 7的保護模式是Vista中的一項新特色,隸屬於使用者賬戶控制(UAC)的一部分。保護模式通過對運行於IE進程內的代碼進行限制,來達到保護電腦的目的,即便一個惡意網頁利用了IE或IE外掛程式中的某個代碼注入bug,它也不能對系統造成損害。            完整性層級及UIPI         Windows Vista對需要保證其安全的對象引入了一個新的屬性:強制完整性層級,由以下四個層級組成: Ø 系統級:由系統組件使用,通常應用程式不應使用。Ø 進階:運行於提升至管理員權限的進程所有。Ø 中級:運行於正常狀態的進程所有。Ø 低級:由IE及Windows Mail使用,以提供保護模式。  Windows在運行某個進程時,通常也會包含其完整性層級,且一旦進程運行之後,層級不可更改,只能在進程建立的那一刻進行設定。進程的完整性層級會帶來以下三種主要的影響: 1、 進程所建立的任何安全性實體都具有同樣的完整性層級。2、 進程不能訪問比自身完整性層級更高的資源。3、 進程不能向比自身完整性層級更高的進程發送視窗訊息。  以上列表並非完整,但這三個對IE外掛程式的影響尤為顯著,前兩者防止低層級進程篡改 IPC資源,如包含敏感性資料的共用記憶體、或影響程式正確執行的關鍵資料;後者則被稱為使用者介面進程隔離(User Interface Process Isolation  UIPI),設計於防止某些破壞性的攻擊,如攻擊者通過發送訊息,誘使某個進程運行不安全的程式碼。            虛擬化         虛擬化(在微軟的某些文檔中也稱為重新導向)是一種防止進程向註冊表及檔案系統受保護地區寫入的功能,且不會影響程式的正常功能。對完整性為中級的進程來說,受保護地區為系統關鍵地區,如HKLM、System32及Program Files目錄,等等;對完整性為低級的進程來說,其受到的限制更嚴,只能對註冊表及檔案系統的特定低許可權地區進行寫入,任何對此地區之外的寫請求都會被禁止。         當某個進程想要對其沒有許可權的地區進行寫入時,虛擬化這時就起作用了,它把這些寫操作重新導向至目前使用者個人設定檔(User Profile)下的目錄(或註冊表鍵),最終的寫操作就寫到這裡。之後,當程式想要讀取資料時,讀操作也被重新導向至此,正確讀取了之前寫入的資料。         虛擬化技術也會對IE外掛程式有所影響,因為外掛程式再也不能為了與其他進程共用資料,而把配置資訊寫入到註冊表中了(甚至HKCU下也不行),同時,在那些外掛程式可寫資料之處,也是極受約束的,只有一些IE特定的目錄,如Favorites及Cookies可寫。            保護模式何時開啟         在Vista預設狀態下,IE均運行在保護模式中,狀態列()顯示了保護模式是否啟用:             關閉UAC,就可以徹底關掉保護模式了,在Internet屬性對話方塊的安全頁中,不勾選“啟用保護模式”;也可運行一個提升許可權後的新IE執行個體,來臨時繞過保護模式,但這樣做之後,會使IE運行在進階完整性層級,而不像普通程式那樣在中級。            樣本程式及外掛程式         範例程式碼中包含了兩個工程,第一個工程是IEExtension,它是一個停靠在IE視窗底部的一個外掛程式:             第二個工程是DemoAPP,其為一個用作通訊的exe檔案,它本身代碼並不多,關鍵代碼都在 IEExtension中與其進行通訊之處。外掛程式中成對出現的按鈕代表了在保護模式中不能正常工作的方法(按鈕1)及新的可工作於保護模式中的方法(按鈕 2),清單控制項用於顯示狀態資訊,如Windows API的傳回值。         以下將主要講解外掛程式需要怎麼做才能正常工作於保護模式中,並在範例程式碼中介紹一些API,每個主題都相對應一個(或一對)按鈕。            保護模式之下的解決辦法         IE 7中增加了幾個新的API,它們位於ieframe.dll中,外掛程式可以使用它們來執行那些在保護模式中受限的功能,既可以直接通過iepmapi.lib連結到這些API,也可以通過 LoadLibrary()/GetProcAddress()擷取運行時的函數指標,如果想要外掛程式在Vista之前的Windows平台上也能正常載入,就必須使用後一種方法了。         權限提高的大多數功能是通過一個代理進程(ieuser.exe)來實現的,只要IE進程運行於低完整性層級中,它就不能執行更高許可權的任務,這也是ieuser.exe這個角色的目的所在。            在運行時探知保護模式         如果想知道外掛程式是否運行在IE進程的保護模式中,可使用IEIsProtectedModeProcess(): HRESULT IEIsProtectedModeProcess(BOOL* pbResult);           如果傳回值是一個成功的HRESULT且*pbResult為TRUE,則可以確定為保護模式;另外,基於*pbResult的傳回值,也可決定代碼下一步該怎麼辦:  HRESULT hr;BOOL bProtectedMode = FALSE;  hr = IEIsProtectedModeProcess ( &bProtectedMode );  if ( SUCCEEDED(hr) && bProtectedMode )    // IE運行於保護模式中 else// IE 未運行於保護模式中            檔案系統的寫入         當處於保護模式時,外掛程式只能寫到使用者設定檔下的某些目錄中,且只有Temp、 Temporary Internet Files、Cookies、Favorites這些低完整性層級目錄為可寫。但IE 7也照顧到了某些相容性問題,對某些常用的目錄進行虛擬化,對這些目錄的寫入會被重新導向至Temporary Internet Files的子目錄中;如果外掛程式想要對敏感位置進行寫入,如Windows目錄,那麼寫操作會失敗。         回到話題上來,當外掛程式想要對檔案系統進行寫入時,應使用 IEGetWriteableFolderPath() API而不是GetSpecialFolderPath()、GetFolderPath()、SHGetKnownFolderPath()。 IEGetWriteableFolderPath()可探知保護模式,如果外掛程式請求的目錄不允許寫入,IEGetWriteableFolderPath()會返回E_ACCESSDENIED,IEGetWriteableFolderPath() 的原型如下: HRESULT IEGetWriteableFolderPath(GUID clsidFolderID, LPWSTR* lppwstrPath);           GUID為定義在knownfolders.h標頭檔中的 FOLDERID_InternetCache、FOLDERID_Cookies及FOLDERID_History,另外,似乎沒有與Temp目錄對應的GUID,所以當需要寫臨時檔案時,推薦使用FOLDERID_InternetCache。以下為在緩衝中建立臨時檔案的一段代碼:  HRESULT hr;LPWSTR pwszCacheDir = NULL;TCHAR szTempFile[MAX_PATH] = {0};  hr = IEGetWriteableFolderPath(FOLDERID_InternetCache, &pwszCacheDir);  if ( SUCCEEDED(hr) )    {    GetTempFileName(CW2CT(pwszCacheDir), _T("bob"), 0, szTempFile);    CoTaskMemFree(pwszCacheDir);     // szTempFile中現在為臨時檔案的完整路徑}           如果IEGetWriteableFolderPath()成功,它會分配一塊緩衝區並在pwszCacheDir中返回其地址,我們只須把這個目錄傳遞給GetTempFileName(),接著用CoTaskMemFree()釋放緩衝區就行了。         IEGetWriteableFolderPath()不僅是用於寫臨時檔案,外掛程式也可在保護模式的另存新檔對話方塊中用到它,這將在下面的“提示使用者儲存檔案”一節中說到。            註冊表的寫入         因為註冊表是系統的關鍵地區,所以,不應允許運行在瀏覽器中的代碼修改其中的任何部分,以防止運行惡意代碼。為此,只有一個索引值對外掛程式來說可寫,與檔案系統一樣,這個鍵也是在目前使用者設定檔下的低許可權地區,可通過調用 IEGetWriteableHKCU()來擷取此鍵的控制代碼: HRESULT IEGetWriteableHKCU(HKEY* phKey);           如果函數成功,就可在其他的註冊表API中使用這個返回的HKEY來寫入資料了。            提示使用者儲存檔案         當IE運行於保護模式時,外掛程式還有一個辦法(非直接的)可寫入到檔案系統中,而且是在低許可權地區之外,這是通過調用IEShowSaveFileDialog()顯示一個儲存檔案對話方塊來完成的。如果使用者輸入了一個檔案名稱,外掛程式就能通過調用 IESaveFile()讓IE來寫這個檔案。要注意的是,這個操作總是會顯示儲存檔案對話方塊,以確保使用者知道將要寫入一個檔案。          儲存檔案的步驟如下:1、 調用IEShowSaveFileDialog()顯示儲存檔案對話方塊。2、 調用IEGetWriteableFolderPath()擷取IE緩衝目錄。3、 把資料寫到緩衝目錄的一個臨時檔案中。4、 調用IESaveFile()把資料複製到使用者選擇的檔案中。5、 清理臨時檔案  IEShowSaveFileDialog()是對通用檔案儲存對話方塊的一個封裝函數: HRESULT IEShowSaveFileDialog( HWND    hwnd, LPCWSTR lpwstrInitialFileName, LPCWSTR lpwstrInitialDir, LPCWSTR lpwstrFilter, LPCWSTR lpwstrDefExt, DWORD   dwFilterIndex, DWORD   dwFlags, LPWSTR* lppwstrDestinationFilePath, HANDLE* phState);           hwnd是一個外掛程式所有的視窗控制代碼,IE將會使用最頂層的所有者視窗作為對話方塊的父視窗;lppwstrDestinationFilePath是一個指向LPWSTR的指標,其為使用者選擇的檔案路徑;phState是一個指向 HANDLE的指標,其為使用者所選擇檔案的控制代碼,在調用其他API時,也會用到這個控制代碼。其他參數與OPENFILENAME結構中的對應成員用法類似。         IEShowSaveFileDialog()返回S_OK代表使用者選擇了某個檔案名稱,S_FALSE代表取消了對話方塊,而失敗的HRESULT代表API未成功。         下面範例程式碼中首先調用IEShowSaveFileDialog()提示使用者輸入檔案路徑:  void CBandDialog::OnSaveLog(UINT uCode, int nID, HWND hwndCtrl){HRESULT hr;HANDLE hState;LPWSTR pwszSelectedFilename = NULL;const DWORD dwSaveFlags =        OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |        OFN_OVERWRITEPROMPT;  // Get a filename from the user. hr = IEShowSaveFileDialog (         m_hWnd, L"Saved log.txt", NULL,         L"Text files|*.txt|All files|*.*|",         L"txt", 1, dwSaveFlags, &pwszSelectedFilename,         &hState );  if ( S_OK != hr )return;           接下來,調用IEGetWriteableFolderPath()擷取可寫的緩衝目錄路徑:  LPWSTR pwszCacheDir = NULL;TCHAR szTempFile[MAX_PATH] = {0};  // 擷取IE緩衝目錄路徑,其為保護模式下可寫的目錄 hr = IEGetWriteableFolderPath ( FOLDERID_InternetCache, &pwszCacheDir );  if ( SUCCEEDED(hr) )    {    // 得到目錄中的一個臨時檔案名稱    GetTempFileName ( CW2CT(pwszCacheDir), _T("bob"), 0, szTempFile );    CoTaskMemFree ( pwszCacheDir );     // 把資料寫到臨時檔案中    hr = WriteLogFile ( szTempFile );}           如果一切正常,接著會調用IESaveFile(),IESaveFile()接受 IEShowSaveFileDialog()返回的狀態控制代碼及臨時檔案路徑作為參數,要注意的是,這個HANDLE不是標準的控制代碼,其無需關閉,在調用完IESaveFile()之後,HANDLE會自動釋放。         如果因為某些原因IESaveFile()的調用未完成,例如在寫臨時檔案時發生錯誤,那就必須清理HANDLE及IEShowSaveFileDialog()中分配的臨時空間,這由IECancelSaveFile()來完成。  if ( SUCCEEDED(hr) )    {    // 如果寫檔案成功,IE就會把資料儲存在使用者選擇的路徑中    hr = IESaveFile ( hState, T2CW(szTempFile) );     // 清理臨時檔案    DeleteFile ( szTempFile );    } else    {    // 未完成儲存操作,那就只有取消它了    IECancelSaveFile ( hState );}            在外掛程式與其他程式間通訊         前面介紹的內容都是與檔案系統及註冊表相關的,其限制IE調用某些API對系統造成損害,下面涉及到一些更複雜的內容,與其他運行在更高完整性層級中程式的進程間通訊(IPC),這又分為兩種類型:核心對象及視窗訊息。  一、建立IPC對象外掛程式與單獨進程的進程間通訊,涉及到NT安全API及強制完整性層級檢查,預設狀態下,會阻止外掛程式到單獨進程的通訊,因為外部程式運行在比IE更高的完整性層級中。         如果外部程式要建立一個外掛程式可使用的核心對象(如event或mutex),那就必須降低這個對象的完整性層級,以便外掛程式可以訪問它。外部程式可使用安全API,通過修改對象的ACL來降低其完整性層級,下面的代碼來自MSDN,接受一個核心對象的 HANDLE作為參數,並設定其完整性層級為低:  // LABEL_SECURITY_INFORMATION  SDDL  SACL被設為低完整性層級LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"; bool SetObjectToLowIntegrity( HANDLE hObject, SE_OBJECT_TYPE type = SE_KERNEL_OBJECT){bool bRet = false;DWORD dwErr = ERROR_SUCCESS;PSECURITY_DESCRIPTOR pSD = NULL;PACL pSacl = NULL;BOOL fSaclPresent = FALSE;BOOL fSaclDefaulted = FALSE;  if ( ConvertStringSecurityDescriptorToSecurityDescriptorW (         LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &pSD, NULL ) )    {    if ( GetSecurityDescriptorSacl (           pSD, &fSaclPresent, &pSacl, &fSaclDefaulted ) )      {      dwErr = SetSecurityInfo (                hObject, type, LABEL_SECURITY_INFORMATION,                NULL, NULL, NULL, pSacl );       bRet = (ERROR_SUCCESS == dwErr);      }     LocalFree ( pSD );    }  return bRet;}           在我們的樣本程式中,使用了兩個mutex,目的是允許外掛程式判別何時程式在運行,當 DemoApp程式啟動時會建立這兩個mutex,當點擊其中一個Open Mutex按鈕時,外掛程式會試圖開啟它們。Mutex 1具有預設完整性層級,而Mutex 2被SetObjectToLowIntegrity()設為低完整性層級,這意味著一旦處於保護模式,外掛程式將只能訪問Mutex 2,下面是點擊Open Mutex按鈕之後的程式輸出:             保護模式的另一個作用是外掛程式不能啟動一個繼承自核心物件控點的單獨進程,例如,處於保護模式時,樣本中的外掛程式不能通過建立一個檔案對應物件,來啟動一個單獨的進程(CreateProcess()中的參數bInheritHandles為 TRUE),且使這個進程繼承了檔案對應物件的控制代碼。  HANDLE hMapping;SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };  sa.bInheritHandle = TRUE;  hMapping = CreateFileMapping( INVALID_HANDLE_VALUE, &sa,                                PAGE_READWRITE, 0, cbyData, NULL );  //在此可把資料放在共用記憶體塊中  //運行EXE並傳遞給它共用記憶體控制代碼CString sCommandLine;BOOL bSuccess;STARTUPINFO si = { sizeof(STARTUPINFO) };PROCESS_INFORMATION pi = {0};  sCommandLine.Format( _T("\"C:\\path\\to\\DemoApp.exe\" /h:%p"),                       hMapping );  bSuccess = CreateProcess(               NULL, sCommandLine.GetBuffer(0), NULL, NULL,               TRUE, // TRUE代表新的進程應該繼承控制代碼               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );           DemoApp會從/h選項中讀取控制代碼值,並用它來調用MapViewOfFile()完成資料讀取,這也是使新進程自動接收到某個核心物件控點的標準方法,但處於保護模式時,新進程實際上由代理進程啟動,也正是因為IE進程不會直接啟動新進程,所以控制代碼繼承失效。         要突破這個限制,外掛程式可為IPC對象使用一個預定義名,這樣其他進程就能訪問此對象了(因為這個對象為低完整性層級)。如果不想使用預定義名,也可在運行時產生一個“名字”(如為名字使用一個GUID),並把它傳遞給其他的單獨進程。  //得到一個用作共用記憶體對象名的GUIDGUID guid = {0};WCHAR wszGuid[64] = {0};HRESULT hr;  CoCreateGuid( &guid ); StringFromGUID2( guid, wszGuid, _countof(wszGuid) );  //建立檔案對應物件,因為控制代碼不可被繼承,所以無需SECURITY_ATTRIBUTES結構HANDLE hMapping;  hMapping = CreateFileMapping( INVALID_HANDLE_VALUE, NULL,                                PAGE_READWRITE, 0, cbyData, CW2CT(wszGuid) );  //在此可把資料放在共用記憶體塊中  //運行EXE並傳遞給它共用記憶體對象名 //注意CreateProcess()中的參數bInheritHandles為FALSECString sCommandLine;BOOL bSuccess;STARTUPINFO si = { sizeof(STARTUPINFO) };PROCESS_INFORMATION pi = {0};  sCommandLine.Format( _T("\"C:\\path\\to\\DemoApp.exe\" /n:%ls"),                       wszGuid );  bSuccess = CreateProcess(               NULL, sCommandLine.GetBuffer(0), NULL, NULL,               FALSE, // FALSE代表新的進程不繼承控制代碼               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );           這樣,EXE就可在命令列中接收到IPC對象名,它接著會調用OpenFileMapping()來訪問這個對象,另外,這個方法中,最重要的一點是要非常留意對象生命期。以下是使用控制代碼繼承的步驟:  1、 外掛程式建立IPC對象,共置引用計數為1。2、 外掛程式啟動新進程,其繼承了控制代碼。這個操作把對象引用計數增加為2。3、 外掛程式能立即關閉它的控制代碼,因為它不再需要此對象。引用計數降為1。4、 新進程可通過IPC對象來進行所需的操作了。因為它擁有一個開啟的控制代碼,對象直到新進程關閉控制代碼後才會結束。  如果依照上述步驟,且僅是把對象名傳遞給EXE,那麼實際上是建立了一個“競跑”狀態,外掛程式可能在EXE有機會開啟控制代碼之前就把它關閉了(IPC對象也被刪除了)。以下是修改後的步驟:  1、 外掛程式建立IPC對象,共置引用計數為1。2、 外掛程式啟動新進程,並傳遞給它IPC對象名。此時引用計數仍為1。3、 外掛程式不能馬上關閉它的控制代碼,它需要等待直到新進程已開啟此對象的一個控制代碼。此時需要做一些同步。4、 新進程開啟物件控點並讀取資料,此時,它可以發訊號給外掛程式以喚醒其線程,外掛程式現在可以安全地關閉它的控制代碼了。  在樣本程式中,我們讓DemoApp在建立主視窗之前,先從共用記憶體中讀取資料。外掛程式在調用完CreateProcess()之後,接下來就可調用WaitForInputIdle(),這個函數使線程阻塞,直到DemoApp的主視窗建立並顯示出來。一旦DemoApp線程處於空閑狀態,它將不再使用共用記憶體,此時外掛程式就可以安全地關閉其控制代碼了。         當點擊“Run EXE 1”按鈕時,程式把當前日期及時間寫到共用記憶體中,並傳遞給DemoApp一個控制代碼,如果此時處於保護模式,這個方法將會失敗,DemoApp會返回一個非法控制代碼錯誤;而點擊“Run EXE 2”按鈕時,將傳遞檔案對應物件名給DemoApp,程式此時就會顯示讀取自共用記憶體的資料了。    二、接收視窗訊息UIPI可防止特定視窗訊息(即訊息值大於或等於WM_USER的所有訊息)從低完整性層級程式中發送到比其更進階的程式中。如果你的程式需要接收來自外掛程式的訊息,可調用ChangeWindowMessageFilter()來允許特定訊息通過: BOOL ChangeWindowMessageFilter(UINT message, DWORD dwFlag);           Message為訊息值,dwFlag表明是否允許或阻止此訊息,MSGFLT_ADD允許訊息,而MSGFLT_REMOVE阻止訊息。對待其他進程的訊息需小心謹慎,如果通過訊息來接收資料,對接收到的資料必須仔細確認及驗證,因為進程間的訊息來無定所,很可能被惡意利用。         樣本程式示範了如何通過註冊視窗訊息來進行通訊,使用mutex的例子中有兩種訊息,DemoApp將在OnInitDialog()中允許第二種訊息通過:  m_uRegisteredMsg1 = RegisterWindowMessage( REGISTERED_MSG1_NAME );m_uRegisteredMsg2 = RegisterWindowMessage( REGISTERED_MSG2_NAME );ChangeWindowMessageFilter( m_uRegisteredMsg2, MSGFLT_ADD );           當點擊程式中“Send Message”按鈕時,將會看到以下輸出:             第一種訊息不允許通過,所以SendMessage()返回0。            保護模式中的其他限制一、運行其他程式IE還有一種機制用於防止惡意代碼進行通訊或啟動其他進程,如果某個外掛程式試圖啟動另一個進程,IE在啟動進程之前,會請求使用者的許可,例如,點擊“查看源檔案”命令會看到以下提示:             如果外掛程式需要運行某個單獨的EXE,可添加一個註冊表索引值以告之IE此EXE是受信任程式,可無須提示就運行,控制此行為的索引值是:HKLM\Software\Microsoft\Internet Explorer\Low Rights\ElevationPolicy;建立一個新的GUID,再在ElevationPolicy下添加一個新鍵,名稱就為剛才的GUID,在新鍵中,建立下面三個值:  Ø AppName:可執行檔名,如:DemoApp.exe。Ø AppPath:EXE位於的目錄。Ø Policy:設為3的DWORD值。  如果你勾選了前面提示中的“不再對此程式顯示此警告”,IE自己也會建立這個鍵。  二、拖放到其他程式如果從網頁中把某些內容拖放到其他程式中,也會顯示一個類似的提示:             同樣,也能通過在註冊表中建立一個索引值而不顯示這個提示,格式與前面的一樣,只不過這次索引值位於DragDrop,而不是ElevationPolicy下。
相關文章

聯繫我們

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

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

A Free Trial That Lets You Build Big!

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

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

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

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