# Bins & Bellek Tahsisi
Sıfırdan kahraman olmak için AWS hackleme öğrenin htARTE (HackTricks AWS Kırmızı Takım Uzmanı)! HackTricks'ı desteklemenin diğer yolları: * **Şirketinizi HackTricks'te reklamını görmek istiyorsanız** veya **HackTricks'i PDF olarak indirmek istiyorsanız** [**ABONELİK PLANLARI**]'na(https://github.com/sponsors/carlospolop) göz atın! * [**Resmi PEASS & HackTricks ürünleri**](https://peass.creator-spring.com) edinin * [**PEASS Ailesi'ni**](https://opensea.io/collection/the-peass-family) keşfedin, özel [**NFT'lerimiz**](https://opensea.io/collection/the-peass-family) koleksiyonumuz * **Katılın** 💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) veya bizi **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)** takip edin.** * **Hacking püf noktalarınızı paylaşarak PR'lar göndererek** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github depolarına katkıda bulunun.
## Temel Bilgiler Parçaların nasıl depolandığındaki verimliliği artırmak için her parça sadece bir bağlı listede değil, birkaç türde depolanır. Bunlar "bins"lerdir ve 5 tür bin vardır: [62](https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=6e766d11bc85b6480fa5c9f2a76559f8acf9deb5;hb=HEAD#l1407) küçük bins, 63 büyük bins, 1 sıralanmamış bin, 10 hızlı bins ve her iş parçacığı için 64 tcache bin'i. Her sıralanmamış, küçük ve büyük binin başlangıç adresi aynı dizinin içindedir. İndeks 0 kullanılmaz, 1 sıralanmamış bin, 2-64 arası küçük bins ve 65-127 arası büyük binslerdir. ### Tcache (İş Parçacığı Başına Önbellek) Binsleri İş parçacıkları kendi belleğine sahip olmaya çalışsa da (bkz. [Arenalar](bins-and-memory-allocations.md#arenas) ve [Alt bellekler](bins-and-memory-allocations.md#subheaps)), birçok iş parçacığına sahip bir işlem (örneğin bir web sunucusu) **başka iş parçacıklarıyla belleği paylaşabilir**. Bu durumda, ana çözüm **kilitlerin** kullanılmasıdır, bu da iş parçacıklarını **önemli ölçüde yavaşlatabilir**. Bu nedenle, bir tcache, parçaları birleştirmeyen **tek yönlü bağlı listedir** ve her iş parçacığının **64 tek yönlü tcache bin'i** vardır. Her bin, [64 bit sistemlerde 24 ile 1032B ve 32 bit sistemlerde 12 ile 516B arasında](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315) aynı boyutta en fazla [7 parçaya](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323) sahip olabilir. Bir iş parçacığı bir parçayı serbest bıraktığında, eğer tcache'e ayrılmak için çok büyük değilse ve ilgili tcache bin **dolu değilse** (zaten 7 parça), **oraya ayrılacaktır**. Tcache'e gidemezse, genel olarak serbest bırakma işlemini gerçekleştirebilmek için bellek kilidinin açılmasını beklemesi gerekir. Bir **parça ayrıldığında**, eğer **Tcache'te ihtiyaç duyulan boyutta boş bir parça varsa**, onu kullanır, yoksa genel binslerde bir tane bulmak veya yeni bir tane oluşturmak için bellek kilidinin açılmasını beklemesi gerekir.\ Bu durumda bir optimizasyon da vardır, bu durumda bellek kilidi açıkken, iş parçacığı **istenen boyutta Tcache'ini heap parçalarıyla (7) doldurur**, böylece daha fazla ihtiyaç duyarsa, onları Tcache'te bulabilir. ```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; } ``` Derleyin ve ana işlevden ret opcode'unda bir kesme noktası ile hata ayıklamayı yapın. Sonra gef ile kullanımda olan tcache binini görebilirsiniz: ```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 Yapıları ve Fonksiyonlar Aşağıdaki kodda **maksimum kovalar** ve **her indeks başına parçalar**, çift serbest bırakmaları önlemek için oluşturulan **`tcache_entry`** yapısı ve her bir iş parçacığının kovanın her indeksine ait adresleri depolamak için kullandığı **`tcache_perthread_struct`** yapısı görülebilir.
tcache_entry ve 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; ```
`__tcache_init` işlevi, `tcache_perthread_struct` nesnesi için alan oluşturan ve ayıran işlevdir.
tcache_init kodu ```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 İndeksleri Tcache, **her bir indeksin boyutuna ve başlangıç işaretçilerine bağlı olarak birkaç farklı bina sahiptir ve her indeks için ilk parçanın ve indeks başına düşen parça miktarının bir parça içinde bulunur**. Bu, bu bilgiyi içeren parçayı (genellikle ilk parçayı) bulmak, tüm tcache başlangıç noktalarını ve Tcache parça miktarını bulmayı mümkün kılar. ### Hızlı Binalar Hızlı binalar, **küçük parçalar için bellek tahsisini hızlandırmak için tasarlanmıştır** ve son zamanlarda serbest bırakılan parçaları hızlı erişim yapısında tutarak çalışır. Bu binalar, Son Giren İlk Çıkar (LIFO) yaklaşımını kullanır, yani **en son serbest bırakılan parça, yeniden tahsis isteği olduğunda ilk** olarak yeniden kullanılır. Bu davranış, hız için avantajlıdır, çünkü bir yığının üstünden (LIFO) ekleme ve çıkarma yapmak, bir kuyruktan (FIFO) daha hızlıdır. Ayrıca, **hızlı binalar tek yönlü bağlı listeleri kullanır**, çift yönlü değil, bu da hızı daha da artırır. Hızlı binalardaki parçalar komşularla birleştirilmediği için, ortadan kaldırma izni veren karmaşık bir yapıya ihtiyaç yoktur. Tek yönlü bağlı liste, bu işlemler için daha basit ve daha hızlıdır. Temelde burada olan şey, başlık (kontrol edilecek ilk parçanın işaretçisi) her zaman o boyuttaki en son serbest bırakılan parçaya işaret eder. Dolayısıyla: * Bu boyutta yeni bir parça tahsis edildiğinde, başlık kullanılacak bir boş parçaya işaret eder. Bu boş parça, kullanılacak bir sonraki parçaya işaret ettiği için, bu adres başlıkta saklanır, böylece bir sonraki tahsisat nereden alınacağını bilir * Bir parça serbest bırakıldığında, boş parça mevcut kullanılabilir parçanın adresini kaydeder ve bu yeni serbest bırakılan parçanın adresi başlığa konur Bağlı liste maksimum boyutu `0x80` ve bir `0x20-0x2f` boyutundaki parça `0` indeksinde olacak, bir `0x30-0x3f` boyutundaki parça `1` indeksinde olacak şekilde düzenlenmiştir. {% hint style="danger" %} Hızlı binalardaki parçalar mevcut olarak ayarlanmadığından, etraflarındaki diğer boş parçalarla birleştirilebilme yeteneğine sahip olmamaları nedeniyle bir süre hızlı bina parçaları olarak tutulurlar. {% 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) ``` <özet> Hızlı bir blok parçası örneği ekleyin ```c #include #include 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; } ``` Not: Aynı boyutta 8 parça ayırdığımızı ve serbest bıraktığımızı ve bunların tcache'i doldurduğunu ve sekizincisinin hızlı parçada depolandığını gözlemleyin. Bunu derleyin ve ana fonksiyonun ret opcode'unda bir kesme noktası ile hata ayıklamayı başlatın. Sonra gef ile tcache binin dolduğunu ve bir parçanın hızlı binde olduğunu görebilirsiniz: ```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 ``` ### Sıralanmamış kova Sıralanmamış kova, bellek tahsisini hızlandırmak için heap yöneticisi tarafından kullanılan bir **önbellek**tir. İşte nasıl çalışır: Bir program bir parçayı serbest bıraktığında ve bu parça bir tcache veya hızlı kova içinde tahsis edilemiyorsa ve üst parça ile çakışmıyorsa, heap yöneticisi hemen onu belirli bir küçük veya büyük kovaya koymaz. Bunun yerine, öncelikle **bitişik boş parçalarla birleştirmeye** çalışır ve daha büyük bir boş bellek bloğu oluşturur. Daha sonra, bu yeni parçayı "sıralanmamış kova" olarak adlandırılan genel bir kovaya yerleştirir. Bir program **bellek istediğinde**, heap yöneticisi **sıralanmamış kovayı kontrol eder** ve yeterli boyutta bir parça bulunup bulunmadığını kontrol eder. Bir tane bulursa, hemen kullanır. Sıralanmamış kovada uygun bir parça bulamazsa, bu listedeki tüm parçaları boyutlarına bağlı olarak küçük veya büyük kovalarına taşır. Dikkat edin ki daha büyük bir parça ikiye bölünürse ve geri kalanı MINSIZE'dan büyükse, tekrar sıralanmamış kovaya yerleştirilecektir. Bu nedenle, sıralanmamış kova, son zamanlarda serbest bırakılan belleği hızlı bir şekilde yeniden kullanarak bellek tahsisini hızlandırmak ve zaman alıcı aramaları ve birleştirmeleri azaltmak için bir yöntemdir. {% hint style="danger" %} Farklı kategorilerdeki parçalar olsalar bile, mevcut bir parça başka bir mevcut parçayla çakışıyorsa (başlangıçta farklı kovalara ait olsalar bile), birleştirileceklerdir. {% endhint %}
Bir sıralanmamış parça örneği ekle ```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; } ``` Not: Aynı boyutta 9 parça ayırdığımızı ve serbest bıraktığımızı **tcache'i dolduracak şekilde** ve sekizincisinin **fastbin için çok büyük olduğu için** sıralanmamış binde depolandığını gözlemleyin ve dokuzuncusu serbest bırakılmadığı için dokuzuncu ve sekizincisinin **üst parça ile birleştirilmediğini** unutmayın. Bunu derleyin ve ana fonksiyondan dönüş komutunda bir kesme noktası ile hata ayıklamayı yapın. Sonra gef ile tcache bininin doluluk durumunu ve sıralanmamış bindeki bir parçayı görebilirsiniz: ```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. ```
### Küçük Bins Küçük binalar büyük binalardan daha hızlıdır ancak hızlı binalardan daha yavaştır. 62'nin her bir kutusu aynı boyutta parçalara sahip olacaktır: 16, 24, ... (32 bit için maksimum 504 bayt ve 64 bit için 1024 bayt). Bu, bir alana yer ayrılması gereken binin bulunmasında, bu listelerde girişlerin eklenmesi ve çıkarılmasında hız sağlar. Küçük binin boyutu, binin indeksine göre nasıl hesaplandığı aşağıdaki gibidir: * En küçük boyut: 2\*4\*indeks (örneğin, indeks 5 -> 40) * En büyük boyut: 2\*8\*indeks (örneğin, 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) ``` ```c void *choose_bin(size_t size) { if (size <= SMALL_BIN_SIZE) { return small_bin; } else { return large_bin; } } ``` ```c #define bin_index(sz) \ ((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz)) ``` <özet>Küçük bir parça örneği ekle ```c #include #include 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; } ``` Not: Aynı boyutta 9 parça ayırdığımızı ve bunların **tcache'i doldurduğunu** ve sekizincisinin **fastbin için çok büyük olduğu için düzensiz kutuya** saklandığını ve dokuzuncusunun serbest bırakılmadığını, bu yüzden dokuzuncu ve sekizincinin **üst parça ile birleştirilmediğini** gözlemleyin. Daha sonra 0x110 boyutunda daha büyük bir parça ayırıyoruz, bu da **düzensiz kutudaki parçanın küçük kutuya gitmesine neden olur**. Derleyin ve ana fonksiyondaki ret opcode'unda bir kesme noktası ile hata ayıklamayı yapın. Sonra gef ile tcache bininin dolu olduğunu ve küçük kutuda bir parçanın olduğunu görebilirsiniz: ```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. ``` ### Büyük kovalar Küçük kovaların aksine, her **büyük kova bir aralıkta parça boyutlarını yönetir**. Bu daha esnek bir yapıya sahiptir, çeşitli boyutları ayrı ayrı bir kova gerektirmeden sistemde **barındırabilir**. Bir bellek tahsis edici içinde büyük kovalar, küçük kovaların bittiği yerden başlar. Büyük kovalar için aralıklar giderek büyür, yani ilk kova 512 ile 576 bayt arasındaki parçaları kapsayabilirken, bir sonraki kova 576 ile 640 bayt arasındakileri kapsar. Bu desen devam eder ve en büyük kova 1MB üzerindeki tüm parçaları içerir. Büyük kovalar, bir tahsis için en iyi uyumu bulmak için **değişen parça boyutlarının listesini sıralamak ve aramak zorunda olduklarından küçük kovalara göre daha yavaş çalışırlar**. Bir parça büyük bir kovaya eklendiğinde sıralanmalıdır ve bellek tahsis edildiğinde sistem doğru parçayı bulmalıdır. Bu ekstra iş onları **daha yavaş** yapar, ancak büyük tahsisler küçük olanlardan daha az olduğu için kabul edilebilir bir takas yapılır. Şunlar vardır: - 64B aralığında 32 kova (küçük kovalarla çakışır) - 512B aralığında 16 kova (küçük kovalarla çakışır) - 4096B aralığında 8 kova (kısmen küçük kovalarla çakışır) - 32768B aralığında 4 kova - 262144B aralığında 2 kova - Geriye kalan boyutlar için 1 kova
Büyük kova boyutları kodu ```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)) ```
Büyük bir parça örneği ekle ```c #include #include int main(void) { char *chunks[2]; chunks[0] = malloc(0x1500); chunks[1] = malloc(0x1500); free(chunks[0]); chunks[0] = malloc(0x2000); return 0; } ``` İki büyük tahsisat yapılır, sonra biri serbest bırakılır (onu sırasız kutuya koyar) ve daha büyük bir tahsisat yapılır (serbest bırakılanı sırasız kutudan büyük kutuya taşır). Bunu derleyin ve ana işlevden dönüş işlem kodunda bir kesme noktasıyla hata ayıklayın. Sonra gef ile tcache kutusunun dolmasını ve büyük kutuda bir parçanın olduğunu görebilirsiniz: ```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. ```
### En Üst Parça ```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)) ``` Temelde, bu şu anda mevcut olan heap'i içeren bir parçadır. Bir malloc işlemi gerçekleştirildiğinde, kullanılabilecek herhangi bir boş parça yoksa, bu üst parça gerekli alanı vererek boyutunu azaltacaktır.\ Top Chunk'a işaretçi, `malloc_state` yapısında saklanır. Ayrıca, başlangıçta, üst parça olarak sırasız parçayı kullanmak mümkündür.
Üst Parça örneğini gözlemleyin ```c #include #include int main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); return 0; } ``` Sonra derleyip hata ayıkladıktan sonra main'in ret opcode'unda bir kesme noktası ile malloc'un döndürdüğü adresin `0xaaaaaaac12a0` olduğunu gördüm ve bunlar chunk'lar: ```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 ``` Üst parçanın `0xaaaaaaac1ae0` adresinde olduğu görülebilir. Bu, en son ayrılan parçanın boyutunun `0x410` ve `0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0` olduğu için şaşırtıcı değildir.\ Ayrıca, Üst parçanın uzunluğu da parça başlığında görülebilir: ```bash gef➤ x/8wx 0xaaaaaaac1ae0 - 16 0xaaaaaaac1ad0: 0x00000000 0x00000000 0x00020531 0x00000000 0xaaaaaaac1ae0: 0x00000000 0x00000000 0x00000000 0x00000000 ```
### Son Hatırlatma `malloc` kullanıldığında ve bir parça bölündüğünde (örneğin bağlı olmayan listeden veya üst parçadan), bölünmüş parçadan kalan kısım ile oluşturulan parçaya Son Hatırlatma denir ve işaretçisi `malloc_state` yapısında saklanır. ## Tahsis Akışı Şuna göz atın: {% content-ref url="heap-memory-functions/malloc-and-sysmalloc.md" %} [malloc-and-sysmalloc.md](heap-memory-functions/malloc-and-sysmalloc.md) {% endcontent-ref %} ## Serbest Akışı Şuna göz atın: {% content-ref url="heap-memory-functions/free.md" %} [free.md](heap-memory-functions/free.md) {% endcontent-ref %} ## Yığın Fonksiyonları Güvenlik Kontrolleri Yığın içinde sıkça kullanılan fonksiyonlar tarafından gerçekleştirilen güvenlik kontrollerini kontrol edin: {% 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 %} ## Referanslar * [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/)
Sıfırdan kahraman olmak için AWS hackleme öğrenin htARTE (HackTricks AWS Red Team Expert)! HackTricks'ı desteklemenin diğer yolları: * **Şirketinizi HackTricks'te reklamını görmek istiyorsanız** veya **HackTricks'i PDF olarak indirmek istiyorsanız** [**ABONELİK PLANLARI**](https://github.com/sponsors/carlospolop)'na göz atın! * [**Resmi PEASS & HackTricks ürünlerini**](https://peass.creator-spring.com) edinin * [**The PEASS Family'yi**](https://opensea.io/collection/the-peass-family) keşfedin, özel [**NFT'lerimiz**](https://opensea.io/collection/the-peass-family) koleksiyonumuzu * **💬 [Discord grubuna](https://discord.gg/hRep4RUj7f) veya [telegram grubuna](https://t.me/peass) katılın veya** bizi **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)'ı takip edin. * **Hacking püf noktalarınızı paylaşarak PR göndererek** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github depolarına katkıda bulunun.