다음을 통해 공유


드라이버 개발자를 위한 Windows 드라이버 보안 모범 사례

이 항목에서는 Windows 드라이버 코드의 악용 및 남용으로 이어질 수 있는 안전하지 않은 개발 패턴을 요약합니다. 이 항목에서는 개발 권장 사항 및 코드 샘플을 제공합니다. 이러한 모범 사례를 따르면 Windows 커널에서 권한 있는 동작을 수행하는 안전성을 개선하는 데 도움이 됩니다.

안전하지 않은 드라이버 동작 개요

Windows 드라이버가 커널 모드에서 높은 권한 있는 동작을 수행할 것으로 예상되지만 보안 검사를 수행하지 않고 권한 있는 동작에 대한 제약 조건을 추가하는 것은 허용되지 않습니다. 이전의 WHQL인 Windows WHCP(하드웨어 호환성 프로그램)에서는 이 요구 사항을 준수하기 위해 새 드라이버 제출이 필요합니다.

안전하지 않고 위험한 동작의 예로는 다음이 포함되지만 이에 국한되지는 않습니다.

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;
	}
}

참고 항목

드라이버 보안 검사 목록