Share via


Bewährte Methoden für Die Sicherheit von Windows-Treibern für Treiberentwickler

In diesem Thema werden die unsicheren Entwicklungsmuster zusammengefasst, die zur Ausnutzung und Missbrauch Ihres Windows-Treibercodes führen können. Dieses Thema enthält Entwicklungsempfehlungen und Codebeispiele. Die Verwendung dieser bewährten Methoden trägt dazu bei, die Sicherheit der Ausführung privilegierter Verhaltensweisen im Windows-Kernel zu verbessern.

Übersicht über das Verhalten unsicherer Treiber

Es wird zwar erwartet, dass Windows-Treiber im Kernelmodus ein hohes Privilegiertes Verhalten ausführen, aber das Ausführen von Sicherheitsüberprüfungen und das Hinzufügen von Einschränkungen für privilegiertes Verhalten ist sicher inakzeptabel. Das Windows Hardware Compatibility Program (WHCP), früher WHQL, erfordert neue Treiberübermittlungen, um diese Anforderung zu erfüllen.

Beispiele für unsicheres und gefährliches Verhalten sind unter anderem folgendes:

Möglichkeit zum Lesen und Schreiben von MSRs

Erhöhen der Sicherheit des Lesens aus MSRs

Im ersten ReadMsr-Beispiel ermöglicht der Treiber unsicheres Verhalten, indem alle Register beliebig gelesen werden können. Dies kann zu Missbrauch durch böswillige Prozesse im Benutzermodus führen.

Func ReadMsr(int dwMsrIdx) 
{
	int value = __readmsr(dwMsrIdx); // Unsafe, can read from any MSR
	return value;
}

Wenn ihr Szenario das Lesen von MSRs erfordert, muss der Treiber immer überprüfen, ob das Register, aus dem gelesen werden soll, auf den erwarteten Index oder Bereich beschränkt ist. Es folgen zwei Beispiele für die Implementierung des Vorgangs zum sicheren Lesen.

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

Verbessern der Sicherheit des Schreibens in MSRs

Im ersten WriteMsr-Beispiel ermöglicht der Treiber unsicheres Verhalten, indem er erlaubt, dass alle Register beliebig in geschrieben werden. Dies kann zu Missbrauch durch böswillige Prozesse führen, um Berechtigungen im Benutzermodus zu erhöhen und in alle MSRs zu schreiben.

Func WriteMsr(int dwMsrIdx) 
{
	int value = __writemsr(dwMsrIdx); // Unsafe, can write to any MSR
	return value;
}

Wenn ihr Szenario das Schreiben in MSRs erfordert, muss der Treiber immer überprüfen, ob das Register, in das geschrieben werden soll, auf den erwarteten Index oder Bereich beschränkt ist. Es folgen zwei Beispiele für die Implementierung des Vorgangs zum sicheren Schreiben.

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

Möglichkeit zum Lesen und Schreiben in Porteingabe und -ausgabe

Erhöhen der Sicherheit beim Lesen von Port-E/A

Vorsicht ist geboten, wenn die Möglichkeit geboten wird, die Porteingabe/-ausgabe (E/A) zu lesen. Dieses Codebeispiel ist unsicher.

Func ArbitraryInputPort(int inPort) 
{
	dwResult = __indword(inPort); // Unsafe, allows for arbitrary reading from Input Port
	return dwResult; 
}

Um den Missbrauch und exploit des Treibers zu verhindern, muss der erwartete Eingabeport auf die erforderliche Nutzungsgrenze beschränkt werden.

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

Verbessern der Schreibsicherheit für Port-E/A

Vorsicht ist geboten, wenn die Möglichkeit zum Schreiben in Porteingabe/-ausgabe (E/A) bereitgestellt werden kann. Dieses Codebeispiel ist unsicher.

Func ArbitraryOutputPort(int outPort, DWORD dwValue) 
{
	__outdword(OutPort, dwValue); // Unsafe, allows for arbitrary writing to Output Port
}

Um den Missbrauch und exploit des Treibers zu verhindern, muss der erwartete Eingabeport auf die erforderliche Nutzungsgrenze beschränkt werden.

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

Möglichkeit zum Lesen und Schreiben von Kernel-, physischem oder Gerätespeicher

Erhöhen der Sicherheit von Memcpy

Dieser Beispielcode zeigt die nicht eingeschränkte und unsichere Verwendung der sicheren Nutzung des physischen Arbeitsspeichers.

Func ArbitraryMemoryCopy(src, dst, length) 
{
	memcpy(dst, src, length); // Unsafe, can read and write anything from physical memory
}

Wenn Ihr Szenario das Lesen und Schreiben von Kernel-, physischem oder Gerätespeicher erfordert, muss der Treiber immer überprüfen, ob die Quelle und die Ziele auf die erwarteten Indizes oder Bereiche beschränkt sind.

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

Erhöhen der Sicherheit von ZwMapViewOfSection

Das folgende Beispiel veranschaulicht die unsichere und nicht ordnungsgemäße Methode zum Lesen und Schreiben von physischem Arbeitsspeicher aus dem Benutzermodus mithilfe der ZwOpenSection- und ZwMapViewOfSection-APIs.

Func ArbitraryMap(PHYSICAL_ADDRESS Address)
{
	ZwOpenSection(&hSection, ... ,"\Device\PhysicalMemory");
	ZwMapViewOfSection(hSection, -1, 0, 0, 0, Address, ...);
}

Um den Missbrauch und die Ausnutzung des Lese-/Schreibverhaltens des Treibers durch schädliche Benutzermodusprozesse zu verhindern, muss der Treiber die Eingabeadresse überprüfen und die Speicherzuordnung nur auf die erforderliche Nutzungsgrenze für das Szenario beschränken.

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

Erhöhen der Sicherheit von MmMapLockedPagesSpecifyCache

Das folgende Beispiel veranschaulicht die unsichere und nicht ordnungsgemäße Methode zum Lesen und Schreiben von physischem Arbeitsspeicher aus dem Benutzermodus mithilfe der APIs MmMapIoSpace, IoAllocateMdl und MmMapLockedPagesSpecifyCache.

Func ArbitraryMap(PHYSICAL_ADDRESS paAddress)
{
	lpAddress = MmMapIoSpace(paAddress, qwSize, ...);
	pMdl = IoAllocateMdl( lpAddress, ...);
	MmMapLockedPagesSpecifyCache(pMdl, UserMode, ... );
}

Um den Missbrauch und die Ausnutzung des Lese-/Schreibverhaltens des Treibers durch schädliche Benutzermodusprozesse zu verhindern, muss der Treiber die Eingabeadresse überprüfen und die Speicherzuordnung nur auf die erforderliche Nutzungsgrenze für das Szenario beschränken.

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

Weitere Informationen

Sicherheitscheckliste für Treiber