52 KiB
बिन्स और मेमोरी आवंटन
जानें AWS हैकिंग को शून्य से हीरो तक htARTE (HackTricks AWS Red Team Expert) के साथ!
HackTricks का समर्थन करने के अन्य तरीके:
- अगर आप चाहते हैं कि आपकी कंपनी HackTricks में विज्ञापित हो या HackTricks को PDF में डाउनलोड करें तो सब्सक्रिप्शन प्लान्स देखें!
- आधिकारिक PEASS और HackTricks स्वैग प्राप्त करें
- हमारा विशेष NFTs संग्रह, The PEASS Family खोजें
- शामिल हों 💬 डिस्कॉर्ड समूह या टेलीग्राम समूह या हमें ट्विटर 🐦 @hacktricks_live** पर फॉलो** करें।
- अपने हैकिंग ट्रिक्स साझा करें द्वारा PRs सबमिट करके HackTricks और HackTricks Cloud github repos में।
मूल जानकारी
चंक्स को स्टोर करने की कुशलता को बढ़ाने के लिए हर चंक्स को केवल एक लिंक्ड सूची में नहीं रखा जाता है, बल्कि कई प्रकार के होते हैं। ये बिन्स होते हैं और 5 प्रकार के बिन्स होते हैं: 62 small bins, 63 large bins, 1 unsorted bin, 10 fast bins और 64 tcache bins प्रति थ्रेड।
प्रत्येक अनसॉर्टेड, स्मॉल और लार्ज बिन्स के लिए प्रारंभिक पता एक ही एरे में होता है। इंडेक्स 0 अप्रयुक्त है, 1 अनसॉर्टेड बिन है, बिन्स 2-64 स्मॉल बिन्स हैं और बिन्स 65-127 लार्ज बिन्स हैं।
Tcache (प्रति-थ्रेड कैश) बिन्स
हालांकि थ्रेड्स अपनी खुद की हीप रखने की कोशिश करते हैं (देखें Arenas और Subheaps), ऐसे प्रकार के प्रक्रियाओं के साथ (जैसे एक वेब सर्वर) हीप को दूसरे थ्रेड्स के साथ साझा करने की संभावना होती है। इस मामले में, मुख्य समाधान है लॉकर्स का उपयोग, जो थ्रेड्स को काफी धीमा कर सकता है।
इसलिए, एक tcache एक फास्ट बिन के समान है उस प्रकार की यह एक सिंगल लिंक्ड सूची है जो चंक्स को मर्ज नहीं करती। प्रत्येक थ्रेड के पास 64 सिंगल-लिंक्ड tcache बिन्स होते हैं। प्रत्येक बिन में 24 से 1032B तक के चंक्स हो सकते हैं 64-बिट सिस्टम्स पर और 12 से 516B तक के चंक्स 32-बिट सिस्टम्स पर।
जब एक थ्रेड एक चंक्स को मुक्त करता है, अगर यह इतना बड़ा नहीं है कि टीकैश में आवंटित किया जा सके और संबंधित tcache बिन भरा नहीं है (पहले से ही 7 चंक्स), तो वहां आवंटित किया जाएगा। अगर यह टीकैश में नहीं जा सकता है, तो यह हीप लॉक का इंतजार करना होगा ताकि वह सार्वजनिक रूप से मुक्ति कार्रवाई कर सके।
जब एक चंक्स आवंटित किया जाता है, अगर टीकैश में आवश्यक आकार का एक मुक्त चंक्स है तो उसे उसका उपयोग करेगा, अगर नहीं, तो वह सार्वजनिक बिन्स में से एक ढूंढने के लिए हीप लॉक का इंतजार करना होगा या एक नया बनाना होगा।
इस मामले में एक और अनुकूलन भी है, इस मामले में, हीप लॉक के साथ होते हुए, थ्रेड अपना टीकैश हीप चंक्स के साथ भरेगा (7) अनुरोधित आकार के, इसलिए यदि इसे अधिक चाहिए, तो वह उन्हें टीकैश में पाएगा।
एक टीकैश चंक्स उदाहरण जोड़ें
```c #include #includeint main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); free(chunk); return 0; }
कंपाइल करें और इसे डीबग करें मुख्य कार्य से रिट ओपकोड में ब्रेकपॉइंट के साथ। फिर gef के साथ आप उपयोग में टीकैश बिन देख सकते हैं:
```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 संरचनाएँ और कार्य
निम्नलिखित कोड में अधिकतम बिन और प्रति सूची टुकड़े, tcache_entry
संरचित को दोहरी मुक्त करने से बचाने के लिए बनाया गया है और tcache_perthread_struct
, एक संरचना जिसे प्रत्येक धागे का पता रखने के लिए उपयोग करता है।
// 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
वह फ़ंक्शन है जो tcache_perthread_struct
ऑब्जेक्ट के लिए स्थान बनाता है और आवंटित करता है।
tcache_init कोड
```c // Fromf942a732d3/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 Indexes
टीकैश में कई बिन होते हैं जो आकार और प्रारंभिक पॉइंटर्स के आधार पर निर्भर करते हैं **प्रत्येक इंडेक्स के पहले चंक और प्रति इंडेक्स चंक की मात्रा एक चंक के अंदर स्थित होती हैं**। इसका मतलब है कि इस जानकारी के साथ चंक को ढूंढना (सामान्यत: पहला), टीकैश के सभी प्रारंभिक पॉइंट्स और टीकैश चंक की मात्रा ढूंढना संभव है।
### Fast bins
फास्ट बिन्स को **छोटे चंकों के लिए मेमोरी आवंटन की गति बढ़ाने के लिए डिज़ाइन किया गया है** जिन्हें एक त्वरित पहुंच संरचना में हाल ही में फ्री किए गए चंक रखकर उपयोग में लाने के लिए उपयोग करते हैं। ये बिन्स एक लास्ट-इन, फर्स्ट-आउट (LIFO) दृष्टिकोण का उपयोग करते हैं, जिसका मतलब है कि **सबसे हाल ही में फ्री किया गया चंक सबसे पहले** उपयोग के लिए पुनः उपयोग किया जाएगा जब एक नई आवंटन अनुरोध हो। यह व्यवहार गति के लिए फायदेमंद है, क्योंकि एक स्टैक (LIFO) से ऊपर से डालना और हटाना तेज होता है तुलनात्मक रूप से एक कतार (FIFO) के साथ।
इसके अतिरिक्त, **फास्ट बिन्स एकल लिंक्ड लिस्ट का उपयोग करते हैं**, डबल लिंक्ड नहीं, जो गति को और बढ़ाता है। क्योंकि फास्ट बिन्स में चंक्स को पड़ोसियों के साथ मिलाया नहीं जाता, इसके लिए एक जटिल संरचना की आवश्यकता नहीं है जो मध्य से हटाने की अनुमति देती है। एकल लिंक्ड लिस्ट इन ऑपरेशन के लिए इसे सरल और तेज बनाता है।
मूल रूप से, यहाँ यह होता है कि हेडर (पहले चंक के पॉइंटर) हमेशा उस आकार के सबसे हाल ही में फ्री किए गए चंक की ओर पॉइंट कर रहा है। तो:
* जब उस आकार का नया चंक आवंटित किया जाता है, हेडर एक उपयोग के लिए एक फ्री चंक की ओर पॉइंट कर रहा है। क्योंकि यह फ्री चंक उपयोग के लिए अगले चंक की ओर पॉइंट कर रहा है, इस पते को हेडर में संग्रहीत किया जाता है ताकि अगली आवंटन जाने की जानकारी हो कि कहाँ से एक उपलब्ध चंक प्राप्त करना है
* जब एक चंक फ्री किया जाता है, तो फ्री चंक वर्तमान उपलब्ध चंक का पता सहेजेगा और इस नए फ्री चंक का पता हेडर में डाल दिया जाएगा
लिंक्ड लिस्ट का अधिकतम आकार `0x80` है और वे इस प्रकार से संगठित हैं कि आकार `0x20-0x2f` का एक चंक इंडेक्स `0` में होगा, आकार `0x30-0x3f` `idx` `1` में होगा...
{% hint style="danger" %}
फास्ट बिन्स में चंक उपलब्ध नहीं होते हैं इसलिए वे कुछ समय तक फास्ट बिन चंक के रूप में रखे जाते हैं बजाय इसके कि उन्हें उनके आसपास के अन्य फ्री चंक्स के साथ मर्ज किया जा सके।
{% 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)
एक फास्टबिन चंक उदाहरण जोड़ें
```c #include #includeint main(void) { char *chunks[8]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 8; i++) { chunks[i] = malloc(24); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
नोट करें कि हम एक ही आकार के 8 चंक को आवंटित और मुक्त करते हैं ताकि वे टीकैश भर जाएं और आठवां चंक तेज चंक में संग्रहीत हो।
इसे कंपाइल करें और मुख्य कार्य से रेट ऑपकोड में ब्रेकपॉइंट के साथ डीबग करें। फिर gef के साथ आप टीकैश बिन भरते हुए और तेज बिन में एक चंक देख सकते हैं:
```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
अवर्गीकृत बिन
अवर्गीकृत बिन एक कैश है जिसे हीप प्रबंधक द्वारा मेमोरी आवंटन को तेज़ बनाने के लिए उपयोग किया जाता है। यहाँ यह कैसे काम करता है: जब एक प्रोग्राम एक टुकड़ा मुक्त करता है, और यदि यह टुकड़ा एक टीकैश या फास्ट बिन में आवंटित नहीं किया जा सकता है और यह ऊपरी टुकड़े के साथ टकरा नहीं रहा है, तो हीप प्रबंधक इसे तुरंत किसी विशिष्ट छोटे या बड़े बिन में नहीं डालता है। बजाय इसके, यह पहले किसी भी पड़ोसी फ्री टुकड़ों के साथ मर्ज करने की कोशिश करता है ताकि एक अधिक बड़ा फ्री मेमोरी ब्लॉक बना सके। फिर, यह नया टुकड़ा एक सामान्य बिन में रखता है जिसे "अवर्गीकृत बिन" कहा जाता है।
जब एक प्रोग्राम मेमोरी के लिए मांग करता है, तो हीप प्रबंधक अवर्गीकृत बिन की जाँच करता है कि क्या कोई पर्याप्त आकार का टुकड़ा है। अगर वह एक पाता है, तो वह इसे तुरंत उपयोग करता है। अगर अवर्गीकृत बिन में एक उपयुक्त टुकड़ा नहीं मिलता है, तो यह सभी टुकड़े इस सूची को उनके संबंधित बिन में ले जाता है, या तो छोटे या बड़े, उनके आकार के आधार पर।
ध्यान दें कि अगर एक बड़ा टुकड़ा 2 हिस्सों में विभाजित किया गया है और बाकी भाग MINSIZE से अधिक है, तो यह अवर्गीकृत बिन में वापस रखा जाएगा।
इसलिए, अवर्गीकृत बिन मेमोरी आवंटन को तेज़ करने का एक तरीका है जिसमें हाल ही में मुक्त की गई मेमोरी को तेज़ी से पुनः उपयोग किया जाता है और समय ग्राहक खोज और मर्ज की आवश्यकता को कम किया जाता है।
{% hint style="danger" %} ध्यान दें कि यदि चंक्स विभिन्न श्रेणियों के हों, अगर एक उपलब्ध चंक दूसरे उपलब्ध चंक के साथ टकरा रहा है (यदी वे मूल रूप से विभिन्न बिनों में हैं), तो वे मर्ज किए जाएंगे। {% endhint %}
एक अवर्गीकृत टुकड़ा उदाहरण जोड़ें
```c #include #includeint main(void) { char *chunks[9]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
return 0; }
नोट करें कि हम एक ही आकार के 9 चंक को आवंटित और मुक्त करते हैं ताकि वे **टीकैश** भर जाएं और आठवां चंक अनुक्रमणिक बिन में स्टोर किया जाता है क्योंकि यह **फास्टबिन के लिए बहुत बड़ा** है और नौवां चंक मुक्त नहीं किया गया है इसलिए नौवां और आठवां **टॉप चंक के साथ मर्ज नहीं होते**।
इसे कंपाइल करें और मुख्य फ़ंक्शन से रेट ओपकोड में ब्रेकपॉइंट के साथ डीबग करें। फिर gef के साथ आप टीकैश बिन भरते हुए और अनुक्रमित बिन में एक चंक देख सकते हैं:
```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.
छोटे बिन
छोटे बिन बड़े बिनों से तेज होते हैं लेकिन फास्ट बिन्स से धीमे होते हैं।
62 के प्रत्येक बिन में एक ही आकार के चंक्स होंगे: 16, 24, ... (32 बिट्स में 504 बाइट तक और 64 बिट्स में 1024 तक का अधिकतम आकार)। यह एक जगह को आवंटित करने और इन सूचियों पर प्रविष्टियों को डालने और हटाने में गति में मदद करता है।
यहां छोटे बिन का आकार बिन के सूचकांक के अनुसार कैसे निर्धारित किया जाता है:
- सबसे छोटा आकार: 2*4*सूचकांक (उदाहरण: सूचकांक 5 -> 40)
- सबसे बड़ा आकार: 2*8*सूचकांक (उदाहरण: सूचकांक 5 -> 80)
// From https://github.com/bminor/glibc/blob/a07e000e82cb71238259e674529c37c12dc7d423/malloc/malloc.c#L1711
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > CHUNK_HDR_SZ)
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\
+ SMALLBIN_CORRECTION)
छोटे और बड़े बिन्स के बीच चयन करने के लिए फ़ंक्शन:
#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))
एक छोटे टुकड़े का उदाहरण जोड़ें
```c #include #includeint main(void) { char *chunks[10]; int i;
// Loop to allocate memory 8 times for (i = 0; i < 9; i++) { chunks[i] = malloc(0x100); if (chunks[i] == NULL) { // Check if malloc failed fprintf(stderr, "Memory allocation failed at iteration %d\n", i); return 1; } printf("Address of chunk %d: %p\n", i, (void *)chunks[i]); }
// Loop to free the allocated memory for (i = 0; i < 8; i++) { free(chunks[i]); }
chunks[9] = malloc(0x110);
return 0; }
नोट करें कि हम कैसे 9 बड़े चंक को आवंटित और फ्री करते हैं जो **टीकैश** को भर देते हैं और आठवां चंक अनुक्रमित बिन में स्टोर किया जाता है क्योंकि यह **फास्टबिन के लिए बहुत बड़ा है** और नौवां चंक फ्री नहीं है इसलिए नौवां और आठवां **टॉप चंक के साथ मर्ज नहीं होते हैं**। फिर हम 0x110 का एक बड़ा चंक आवंटित करते हैं जिससे **अनुक्रमित बिन में चंक छोटे बिन में जाता है**।
इसे कंपाइल करें और मुख्य फ़ंक्शन से रेट ओपकोड में ब्रेकपॉइंट के साथ डीबग करें। फिर gef के साथ आप टीकैश बिन भरते हुए और छोटे बिन में एक चंक देख सकते हैं:
```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.
बड़े बिन
छोटे बिनों की तरह, जो निश्चित आकारों के टुकड़ों का प्रबंधन करते हैं, प्रत्येक बड़े बिन एक चंक आकारों की श्रेणी का प्रबंधन करता है। यह अधिक लचीला है, विभिन्न आकारों को समायोजित करने के लिए प्रणाली को अलग-अलग आकार के लिए एक अलग बिन की आवश्यकता नहीं होती।
एक मेमोरी आवंटक, बड़े बिन छोटे बिनों के समाप्त होने पर शुरू होते हैं। बड़े बिनों के लिए सीमाएँ प्रगतिशील रूप से बढ़ती हैं, जिसका मतलब है पहला बिन 512 से 576 बाइट तक के चंक को कवर कर सकता है, जबकि अगला 576 से 640 बाइट को कवर करता है। यह पैटर्न जारी रहता है, सबसे बड़े बिन में सभी 1MB से ऊपर के चंक शामिल होते हैं।
बड़े बिन छोटे बिनों की तुलना में धीमे होते हैं क्योंकि उन्हें एक भिन्न चंक आकारों की सूची के माध्यम से क्रमबद्ध और खोजना पड़ता है ताकि आवंटन के लिए सर्वोत्तम फिट ढूंढा जा सके। जब एक चंक बड़े बिन में डाला जाता है, तो उसे क्रमबद्ध किया जाना चाहिए, और जब मेमोरी आवंटित की जाती है, तो प्रणाली को सही चंक ढूंढना पड़ता है। यह अतिरिक्त काम उन्हें धीमा बनाता है, लेकिन क्योंकि बड़े आवंटन छोटे से कम होते हैं, इसे स्वीकार्य व्यापार माना जाता है।
हैं:
- 32 बिन 64B रेंज के (छोटे बिनों के साथ टकराते हैं)
- 16 बिन 512B रेंज के (छोटे बिनों के साथ टकराते हैं)
- 8 बिन 4096B रेंज के (कुछ हिस्सा छोटे बिनों के साथ टकराता है)
- 4 बिन 32768B रेंज के
- 2 बिन 262144B रेंज के
- बची हुई आकारों के लिए 1 बिन
बड़े बिन आकार कोड
```c // Froma07e000e82/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>एक बड़े चंक का उदाहरण जोड़ें</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 बड़ी आवंटन किए जाते हैं, फिर एक को मुक्त किया जाता है (जिससे यह अनसॉर्टेड बिन में जाता है) और एक बड़ी आवंटन किया जाता है (जिससे मुक्त वाला अनसॉर्टेड बिन से बड़े बिन में जाता है)।
इसे कंपाइल करें और मुख्य कार्य से रेट ओपकोड में ब्रेकपॉइंट के साथ डीबग करें। फिर gef के साथ आप टीकैश बिन भरने और बड़े बिन में एक चंक देख सकते हैं:
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.
शीर्ष टुकड़ी
// 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))
आम तौर पर, यह एक टुकड़ा है जिसमें सभी वर्तमान मेमोरी है। जब एक malloc किया जाता है, अगर कोई उपलब्ध फ्री चंक उपयोग के लिए नहीं है, तो यह टॉप चंक अपने आकार को कम करके आवश्यक स्थान प्रदान करेगा।
टॉप चंक का पॉइंटर malloc_state
स्ट्रक्ट में संग्रहीत है।
इसके अतिरिक्त, शुरुआत में, अनसॉर्टेड चंक का उपयोग टॉप चंक के रूप में किया जा सकता है।
टॉप चंक उदाहरण देखें
```c #include #includeint main(void) { char *chunk; chunk = malloc(24); printf("Address of the chunk: %p\n", (void *)chunk); gets(chunk); return 0; }
जब मैंने इसे कंपाइल और डीबग किया और मुख्य कोड के रेट ओपकोड में एक ब्रेक पॉइंट के साथ देखा, तो मुझे पता चला कि मैलोक ने पता दिया: `0xaaaaaaac12a0` और ये चंक हैं:
```bash
gef➤ heap chunks
Chunk(addr=0xaaaaaaac1010, size=0x290, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac1010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12a0 41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac12c0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12c0 41 64 64 72 65 73 73 20 6f 66 20 74 68 65 20 63 Address of the c]
Chunk(addr=0xaaaaaaac16d0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac16d0 41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 00 AAAAAAA.........]
Chunk(addr=0xaaaaaaac1ae0, size=0x20530, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) ← top chunk
जहां देखा जा सकता है कि शीर्ष चंक का पता 0xaaaaaaac1ae0
पर है। यह कोई आश्चर्य नहीं है क्योंकि नवीनतम आवंटित चंक 0xaaaaaaac12a0
में था जिसका आकार 0x410
था और 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0
।
शीर्ष चंक की लंबाई को उसके चंक हेडर पर भी देखा जा सकता है:
gef➤ x/8wx 0xaaaaaaac1ae0 - 16
0xaaaaaaac1ad0: 0x00000000 0x00000000 0x00020531 0x00000000
0xaaaaaaac1ae0: 0x00000000 0x00000000 0x00000000 0x00000000
अंतिम अनुस्मारक
जब malloc का उपयोग किया जाता है और एक चंक विभाजित किया जाता है (अनलिंक्ड सूची से या ऊपरी चंक से उदाहरण के लिए), विभाजित चंक से बची हुई चंक से बनाई गई चंक को अंतिम अनुस्मारक कहा जाता है और इसका पॉइंटर malloc_state
संरचि में संग्रहीत किया जाता है।
आवंटन प्रवाह
जांच करें:
{% content-ref url="heap-memory-functions/malloc-and-sysmalloc.md" %} malloc-and-sysmalloc.md {% endcontent-ref %}
मुक्ति प्रवाह
जांच करें:
{% content-ref url="heap-memory-functions/free.md" %} free.md {% endcontent-ref %}
हीप फ़ंक्शन सुरक्षा जांच
हीप में प्रमुख रूप से उपयोग किए जाने वाले फ़ंक्शनों द्वारा की जाने वाली सुरक्षा जांच कीजिए:
{% content-ref url="heap-memory-functions/heap-functions-security-checks.md" %} heap-functions-security-checks.md {% endcontent-ref %}
संदर्भ
- https://azeria-labs.com/heap-exploitation-part-1-understanding-the-glibc-heap-implementation/
- https://azeria-labs.com/heap-exploitation-part-2-glibc-heap-free-bins/
- https://heap-exploitation.dhavalkapil.com/diving_into_glibc_heap/core_functions
- https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/implementation/tcache/
htARTE (HackTricks AWS Red Team Expert) के साथ शून्य से हीरो तक AWS हैकिंग सीखें htARTE (HackTricks AWS Red Team Expert)!
HackTricks का समर्थन करने के अन्य तरीके:
- यदि आप अपनी कंपनी का विज्ञापन देखना चाहते हैं HackTricks में या HackTricks को PDF में डाउनलोड करना चाहते हैं तो सब्सक्रिप्शन प्लान्स देखें!
- आधिकारिक PEASS & HackTricks स्वैग प्राप्त करें
- हमारे विशेष NFTs संग्रह The PEASS Family खोजें
- शामिल हों 💬 डिस्कॉर्ड समूह या टेलीग्राम समूह या हमें ट्विटर 🐦 @hacktricks_live** पर फॉलो** करें।
- हैकिंग ट्रिक्स साझा करें हैकिंग ट्रिक्स को पीआर के माध्यम से HackTricks और HackTricks Cloud github रेपो में सबमिट करके।