限制核心模式驅動程式中高特殊許可權行為的最佳做法
本主題摘要說明可能導致利用和濫用 Windows 核心驅動程式碼的不安全開發模式。 本主題提供開發建議和程式代碼範例,以協助限制特殊許可權行為。 遵循這些最佳做法有助於改善在 Windows 核心中執行特殊許可權行為的安全性。
不安全的驅動程序行為概觀
雖然 Windows 驅動程式預期會在核心模式中執行高特殊許可權行為,但不會執行安全性檢查,且對特殊許可權行為新增條件約束是不可接受的。 Windows 硬體相容性計畫 (WHCP),先前稱為WHQL,需要新的驅動程式提交,以符合這項需求。
不安全的和危險行為的範例包括,但不限於下列事項:
提供讀取和寫入 MSR 的能力
增強從 MSR 讀取的安全性
在此 ReadMsr 範例中,驅動程式允許使用 __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),例如用於反惡意軟體和防毒解決方案的那些,不得被終止。 公開這項功能可讓攻擊者終止系統上的安全性保護。
如果您的情境需要終止程式,則必須實作下列檢查,以保護避免任意程式終止,並使用 PsLookupProcessByProcessId 和 PsIsProtectedProcess
。
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)能力時,必須小心。 這個使用 __indword 的程式代碼範例不安全。
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)進行寫入操作時,必須謹慎行事。 這個使用 __outword 的程式代碼範例不安全。
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;
}
}