malloc & sysmalloc

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Υποστηρίξτε το HackTricks

Σύνοψη Τάξης Διάθεσης

(Δεν εξηγούνται ελέγχοι σε αυτήν τη σύνοψη και κάποιες περιπτώσεις έχουν παραλειφθεί για συντομία)

  1. Η __libc_malloc προσπαθεί να αποκτήσει ένα κομμάτι από το tcache, αν δεν υπάρχει καλεί το _int_malloc

  2. _int_malloc :

  3. Προσπαθεί να δημιουργήσει την αρένα αν δεν υπάρχει

  4. Αν υπάρχει οποιοδήποτε fast bin chunk του σωστού μεγέθους, το χρησιμοποιεί

  5. Γεμίζει το tcache με άλλα fast chunks

  6. Αν υπάρχει οποιοδήποτε small bin chunk του σωστού μεγέθους, το χρησιμοποιεί

  7. Γεμίζει το tcache με άλλα κομμάτια του ίδιου μεγέθους

  8. Αν το ζητούμενο μέγεθος δεν είναι για small bins, συγχωνεύει το fast bin στο unsorted bin

  9. Ελέγχει το unsorted bin, χρησιμοποιεί το πρώτο κομμάτι με αρκετό χώρο

  10. Αν το βρεθέν κομμάτι είναι μεγαλύτερο, το διαιρεί για να επιστρέψει ένα μέρος και προσθέτει το υπόλοιπο πίσω στο unsorted bin

  11. Αν ένα κομμάτι είναι του ίδιου μεγέθους με το ζητούμενο μέγεθος, το χρησιμοποιεί για να γεμίσει το tcache αντί να το επιστρέψει (μέχρι το tcache να γεμίσει, τότε επιστρέφει το επόμενο)

  12. Για κάθε κομμάτι μικρότερου μεγέθους που ελέγχεται, το τοποθετεί στο αντίστοιχο small ή large bin

  13. Ελέγχει το large bin στο δείκτη του ζητούμενου μεγέθους

  14. Ξεκινά από το πρώτο κομμάτι που είναι μεγαλύτερο από το ζητούμενο μέγεθος, αν βρεθεί κάποιο επιστρέφει αυτό και προσθέτει τα υπόλοιπα στο small bin

  15. Ελέγχει τα large bins από τους επόμενους δείκτες μέχρι το τέλος

  16. Από τον επόμενο μεγαλύτερο δείκτη ελέγχει για οποιοδήποτε κομμάτι, διαιρεί το πρώτο βρεθέν κομμάτι για να το χρησιμοποιήσει για το ζητούμενο μέγεθος και προσθέτει το υπόλοιπο στο unsorted bin

  17. Αν δεν βρεθεί τίποτα στα προηγούμενα bins, παίρνει ένα κομμάτι από το top chunk

  18. Αν το top chunk δεν ήταν αρκετά μεγάλο, το μεγαλώνει με το sysmalloc

__libc_malloc

Η συνάρτηση malloc καλεί πραγματικά την __libc_malloc. Αυτή η συνάρτηση θα ελέγξει το tcache για να δει αν υπάρχει διαθέσιμο κομμάτι του επιθυμητού μεγέθους. Αν υπάρχει, θα το χρησιμοποιήσει και αν όχι, θα ελέγξει αν είναι μονό νήμα και σε αυτήν την περίπτωση θα καλέσει το _int_malloc στην κύρια αρένα, και αν όχι, θα καλέσει το _int_malloc στην αρένα του νήματος.

Κώδικας __libc_malloc

```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c

#if IS_IN (libc) void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim;

_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX");

if (!__malloc_initialized) ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes = checked_request2size (bytes); if (tbytes == 0) { __set_errno (ENOMEM); return NULL; } size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache != NULL && tcache->counts[tc_idx] > 0) { victim = tcache_get (tc_idx); return tag_new_usable (victim); } DIAG_POP_NEEDS_COMMENT; #endif

if (SINGLE_THREAD_P) { victim = tag_new_usable (_int_malloc (&main_arena, bytes)); assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || &main_arena == arena_for_chunk (mem2chunk (victim))); return victim; }

arena_get (ar_ptr, bytes);

victim = _int_malloc (ar_ptr, bytes); /* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }

if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex);

victim = tag_new_usable (victim);

assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || ar_ptr == arena_for_chunk (mem2chunk (victim))); return victim; }

</details>

Σημειώστε πώς πάντα θα επισημαίνει τον επιστρεφόμενο δείκτη με `tag_new_usable`, από τον κώδικα:
```c
void *tag_new_usable (void *ptr)

