設計應用程式以低完整性層級執行

在低完整性層級執行應用程式程式的簡單方式,就是將可執行檔的完整性層級設定為低完整性。 啟動該影像檔時,應用程式程式會以低完整性層級啟動。 例如,假設我們想要以低完整性程式執行 Windows 計算機應用程式。

若要以低完整性執行calc.exe

  1. 將c:\Windows\system32\calc.exe複本複製到暫存資料夾。

  2. 使用 icacls 程式,使用 icacls 命令將暫存檔案lowcalc.exe的完整性層級設定為低完整性:

    icacls lowcalc.exe /setintegritylevel Low

  3. 執行calc.exe的低完整性版本。

下圖顯示以低完整性程式執行 Windows 計算機的步驟。

圖 9 以低完整性啟動 Windows 計算機

您可以使用進程總管來確認映射檔lowcalc.exe確實在低完整性上執行。 [完整性層級] 資料行位於影像右側。

圖 10 低計算機程式

並非所有應用程式程式都會在低完整性進程中正常執行。 低完整性程式沒有檔案系統本機設定檔區域或 HKCU 下登錄下大部分區域的寫入權限。 如果程式不想要的惡意軟體,低完整性程式無法取得使用者設定檔的寫入權限,是件好事。 但對於受保護模式 Internet Explorer 之類的應用程式,可能需要重新設計一些功能,才能正確取得應用程式的所有功能。

使用來自 Sysinternals.com 的進程監視器之類的公用程式,瞭解應用程式目前用於寫入存取權的檔案和登錄資源,在低完整性執行時將會失敗。

雖然可以變更應用程式以完全低完整性執行,但應用程式的某些功能只有在中完整性程式中實作時,才能正常運作。 在低完整性執行的應用程式在低完整性程式中可能會有一個部分的應用程式,例如處理來自網際網路的不受信任資料。 應用程式的另一個部分可以在中完整性 「訊息代理程式」進程中實作,以處理一組由使用者起始的小型動作。 您可以使用各種 IPC 機制來處理應用程式中低完整性和中完整性程式之間的通訊。 應用程式的中完整性部分必須假設低完整性程式中的所有資料和程式碼都不受信任。

受保護的模式 Internet Explorer 是重新設計以在低完整性程式中執行的應用程式。 如需受保護模式 Internet Explorer 的詳細資訊,請參閱瞭解及使用受保護的模式 Internet Explorer () https://go.microsoft.com/fwlink/?LinkId=90931

設計應用程式以低完整性執行的主要主題如下:

  • 以低完整性啟動子進程
  • 低完整性應用程式的可寫入位置
  • 低完整性與較高層級進程之間的通訊

以低完整性啟動進程

根據預設,子進程會繼承其父進程的完整性層級。 若要啟動低完整性程式,您必須使用 CreateProcessAsUser 函式來啟動具有低完整性存取權杖的新子進程。 若要從中完整性進程啟動低完整性進程,您必須明確地將新進程啟動為低完整性。

啟動低完整性程式

  1. 複製目前進程的控制碼,此控制碼位於中度完整性層級。

  2. 使用 SetTokenInformation 將存取權杖中的完整性層級設定為 Low。

  3. 使用 CreateProcessAsUser,使用低完整性存取權杖的控制碼來建立新的進程。

CreateProcessAsUser 會更新新子進程中的安全性描述元,以及存取權杖的安全性描述項,以符合低完整性存取權杖的完整性層級。

下列程式碼範例示範此程式。

