Udostępnij przez


Implementowanie kodu zgodnego z integralnością pamięci

W tej sekcji opisano sposób implementowania kodu zgodnego z integralnością pamięci.

Uwaga

Integralność pamięci jest czasami określana jako integralność kodu chronionego przez hypervisor (HVCI) lub jako integralność kodu wymuszana przez hypervisori została pierwotnie wydana jako część Device Guard. Funkcja Device Guard nie jest już używana z wyjątkiem lokalizacji integralności pamięci i ustawień VBS w zasadach grupy lub rejestrze systemu Windows.

Aby zaimplementować zgodny kod, upewnij się, że kod sterownika wykonuje następujące czynności:

  • Domyślnie włącza NX
  • Używa interfejsów API NX i flag do alokacji pamięci (NonPagedPoolNx)
  • Nie używa sekcji, które są zarówno zapisywalne, jak i wykonywalne
  • Nie próbuje bezpośrednio modyfikować pamięci systemu wykonywalnego
  • Nie używa kodu dynamicznego w jądrze
  • Nie ładuje plików danych jako pliku wykonywalnego
  • Wyrównanie sekcji jest wielokrotnością 0x1000 (PAGE_SIZE). Np. DRIVER_ALIGNMENT=0x1000

Następująca lista identyfikatorów DDI, które nie są zarezerwowane do użytku systemowego, może być dotknięta.

Nazwa identyfikatora DDI
ExAllocatePool
ExAllocatePoolWithQuota
ExAllocatePoolWithQuotaTag
ExAllocatePoolWithTag
ExAllocatePoolWithTagPriority
ExInitializeNPagedLookasideList
ExInitializeLookasideListEx
MmAllocateContiguousMemory
MmAllocateContiguousMemorySpecifyCache
MmAllocateContiguousMemorySpecifyCacheNode
MmAllocateContiguousNodeMemory
MmCopyMemory
MmMapIoSpace
MmMapLockedPages
MmMapLockedPagesSpecifyCache
MmProtectMdlSystemAddress
ZwAllocateVirtualMemory
ZwCreateSection
ZwMapViewOfSection
NtCreateSection
NtMapViewOfSection
ClfsCreateMarshallingArea
NDIS
NdisAllocateMemoryWithTagPriority
Magazynowanie
StorPortGetDataInBufferSystemAddress
StorPortGetSystemAddress
ChangerClassAllocatePool
Wyświetlacz
DxgkCbMapMemory
VideoPortAllocatePool
Audio Miniport
IMiniportDMus::NewStream
IMiniportMidi::NewStream
IMiniportWaveCyclic::NewStream
IPortWavePci::NewMasterDmaChannel
IMiniportWavePci::NewStream
Klasa portu audio
PcNewDmaChannel
PcNewResourceList
PcNewResourceSublist
IFS
FltAllocatePoolAlignedWithTag
FltAllocateContext
WDF
WdfLookasideListCreate
WdfMemoryCreate
WdfDeviceAllocAndQueryProperty
WdfDeviceAllocAndQueryPropertyEx
WdfFdoInitAllocAndQueryProperty
WdfFdoInitAllocAndQueryPropertyEx
WdfIoTargetAllocAndQueryTargetProperty
WdfRegistryQueryMemory

Testowanie zgodności sterowników integralności pamięci za pomocą testów integralności kodu w HLK

Aby uzyskać więcej informacji na temat testu zabezpieczeń dotyczącego podstawowych zasad systemu, zobacz Test gotowości integralności kodu HyperVisor i integralność pamięci oraz VBS.

Aby uzyskać więcej informacji na temat powiązanego testu podstawowych funkcji urządzenia, zobacz Testy Device.DevFund.

Poniższa tabela służy do interpretowania danych wyjściowych i określania, jakie zmiany kodu sterownika są potrzebne do naprawienia różnych typów niezgodności integralności pamięci.

Ostrzeżenie Odkupienie

Wykonywanie typu puli

Obiekt wywołujący określił typ puli wykonywalnych. Wywoływanie funkcji przydzielania pamięci, która żąda pamięci wykonywalnej.

Upewnij się, że wszystkie typy puli zawierają nie wykonywalną flagę NX.

Wykonywanie ochrony strony

Obiekt wywołujący określił ochronę strony wykonywalnych.

Określ maskę ochrony strony "no execute".

Wykonywanie mapowania stron

Wywołujący określił mapowanie listy deskryptora pamięci wykonywalnej (MDL).

Upewnij się, że używana maska zawiera MdlMappingNoExecute. Aby uzyskać więcej informacji, zobacz MmGetSystemAddressForMdlSafe