Allocate a new random color and use it to color the user region of
a chunk; this may include data from the subsequent chunk's header
if tagging is sufficiently fine grained.  Returns PTR suitably
recolored for accessing the memory there.

_int_malloc

Αυτή είναι η συνάρτηση που εκχωρεί μνήμη χρησιμοποιώντας τα άλλα bins και το top chunk.

  • Έναρξη

Ξεκινάει ορίζοντας μερικές μεταβλητές και παίρνοντας το πραγματικό μέγεθος που χρειάζεται ο χώρος μνήμης που ζητείται:

Γρήγορος Κάδος

Εάν το απαιτούμενο μέγεθος βρίσκεται μέσα στα μεγέθη των Γρήγορων Κάδων, προσπαθήστε να χρησιμοποιήσετε ένα κομμάτι από τον γρήγορο κάδο. Βασικά, με βάση το μέγεθος, θα βρει το δείκτη του γρήγορου κάδου όπου θα πρέπει να βρίσκονται έγκυρα κομμάτια και, αν υπάρχει κάποιο, θα επιστρέψει ένα από αυτά. Επιπλέον, εάν ο tcache είναι ενεργοποιημένος, θα γεμίσει τον tcache κάδο αυτού του μεγέθους με γρήγορους κάδους.

Κατά τη διάρκεια αυτών των ενεργειών, πραγματοποιούνται ορισμένοι έλεγχοι ασφαλείας εδώ:

  • Εάν το κομμάτι δεν είναι σωστά ευθυγραμμισμένο: malloc(): unaligned fastbin chunk detected 2

  • Εάν το επόμενο κομμάτι δεν είναι σωστά ευθυγραμμισμένο: malloc(): unaligned fastbin chunk detected

  • Εάν το επιστρεφόμενο κομμάτι έχει ένα μέγεθος που δεν είναι σωστό λόγω του δείκτη του στον γρήγορο κάδο: malloc(): memory corruption (fast)

  • Εάν οποιοδήποτε κομμάτι που χρησιμοποιείται για να γεμίσει τον tcache δεν είναι σωστά ευθυγραμμισμένο: malloc(): unaligned fastbin chunk detected 3

malloc_συγχώνευση

Αν δεν πρόκειται για ένα μικρό κομμάτι, είναι ένα μεγάλο κομμάτι, και σε αυτήν την περίπτωση καλείται το malloc_consolidate για να αποφευχθεί η διάσπαση μνήμης.

Μη ταξινομημένος κάδος

Είναι καιρός να ελέγξουμε τον μη ταξινομημένο κάδο για ένα πιθανό έγκυρο κομμάτι που μπορούμε να χρησιμοποιήσουμε.

Έναρξη

Αυτό ξεκινά με ένα μεγάλο βρόχο που θα διατρέχει τον μη ταξινομημένο κάδο προς την κατεύθυνση bk μέχρι να φτάσει στο τέλος (τη δομή arena) με while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

Επιπλέον, πραγματοποιούνται ορισμένοι έλεγχοι ασφαλείας κάθε φορά που λαμβάνεται υπόψη ένα νέο κομμάτι:

  • Εάν το μέγεθος του κομματιού είναι παράξενο (πολύ μικρό ή πολύ μεγάλο): malloc(): invalid size (unsorted)

  • Εάν το μέγεθος του επόμενου κομματιού είναι παράξενο (πολύ μικρό ή πολύ μεγάλο): malloc(): invalid next size (unsorted)

  • Εάν το προηγούμενο μέγεθος που υποδηλώνεται από το επόμενο κομμάτι διαφέρει από το μέγεθος του κομματιού: malloc(): mismatching next->prev_size (unsorted)

  • Εάν δεν ισχύει victim->bck->fd == victim ή δεν ισχύει victim->fd == av (arena): malloc(): unsorted double linked list corrupted

  • Δεδομένου ότι ελέγχουμε πάντα τον τελευταίο, το fd του πρέπει να δείχνει πάντα προς τη δομή arena.

  • Εάν το επόμενο κομμάτι δεν υποδηλώνει ότι το προηγούμενο είναι σε χρήση: malloc(): invalid next->prev_inuse (unsorted)

