Gdy procesor znajduje się w trybie użytkownika, procesor nie może wykonywać uprzywilejowanych instrukcji i nie może uzyskać dostępu do pamięci miejsca w jądrze.
A gdy procesor jest w trybie jądra, może wykonywać wszystkie instrukcje i mieć dostęp do całej pamięci.
Teraz w Linuksie program trybu użytkownika może uzyskać dostęp do całej pamięci (używając /dev/mem
) i może wykonać dwie uprzywilejowane instrukcje IN
oraz OUT
(używając iopl()
myślę).
Więc program trybu użytkownika w Linuksie może robić większość rzeczy (myślę, że większość rzeczy), które można zrobić w trybie jądra.
Czy nie zezwalanie, aby program trybu użytkownika miał całą tę moc, jest sprzeczny z celem posiadania trybów procesora?
iopl
nie zezwala na wszystkie uprzywilejowane instrukcje, więc nadal jest użyteczny, aby upewnić się, żeinvd
błędny program przestrzeni użytkownika nie zostanie przypadkowo uruchomiony , przeskakując przez uszkodzony wskaźnik funkcji wskazujący na wykonywalną pamięć zaczynającą się od0F 08
bajtów. Dodałem odpowiedź z niektórymi przyczynami niezwiązanymi z bezpieczeństwem, dlaczego użyteczne jest, aby procesy w przestrzeni użytkownika podniosły ich uprawnienia.Tylko w ten sam sposób, w jaki
modprobe
„pokonuje” bezpieczeństwo, ładując nowy kod do jądra.Z różnych powodów czasem lepiej jest mieć częściowo uprzywilejowany kod (np. Sterowniki graficzne na serwerze X) działający w przestrzeni użytkownika, a nie w wątku jądra.
kill
zrobić łatwiej, chyba że zablokuje HW.Nie robi wiele dla bezpieczeństwa, ale ma wiele zalet związanych z niezawodnością i architekturą oprogramowania.
Pieczenie sterowników graficznych w jądrze może zredukować przełączanie kontekstu między klientami X a serwerem X, jak tylko jeden użytkownik-> jądro-> użytkownik, zamiast konieczności wprowadzania danych do innego procesu przestrzeni użytkowej, ale serwery X były historycznie za duże i zbyt błędne chcieć ich w pełni w jądrze.
Tak, złośliwy kod z tymi uprawnieniami może w razie potrzeby przejąć jądro,
/dev/mem
modyfikując kod jądra.Lub na przykład na x86, uruchom
cli
instrukcję, aby wyłączyć przerwania na tym rdzeniu poiopl
wywołaniu systemowym, aby ustawić poziom uprawnień IO na ring 0.Ale nawet
iopl
„tylko” x86 daje dostęp do niektórych instrukcji : in / out (i wersje string in / out) oraz cli / sti. Nie pozwala używaćrdmsr
aniwrmsr
odczytywać ani zapisywać „rejestrów specyficznych dla modelu” (np.IA32_LSTAR
Który ustawia adres punktu wejścia jądra dlasyscall
instrukcji x86-64 ), ani nielidt
zastępuje tablicy deskryptorów przerwań (co pozwoliłoby ci całkowicie wziąć na maszynie z istniejącego jądra, przynajmniej na tym rdzeniu.)Nie można nawet odczytać rejestrów kontrolnych (takich jak CR3, który zawiera adres fizyczny katalogu stron najwyższego poziomu, do którego atakujący może się przydać jako przesunięcie w
/dev/mem
celu zmodyfikowania własnych tabel stron jako alternatywy dlammap
większej ilości danych/dev/mem
. )invd
(unieważnij wszystkie pamięci podręczne bez zapisywania !! ( przypadek użycia = wczesny BIOS przed skonfigurowaniem pamięci RAM)) to kolejna zabawna, która zawsze wymaga pełnego CPL 0 (aktualny poziom uprawnień), nie tylko IOPL. Nawetwbinvd
jest uprzywilejowany, ponieważ jest tak wolny (i nie jest przerywany) i musi opróżniać wszystkie pamięci podręczne na wszystkich rdzeniach. (Patrz Czy istnieje sposób, aby wypłukać całą pamięć podręczną procesora związanych z programem? I wykorzystanie instrukcji WBINVD )Błędy, które powodują przeskakiwanie na zły adres z działającymi danymi jako kodem, nie mogą więc przypadkowo wykonać żadnej z tych instrukcji na serwerze X w przestrzeni użytkownika.
Obecny poziom uprawnień (w trybie chronionym i długim) to niskie 2 bity
cs
(selektora segmentu kodu) .mov eax, cs
/and eax, 3
działa w dowolnym trybie, aby odczytać poziom uprawnień.Aby napisać poziom uprawnień, wykonaj a
jmp far
lubcall far
ustawCS:RIP
(ale wpis GDT / LDT dla segmentu docelowego może go ograniczyć na podstawie starego poziomu uprawnień, dlatego przestrzeń użytkownika nie może tego zrobić, aby się podnieść). Lub używaszint
lub,syscall
aby przełączyć na pierścień 0 w punkcie wejścia jądra.źródło