mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 20:53:37 +00:00
615 lines
36 KiB
Markdown
615 lines
36 KiB
Markdown
|
# Bins & Aloakcije memorije
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><strong>Naučite hakovanje AWS-a od nule do heroja sa</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||
|
|
||
|
Drugi načini podrške HackTricks-u:
|
||
|
|
||
|
* Ako želite da vidite svoju **kompaniju reklamiranu na HackTricks-u** ili da **preuzmete HackTricks u PDF formatu** proverite [**PLANOVE ZA PRIJAVU**](https://github.com/sponsors/carlospolop)!
|
||
|
* Nabavite [**zvanični PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
|
* Otkrijte [**Porodicu PEASS**](https://opensea.io/collection/the-peass-family), našu kolekciju ekskluzivnih [**NFT-ova**](https://opensea.io/collection/the-peass-family)
|
||
|
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Podelite svoje hakovanje trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
|
||
|
|
||
|
</details>
|
||
|
|
||
|
## Osnovne informacije
|
||
|
|
||
|
Kako bi se poboljšala efikasnost čuvanja blokova, svaki blok nije samo u jednoj povezanoj listi, već postoji nekoliko tipova. To su binovi i postoji 5 tipova binova: [62](https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=6e766d11bc85b6480fa5c9f2a76559f8acf9deb5;hb=HEAD#l1407) mali binovi, 63 velika binova, 1 nesortirani bin, 10 brzih binova i 64 tcache binova po niti.
|
||
|
|
||
|
Početna adresa za svaki nesortirani, mali i veliki bin je unutar istog niza. Indeks 0 nije korišćen, 1 je nesortirani bin, binovi 2-64 su mali binovi, a binovi 65-127 su veliki binovi.
|
||
|
|
||
|
### Tcache (Keš po niti) Binovi
|
||
|
|
||
|
Iako niti pokušavaju da imaju svoj sopstveni hip (videti [Arenas](bins-and-memory-allocations.md#arenas) i [Pod-hipove](bins-and-memory-allocations.md#subheaps)), postoji mogućnost da će proces sa puno niti (kao što je veb server) **završiti deljenjem hipa sa drugim nitima**. U ovom slučaju, glavno rešenje je korišćenje **brava**, što može **znatno usporiti niti**.
|
||
|
|
||
|
Stoga, tcache je sličan brzom binu po niti na način da je to **jednostruko povezana lista** koja ne spaja blokove. Svaka nit ima **64 jednostruko povezana tcache binova**. Svaki bin može imati maksimalno [7 blokova iste veličine](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323) u rasponu od [24 do 1032B na 64-bitnim sistemima i 12 do 516B na 32-bitnim sistemima](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315).
|
||
|
|
||
|
Kada nit oslobodi blok, ako nije prevelik da bi bio dodeljen u tcache i odgovarajući tcache bin **nije pun** (već 7 blokova), **biće dodeljen tamo**. Ako ne može ići u tcache, moraće da sačeka da se hip zaključa kako bi mogao da izvrši globalnu operaciju oslobađanja.
|
||
|
|
||
|
Kada se **blok dodeli**, ako postoji slobodan blok potrebne veličine u **Tcache-u, biće korišćen**, ako ne, moraće da sačeka da se hip zaključa kako bi mogao da pronađe jedan u globalnim binovima ili da napravi novi.\
|
||
|
Postoji i optimizacija, u ovom slučaju, dok ima zaključan hip, nit **će popuniti svoj Tcache hip blokovima (7) tražene veličine**, tako da ako zatreba više, naći će ih u Tcache-u.
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Dodaj primer tcache bloka</summary>
|
||
|
```c
|
||
|
#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);
|
||
|
free(chunk);
|
||
|
return 0;
|
||
|
}
|
||
|
```
|
||
|
Kompajlirajte ga i debagujte sa prekidnom tačkom u ret opcode-u iz glavne funkcije. Zatim, pomoću gef-a 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 je moguće videti **maksimalne binove** i **delove po indeksu**, strukturu **`tcache_entry`** kreiranu da bi se izbeglo duplo oslobađanje i **`tcache_perthread_struct`**, strukturu koju svaka nit koristi da bi čuvala adrese za svaki indeks bin-a.
|
||
|
```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`
|
||
|
```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));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
#### Tcache Indeksi
|
||
|
|
||
|
Tcache ima nekoliko binova u zavisnosti od veličine, a početni pokazivači na **prvi chunk svakog indeksa i količina chunk-ova po indeksu se nalaze unutar chunk-a**. To znači da je moguće pronaći sve početne tačke tcache-a i količinu Tcache chunk-ova lociranjem chunk-a sa ovim informacijama (obično prvi).
|
||
|
|
||
|
### Brzi binovi
|
||
|
|
||
|
Brzi binovi su dizajnirani da **ubrzaju dodelu memorije za male chunk-ove** čuvanjem nedavno oslobođenih chunk-ova u strukturi sa brzim pristupom. Ovi binovi koriste pristup poslednji unutra, prvi napolje (LIFO) pristup, što znači da je **najskorije oslobođeni chunk prvi** koji će biti ponovo korišćen kada postoji nova zahtev za alokacijom. Ovo ponašanje je korisno za brzinu, jer je brže ubaciti i ukloniti sa vrha steka (LIFO) u poređenju sa redom (FIFO).
|
||
|
|
||
|
Dodatno, **brzi binovi koriste jednostruko povezane liste**, a ne dvostruko povezane, što dodatno poboljšava brzinu. Budući da se chunk-ovi 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 osnovi, ono što se dešava ovde je da je zaglavlje (pokazivač na prvi chunk koji treba proveriti) uvek usmereno ka poslednjem oslobođenom chunk-u te veličine. Dakle:
|
||
|
|
||
|
* Kada se alocira novi chunk te veličine, zaglavlje pokazuje na slobodan chunk za korišćenje. Pošto ovaj slobodan chunk pokazuje na sledeći koji treba koristiti, ova adresa se čuva u zaglavlju kako bi sledeća alokacija znala gde da dobije dostupan chunk.
|
||
|
* Kada se chunk oslobodi, slobodan chunk će sačuvati adresu trenutno dostupnog chunk-a, a adresa ovog novootvorenog chunk-a će biti stavljena u zaglavlje.
|
||
|
|
||
|
Maksimalna veličina povezane liste je `0x80` i organizovane su tako da će chunk veličine `0x20-0x2f` biti u indeksu `0`, chunk veličine `0x30-0x3f` bi bio u `idx` `1`...
|
||
|
|
||
|
{% hint style="danger" %}
|
||
|
Chunk-ovi u brzim binovima nisu označeni kao dostupni, tako da se čuvaju kao chunk-ovi brzih binova neko vreme umesto da mogu da se spoje sa drugim slobodnim chunk-ovima koji ih okružuju.
|
||
|
{% endhint %}
|
||
|
```c
|
||
|
// 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)
|
||
|
```
|
||
|
<detalji>
|
||
|
|
||
|
<sumiraj>Dodajte primer brze binarne čestice</sumiraj>
|
||
|
```c
|
||
|
#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;
|
||
|
}
|
||
|
```
|
||
|
Zabeležite kako alociramo i oslobađamo 8 blokova iste veličine tako da popune tcache, a osmi je smešten u brzom bloku.
|
||
|
|
||
|
Kompajlirajte ga i debagujte sa prekidnom tačkom u ret opcode-u glavne funkcije. Zatim, pomoću gef-a možete videti popunjavanje tcache bin-a i jedan blok u brzom binu:
|
||
|
```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đena kanta
|
||
|
|
||
|
Neuređena kanta je **keš** koji koristi menadžer hipa kako bi ubrzao dodelu memorije. Evo kako funkcioniše: Kada program oslobodi komad, i ako taj komad ne može biti dodeljen u tcache ili brzoj kanti i ne sudara se sa vršnim komadom, menadžer hipa ga ne stavlja odmah u određenu malu ili veliku kantu. Umesto toga, prvo pokušava da ga **spoji sa bilo kojim susednim slobodnim komadima** kako bi stvorio veći blok slobodne memorije. Zatim, smešta ovaj novi komad u opštu kantu nazvanu "neuređena kanta".
|
||
|
|
||
|
Kada program **zatraži memoriju**, menadžer hipa **proverava neuređenu kantu** da vidi da li postoji dovoljno veliki komad. Ako pronađe jedan, odmah ga koristi. Ako ne pronađe odgovarajući komad u neuređenoj kanti, premestiće sve komade sa ove liste u njihove odgovarajuće kante, bilo male ili velike, na osnovu njihove veličine.
|
||
|
|
||
|
Imajte na umu da ako se veći komad podeli na 2 polovine i ako je preostali deo veći od MINSIZE, biće vraćen nazad u neuređenu kantu. 
|
||
|
|
||
|
Dakle, neuređena kanta je način da se ubrza dodela memorije brzim ponovnim korišćenjem nedavno oslobođene memorije i smanjenjem potrebe za vremenski zahtevnim pretragama i spajanjima.
|
||
|
|
||
|
{% hint style="danger" %}
|
||
|
Imajte na umu da čak i ako su komadi različitih kategorija, ako dostupan komad sudara sa drugim dostupnim komadom (čak i ako su originalno pripadali različitim kantama), biće spojeni.
|
||
|
{% endhint %}
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Dodaj primer neuređenog komada</summary>
|
||
|
```c
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
```
|
||
|
Primetite kako alociramo i oslobađamo 9 blokova iste veličine tako da **popunjavaju tcache**, a osmi je smešten u unsorted bin jer je **prevelik za fastbin**, dok deveti nije oslobođen tako da deveti i osmi **ne budu spojeni sa vršnim blokom**.
|
||
|
|
||
|
Kompajlirajte i debagujte sa prekidnom tačkom u ret opcode-u iz glavne funkcije. Zatim pomoću gef-a možete videti popunjavanje tcache bin-a i jedan blok u unsorted bin-u:
|
||
|
```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.
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
### Mali Bins
|
||
|
|
||
|
Mali binovi su brži od velikih binova, ali sporiji od brzih binova.
|
||
|
|
||
|
Svaki bin od 62 će imati **blokove iste veličine**: 16, 24, ... (sa maksimalnom veličinom od 504 bajta u 32 bitnom i 1024 u 64 bitnom režimu). Ovo pomaže u brzini pronalaženja binova gde treba alocirati prostor i ubacivanju i uklanjanju unosa sa ovih lista.
|
||
|
|
||
|
Ovako se računa veličina malog bina prema indeksu bina:
|
||
|
|
||
|
* Najmanja veličina: 2\*4\*indeks (npr. indeks 5 -> 40)
|
||
|
* Najveća veličina: 2\*8\*indeks (npr. indeks 5 -> 80)
|
||
|
```c
|
||
|
// 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)
|
||
|
```
|
||
|
Funkcija za izbor između malih i velikih binova:
|
||
|
```c
|
||
|
#define bin_index(sz) \
|
||
|
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
|
||
|
```
|
||
|
<detalji>
|
||
|
|
||
|
<summary>Dodajte primer malog bloka</summary>
|
||
|
```c
|
||
|
#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;
|
||
|
}
|
||
|
```
|
||
|
Primetite kako alociramo i oslobađamo 9 blokova iste veličine tako da **popunimo tcache** i osmi je smešten u unsorted bin jer je **prevelik za fastbin**, a deveti nije oslobođen tako da deveti i osmi **ne budu spojeni sa vršnim blokom**. Zatim alociramo veći blok od 0x110 što dovodi do toga da **blok u unsorted binu pređe u small bin**.
|
||
|
|
||
|
Kompajlirajte i debagujte sa prekidnom tačkom u ret opcode-u iz glavne funkcije. Zatim sa gef-om možete videti popunjen tcache bin i jedan blok u small 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.
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
### Velike kante
|
||
|
|
||
|
Za razliku od malih kanti, koje upravljaju komadićima fiksnih veličina, **svaka velika kanta upravlja opsegom veličina komadića**. Ovo je fleksibilnije, omogućavajući sistemu da se prilagodi **različitim veličinama** bez potrebe za posebnom kantom za svaku veličinu.
|
||
|
|
||
|
U memorijskom alokatoru, velike kante počinju tamo gde se završavaju male kante. Opsezi za velike kante postaju sve veći, što znači da prva kanta može pokrivati komadiće 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 komadiće veće od 1MB.
|
||
|
|
||
|
Velike kante sporije rade u poređenju sa malim kantama jer moraju **sortirati i pretraživati listu različitih veličina komadića kako bi pronašle najbolje odgovarajuće** za alokaciju. Kada se komadić ubaci u veliku kantu, mora biti sortiran, a prilikom alokacije memorije, sistem mora pronaći odgovarajući komadić. Ovaj dodatni rad ih čini **sporijim**, ali budući da su velike alokacije manje uobičajene od malih, to je prihvatljiva trgovina.
|
||
|
|
||
|
Postoje:
|
||
|
|
||
|
* 32 kante opsega 64B (sudaraju se sa malim kantama)
|
||
|
* 16 kanti opsega 512B (sudaraju se sa malim kantama)
|
||
|
* 8 kanti opsega 4096B (delimično se sudaraju sa malim kantama)
|
||
|
* 4 kante opsega 32768B
|
||
|
* 2 kante opsega 262144B
|
||
|
* 1 kanta za preostale veličine
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Kod veličina velikih kanti</summary>
|
||
|
```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 velikog bloka</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;
|
||
|
}
|
||
|
```
|
||
|
Dve velike alokacije se vrše, zatim jedna se oslobađa (stavlja se u unsorted bin) i pravi se veća alokacija (premeštanje oslobođene u unsorted bin u large bin).
|
||
|
|
||
|
Kompajlirajte i debagujte sa prekidnom tačkom u ret opcode-u glavne funkcije. Zatim, pomoću gef-a možete videti popunjavanje tcache bina i jedan chunk u large binu:
|
||
|
```bash
|
||
|
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.
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
### Vrhunski blok
|
||
|
```c
|
||
|
// 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))
|
||
|
```
|
||
|
Osnovno, ovo je deo koji sadrži sav trenutno dostupan heap. Kada se izvrši malloc, ako nema dostupnog slobodnog chunk-a za korišćenje, ovaj top chunk će smanjiti svoju veličinu pružajući potreban prostor.\
|
||
|
Pokazivač na Top Chunk se čuva u strukturi `malloc_state`.
|
||
|
|
||
|
Osim toga, na početku je moguće koristiti nesortirani chunk kao top chunk.
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Posmatrajte primer Top Chunk-a</summary>
|
||
|
```c
|
||
|
#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;
|
||
|
}
|
||
|
```
|
||
|
Nakon kompajliranja i debagovanja sa prekidnom tačkom u ret opcode-u glavne funkcije, primetio sam da je malloc vratio adresu: `0xaaaaaaac12a0` i ovo su chunk-ovi:
|
||
|
```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 vrhunski blok na adresi `0xaaaaaaac1ae0`. To nije iznenađenje jer je poslednji alocirani blok bio na `0xaaaaaaac12a0` sa veličinom `0x410` i `0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0`.\
|
||
|
Takođe je moguće videti dužinu vrhunskog bloka na njegovom zaglavlju bloka:
|
||
|
```bash
|
||
|
gef➤ x/8wx 0xaaaaaaac1ae0 - 16
|
||
|
0xaaaaaaac1ad0: 0x00000000 0x00000000 0x00020531 0x00000000
|
||
|
0xaaaaaaac1ae0: 0x00000000 0x00000000 0x00000000 0x00000000
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
### Poslednje podsećanje
|
||
|
|
||
|
Kada se koristi malloc i deo je podeljen (na primer, iz nevezane liste ili iz vršnog bloka), deo koji je kreiran od preostalog dela podeljenog bloka naziva se Poslednje podsećanje i njegov pokazivač se čuva u strukturi `malloc_state`.
|
||
|
|
||
|
## Tok dodele
|
||
|
|
||
|
Pogledajte:
|
||
|
|
||
|
{% content-ref url="heap-memory-functions/malloc-and-sysmalloc.md" %}
|
||
|
[malloc-and-sysmalloc.md](heap-memory-functions/malloc-and-sysmalloc.md)
|
||
|
{% endcontent-ref %}
|
||
|
|
||
|
## Tok oslobađanja
|
||
|
|
||
|
Pogledajte:
|
||
|
|
||
|
{% content-ref url="heap-memory-functions/free.md" %}
|
||
|
[free.md](heap-memory-functions/free.md)
|
||
|
{% endcontent-ref %}
|
||
|
|
||
|
## Provere bezbednosti funkcija hipa
|
||
|
|
||
|
Proverite provere bezbednosti koje obavljaju često korišćene funkcije u hipu u:
|
||
|
|
||
|
{% content-ref url="heap-memory-functions/heap-functions-security-checks.md" %}
|
||
|
[heap-functions-security-checks.md](heap-memory-functions/heap-functions-security-checks.md)
|
||
|
{% endcontent-ref %}
|
||
|
|
||
|
## Reference
|
||
|
|
||
|
* [https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/](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://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/)
|
||
|
* [https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions](https://heap-exploitation.dhavalkapil.com/diving\_into\_glibc\_heap/core\_functions)
|
||
|
* [https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/)
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><strong>Naučite hakovanje AWS-a od nule do heroja sa</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||
|
|
||
|
Drugi načini podrške HackTricks-u:
|
||
|
|
||
|
* Ako želite da vidite svoju **kompaniju reklamiranu na HackTricks-u** ili da **preuzmete HackTricks u PDF formatu** proverite [**PLANOVE ZA PRETPLATU**](https://github.com/sponsors/carlospolop)!
|
||
|
* Nabavite [**zvanični PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||
|
* Otkrijte [**The PEASS Family**](https://opensea.io/collection/the-peass-family), našu kolekciju ekskluzivnih [**NFT-ova**](https://opensea.io/collection/the-peass-family)
|
||
|
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Podelite svoje hakovanje trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
|
||
|
|
||
|
</details>
|