Εάν αυτό ήταν επιτυχές, επιστρέψτε το κομμάτι και τελειώσαμε, αλλιώς συνεχίστε την εκτέλεση της συνάρτησης...

εάν η διάσταση είναι ίση

Συνεχίστε να αφαιρείτε το κομμάτι από το bin, σε περίπτωση που το ζητούμενο μέγεθος είναι ακριβώς όπως του κομματιού:

  • Εάν το tcache δεν είναι γεμάτο, προσθέστε το στο tcache και συνεχίστε να υποδεικνύετε ότι υπάρχει ένα κομμάτι tcache που θα μπορούσε να χρησιμοποιηθεί

  • Εάν το tcache είναι γεμάτο, απλώς χρησιμοποιήστε το επιστρέφοντάς το

Όρια _int_malloc

Σε αυτό το σημείο, αν κάποιο τμήμα αποθηκεύτηκε στο tcache που μπορεί να χρησιμοποιηθεί και έχει φτάσει το όριο, απλά επιστρέψτε ένα tcache τμήμα.

Επιπλέον, αν φτάσει το MAX_ITERS, διακόψτε τον βρόγχο και αποκτήστε ένα τμήμα με διαφορετικό τρόπο (top chunk).

Αν η return_cached έχει οριστεί, απλά επιστρέψτε ένα τμήμα από το tcache για να αποφύγετε μεγαλύτερες αναζητήσεις.

Αν δεν βρεθεί ένα κομμάτι που να είναι κατάλληλο για αυτό, συνεχίστε

Μεγάλο Bin (επόμενο μεγαλύτερο)

Αν στο συγκεκριμένο μεγάλο bin δεν υπήρχε κανένα κομμάτι που θα μπορούσε να χρησιμοποιηθεί, ξεκινήστε έναν βρόχο μέσω όλων των επόμενων μεγάλων bin (ξεκινώντας από τον αμέσως μεγαλύτερο) μέχρι να βρεθεί ένα (αν υπάρχει).

Το υπόλοιπο του κομματιού που χωρίστηκε προστίθεται στον μη ταξινομημένο bin, ενημερώνεται το last_reminder και πραγματοποιείται έλεγχος ασφαλείας:

  • bck->fd-> bk != bck: malloc(): corrupted unsorted chunks2

Έλεγχοι sysmalloc

Ξεκινά ανακτώντας πληροφορίες για τον παλιό κορυφαίο κομμάτι και ελέγχοντας ότι κάποιες από τις ακόλουθες συνθήκες είναι αληθείς:

  • Το παλιό μέγεθος του σωρού είναι 0 (νέος σωρός)

  • Το μέγεθος του προηγούμενου σωρού είναι μεγαλύτερο από το MINSIZE και ο παλιός Κορυφή είναι σε χρήση

  • Ο σωρός είναι ευθυγραμμισμένος με το μέγεθος της σελίδας (0x1000 έτσι ώστε τα χαμηλότερα 12 bits να είναι 0)

Στη συνέχεια ελέγχει επίσης ότι:

  • Το παλιό μέγεθος δεν έχει αρκετό χώρο για να δημιουργηθεί ένα κομμάτι για το ζητούμενο μέγεθος

Κύριος χώρος sysmalloc

Ξεκινά υπολογίζοντας το ποσό της μνήμης που χρειάζεται. Θα ξεκινήσει ζητώντας συνεχόμενη μνήμη, έτσι ώστε σε αυτήν την περίπτωση να είναι δυνατή η χρήση της παλιάς μνήμης που δεν χρησιμοποιείται. Επίσης, πραγματοποιούνται κάποιες λειτουργίες ευθυγράμμισης.

sysmalloc κυρίως συνέχεια αρένας

Αν το προηγούμενο δεν επέστρεψε MORECORE_FAILURE, αν λειτούργησε δημιουργήστε κάποιες ευθυγραμμίσεις:

Last updated