實作記憶體完整性相容程序代碼
本節說明如何實作記憶體完整性相容程序代碼。
注意
記憶體完整性有時稱為 受 Hypervisor 保護的程式代碼完整性, (HVCI) 或 Hypervisor 強制執行的程式代碼完整性,最初是發行為 Device Guard 的一部分。 除了在 群組原則 或 Windows 登錄中尋找記憶體完整性和 VBS 設定,否則不再使用 Device Guard。
若要實作相容的程式代碼,請確定您的驅動程式程式代碼會執行下列動作:
- 默認加入加入 NX
- 使用 NX API/旗標進行記憶體配置 (NonPagedPoolNx)
- 不使用可寫入和可執行的區段
- 不會嘗試直接修改可執行的系統記憶體
- 不會在核心中使用動態程序代碼
- 不會將資料檔載入為可執行檔
- 區段對齊是0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000
下列未保留供系統使用的 DIS 清單可能會受到影響:
使用 HLK 中的程式碼完整性測試來測試記憶體完整性驅動程式相容性
如需相關系統基本概念安全性測試的詳細資訊,請參閱 HyperVisor 程式代碼完整性整備測試和記憶體完整性和 VBS。
如需相關裝置基本概念測試的詳細資訊,請參閱 Device.DevFund 測試。
使用下表來解譯輸出,並判斷修正不同類型的記憶體完整性不相容所需的驅動程式程式代碼變更。
警告 | 救 贖 |
執行集區類型 |
呼叫端指定可執行的集區類型。 呼叫要求可執行記憶體的記憶體配置函式。 請確定所有集區類型都包含非可執行的 NX 旗標。 |
執行頁面保護 |
呼叫端指定可執行的頁面保護。 指定「無執行」頁面保護遮罩。 |
執行頁面對應 |
呼叫端指定了可執行記憶體描述元清單, (MDL) 對應。 請確定使用的遮罩包含 MdlMappingNoExecute。 如需詳細資訊,請參閱 MmGetSystemAddressForMdlSafe |
Execute-Write 區段 |
映像包含可執行檔和可寫入區段。 |
區段對齊失敗 |
影像包含未對齊頁面的區段。 區段對齊必須是0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000 |
可執行檔區段中的 IAT |
匯入地址數據表 (IAT) ,不應該是記憶體的可執行區段。 當 IAT 位於僅限記憶體區段的讀取和執行 (RX) 時,就會發生此問題。 這表示 OS 將無法寫入 IAT,以設定所參考 DLL 所在位置的正確位址。 其中一種方式是使用 /MERGE (在程式代碼連結中使用 [合併區段]) 選項。 例如,如果 .rdata (只讀初始化的資料) 與 .text 數據合併 (可執行檔程式代碼) ,則 IAT 最終可能會出現在記憶體的可執行檔區段中。 |
不支援的 Relocs
在 Windows 10 版本 1507 到 Windows 10 1607 版,因為使用位址空間配置隨機化 (ASLR) 位址對齊和記憶體重新配置可能會發生問題。 操作系統需要重新放置連結器將預設基位址設定為 ASLR 指派的實際位置的位址。 此重新配置無法跨越頁面界限。 例如,假設 64 位位址值會從頁面上的位移0x3FFC開始。 其位址值會重疊至位移0x0003的下一頁。 Windows 10 1703 版之前,不支援這種類型的重疊重新置放。
當全域結構類型變數初始化表達式有另一個全域的不對齊指標時,就會發生這種狀況,因此鏈接器無法移動變數,以避免重新配置中斷。 鏈接器會嘗試移動變數,但在某些情況下,可能無法執行此動作 (例如,具有大型未對齊結構或大型錯誤結構陣列) 。 適當的話,應該使用 /Gy (COMDAT) 選項來組合模組,讓鏈接器盡可能對齊模組程序代碼。
#include <pshpack1.h>
typedef struct _BAD_STRUCT {
USHORT Value;
CONST CHAR *String;
} BAD_STRUCT, * PBAD_STRUCT;
#include <poppack.h>
#define BAD_INITIALIZER0 { 0, "BAD_STRING" },
#define BAD_INITIALIZER1 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0
#define BAD_INITIALIZER2 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1
#define BAD_INITIALIZER3 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2
#define BAD_INITIALIZER4 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3
BAD_STRUCT MayHaveStraddleRelocations[4096] = { // as a global variable
BAD_INITIALIZER4
};
還有其他情況涉及使用組合器程序代碼,此問題也可能發生。
驅動程式驗證程式代碼完整性
使用驅動程式驗證程式代碼完整性選項旗標 (0x02000000) 啟用額外的檢查,以驗證此功能的合規性。 若要從命令行啟用此功能,請使用下列命令。
verifier.exe /flags 0x02000000 /driver <driver.sys>
若要在使用驗證器 GUI 時選擇此選項,請選取 [為程式代碼開發人員建立 自定義設定 () ],選取 [ 下一步],然後選取 [ 程序代碼完整性檢查]。
您可以使用驗證程式命令列 /query 選項來顯示目前的驅動程式驗證程式資訊。
verifier /query