void CreateLowProcess()
{
 
    BOOL                  fRet;
    HANDLE                hToken        = NULL;
    HANDLE                hNewToken     = NULL;
    PSID                  pIntegritySid = NULL;
    TOKEN_MANDATORY_LABEL TIL           = {0};
    PROCESS_INFORMATION   ProcInfo      = {0};
    STARTUPINFO           StartupInfo   = {0};

 // Notepad is used as an example
 WCHAR wszProcessName[MAX_PATH] =
   L"C:\\Windows\\System32\\Notepad.exe";

 // Low integrity SID
 WCHAR wszIntegritySid[20] = L"S-1-16-1024";
 PSID pIntegritySid = NULL;

    fRet = OpenProcessToken(GetCurrentProcess(),
                            TOKEN_DUPLICATE |
                              TOKEN_ADJUST_DEFAULT |
                              TOKEN_QUERY |
                              TOKEN_ASSIGN_PRIMARY,
                            &hToken);

    if (!fRet)
    {
        goto CleanExit;
    }

    fRet = DuplicateTokenEx(hToken,
                            0,
                            NULL,
                            SecurityImpersonation,
                            TokenPrimary,
                            &hNewToken);

    if (!fRet)
    {
        goto CleanExit;
    }

    fRet = ConvertStringSidToSid(wszIntegritySid, &pIntegritySid);

    if (!fRet)
    {
        goto CleanExit;
    }

    TIL.Label.Attributes = SE_GROUP_INTEGRITY;
    TIL.Label.Sid        = pIntegritySid;

    //
    // Set the process integrity level
    //

    fRet = SetTokenInformation(hNewToken,
                               TokenIntegrityLevel,
                               &TIL,
                               sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid));

    if (!fRet)
    {
        goto CleanExit;
    }

    //
    // Create the new process at Low integrity
    //

    fRet  = CreateProcessAsUser(hNewToken,
                                NULL,
                                wszProcessName,
                                NULL,
                                NULL,
                                FALSE,
                                0,
                                NULL,
                                NULL,
                                &StartupInfo,
                                &ProcInfo);

CleanExit:

    if (ProcInfo.hProcess != NULL)
    {
        CloseHandle(ProcInfo.hProcess);
    }

    if (ProcInfo.hThread != NULL)
    {
        CloseHandle(ProcInfo.hThread);
    }

    LocalFree(pIntegritySid);

    if (hNewToken != NULL)
    {
        CloseHandle(hNewToken);
    }

    if (hToken != NULL)
    {
        CloseHandle(hToken);
    }

    return fRet;
}

低完整性的可寫入位置

Windows Vista 具有指派低必要標籤的特定檔案和登錄位置,以允許低完整性應用程式寫入權限。 表 10 顯示這些可寫入的位置。

表格 10 針對低強制標籤可寫入的位置

位置 可寫入的區域

登錄

低完整性進程可以在 HKEY_CURRENT_USER\Software\AppDataLow 下寫入並建立子機碼

檔案系統

低完整性進程可以在 %USER PROFILE%\AppData\LocalLow 下寫入和建立子資料夾

降低資源強制標籤

由於潛在的安全性風險,我們不建議您設計較高的完整性程式,以接受輸入或與低完整性進程共用資源。 低完整性進程可能會嘗試惡意行為。 不過,您可能需要依設計執行此動作。

注意

接受較低完整性處理常式之資源或共用資源的應用程式應該假設較低完整性進程所提供的資料無法受信任,然後應該執行適當的驗證。 例如,受保護的模式 Internet Explorer 會顯示 Internet Explorer User Broker 進程的 [另存 檔] 對話方塊。 這可讓使用者確認他們想要使用比受保護模式 Internet Explorer 更高許可權執行的進程來儲存檔案。

由於低完整性應用程式只能寫入低完整性資源,因此您必須降低共用資源的完整性層級。

降低共用資源的完整性層級

  1. 建立定義低強制標籤的 SDDL 安全性描述元。

  2. 將 SDDL 字串轉換為安全性描述元。

  3. 將低完整性屬性指派給安全性描述項。

  4. 將安全性描述項指派給共用資源。

下列程式碼範例顯示此程式。

#include <sddl.h>
#include <AccCtrl.h>
#include <Aclapi.h>

