Share via


Procedure consigliate per la sicurezza dei driver di Windows per gli sviluppatori di driver

Questo argomento riepiloga i modelli di sviluppo non sicuri che possono causare lo sfruttamento e l'abuso del codice del driver Di Windows. In questo argomento vengono forniti consigli per lo sviluppo e esempi di codice. Seguendo queste procedure consigliate, sarà possibile migliorare la sicurezza dell'esecuzione del comportamento con privilegi nel kernel di Windows.

Panoramica del comportamento del driver non sicuro

Sebbene sia previsto che i driver di Windows eseguano un comportamento con privilegi elevati in modalità kernel, non eseguano controlli di sicurezza e non vengano aggiunti vincoli sul comportamento con privilegi non è accettabile. Il programma di compatibilità hardware Windows (WHCP), in precedenza WHQL, richiede nuovi invii di driver per rispettare questo requisito.

Esempi di comportamenti non sicuri e pericolosi includono, ma non sono limitati a, quanto segue:

Possibilità di leggere e scrivere msr

Miglioramento della sicurezza della lettura da msR

Nel primo esempio di ReadMsr, il driver consente il comportamento non sicuro consentendo la lettura arbitraria di tutti i registri. Ciò può causare l'abuso da processi dannosi in modalità utente.

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

Se lo scenario richiede la lettura da msr, il driver deve sempre verificare che il registro da cui leggere sia vincolato all'indice o all'intervallo previsto. Due esempi di come implementare l'operazione di lettura sicura seguono.

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

Miglioramento della sicurezza della scrittura in MSR

Nel primo esempio writeMsr il driver consente il comportamento non sicuro consentendo la scrittura arbitraria di tutti i registri. Ciò può causare l'abuso da parte di processi dannosi per elevare i privilegi in modalità utente e scrivere in tutte le richieste di archiviazione.

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

Se lo scenario richiede la scrittura in MSR, il driver deve sempre verificare che il registro da scrivere in sia vincolato all'indice o all'intervallo previsto. Due esempi di come implementare l'operazione di scrittura sicura seguono.

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

Possibilità di leggere e scrivere nell'input e nell'output della porta

Miglioramento della sicurezza della lettura da I/O porta

Attenzione deve essere usata, quando si fornisce la possibilità di leggere l'input/output della porta (I/O). Questo esempio di codice non è sicuro.

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

Per evitare l'abuso e l'exploit del driver, la porta di input prevista deve essere vincolata al limite di utilizzo richiesto.

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

Miglioramento della sicurezza della scrittura in I/O porta

Attenzione deve essere usata, quando si fornisce la possibilità di scrivere in Input/output porta (I/O). Questo esempio di codice non è sicuro.

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

Per evitare l'abuso e l'exploit del driver, la porta di input prevista deve essere vincolata al limite di utilizzo richiesto.

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

Possibilità di leggere e scrivere kernel, fisici o memoria del dispositivo

Miglioramento della sicurezza di Memcpy

Questo codice di esempio mostra un uso non vincolato e non sicuro dell'uso sicuro della memoria fisica.

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

Se lo scenario richiede la lettura e la scrittura di kernel, memoria fisica o dispositivo, il driver deve sempre verificare che l'origine e le destinazioni siano vincolate agli indici o agli intervalli previsti.

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

Miglioramento della sicurezza di ZwMapViewOfSection

Nell'esempio seguente viene illustrato il metodo non sicuro e non corretto per leggere e scrivere memoria fisica dalla modalità utente usando le API ZwOpenSection e ZwMapViewOfSection.

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

Per evitare l'abuso e l'exploit del comportamento di lettura/scrittura del driver da processi in modalità utente dannosi, il driver deve convalidare l'indirizzo di input e limitare il mapping della memoria solo al limite di utilizzo richiesto per lo scenario.

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

Miglioramento della sicurezza di MmMapLockedPagesSpecifyCache

Nell'esempio seguente viene illustrato il metodo non sicuro e non corretto per leggere e scrivere memoria fisica dalla modalità utente usando le API MmMapIoSpace, IoAllocateMdl e MmMapLockedPagesSpecifyCache.

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

Per evitare l'abuso e l'exploit del comportamento di lettura/scrittura del driver da processi in modalità utente dannosi, il driver deve convalidare l'indirizzo di input e limitare il mapping della memoria solo al limite di utilizzo richiesto per lo scenario.

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

Vedere anche

Elenco di controllo per la sicurezza dei driver