Dela via


Metodtips för att begränsa högprivilegierat beteende i kernellägesdrivrutiner

Det här avsnittet sammanfattar de osäkra utvecklingsmönstren som kan leda till utnyttjande och missbruk av din Windows-kerneldrivrutinskod. Det här avsnittet innehåller utvecklingsrekommendationer och kodexempel för att begränsa privilegierat beteende. Genom att följa dessa metodtips kan du förbättra säkerheten för att utföra privilegierat beteende i Windows-kerneln.

Översikt över osäkert drivrutinsbeteende

Det förväntas att Windows-drivrutiner utför högprivilegierat beteende i kernelläge, men det är oacceptabelt att inte utföra säkerhetskontroller och lägga till begränsningar för privilegierat beteende. Windows Hardware Compatibility Program (WHCP), tidigare WHQL, kräver att nya drivrutinsöverföringar uppfyller detta krav.

Exempel på oskyddat och farligt beteende omfattar, men är inte begränsat till, följande:

Ge möjlighet att läsa och skriva MSR

Förbättra säkerheten för avläsning från MSRs

I det här ReadMsr-exemplet tillåter drivrutinen osäkert beteende genom att tillåta att alla register läss godtyckligt med hjälp av det __readmsr modellspecifika registret. Detta kan leda till missbruk av skadliga processer i användarläge.

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

Om ditt scenario kräver läsning från MSR måste drivrutinen alltid kontrollera att registret som ska läsas från är begränsat till det förväntade indexet eller intervallet. Två exempel på hur du implementerar säker läsåtgärd följer.

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

Förbättra säkerheten vid skrivning till MSR:er

I det första WriteMsr-exemplet tillåter drivrutinen osäkert beteende genom att tillåta att alla register skrivs godtyckligt till. Skadliga processer kan missbruka detta för att höja behörigheten i användarläge och skriva till alla MSR-register.

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

Om ditt scenario kräver att du skriver till MSR måste drivrutinen alltid kontrollera att registret som ska skrivas till är begränsat till det förväntade indexet eller intervallet. Två exempel på hur du implementerar den säkra skrivåtgärden följer.

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

Ge möjlighet att avsluta processer

Extrem försiktighet måste användas när du implementerar funktioner i drivrutinen, vilket gör att processer kan avslutas. Skyddade processer och processer med skyddat processtillägg (PPL), som de som används av anti-malware och antiviruslösningar, får inte avslutas. Genom att exponera den här funktionen kan angripare avsluta säkerhetsskyddet i systemet.

Om ditt scenario kräver att processen avslutas måste följande kontroller implementeras för att skydda mot godtycklig processavslutning med hjälp av PsLookupProcessByProcessId och 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;
}

Ge möjlighet att läsa och skriva till portindata och utdata

Förbättra säkerheten för läsning från port-I/O

Försiktighet måste användas när du ger möjlighet att läsa till portindata/utdata (I/O). Det här kodexemplet som använder __indword är osäkert.

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

För att förhindra missbruk och utnyttjande av drivrutinen måste den förväntade indataporten begränsas till den användningsgräns som krävs.

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

Förbättra säkerheten vid skrivning till port-I/O

Försiktighet måste användas när du ger möjlighet att skriva till portindata/utdata (I/O). Det här kodexemplet som använder __outword är osäkert.

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

För att förhindra missbruk och utnyttjande av drivrutinen måste den förväntade indataporten begränsas till den användningsgräns som krävs.

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

Ge möjlighet att läsa och skriva kernelminne, fysiskt minne eller enhetsminne

Förbättra säkerheten för Memcpy

Den här exempelkoden visar obegränsad och osäker användning av säker användning av fysiskt minne.

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

Om ditt scenario kräver att du läser och skriver kernelminne, fysiskt minne eller enhetsminne måste drivrutinen alltid kontrollera att källan och målen är begränsade till de förväntade indexen eller intervallen.

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

Förbättra säkerheten för ZwMapViewOfSection

I följande exempel visas den osäkra och felaktiga metoden för att läsa och skriva fysiskt minne från användarläge med hjälp av ZwOpenSection- och ZwMapViewOfSection API:er.

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

För att förhindra missbruk och utnyttjande av drivrutinens läs-/skrivbeteende genom skadliga användarlägesprocesser måste drivrutinen verifiera indataadressen och begränsa minnesmappningen endast till den användningsgräns som krävs för scenariot.

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

Förbättra säkerheten för MmMapLockedPagesSpecifyCache

I följande exempel visas den osäkra och felaktiga metoden för att läsa och skriva fysiskt minne från användarläge med hjälp av MmMapIoSpace, IoAllocateMdl och MmMapLockedPagesSpecifyCache API:er.

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

För att förhindra missbruk och utnyttjande av drivrutinens läs-/skrivbeteende genom skadliga användarlägesprocesser måste drivrutinen verifiera indataadressen och begränsa minnesmappningen endast till den användningsgräns som krävs för scenariot.

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

Se även

Checklista för drivrutinssäkerhet