Udostępnij za pośrednictwem


Ochrona przepływu sterowania dla bezpieczeństwa platformy

Co to jest funkcja Control Flow Guard?

Control Flow Guard (CFG) to wysoce zoptymalizowana funkcja zabezpieczeń platformy, która została utworzona w celu zwalczania luk w zabezpieczeniach związanych z uszkodzeniem pamięci. Umieszczając ścisłe ograniczenia dotyczące tego, skąd aplikacja może wykonywać kod, znacznie trudniej jest wykorzystać luki w zabezpieczeniach, takie jak przepełnienie buforu. CFG rozszerza poprzednie technologie ograniczania ryzyka wykorzystania, takie jak /GS (Buffer Security Check), Data Execution Prevention (DEP)i Randomizacja układu przestrzeni adresowej (ASLR).

Korzystanie z usługi CFG może pomóc w:

  • Zapobiegaj korygowaniu pamięci i atakom ransomware.
  • Ogranicz możliwości serwera tylko do tego, co jest potrzebne w określonym momencie w czasie, aby zmniejszyć obszar ataków.
  • Utrudnij wykorzystywanie dowolnego kodu poprzez luki w zabezpieczeniach, takie jak przepełnienia bufora.

Ta funkcja jest dostępna w programie Microsoft Visual Studio i działa w wersji systemu Windows obsługujących usługę CFG; Systemy Windows 10 i Windows 11 na kliencie i windows Server 2019 lub nowszym po stronie serwera.

Deweloperzy są zdecydowanie zachęcani do włączania cfg dla swoich aplikacji. Nie musisz włączać CFG dla każdej części kodu, ponieważ kombinacja kodu z włączonym CFG i kodu bez obsługi CFG zostanie wykonana prawidłowo. Jednak niewłączenie usługi CFG dla całego kodu może otwierać luki w ochronie. Ponadto kod z włączoną usługą CFG działa prawidłowo na wersjach systemu Windows CFG-Unaware i dlatego jest z nimi w pełni zgodny.

Jak mogę włączyć usługę CFG?

W większości przypadków nie trzeba zmieniać kodu źródłowego. Wystarczy dodać opcję do projektu programu Visual Studio, a kompilator i konsolidator włączy usługę CFG.

Najprostszą metodą jest przejście do Project | Właściwości | Właściwości konfiguracji | C/C++ | Generowanie kodu i wybierz Tak (/guard:cf) dla funkcji Control Flow Guard.

Zrzut ekranu przedstawiający właściwość Control Flow Guard w konfiguracji kompilacji generowania kodu programu Visual Studio.

Alternatywnie dodaj /guard:cf do Project | Właściwości | Właściwości konfiguracji | C/C++ | Wiersz polecenia | Dodatkowe opcje (dla kompilatora) i /guard:cf do Project | Właściwości | Właściwości konfiguracji | Linker | Wiersz polecenia | Dodatkowe opcje (dla linkera).

Zrzut ekranu przedstawiający konfigurację kompilacji Dodatkowe opcje wiersza polecenia C/C++ w programie Visual Studio z właściwością ustawioną na /guard:cf

Zrzut ekranu przedstawiający konfigurację kompilacji opcji dodatkowych w wierszu polecenia konsolidatora w programie Visual Studio z właściwością ustawioną na /guard:cf

Aby uzyskać dodatkowe informacje, zobacz /guard (Włącz funkcję Control Flow Guard).

Jeśli tworzysz projekt z poziomu wiersza polecenia, możesz dodać te same opcje. Jeśli na przykład kompilujesz projekt o nazwie test.cpp, użyj polecenia cl /guard:cf test.cpp /link /guard:cf.

Istnieje również możliwość dynamicznego kontrolowania zestawu adresów docelowych icall, które są uznawane za prawidłowe przez CFG, używając SetProcessValidCallTargets z interfejsu API zarządzania pamięcią. Tego samego API można użyć do określenia, czy strony są nieprawidłowe, czy prawidłowe cele dla CFG. Funkcje VirtualProtect i VirtualAlloc domyślnie traktują określony region stron wykonywalnych i zatwierdzonych jako prawidłowe obiekty docelowe wywołania pośredniego. Można zastąpić to zachowanie, na przykład podczas implementowania kompilatora just-in-time, określając PAGE_TARGETS_INVALID podczas wywoływania VirtualAlloc lub PAGE_TARGETS_NO_UPDATE podczas wywoływania VirtualProtect zgodnie z opisem w sekcji Stałe ochrony pamięci.

Jak sprawdzić, czy plik binarny znajduje się pod kontrolą funkcji Flow Guard?

Uruchom narzędzie dumpbin (uwzględnione w instalacji programu Visual Studio) z opcjami /headers i /loadconfig w wierszu polecenia programu Visual Studio: dumpbin /headers /loadconfig test.exe. Dane wyjściowe pliku binarnego w obszarze CFG powinny wskazywać, że wartości nagłówka zawierają wartość "Guard" i że wartości konfiguracji ładowania obejmują "CF Instrumented" i "tabela FID present".

Zrzut ekranu przedstawiający dane wyjściowe z polecenia dumpbin /headers

Zrzut ekranu danych wyjściowych z dumpbin /loadconfig

Jak działa cfg naprawdę?

Luki w zabezpieczeniach oprogramowania są często wykorzystywane przez zapewnienie mało prawdopodobnych, nietypowych lub ekstremalnych danych uruchomionym programowi. Na przykład osoba atakująca może wykorzystać lukę w zabezpieczeniach przepełnienia buforu, zapewniając więcej danych wejściowych do programu niż oczekiwano, tym samym nadmiernie uruchamiając obszar zarezerwowany przez program do przechowywania odpowiedzi. Może to spowodować uszkodzenie sąsiadującej pamięci, która może zawierać wskaźnik funkcji. Gdy program wywołuje tę funkcję, może przejść do niezamierzonej lokalizacji określonej przez osobę atakującą.

Jednak silna kombinacja wsparcia podczas kompilacji i w czasie wykonywania z CFG implementuje integralność przepływu sterowania, która ściśle ogranicza miejsce wykonywania instrukcji wywołań pośrednich.

Kompilator wykonuje następujące czynności:

  1. Dodaje uproszczone kontrole zabezpieczeń do skompilowanego kodu.
  2. Określa zestaw funkcji w aplikacji, które są prawidłowymi miejscami docelowymi dla wywołań pośrednich.

Obsługa środowiska uruchomieniowego zapewniana przez jądro systemu Windows:

  1. Efektywnie utrzymuje stan identyfikujący prawidłowe cele wywołań pośrednich.
  2. Implementuje logikę, która sprawdza, czy element docelowy wywołania pośredniego jest prawidłowy.

Aby zilustrować:

Diagram ilustrujący kontrole CFG wstawione przez kompilator.

Gdy sprawdzanie CFG kończy się niepowodzeniem w czasie wykonywania, system Windows natychmiast kończy program, powodując w ten sposób przerwanie wszelkich luk w zabezpieczeniach, które próbują pośrednio wywołać nieprawidłowy adres.

/guard (Włącz funkcję Control Flow Guard)

/GUARD (Włącz kontrole Guard)