適用於驅動程式開發人員的 Windows 驅動程式安全性最佳做法
本主題摘要說明可能導致惡意探索和濫用 Windows 驅動程式程式代碼的不安全開發模式。 本主題提供開發建議和程式碼範例。 遵循這些最佳做法有助於改善在 Windows 核心中執行特殊許可權行為的安全性。
不安全的驅動程序行為概觀
雖然 Windows 驅動程式預期會在核心模式中執行高許可權行為,但不會執行安全性檢查,且在特殊許可權行為上新增條件約束是無法接受的。 Windows 硬體相容性計畫 (WHCP),先前稱為WHQL,需要新的驅動程式提交,以符合這項需求。
不安全和危險行為的範例包括,但不限於下列專案:
提供讀取和寫入 MSR 的能力
增強從 MSR 讀取的安全性
在第一個 ReadMsr 範例中,驅動程式允許任意讀取任何和所有緩存器,以允許不安全的行為。 這可能會導致使用者模式中的惡意進程濫用。
Func ReadMsr(int dwMsrIdx)
{
int value = __readmsr(dwMsrIdx); // Unsafe, can read from any MSR
return value;
}
如果您的案例需要從 MSR 讀取,驅動程式必須一律檢查要讀取的緩存器是否受限於預期的索引或範圍。 如何實作安全讀取作業的兩個範例如下。
Func ConstrainedReadMsr(int dwMsrIdx)
{
int value = 0;
if (dwMsrIdx == expected_index) // Blocks from reading anything
{
value = __readmsr(dwMsrIdx); // Can only read the expected MSR
}
else
{
return error;
}
return value;
}
// OR
Func ConstrainedReadMsr(int dwMsrIdx)
{
int value = 0;
if (min_range <= dwMsrIdx <= max_range) // Blocks from reading anything
{
value = __readmsr(dwMsrIdx); // Can only from the expected range of MSRs
}
else
{
return error;
}
return value;
}
增強寫入 MSR 的安全性
在第一個 WriteMsr 範例中,驅動程式允許任何和所有緩存器任意寫入,以允許不安全的行為。 這可能會導致惡意程序濫用,以提升使用者模式中的許可權,並寫入所有 MSR。
Func WriteMsr(int dwMsrIdx)
{
int value = __writemsr(dwMsrIdx); // Unsafe, can write to any MSR
return value;
}
如果您的案例需要寫入 MSR,驅動程式必須一律檢查要寫入的緩存器是否受限於預期的索引或範圍。 如何實作安全寫入作業的兩個範例如下。
Func ConstrainedWriteMsr(int dwMsrIdx)
{
int value = 0;
if (dwMsrIdx == expected_index) // Blocks from reading anything
{
value = __writemsr(dwMsrIdx); // Can only write to the expected constrained MSR
}
else
{
return error;
}
return value;
}
// OR
Func ConstrainedWriteMSR(int dwMsrIdx)
{
int value = 0;
if (min_range <= dwMsrIdx <= max_range) // Blocks from reading anything
{
value = __writemsr(dwMsrIdx); // Can only write to the expected constrained MSR
}
else
{
return error;
}
return value;
}
提供終止進程的能力
在驅動程式中實作功能時,必須特別小心,以允許終止進程。 受保護的進程和受保護的進程光 (PPL) 程式,例如反惡意代碼和防毒解決方案所使用的程式,不得終止。 公開這項功能可讓攻擊者終止系統上的安全性保護。
如果您的案例需要進程終止,則必須實作下列檢查,以防止任意進程終止:
Func ConstrainedProcessTermination(DWORD dwProcessId)
{
// Function to check if a process is a Protected Process Light (PPL)
NTSTATUS status;
BOOLEAN isPPL = FALSE;
PEPROCESS process;
HANDLE hProcess;
// Open the process
status = PsLookupProcessByProcessId(processId, &process);
if (!NT_SUCCESS(status)) {
return FALSE;
}
// Check if the process is a PPL
if (PsIsProtectedProcess(process)) {
isPPL = TRUE;
}
// Dereference the process
ObDereferenceObject(process);
return isPPL;
}
提供讀取和寫入埠輸入和輸出的能力
增強從埠 IO 讀取的安全性
當提供能夠讀取到埠輸入/輸出 (I/O) 的能力時,必須使用警告。 此程式代碼範例不安全。
Func ArbitraryInputPort(int inPort)
{
dwResult = __indword(inPort); // Unsafe, allows for arbitrary reading from Input Port
return dwResult;
}
若要防止驅動程式的濫用和惡意探索,預期的輸入埠必須限制為必要的使用界限。
Func ConstrainedInputPort(int inPort)
{
// The expected input port must be constrained to the required usage boundary to prevent abuse
if(inPort == expected_InPort)
{
dwResult = __indword(inPort);
}
else
{
return error;
}
return dwResult;
}
增強寫入至埠 IO 的安全性
當提供寫入埠輸入/輸出 (I/O) 的能力時,必須使用警告。 此程式代碼範例不安全。
Func ArbitraryOutputPort(int outPort, DWORD dwValue)
{
__outdword(OutPort, dwValue); // Unsafe, allows for arbitrary writing to Output Port
}
若要防止驅動程式的濫用和惡意探索,預期的輸入埠必須限制為必要的使用界限。
Func ConstrainedOutputPort(int outPort, DWORD dwValue)
{
// The expected output port must be constrained to the required usage boundary to prevent abuse
if(outPort == expected_OutputPort)
{
__outdword(OutPort, dwValue); // checks on InputPort
}
else
{
return error;
}
}
提供讀取和寫入核心、實體或裝置記憶體的能力
增強 Memcpy 的安全性
此範例程式代碼顯示安全使用物理記憶體不受限制且不安全的使用。
Func ArbitraryMemoryCopy(src, dst, length)
{
memcpy(dst, src, length); // Unsafe, can read and write anything from physical memory
}
如果您的案例需要讀取和寫入核心、實體或裝置記憶體,驅動程序必須一律檢查來源和目的地是否受限於預期的索引或範圍。
Func ConstrainedMemoryCopy(src, dst, length)
{
// valid_src and valid_dst must be constrained to required usage boundary to prevent abuse
if(src == valid_Src && dst == valid_Dst)
{
memcpy(dst, src, length);
}
else
{
return error;
}
}
增強 ZwMapViewOfSection 的安全性
下列範例說明使用 ZwOpenSection 和 ZwMapViewOfSection API 從使用者模式讀取和寫入物理記憶體的不安全和不當方法。
Func ArbitraryMap(PHYSICAL_ADDRESS Address)
{
ZwOpenSection(&hSection, ... ,"\Device\PhysicalMemory");
ZwMapViewOfSection(hSection, -1, 0, 0, 0, Address, ...);
}
若要防止惡意使用者模式進程濫用和惡意探索驅動程式的讀取/寫入行為,驅動程式必須驗證輸入位址,並將記憶體對應限製為案例所需的使用界限。
Func ConstrainedMap(PHYSICAL_ADDRESS paAddress)
{
// expected_Address must be constrained to required usage boundary to prevent abuse
if(paAddress == expected_Address)
{
ZwOpenSection(&hSection, ... ,"\Device\PhysicalMemory");
ZwMapViewOfSection(hSection, -1, 0, 0, 0, paAddress, ...);
}
else
{
return error;
}
}
增強 MmMapLockedPagesSpecifyCache 的安全性
下列範例說明使用 MmMapIoSpace、IoAllocateMdl 和 MmMapLockedPagesSpecifyCache API 從使用者模式讀取和寫入物理記憶體的不安全和不當方法。
Func ArbitraryMap(PHYSICAL_ADDRESS paAddress)
{
lpAddress = MmMapIoSpace(paAddress, qwSize, ...);
pMdl = IoAllocateMdl( lpAddress, ...);
MmMapLockedPagesSpecifyCache(pMdl, UserMode, ... );
}
若要防止惡意使用者模式進程濫用和惡意探索驅動程式的讀取/寫入行為,驅動程式必須驗證輸入位址,並將記憶體對應限製為案例所需的使用界限。
Func ConstrainedMap(PHYSICAL_ADDRESS paAddress)
{
// expected_Address must be constrained to required usage boundary to prevent abuse
if(paAddress == expected_Address && qwSize == valid_Size)
{
lpAddress = MmMapIoSpace(paAddress, qwSize, ...);
pMdl = IoAllocateMdl( lpAddress, ...);
MmMapLockedPagesSpecifyCache(pMdl, UserMode, ... );
}
else
{
return error;
}
}