malloc & sysmalloc
Last updated
Last updated
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE) Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
(W tym podsumowaniu nie są wyjaśnione żadne kontrole, a niektóre przypadki zostały pominięte dla zwięzłości)
__libc_malloc
próbuje uzyskać kawałek z tcache, jeśli nie, wywołuje _int_malloc
_int_malloc
:
Próbuje wygenerować arenę, jeśli jej nie ma
Jeśli istnieje jakiś kawałek fast bin odpowiedniego rozmiaru, użyj go
Wypełnij tcache innymi szybkimi kawałkami
Jeśli istnieje jakiś kawałek small bin odpowiedniego rozmiaru, użyj go
Wypełnij tcache innymi kawałkami tego samego rozmiaru
Jeśli żądany rozmiar nie jest dla małych binów, scal fast bin z unsorted bin
Sprawdź unsorted bin, użyj pierwszego kawałka z wystarczającą ilością miejsca
Jeśli znaleziony kawałek jest większy, podziel go, aby zwrócić część i dodaj resztę z powrotem do unsorted bin
Jeśli kawałek ma taki sam rozmiar jak żądany rozmiar, użyj go do wypełnienia tcache zamiast go zwracać (aż tcache będzie pełne, wówczas zwróć następny)
Dla każdego sprawdzonego kawałka mniejszego rozmiaru, umieść go w odpowiednim małym lub dużym binie
Sprawdź duży bin w indeksie żądanego rozmiaru
Zacznij od pierwszego kawałka, który jest większy niż żądany rozmiar, jeśli znajdziesz taki, zwróć go i dodaj resztki do małego bina
Sprawdź duże biny od następnych indeksów do końca
Od następnego większego indeksu sprawdź, czy jest jakiś kawałek, podziel pierwszy znaleziony kawałek, aby użyć go do żądanego rozmiaru i dodaj resztę do unsorted bin
Jeśli w poprzednich binach nic nie znaleziono, pobierz kawałek z top chunk
Jeśli top chunk nie był wystarczająco duży, powiększ go za pomocą sysmalloc
Funkcja malloc
faktycznie wywołuje __libc_malloc
. Ta funkcja sprawdzi tcache, aby zobaczyć, czy jest dostępny kawałek o pożądanym rozmiarze. Jeśli jest, użyje go, a jeśli nie, sprawdzi, czy jest to pojedynczy wątek, a w takim przypadku wywoła _int_malloc
w głównej arenie, a jeśli nie, wywoła _int_malloc
w arenie wątku.
Nadszedł czas, aby sprawdzić nieposortowany blok w poszukiwaniu potencjalnego poprawnego fragmentu do użycia.
Zaczynamy od dużego pętli, która będzie przeszukiwać nieposortowany blok w kierunku bk
aż do momentu dotarcia do końca (struktura areny) za pomocą while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
Ponadto, po każdym rozważeniu nowego fragmentu wykonywane są pewne kontrole bezpieczeństwa:
Jeśli rozmiar fragmentu jest dziwny (za mały lub za duży): malloc(): invalid size (unsorted)
Jeśli rozmiar następnego fragmentu jest dziwny (za mały lub za duży): malloc(): invalid next size (unsorted)
Jeśli rozmiar poprzedniego wskazanego przez następny fragment różni się od rozmiaru fragmentu: malloc(): mismatching next->prev_size (unsorted)
Jeśli nie victim->bck->fd == victim
lub nie victim->fd == av
(arena): malloc(): unsorted double linked list corrupted
Ponieważ zawsze sprawdzamy ostatni, jego fd
powinien zawsze wskazywać na strukturę areny.
Jeśli następny fragment nie wskazuje, że poprzedni jest w użyciu: malloc(): invalid next->prev_inuse (unsorted)
Jeśli to się powiodło, zwróć fragment i koniec, jeśli nie, kontynuuj wykonywanie funkcji...
Kontynuuj usuwanie fragmentu z pojemnika, jeśli żądany rozmiar jest dokładnie taki sam jak rozmiar fragmentu:
Jeśli pamięć podręczna (tcache) nie jest wypełniona, dodaj ją do pamięci podręcznej (tcache) i kontynuuj wskazując, że istnieje fragment pamięci podręcznej (tcache), który można wykorzystać
Jeśli pamięć podręczna (tcache) jest pełna, po prostu jej użyj, zwracając ją
Jeśli limity nie zostały osiągnięte, kontynuuj z kodem...
Jeśli żądanie jest duże (nie znajduje się w małym kubełku) i nie zwróciliśmy jeszcze żadnego fragmentu, pobierz indeks żądanej wielkości w dużym kubełku, sprawdź, czy nie jest pusty lub czy największy fragment w tym kubełku jest większy niż żądana wielkość, a w takim przypadku znajdź najmniejszy fragment, który można wykorzystać dla żądanej wielkości.
Jeśli pozostała przestrzeń z ostatecznie użytego fragmentu może być nowym fragmentem, dodaj go do kubełka nieuporządkowanego, a ostatnia_pamięć jest aktualizowana.
Wykonywana jest kontrola bezpieczeństwa podczas dodawania przypomnienia do kubełka nieuporządkowanego:
bck->fd-> bk != bck
: malloc(): uszkodzone nieuporządkowane fragmenty
W tym momencie nadszedł czas, aby uzyskać nowy kawałek z Górnego kawałka (jeśli jest wystarczająco duży).
Zaczyna się od sprawdzenia bezpieczeństwa, upewniając się, że rozmiar kawałka nie jest zbyt duży (uszkodzony):
chunksize(av->top) > av->system_mem
: malloc(): uszkodzony rozmiar górnego kawałka
Następnie zostanie użyte miejsce z górnego kawałka, jeśli jest wystarczająco duże, aby utworzyć kawałek o żądanym rozmiarze.
Jeśli nie ma wystarczająco dużo miejsca, a istnieją szybkie kawałki, zostaną one scalone, a następnie spróbowane ponownie.
W końcu, jeśli nie ma wystarczająco dużo miejsca, użyj sysmalloc
, aby zaalokować odpowiedni rozmiar.
Zaczyna od uzyskania informacji o starym szczycie bloku i sprawdzenia, czy niektóre z następujących warunków są spełnione:
Stary rozmiar sterty wynosi 0 (nowa sterta)
Rozmiar poprzedniej sterty jest większy niż MINSIZE, a stary Top jest w użyciu
Sterta jest wyrównana do rozmiaru strony (0x1000, więc dolne 12 bitów muszą być równe 0)
Następnie sprawdza również, czy:
Stary rozmiar nie ma wystarczająco miejsca, aby utworzyć kawałek o żądanym rozmiarze