Sekcja Execute-Write

Plik obrazu zawiera sekcję wykonywalną i zapisywalną.

Błędy wyrównania sekcji

Obraz zawiera sekcję, która nie jest wyrównana do strony.

Wyrównanie sekcji musi być wielokrotną 0x1000 (PAGE_SIZE). Np. DRIVER_ALIGNMENT=0x1000

IAT w sekcji wykonywalnego pliku

Tabela adresów importu (IAT) nie powinna być sekcją wykonywalną pamięci.

Ten problem występuje, gdy IAT znajduje się w sekcji pamięci przeznaczonej wyłącznie do odczytu i wykonywania (RX). Oznacza to, że system operacyjny nie będzie mógł zapisywać danych w funkcji IAT w celu ustawienia poprawnych adresów, dla których odwołuje się biblioteka DLL.

Jednym ze sposobów, w jaki może się to zdarzyć, jest użycie opcji /MERGE (Połącz sekcje) w łączeniu kodu. Jeśli na przykład plik rdata (dane inicjowane tylko do odczytu) jest scalany z danymi tekstowymi (kod wykonywalny), możliwe, że dane IAT mogą znajdować się w sekcji wykonywalnej pamięci.


Nieobsługiwane relokacje

W systemie Windows 10 w wersji 1507–Windows 10 w wersji 1607 ze względu na użycie losowego układu przestrzeni adresowej (ASLR) może wystąpić problem z wyrównaniem adresów i relokacją pamięci. System operacyjny musi przenieść adres z miejsca, gdzie linker ustawił domyślny adres podstawowy, do rzeczywistej lokalizacji przypisanej przez metodę losowego rozmieszczania przestrzeni adresowej (ASLR). Ta relokacja nie może przekraczać granicy strony. Rozważmy na przykład 64-bitową wartość adresu rozpoczynającą się od przesunięcia 0x3FFC na stronie. Wartość adresu nakłada się na następną stronę z przesunięciem 0x0003. Ten typ nakładających się relokacji nie jest obsługiwany przed systemem Windows 10 wersja 1703.

Taka sytuacja może wystąpić, gdy inicjalizator zmiennej typu strukturalnego ma niewyrównany wskaźnik do innej globalnej zmiennej, określony w taki sposób, że linker nie może przenieść zmiennej, aby uniknąć przekraczającej relokacji. Linker spróbuje przenieść zmienną, ale istnieją sytuacje, w których może nie być w stanie tego zrobić (na przykład w przypadku dużych niespasowanych struktur lub dużych tablic niespasowanych struktur). W razie potrzeby, moduły powinny być montowane przy użyciu opcji /Gy (COMDAT), aby umożliwić linkerowi jak największe wyrównanie kodu modułu.

#include <pshpack1.h>

typedef struct _BAD_STRUCT {
      USHORT Value;
      CONST CHAR *String;
} BAD_STRUCT, * PBAD_STRUCT;

#include <poppack.h>

#define BAD_INITIALIZER0 { 0, "BAD_STRING" },
#define BAD_INITIALIZER1 \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      \
      BAD_INITIALIZER0      

#define BAD_INITIALIZER2 \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      \
      BAD_INITIALIZER1      

#define BAD_INITIALIZER3 \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      \
      BAD_INITIALIZER2      

#define BAD_INITIALIZER4 \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      \
      BAD_INITIALIZER3      

BAD_STRUCT MayHaveStraddleRelocations[4096] = { // as a global variable
      BAD_INITIALIZER4
};

Istnieją inne sytuacje związane z użyciem kodu asemblera, w których ten problem może również wystąpić.


Integralność kodu weryfikatora sterowników

Użyj flagi opcji integralności kodu weryfikatora sterowników (0x02000000), aby włączyć dodatkowe kontrole sprawdzające zgodność z tą funkcją. Aby włączyć tę funkcję z poziomu wiersza polecenia, użyj następującego polecenia.

verifier.exe /flags 0x02000000 /driver <driver.sys>

Aby wybrać tę opcję, jeśli używasz graficznego interfejsu użytkownika weryfikatora, wybierz pozycję Utwórz ustawienia niestandardowe (dla deweloperów kodu), wybierz przycisk Dalej, a następnie wybierz pozycję Sprawdzanie integralności kodu.

Możesz użyć opcji wiersza polecenia /query weryfikatora, aby wyświetlić bieżące informacje weryfikatora sterownika.

verifier /query

Zobacz też

lista kontrolna dotycząca zabezpieczeń sterownika