void SetLowLabelToFile()
{
 // The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity 
 #define LOW_INTEGRITY_SDDL_SACL_W L"S:(ML;;NW;;;LW)"
 DWORD dwErr = ERROR_SUCCESS;
 PSECURITY_DESCRIPTOR pSD = NULL;  

 PACL pSacl = NULL; // not allocated
 BOOL fSaclPresent = FALSE;
 BOOL fSaclDefaulted = FALSE;
 LPCWSTR pwszFileName = L"Sample.txt";

 if (ConvertStringSecurityDescriptorToSecurityDescriptorW(
     LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &pSD, NULL)) 
 {
  if (GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, 
     &fSaclDefaulted))
  {
   // Note that psidOwner, psidGroup, and pDacl are 
   // all NULL and set the new LABEL_SECURITY_INFORMATION
   dwErr = SetNamedSecurityInfoW((LPWSTR) pwszFileName, 
         SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION, 
         NULL, NULL, NULL, pSacl);
  }
  LocalFree(pSD);
 }
}

應用程式進程可以將安全性實體物件的完整性設定為應用程式進程完整性層級或低於安全性物件的完整性層級。

低完整性與較高完整性程式之間的通訊

低完整性進程不會完全與其他應用程式隔離。 他們可以與其他進程互動。 事實上,如果沒有某種形式的共同作業,以低完整性執行的應用程式似乎完全中斷。

某些形式的 IPC 可用於低完整性進程,以與較高完整性進程通訊。 Windows Vista 中的元件會封鎖下列類型的通訊。

  • UIPI 會封鎖大部分的視窗訊息和進程勾點。

  • 開啟進程並使用 CreateRemoteThread 會遭到進程物件上的強制標籤封鎖。

  • 已封鎖開啟共用記憶體區段以進行寫入存取。

  • 預設強制標籤會封鎖使用由較高完整性進程建立的具名物件進行同步處理。

  • 系結至執行中 COM 服務的實例是 區塊。
    不過,您可以在低完整性程式與較高完整性進程之間使用其他類型的通訊。 您可以使用的通訊類型包括:

  • 剪貼簿 (複製並貼上)

  • 遠端程序呼叫 (RPC)

  • 通訊端

  • 呼叫 ChangeWindowMessageFilter,明確允許從較低完整性進程接收較高完整性進程的視窗訊息

  • 共用記憶體,其中較高完整性進程會明確降低共用記憶體區段上的必要標籤

    重要

    這特別危險,而且較高完整性的程式必須小心,才能驗證寫入共用區段的所有資料。

  • COM 介面,其中啟動啟用許可權是由較高完整性的程式以程式設計方式設定,以允許從低完整性用戶端進行系結

  • 具名管道,建立者會在管道上明確設定強制標籤,以允許存取較低完整性的進程

這些通訊機制可讓低完整性進程與其他應用程式進程互動,例如訊息代理程式進程,專為接受來自低完整性來源的輸入或呼叫而設計。

設計介面的一般指導方針,是低完整性程式將呼叫的介面永遠不會信任呼叫端或輸入資料。 中型完整性訊息代理程式可以提供介面來建立指定路徑的檔案,並允許低完整性應用程式呼叫介面。 不過,這會破壞以低完整性執行應用程式的目的。 較佳的設計是讓低完整性程式呼叫介面,要求中完整性應用程式向使用者呈現通用檔案對話方塊,低完整性進程無法使用視窗訊息來驅動。 如此一來,您可以讓使用者流覽並選取要開啟或建立的檔案,而中完整性程式會執行所有檔案作業。 這種類型的另存新檔案例是受保護模式 Internet Explorer 如何使用自己的訊息代理程式來處理在使用者設定檔中某處儲存網頁的範例。

許多應用程式功能可以在低完整性程式中正確執行。 沒有一組常見的工具可在低完整性執行應用程式。 每個應用程式都不同,並非所有應用程式都需要以低完整性執行。