面向驱动程序开发人员的 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;
}

提供读取和写入端口输入和输出的功能

增强从端口 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;
	}
}

另请参阅

驱动程序安全清单