hacktricks/binary-exploitation/heap/bins-and-memory-allocations.md

37 KiB

Bins & Geheue-toekennings

Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Basiese Inligting

Om die doeltreffendheid van hoe stukke gestoor word te verbeter, is elke stuk nie net in een gekoppelde lys nie, maar daar is verskeie tipes. Dit is die bakkies en daar is 5 tipes bakkies: 62 klein bakkies, 63 groot bakkies, 1 ongesorteerde bak, 10 vinnige bakkies en 64 tcache bakkies per draad.

Die aanvanklike adres vir elke ongesorteerde, klein en groot bakkies is binne dieselfde reeks. Die indeks 0 word nie gebruik nie, 1 is die ongesorteerde bak, bakkies 2-64 is klein bakkies en bakkies 65-127 is groot bakkies.

Tcache (Per-Draad-Kas) Bakkies

Selfs al probeer drade hul eie hoop hê (sien Arenas en Subheaps), is daar die moontlikheid dat 'n proses met baie drade (soos 'n webbediener) die hoop met ander drade sal deel. In hierdie geval is die hoofoplossing die gebruik van sluiters, wat dalk die drade aansienlik verlangs.

Daarom is 'n tcache soortgelyk aan 'n vinnige bak per draad op die manier dat dit 'n enkele gekoppelde lys is wat nie stukke saamvoeg nie. Elke draad het 64 enkelgekoppelde tcache bakkies. Elke bak kan 'n maksimum van 7 stukke van dieselfde grootte hê wat wissel van 24 tot 1032B op 64-bis-stelsels en 12 tot 516B op 32-bis-stelsels.

Wanneer 'n draad 'n stuk vrymaak, as dit nie te groot is om in die tcache toegewys te word nie en die betrokke tcache bak nie vol is nie (reeds 7 stukke), sal dit daar toegewys word. As dit nie na die tcache kan gaan nie, sal dit moet wag vir die hoopsluit om die vrymaakoperasie globaal uit te voer.

Wanneer 'n stuk toegewys word, as daar 'n vrye stuk van die benodigde grootte in die Tcache is, sal dit dit gebruik, indien nie, sal dit moet wag vir die hoopsluit om een in die globale bakkies te vind of 'n nuwe een te skep.
Daar is ook 'n optimisering, in hierdie geval, terwyl die hoopsluit het, sal die draad sy Tcache met hoopstukke (7) van die gevraagde grootte vul, sodat as dit meer benodig, dit hulle in die Tcache sal vind.

Voeg 'n tcache-stuk voorbeeld by ```c #include #include

int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); free(chunk); return 0; }

Voer dit uit en ontleed dit met 'n breekpunt in die ret opcode van die hooffunksie. Dan kan jy met gef die tcache-bin in gebruik sien:
```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 & Funksies

In die volgende kode is dit moontlik om die maksimum bakkies en stukke per indeks te sien, die tcache_entry struktuur wat geskep is om dubbele vrystellings te voorkom en tcache_perthread_struct, 'n struktuur wat elke draad gebruik om die adresse van elke indeks van die bakkie te stoor.

tcache_entry en tcache_perthread_struct ```c // From f942a732d3/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>

Die funksie `__tcache_init` is die funksie wat die spasie skep en toewys vir die `tcache_perthread_struct` obj

<details>

<summary>tcache_init kode</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 Indekse

Die tcache het verskeie bins afhangende van die grootte en die aanvanklike aanwysers na die eerste stuk van elke indeks en die hoeveelheid stukke per indeks is binne 'n stuk geleë. Dit beteken dat deur die stuk met hierdie inligting te vind (gewoonlik die eerste), dit moontlik is om al die tcache aanvanklike punte en die hoeveelheid Tcache stukke te vind.

Vinnige bins

Vinnige bins is ontwerp om geheue-toekenning vir klein stukke te versnel deur onlangs vrygestelde stukke in 'n vinnig-toeganklike struktuur te hou. Hierdie bins gebruik 'n Laaste-In, Eerste-Uit (LIFO) benadering, wat beteken dat die mees onlangs vrygestelde stuk die eerste is wat hergebruik word wanneer daar 'n nuwe toekenningsversoek is. Hierdie gedrag is voordelig vir spoed, aangesien dit vinniger is om van die boonste van 'n stok (LIFO) in te voeg en te verwyder in vergelyking met 'n ry (FIFO).

Daarbenewens gebruik vinnige bins enkelgelinkte lyste, nie dubbelgelinkte nie, wat die spoed verder verbeter. Aangesien stukke in vinnige bins nie saamgevoeg word met bure nie, is daar geen behoefte aan 'n komplekse struktuur wat verwydering uit die middel moontlik maak nie. 'n Enkelgelinkte lys is eenvoudiger en vinniger vir hierdie operasies.

