Bins & Memory Allocations
Tip
UΔite i veΕΎbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
UΔite i veΕΎbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
UΔite i veΕΎbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
PodrΕΎite HackTricks
- Proverite planove pretplate!
- PridruΕΎite se π¬ Discord grupi ili telegram grupi ili pratite nas na Twitteru π¦ @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Osnovne Informacije
Da bi se poboljΕ‘ala efikasnost naΔina na koji se delovi Δuvaju, svaki deo nije samo u jednoj povezanoj listi, veΔ postoji nekoliko tipova. To su binovi i postoji 5 tipova binova: 62 mali binovi, 63 veliki binovi, 1 neusmereni bin, 10 brzi binovi i 64 tcache binova po niti.
PoΔetna adresa za svaki neusmereni, mali i veliki bin je unutar istog niza. Indeks 0 se ne koristi, 1 je neusmereni bin, binovi 2-64 su mali binovi, a binovi 65-127 su veliki binovi.
Tcache (Per-Thread Cache) Bins
Iako niti pokuΕ‘avaju da imaju svoj vlastiti heap (vidi Arenas i Subheaps), postoji moguΔnost da proces sa puno niti (kao Ε‘to je web server) Δe zavrΕ‘iti deljenjem heapa sa drugim nitima. U ovom sluΔaju, glavno reΕ‘enje je koriΕ‘Δenje zakljuΔavanja, Ε‘to moΕΎe znaΔajno usporiti niti.
Stoga, tcache je sliΔan brzom binu po niti na naΔin da je to jedna povezana lista koja ne spaja delove. Svaka nit ima 64 jednostruko povezane tcache binove. Svaki bin moΕΎe imati maksimalno 7 delova iste veliΔine u rasponu od 24 do 1032B na 64-bitnim sistemima i 12 do 516B na 32-bitnim sistemima.
Kada nit oslobodi deo, ako nije prevelik da bi se alocirao u tcache i odgovarajuΔi tcache bin nije pun (veΔ 7 delova), biΔe alociran tamo. Ako ne moΕΎe da ide u tcache, moraΔe da Δeka na zakljuΔavanje heapa da bi mogla da izvrΕ‘i operaciju oslobaΔanja globalno.
Kada se deo alocira, ako postoji slobodan deo potrebne veliΔine u Tcache, koristiΔe ga, ako ne, moraΔe da Δeka na zakljuΔavanje heapa da bi mogla da pronaΔe jedan u globalnim binovima ili da kreira novi.
TakoΔe postoji optimizacija, u ovom sluΔaju, dok ima zakljuΔavanje heapa, nit Δe napuniti svoj Tcache delovima heapa (7) traΕΎene veliΔine, tako da u sluΔaju da mu zatreba viΕ‘e, naΔi Δe ih u Tcache.
Dodaj primer tcache dela
```c #includeint main(void) { char *chunk; chunk = malloc(24); printf(βAddress of the chunk: %p\nβ, (void *)chunk); gets(chunk); free(chunk); return 0; }
Kompajlirajte ga i debagujte sa taΔkom prekida u ret opcode iz main funkcije. Tada sa gef moΕΎete videti tcache bin u upotrebi:
```bash
gefβ€ heap bins
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tcachebins[idx=0, size=0x20, count=1] β Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Tcache strukture i funkcije
U sledeΔem kodu moguΔe je videti max bins i chunks per index, tcache_entry strukturu kreiranu da bi se izbegli dupli oslobaΔanja i tcache_perthread_struct, strukturu koju svaka nit koristi za Δuvanje adresa za svaki indeks bin-a.
tcache_entry i tcache_perthread_struct
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
define TCACHE_MAX_BINS 64
define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)
/* Only used to pre-fill the tunables. */
define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
/* When βxβ is from chunksize(). */
define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
/* When βxβ is a user-provided size. */
define usize2tidx(x) csize2tidx (request2size (x))
/* With rounding and alignment, the bins are⦠idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit) idx 1 bytes 25..40 or 13..20 idx 2 bytes 41..56 or 21..28 etc. */
/* This is another arbitrary limit, which tunables can change. Each tcache bin will hold at most this number of chunks. */
define TCACHE_FILL_COUNT 7
/* Maximum chunks in tcache bins for tunables. This value must fit the range of tcache->counts[] entries, else they may overflow. */
define MAX_TCACHE_COUNT UINT16_MAX
[β¦]
typedef struct tcache_entry { struct tcache_entry next; / This field exists to detect double frees. */ uintptr_t key; } tcache_entry;
/* There is one of these for each thread, which contains the per-thread cache (hence βtcache_perthread_structβ). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;
</details>
Funkcija `__tcache_init` je funkcija koja kreira i alocira prostor za objekat `tcache_perthread_struct`
<details>
<summary>tcache_init kod</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3241C1-L3274C2
static void
tcache_init(void)
{
mstate ar_ptr;
void *victim = 0;
const size_t bytes = sizeof (tcache_perthread_struct);
if (tcache_shutting_down)
return;
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
if (!victim && ar_ptr != NULL)
{
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
/* In a low memory situation, we may not be able to allocate memory
- in which case, we just keep trying later. However, we
typically do this very early, so either there is sufficient
memory, or there isn't enough memory to do non-trivial
allocations anyway. */
if (victim)
{
tcache = (tcache_perthread_struct *) victim;
memset (tcache, 0, sizeof (tcache_perthread_struct));
}
}
Tcache indeksi
Tcache ima nekoliko binova u zavisnosti od veliΔine, a inicijalni pokazivaΔi na prvi deo svakog indeksa i koliΔina delova po indeksu nalaze se unutar dela. To znaΔi da lociranje dela sa ovom informacijom (obiΔno prvim) omoguΔava pronalaΕΎenje svih tcache inicijalnih taΔaka i koliΔine Tcache delova.
Brzi binovi
Brzi binovi su dizajnirani da ubrza alokaciju memorije za male delove ΔuvajuΔi nedavno osloboΔene delove u strukturi brzog pristupa. Ovi binovi koriste pristup Last-In, First-Out (LIFO), Ε‘to znaΔi da je najnovije osloboΔeni deo prvi koji Δe se ponovo koristiti kada doΔe nova zahtev za alokaciju. Ovo ponaΕ‘anje je korisno za brzinu, jer je brΕΎe umetati i uklanjati sa vrha steka (LIFO) u poreΔenju sa redom (FIFO).
Pored toga, brzi binovi koriste jednostruko povezane liste, a ne dvostruko povezane, Ε‘to dodatno poboljΕ‘ava brzinu. PoΕ‘to se delovi u brzim binovima ne spajaju sa susedima, nema potrebe za sloΕΎenom strukturom koja omoguΔava uklanjanje iz sredine. Jednostruko povezana lista je jednostavnija i brΕΎa za ove operacije.
U suΕ‘tini, ono Ε‘to se ovde deΕ‘ava je da je zaglavlje (pokazivaΔ na prvi deo koji treba proveriti) uvek usmereno na najnovije osloboΔeni deo te veliΔine. Dakle:
- Kada se alocira novi deo te veliΔine, zaglavlje pokazuje na slobodan deo koji se moΕΎe koristiti. PoΕ‘to ovaj slobodan deo pokazuje na sledeΔi koji se moΕΎe koristiti, ova adresa se Δuva u zaglavlju tako da sledeΔa alokacija zna gde da pronaΔe dostupni deo.
- Kada se deo oslobodi, slobodan deo Δe saΔuvati adresu trenutnog dostupnog dela, a adresa ovog novog osloboΔenog dela Δe biti stavljena u zaglavlje.
Maksimalna veliΔina jednostruke povezane liste je 0x80 i organizovane su tako da Δe deo veliΔine 0x20 biti u indeksu 0, deo veliΔine 0x30 biΔe u indeksu 1β¦
Caution
Delovi u brzim binovima nisu postavljeni kao dostupni, tako da se Δuvaju kao delovi brzih binova neko vreme umesto da se mogu spojiti sa drugim slobodnim delovima koji ih okruΕΎuju.
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
/*
Fastbins
An array of lists holding recently freed small chunks. Fastbins
are not doubly linked. It is faster to single-link them, and
since chunks are never removed from the middles of these lists,
double linking is not necessary. Also, unlike regular bins, they
are not even processed in FIFO order (they use faster LIFO) since
ordering doesn't much matter in the transient contexts in which
fastbins are normally used.
Chunks in fastbins keep their inuse bit set, so they cannot
be consolidated with other free chunks. malloc_consolidate
releases all chunks in fastbins and consolidates them with
other free chunks.
*/
typedef struct malloc_chunk *mfastbinptr;
#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx])
/* offset 2 to use otherwise unindexable first 2 bins */
#define fastbin_index(sz) \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
/* The maximum fastbin request size we support */
#define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
#define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)
Dodajte primer fastbin chunk-a
```c #includeint main(void) { char *chunks[8]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 8; i++) { chunks[i] = malloc(24); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, βMemory allocation failed at iteration %d\nβ, i); return 1; } printf(βAddress of chunk %d: %p\nβ, i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
Napomena kako alociramo i oslobaΔamo 8 delova iste veliΔine tako da popune tcache, a osmi se Δuva u fast chunk.
Kompajlirajte to i debagujte sa breakpoint-om u `ret` opcode-u iz `main` funkcije. Tada sa `gef` moΕΎete videti da je tcache bin pun i da je jedan chunk u fast bin:
```bash
gefβ€ heap bins
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tcachebins[idx=0, size=0x20, count=7] β Chunk(addr=0xaaaaaaac1770, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1750, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1730, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1710, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac16f0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac16d0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Fastbins for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Fastbins[idx=0, size=0x20] β Chunk(addr=0xaaaaaaac1790, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
Fastbins[idx=1, size=0x30] 0x00
NeureΔeni kontejner
NeureΔeni kontejner je keΕ‘ koji koristi menadΕΎer heap-a kako bi ubrzao alokaciju memorije. Evo kako to funkcioniΕ‘e: Kada program oslobodi deo, i ako se taj deo ne moΕΎe alocirati u tcache ili brzi kontejner i ne sudara se sa vrhunskim delom, menadΕΎer heap-a ga odmah ne stavlja u odreΔeni mali ili veliki kontejner. Umesto toga, prvo pokuΕ‘ava da spoji sa bilo kojim susednim slobodnim delovima kako bi stvorio veΔi blok slobodne memorije. Zatim, stavlja ovaj novi deo u opΕ‘ti kontejner nazvan βneureΔeni kontejner.β
Kada program traΕΎi memoriju, menadΕΎer heap-a proverava neureΔeni kontejner da vidi da li postoji deo dovoljno velike veliΔine. Ako ga pronaΔe, odmah ga koristi. Ako ne pronaΔe odgovarajuΔi deo u neureΔenom kontejneru, premestiΔe sve delove u ovoj listi u njihove odgovarajuΔe kontejnere, bilo male ili velike, na osnovu njihove veliΔine.
Napomena: ako se veΔi deo podeli na 2 polovine i ostatak je veΔi od MINSIZE, biΔe vraΔen nazad u neureΔeni kontejner.
Dakle, neureΔeni kontejner je naΔin da se ubrza alokacija memorije brzo ponovnim koriΕ‘Δenjem nedavno osloboΔene memorije i smanji potreba za vremenski zahtevnim pretragama i spajanjima.
Caution
Napomena: Δak i ako su delovi razliΔitih kategorija, ako se dostupan deo sudara sa drugim dostupnim delom (Δak i ako prvobitno pripadaju razliΔitim kontejnerima), biΔe spojeni.
Dodaj primer neureΔenog dela
```c #includeint main(void) { char *chunks[9]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, βMemory allocation failed at iteration %d\nβ, i); return 1; } printf(βAddress of chunk %d: %p\nβ, i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
Napomena kako alociramo i oslobaΔamo 9 delova iste veliΔine tako da **popune tcache** i osmi se Δuva u nesortiranom binu jer je **prevelik za fastbin** i deveti nije osloboΔen, tako da se deveti i osmi **ne spajaju sa vrhunskim delom**.
Kompajlirajte to i debagujte sa taΔkom prekida u `ret` opkodu iz `main` funkcije. Tada sa `gef` moΕΎete videti da je tcache bin pun i jedan deo je u nesortiranom binu:
```bash
gefβ€ heap bins
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tcachebins[idx=15, size=0x110, count=7] β Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Fastbins for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Unsorted Bin for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] unsorted_bins[0]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
β Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in unsorted bin.
Male Bine
Male bine su brΕΎe od velikih bina, ali sporije od brzih bina.
Svaki bin od 62 Δe imati delove iste veliΔine: 16, 24, β¦ (sa maksimalnom veliΔinom od 504 bajta u 32bita i 1024 u 64bita). Ovo pomaΕΎe u brzini pronalaΕΎenja bina gde bi prostor trebao biti dodeljen i umetanja i uklanjanja stavki na ovim listama.
Ovako se izraΔunava veliΔina malog bina prema indeksu bina:
- Najmanja veliΔina: 2*4*index (npr. indeks 5 -> 40)
- NajveΔa veliΔina: 2*8*index (npr. indeks 5 -> 80)
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > CHUNK_HDR_SZ)
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\
+ SMALLBIN_CORRECTION)
Π€ΡΠ½ΠΊΡΠΈΡΠ° Π·Π° ΠΈΠ·Π±ΠΎΡ ΠΈΠ·ΠΌΠ΅ΡΡ ΠΌΠ°Π»ΠΈΡ ΠΈ Π²Π΅Π»ΠΈΠΊΠΈΡ ΠΊΠΎΠ½ΡΠ΅ΡΠ½Π΅ΡΠ°:
#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
Dodajte mali primer
```c #includeint main(void) { char *chunks[10]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, βMemory allocation failed at iteration %d\nβ, i); return 1; } printf(βAddress of chunk %d: %p\nβ, i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
chunks[9] = malloc(0x110);
return 0; }
Napomena kako alociramo i oslobaΔamo 9 delova iste veliΔine tako da **popunimo tcache** i osmi se Δuva u nesortiranom binu jer je **prevelik za fastbin**, a deveti nije osloboΔen tako da se deveti i osmi **ne spajaju sa vrhunskim delom**. Zatim alociramo veΔi deo od 0x110 Ε‘to Δini da **deo u nesortiranom binu preΔe u mali bin**.
Kompajlirajte to i debagujte sa taΔkom prekida u `ret` opkodu iz `main` funkcije. Tada sa `gef` moΕΎete videti da je tcache bin pun i da je jedan deo u malom binu:
```bash
gefβ€ heap bins
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Tcachebins[idx=15, size=0x110, count=7] β Chunk(addr=0xaaaaaaac1d10, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1c00, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac1af0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac19e0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac18d0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac17c0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β Chunk(addr=0xaaaaaaac12a0, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Fastbins for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Unsorted Bin for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] Found 0 chunks in unsorted bin.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Small Bins for arena at 0xfffff7f90b00 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] small_bins[16]: fw=0xaaaaaaac1e10, bk=0xaaaaaaac1e10
β Chunk(addr=0xaaaaaaac1e20, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in 1 small non-empty bins.
Velike kante
Za razliku od malih kanti, koje upravljaju delovima fiksnih veliΔina, svaka velika kanta upravlja opsegom veliΔina delova. Ovo je fleksibilnije, omoguΔavajuΔi sistemu da prilagodi razliΔite veliΔine bez potrebe za odvojenom kantom za svaku veliΔinu.
U alokatoru memorije, velike kante poΔinju tamo gde se zavrΕ‘avaju male kante. Opsezi za velike kante postaju progresivno veΔi, Ε‘to znaΔi da prva kanta moΕΎe pokriti delove od 512 do 576 bajtova, dok sledeΔa pokriva od 576 do 640 bajtova. Ovaj obrazac se nastavlja, pri Δemu najveΔa kanta sadrΕΎi sve delove iznad 1MB.
Velike kante su sporije za rad u poreΔenju sa malim kantama jer moraju sortirati i pretraΕΎivati listu delova razliΔitih veliΔina kako bi pronaΕ‘le najbolju opciju za alokaciju. Kada se deo umetne u veliku kantu, mora se sortirati, a kada se memorija alocira, sistem mora pronaΔi pravi deo. Ovaj dodatni rad ih Δini sporijim, ali poΕ‘to su velike alokacije reΔe od malih, to je prihvatljiva kompenzacija.
Postoji:
- 32 kante opsega 64B (sukob sa malim kantama)
- 16 kanti opsega 512B (sukob sa malim kantama)
- 8 kanti opsega 4096B (delimiΔno sukob sa malim kantama)
- 4 kante opsega 32768B
- 2 kante opsega 262144B
- 1 kanta za preostale veliΔine
Kod veliΔina velikih kanti
```c // From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711#define largebin_index_32(sz)
(((((unsigned long) (sz)) >> 6) <= 38) ? 56 + (((unsigned long) (sz)) >> 6) :
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
126)
#define largebin_index_32_big(sz)
(((((unsigned long) (sz)) >> 6) <= 45) ? 49 + (((unsigned long) (sz)) >> 6) :
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
126)
// XXX It remains to be seen whether it is good to keep the widths of
// XXX the buckets the same or whether it should be scaled by a factor
// XXX of two as well.
#define largebin_index_64(sz)
(((((unsigned long) (sz)) >> 6) <= 48) ? 48 + (((unsigned long) (sz)) >> 6) :
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :
126)
- #define largebin_index(sz)
(SIZE_SZ == 8 ? largebin_index_64 (sz) \ - MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz) \
- largebin_index_32 (sz))
</details>
<details>
<summary>Dodajte primer velike koliΔine</summary>
```c
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *chunks[2];
chunks[0] = malloc(0x1500);
chunks[1] = malloc(0x1500);
free(chunks[0]);
chunks[0] = malloc(0x2000);
return 0;
}
2 velike alokacije se izvrΕ‘avaju, zatim se jedna oslobaΔa (stavljajuΔi je u nesortiranu kantu) i vrΕ‘i se veΔa alokacija (premjeΕ‘tajuΔi osloboΔenu iz nesortirane kante u veliku kantu).
Kompajlirajte to i debagujte sa taΔkom prekida u ret opkodu iz main funkcije. Tada sa gef moΕΎete videti da je tcache kanta puna i da je jedan deo u velikoj kanti:
gefβ€ heap bin
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Tcachebins for thread 1 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
All tcachebins are empty
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Fastbins for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Unsorted Bin for arena at 0xfffff7f90b00 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] Found 0 chunks in unsorted bin.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Small Bins for arena at 0xfffff7f90b00 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] Found 0 chunks in 0 small non-empty bins.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Large Bins for arena at 0xfffff7f90b00 ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[+] large_bins[100]: fw=0xaaaaaaac1290, bk=0xaaaaaaac1290
β Chunk(addr=0xaaaaaaac12a0, size=0x1510, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in 1 large non-empty bins.
Gornji deo
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
/*
Top
The top-most available chunk (i.e., the one bordering the end of
available memory) is treated specially. It is never included in
any bin, is used only if no other chunk is available, and is
released back to the system if it is very large (see
M_TRIM_THRESHOLD). Because top initially
points to its own bin with initial zero size, thus forcing
extension on the first malloc request, we avoid having any special
code in malloc to check whether it even exists yet. But we still
need to do so when getting memory from system, so we make
initial_top treat the bin as a legal but unusable chunk during the
interval between initialization and the first call to
sysmalloc. (This is somewhat delicate, since it relies on
the 2 preceding words to be zero during this interval as well.)
*/
/* Conveniently, the unsorted bin can be used as dummy top on first call */
#define initial_top(M) (unsorted_chunks (M))
U suΕ‘tini, ovo je deo koji sadrΕΎi sve trenutno dostupne heap-ove. Kada se izvrΕ‘i malloc, ako ne postoji dostupna slobodna jedinica za koriΕ‘Δenje, ova gornja jedinica Δe smanjiti svoju veliΔinu kako bi dala neophodan prostor.
PokazivaΔ na Gornju Jedinicu se Δuva u malloc_state strukturi.
Pored toga, na poΔetku, moguΔe je koristiti neusortiranu jedinicu kao gornju jedinicu.
Posmatrajte primer Gornje Jedinice
```c #includeint main(void) { char *chunk; chunk = malloc(24); printf(βAddress of the chunk: %p\nβ, (void *)chunk); gets(chunk); return 0; }
ΠΠΎΡΠ»Π΅ ΠΊΠΎΠΌΠΏΠ°ΡΠ»ΠΈΡΠ°ΡΠ° ΠΈ Π΄Π΅Π±Π°Π³ΠΎΠ²Π°ΡΠ° ΡΠ° ΠΏΡΠ΅ΠΊΠΈΠ΄Π½ΠΎΠΌ ΡΠ°ΡΠΊΠΎΠΌ Ρ `ret` ΠΎΠΏΠΊΠΎΠ΄Ρ `main`, Π²ΠΈΠ΄Π΅ΠΎ ΡΠ°ΠΌ Π΄Π° ΡΠ΅ malloc Π²ΡΠ°ΡΠΈΠΎ Π°Π΄ΡΠ΅ΡΡ `0xaaaaaaac12a0` ΠΈ ΠΎΠ²ΠΎ ΡΡ Π΄Π΅Π»ΠΎΠ²ΠΈ:
```bash
gefβ€ heap chunks
Chunk(addr=0xaaaaaaac1010, size=0x290, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac1010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12a0 41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac12c0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12c0 41 64 64 72 65 73 73 20 6f 66 20 74 68 65 20 63 Address of the c]
Chunk(addr=0xaaaaaaac16d0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac16d0 41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac1ae0, size=0x20530, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) β top chunk
Gde se moΕΎe videti da je gornji deo na adresi 0xaaaaaaac1ae0. To nije iznenaΔenje jer je poslednji alocirani deo bio na 0xaaaaaaac12a0 sa veliΔinom 0x410 i 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0.
TakoΔe je moguΔe videti duΕΎinu gornjeg dela na njegovom zaglavlju dela:
gefβ€ x/8wx 0xaaaaaaac1ae0 - 16
0xaaaaaaac1ad0: 0x00000000 0x00000000 0x00020531 0x00000000
0xaaaaaaac1ae0: 0x00000000 0x00000000 0x00000000 0x00000000
Poslednji Ostatak
Kada se koristi malloc i deo se deli (na primer, iz nesortiranog bin-a ili iz gornjeg dela), deo koji se stvara od ostatka podeljenog dela se naziva Poslednji Ostatak i njegov pokazivaΔ se Δuva u malloc_state strukturi.
Tok Alokacije
Pogledajte:
Tok OslobaΔanja
Pogledajte:
Bezbednosne Provere Heap Funkcija
Proverite bezbednosne provere koje vrΕ‘e Δesto koriΕ‘Δene funkcije u heap-u u:
Heap Functions Security Checks
Reference
- https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/
- https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/
- https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/core_functions
- https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/
Tip
UΔite i veΕΎbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
UΔite i veΕΎbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
UΔite i veΕΎbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
PodrΕΎite HackTricks
- Proverite planove pretplate!
- PridruΕΎite se π¬ Discord grupi ili telegram grupi ili pratite nas na Twitteru π¦ @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.