Basies, wat hier gebeur is dat die kop (die aanwyser na die eerste stuk om te ondersoek) altyd na die mees onlangs vrygestelde stuk van daardie grootte wys. So:

  • Wanneer 'n nuwe stuk van daardie grootte toegewys word, wys die kop na 'n vry stuk om te gebruik. Aangesien hierdie vry stuk na die volgende een om te gebruik wys, word hierdie adres in die kop gestoor sodat die volgende toekenning weet waar om 'n beskikbare stuk te kry
  • Wanneer 'n stuk vrygestel word, sal die vry stuk die adres van die huidige beskikbare stuk stoor en die adres van hierdie nuut vrygestelde stuk sal in die kop geplaas word

Die maksimum grootte van 'n gelinkte lys is 0x80 en hulle is georganiseer sodat 'n stuk van grootte 0x20-0x2f in indeks 0 sal wees, 'n stuk van grootte 0x30-0x3f sou in idx 1 wees...

{% hint style="danger" %} Stukke in vinnige bins word nie as beskikbaar ingestel nie, sodat hulle vir 'n tydperk as vinnige bin stukke gehou word in plaas daarvan om saamgevoeg te word met ander vry stukke wat hulle omring. {% endhint %}

// 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)

Voeg 'n vinnigbin brokkie voorbeeld by

#include <stdlib.h>
#include <stdio.h>

int 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;
}

Merk op hoe ons 8 stukke van dieselfde grootte toewys en vrymaak sodat hulle die tcache vul en die agtste een in die vinnige stuk gestoor word.

Kompileer dit en ontleed dit met 'n breekpunt in die ret-opkode van die hooffunksie. dan kan jy met gef sien hoe die tcache-bin vul en die een stuk in die vinnige bin:

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

Ongeordende bin

Die ongeordende bin is 'n cache wat deur die heap-bestuurder gebruik word om geheue-toekenning vinniger te maak. So werk dit: Wanneer 'n program 'n stuk vrymaak, en as hierdie stuk nie in 'n tcache of vinnige bin toegewys kan word en nie bots met die boonste stuk nie, plaas die heap-bestuurder dit nie dadelik in 'n spesifieke klein of groot bin nie. Eerder probeer dit om dit saam te voeg met enige aangrensende vry stukke om 'n groter blok vry geheue te skep. Dan plaas dit hierdie nuwe stuk in 'n algemene bin genaamd die "ongeordende bin."

Wanneer 'n program vir geheue vra, kyk die heap-bestuurder na die ongeordende bin om te sien of daar 'n stuk van genoeg grootte is. As dit een vind, gebruik dit dit dadelik. As dit nie 'n geskikte stuk in die ongeordende bin vind nie, skuif dit al die stukke in hierdie lys na hul onderskeie bakkies, klein of groot, gebaseer op hul grootte.

Let daarop dat as 'n groter stuk in 2 helftes verdeel word en die res groter as MINSIZE is, sal dit teruggeplaas word in die ongeordende bin.

Dus, die ongeordende bin is 'n manier om geheue-toekenning te versnel deur onlangs vrygemaakte geheue vinnig te hergebruik en die behoefte aan tydrowende soektogte en saamvoegings te verminder.

{% hint style="danger" %} Let daarop dat selfs as stukke van verskillende kategorieë is, as 'n beskikbare stuk bots met 'n ander beskikbare stuk (selfs al behoort hulle oorspronklik aan verskillende bakkies), sal hulle saamgevoeg word. {% endhint %}

Voeg 'n voorbeeld van 'n ongeordende stuk by ```c #include #include

int 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; }

Merk op hoe ons 9 stukke van dieselfde grootte toewys en vrymaak sodat hulle die tcache **vul** en die agtste een in die ongesorteerde binne gestoor word omdat dit **te groot vir die fastbin** is en die negende een nie vrygemaak word nie sodat die negende en die agtstes nie met die boonste stuk saamgevoeg word nie.

Kompileer dit en ontleed dit met 'n onderbreking in die ret-opkode van die hooffunksie. Dan kan jy met gef sien hoe die tcache-binne vol is en die een stuk in die ongesorteerde binne:
```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.

Klein Bins

Klein bins is vinniger as groot bins maar stadiger as vinnige bins.

Elke bin van die 62 sal stukke van dieselfde grootte hê: 16, 24, ... (met 'n maksimum grootte van 504 byte in 32bits en 1024 in 64bits). Dit help om die bin te vind waar 'n spasie toegewys moet word en om inskrywings op hierdie lys in te voeg en te verwyder.

Dit is hoe die grootte van die klein bin bereken word volgens die indeks van die bin:

  • Kleinste grootte: 2*4*indeks (bv. indeks 5 -> 40)
  • Grootste grootte: 2*8*indeks (bv. 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)

Funksie om te kies tussen klein en groot bakkies:

#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))

Voeg 'n klein brokkie voorbeeld by

#include <stdlib.h>
#include <stdio.h>

int 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;
}

Merk op hoe ons 9 stukke van dieselfde grootte toewys en vrymaak sodat hulle die tcache vul en die agtste een in die ongesorteerde bin gestoor word omdat dit te groot vir die fastbin is en die negende nie vrygemaak word nie sodat die negende en die agtstes nie saamgevoeg word met die boonste stuk nie. Dan wys ons 'n groter stuk van 0x110 toe wat maak dat die stuk in die ongesorteerde bin na die klein bin gaan.

Kompileer dit en ontleed dit met 'n onderbreking in die ret-opkode van die hooffunksie. Dan kan jy met gef sien hoe die tcache-bin vul en die een stuk in die klein bin:

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.

Groot bakkies

In teenstelling met klein bakkies, wat brokkies van vaste groottes bestuur, hanteer elke groot bak 'n reeks brokkie groottes. Dit is meer buigsaam, wat die stelsel in staat stel om verskeie groottes te akkommodeer sonder om 'n afsonderlike bak vir elke grootte nodig te hê.

In 'n geheue-toewysingsprogram begin groot bakkies waar klein bakkies eindig. Die reekse vir groot bakkies word progressief groter, wat beteken dat die eerste bak moontlik brokkies van 512 tot 576 byte kan dek, terwyl die volgende 576 tot 640 byte dek. Hierdie patroon gaan voort, met die grootste bak wat alle brokkies bo 1MB bevat.

Groot bakkies is stadiger om te bedryf in vergelyking met klein bakkies omdat hulle moet sorteer en deur 'n lys van wisselende brokkie groottes moet soek om die beste pasgemaakte vir 'n toekenning te vind. Wanneer 'n brokkie in 'n groot bak ingevoeg word, moet dit gesorteer word, en wanneer geheue toegewys word, moet die stelsel die regte brokkie vind. Hierdie ekstra werk maak hulle stadiger, maar aangesien groot toekenning minder algemeen is as klein een, is dit 'n aanvaarbare afweging.

Daar is:

  • 32 bakkies van 64B reeks (bots met klein bakkies)
  • 16 bakkies van 512B reeks (bots met klein bakkies)
  • 8 bakkies van 4096B reeks (deels bots met klein bakkies)
  • 4 bakkies van 32768B reeks
  • 2 bakkies van 262144B reeks
  • 1 bak vir oorblywende groottes
Groot bak groottes kode ```c // From a07e000e82/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>Voeg 'n groot brokkie voorbeeld by</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 groot toekenning word uitgevoer, dan word een vrygestel (wat dit in die ongesorteerde bin plaas) en 'n groter toekenning word gemaak (wat die vry een van die ongesorteerde bin na die groot bin skuif).

Kompileer dit en ontleed dit met 'n breekpunt in die ret-opkode van die hooffunksie. dan met gef kan jy die tcache-bin vul en die een blok in die groot bin sien:

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.

Boonste Brok

// 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))

Waarneming van die Top Stuk voorbeeld

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
char *chunk;
chunk = malloc(24);
printf("Address of the chunk: %p\n", (void *)chunk);
gets(chunk);
return 0;
}

Na die samestelling en foutopsporing daarvan met 'n breekpunt in die ret-opkode van hoof, het ek gesien dat die malloc die adres teruggegee het: 0xaaaaaaac12a0 en dit is die brokkie:

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

Waar dit gesien kan word dat die boonste blok by adres 0xaaaaaaac1ae0 is. Dit is geen verrassing nie omdat die mees onlangs toegewysde blok was by 0xaaaaaaac12a0 met 'n grootte van 0x410 en 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0.
Dit is ook moontlik om die lengte van die Boonste blok op sy blokkop te sien:

gef➤  x/8wx 0xaaaaaaac1ae0 - 16
0xaaaaaaac1ad0:	0x00000000	0x00000000	0x00020531	0x00000000
0xaaaaaaac1ae0:	0x00000000	0x00000000	0x00000000	0x00000000

Laaste Herinnering

Wanneer malloc gebruik word en 'n stuk verdeel word (van die ontkoppelde lys of van die boonste stuk byvoorbeeld), word die stuk wat geskep word uit die res van die verdeelde stuk genoem Laaste Herinnering en sy wyser word gestoor in die malloc_state struktuur.

Toekenning Vloei

Kyk na:

{% content-ref url="heap-memory-functions/malloc-and-sysmalloc.md" %} malloc-and-sysmalloc.md {% endcontent-ref %}

Vry Vloei

Kyk na:

{% content-ref url="heap-memory-functions/free.md" %} free.md {% endcontent-ref %}

Heap Funksies Sekuriteitskontroles

Kyk na die sekuriteitskontroles wat uitgevoer word deur baie gebruikte funksies in die heap in:

{% content-ref url="heap-memory-functions/heap-functions-security-checks.md" %} heap-functions-security-checks.md {% endcontent-ref %}

Verwysings

Leer AWS hak vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun: