Bins & Memory Allocations

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°

Basic Information

청크가 μ €μž₯λ˜λŠ” νš¨μœ¨μ„±μ„ κ°œμ„ ν•˜κΈ° μœ„ν•΄, 각 μ²­ν¬λŠ” 단일 μ—°κ²° λ¦¬μŠ€νŠΈμ—λ§Œ μžˆμ§€ μ•Šκ³  μ—¬λŸ¬ μœ ν˜•μ΄ μžˆμŠ΅λ‹ˆλ‹€. 이것이 λ°”λ‘œ 빈(bins)이며, 5κ°€μ§€ μœ ν˜•μ˜ 빈이 μžˆμŠ΅λ‹ˆλ‹€: 62 μž‘μ€ 빈, 63 큰 빈, 1 μ •λ ¬λ˜μ§€ μ•Šμ€ 빈, 10 λΉ λ₯Έ 빈 및 64 μŠ€λ ˆλ“œλ‹Ή tcache 빈.

각 μ •λ ¬λ˜μ§€ μ•Šμ€, μž‘μ€ 및 큰 λΉˆμ— λŒ€ν•œ 초기 μ£Όμ†ŒλŠ” λ™μΌν•œ λ°°μ—΄ 내에 μžˆμŠ΅λ‹ˆλ‹€. 인덱슀 0은 μ‚¬μš©λ˜μ§€ μ•ŠμœΌλ©°, 1은 μ •λ ¬λ˜μ§€ μ•Šμ€ 빈, 2-64λŠ” μž‘μ€ 빈, 65-127은 큰 λΉˆμž…λ‹ˆλ‹€.

Tcache (Per-Thread Cache) Bins

μŠ€λ ˆλ“œκ°€ μžμ‹ μ˜ νž™μ„ κ°€μ§€λ €κ³  μ‹œλ„ν•˜λ”λΌλ„(see Arenas and Subheaps), λ§Žμ€ μŠ€λ ˆλ“œλ₯Ό κ°€μ§„ ν”„λ‘œμ„ΈμŠ€(예: μ›Ή μ„œλ²„)κ°€ λ‹€λ₯Έ μŠ€λ ˆλ“œμ™€ νž™μ„ κ³΅μœ ν•  κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€. 이 경우, μ£Όμš” 해결책은 **락(lockers)**의 μ‚¬μš©μ΄λ©°, μ΄λŠ” μŠ€λ ˆλ“œλ₯Ό μƒλ‹Ήνžˆ 느리게 λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ”°λΌμ„œ, tcacheλŠ” 청크λ₯Ό λ³‘ν•©ν•˜μ§€ μ•ŠλŠ” 단일 μ—°κ²° λ¦¬μŠ€νŠΈλΌλŠ” μ μ—μ„œ μŠ€λ ˆλ“œλ‹Ή λΉ λ₯Έ 빈과 μœ μ‚¬ν•©λ‹ˆλ‹€. 각 μŠ€λ ˆλ“œλŠ” 64개의 단일 μ—°κ²° tcache λΉˆμ„ κ°€μ§‘λ‹ˆλ‹€. 각 λΉˆμ€ 7개의 λ™μΌν•œ 크기의 청크λ₯Ό κ°€μ§ˆ 수 있으며, ν¬κΈ°λŠ” 64λΉ„νŠΈ μ‹œμŠ€ν…œμ—μ„œ 24Bμ—μ„œ 1032B, 32λΉ„νŠΈ μ‹œμŠ€ν…œμ—μ„œ 12Bμ—μ„œ 516Bμž…λ‹ˆλ‹€.

μŠ€λ ˆλ“œκ°€ 청크λ₯Ό ν•΄μ œν•  λ•Œ, tcache에 할당될 수 μžˆμ„ 만큼 크지 μ•Šλ‹€λ©΄ ν•΄λ‹Ή tcache 빈이 가득 μ°¨ μžˆμ§€ μ•Šλ‹€λ©΄(이미 7개의 청크가 μžˆλŠ” 경우), 거기에 ν• λ‹Ήλ©λ‹ˆλ‹€. tcache둜 갈 수 μ—†λ‹€λ©΄, μ „μ—­μ μœΌλ‘œ ν•΄μ œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ νž™ 락을 κΈ°λ‹€λ €μ•Ό ν•©λ‹ˆλ‹€.

청크가 할당될 λ•Œ, ν•„μš”ν•œ 크기의 무료 청크가 Tcache에 μžˆλ‹€λ©΄ μ‚¬μš©ν•˜κ³ , κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ μ „μ—­ λΉˆμ—μ„œ ν•˜λ‚˜λ₯Ό μ°Ύκ±°λ‚˜ μƒˆλ‘œ μƒμ„±ν•˜κΈ° μœ„ν•΄ νž™ 락을 κΈ°λ‹€λ €μ•Ό ν•©λ‹ˆλ‹€.
λ˜ν•œ μ΅œμ ν™”κ°€ 있으며, 이 경우 νž™ 락을 κ°€μ§„ μƒνƒœμ—μ„œ μŠ€λ ˆλ“œλŠ” μš”μ²­λœ 크기의 νž™ 청크(7)둜 Tcacheλ₯Ό μ±„μ›λ‹ˆλ‹€, λ”°λΌμ„œ 더 ν•„μš”ν•  경우 Tcacheμ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

Add a tcache chunk example ```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; }

μ»΄νŒŒμΌν•˜κ³  main ν•¨μˆ˜μ˜ ret opcodeμ—μ„œ 쀑단점을 μ„€μ •ν•˜μ—¬ λ””λ²„κΉ…ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ gefλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš© 쀑인 tcache bin을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€:
```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 ꡬ쑰체 및 ν•¨μˆ˜

λ‹€μŒ μ½”λ“œμ—μ„œλŠ” max bins와 chunks per index, 이쀑 ν•΄μ œλ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μƒμ„±λœ tcache_entry ꡬ쑰체와 각 μŠ€λ ˆλ“œκ°€ bin의 각 μΈλ±μŠ€μ— λŒ€ν•œ μ£Όμ†Œλ₯Ό μ €μž₯ν•˜λŠ” 데 μ‚¬μš©ν•˜λŠ” **tcache_perthread_struct**λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

tcache_entry 및 tcache_perthread_struct ```c // From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c

/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */

define TCACHE_MAX_BINS 64

define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)

/* Only used to pre-fill the tunables. */

define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)

/* When β€œx” is from chunksize(). */

define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)

/* When β€œx” is a user-provided size. */

define usize2tidx(x) csize2tidx (request2size (x))

/* With rounding and alignment, the bins are… idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit) idx 1 bytes 25..40 or 13..20 idx 2 bytes 41..56 or 21..28 etc. */

/* This is another arbitrary limit, which tunables can change. Each tcache bin will hold at most this number of chunks. */

define TCACHE_FILL_COUNT 7

/* Maximum chunks in tcache bins for tunables. This value must fit the range of tcache->counts[] entries, else they may overflow. */

define MAX_TCACHE_COUNT UINT16_MAX

[…]

typedef struct tcache_entry { struct tcache_entry next; / This field exists to detect double frees. */ uintptr_t key; } tcache_entry;

/* There is one of these for each thread, which contains the per-thread cache (hence β€œtcache_perthread_struct”). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;

</details>

ν•¨μˆ˜ `__tcache_init`λŠ” `tcache_perthread_struct` 객체λ₯Ό μƒμ„±ν•˜κ³  곡간을 ν• λ‹Ήν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

<details>

<summary>tcache_init μ½”λ“œ</summary>
```c
// From https://github.com/bminor/glibc/blob/f942a732d37a96217ef828116ebe64a644db18d7/malloc/malloc.c#L3241C1-L3274C2

static void
tcache_init(void)
{
mstate ar_ptr;
void *victim = 0;
const size_t bytes = sizeof (tcache_perthread_struct);

if (tcache_shutting_down)
return;

arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
if (!victim && ar_ptr != NULL)
{
ar_ptr = arena_get_retry (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
}


if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);

/* In a low memory situation, we may not be able to allocate memory
- in which case, we just keep trying later.  However, we
typically do this very early, so either there is sufficient
memory, or there isn't enough memory to do non-trivial
allocations anyway.  */
if (victim)
{
tcache = (tcache_perthread_struct *) victim;
memset (tcache, 0, sizeof (tcache_perthread_struct));
}

}

Tcache 인덱슀

TcacheλŠ” 크기와 각 인덱슀의 첫 번째 청크에 λŒ€ν•œ 초기 포인터 및 μΈλ±μŠ€λ‹Ή 청크 μˆ˜κ°€ 청크 내뢀에 μœ„μΉ˜ν•˜λŠ” μ—¬λŸ¬ 개의 λΉˆμ„ κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 이 정보λ₯Ό κ°€μ§„ 청크(보톡 첫 번째)λ₯Ό 찾으면 λͺ¨λ“  tcache 초기 ν¬μΈνŠΈμ™€ Tcache 청크의 수λ₯Ό 찾을 수 μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

λΉ λ₯Έ 빈

λΉ λ₯Έ λΉˆμ€ μž‘μ€ 청크에 λŒ€ν•œ λ©”λͺ¨λ¦¬ ν• λ‹Ή 속도λ₯Ό 높이기 μœ„ν•΄ μ΅œκ·Όμ— ν•΄μ œλœ 청크λ₯Ό λΉ λ₯Έ μ ‘κ·Ό ꡬ쑰에 μœ μ§€ν•˜λ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 이 λΉˆμ€ ν›„μž…μ„ μΆœ(LIFO) 방식을 μ‚¬μš©ν•˜λ―€λ‘œ, κ°€μž₯ μ΅œκ·Όμ— ν•΄μ œλœ 청크가 μƒˆλ‘œμš΄ ν• λ‹Ή μš”μ²­μ΄ μžˆμ„ λ•Œ κ°€μž₯ λ¨Όμ € μž¬μ‚¬μš©λ©λ‹ˆλ‹€. 이 λ™μž‘μ€ 속도에 μœ λ¦¬ν•˜λ©°, μŠ€νƒμ˜ 맨 μœ„μ—μ„œ μ‚½μž…ν•˜κ³  μ œκ±°ν•˜λŠ” 것이 큐(FIFO)보닀 λΉ λ¦…λ‹ˆλ‹€.

λ˜ν•œ, λΉ λ₯Έ λΉˆμ€ 단일 μ—°κ²° 리슀트λ₯Ό μ‚¬μš©ν•˜λ©°, 이쀑 μ—°κ²° 리슀트λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•„ 속도가 λ”μš± ν–₯μƒλ©λ‹ˆλ‹€. λΉ λ₯Έ 빈의 μ²­ν¬λŠ” 이웃과 λ³‘ν•©λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ€‘κ°„μ—μ„œ μ œκ±°ν•  수 μžˆλŠ” λ³΅μž‘ν•œ ꡬ쑰가 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 단일 μ—°κ²° λ¦¬μŠ€νŠΈλŠ” μ΄λŸ¬ν•œ μž‘μ—…μ— λŒ€ν•΄ 더 κ°„λ‹¨ν•˜κ³  λΉ λ¦…λ‹ˆλ‹€.

기본적으둜 μ—¬κΈ°μ„œ λ°œμƒν•˜λŠ” 것은 헀더(확인할 첫 번째 청크에 λŒ€ν•œ 포인터)κ°€ 항상 ν•΄λ‹Ή 크기의 μ΅œμ‹  ν•΄μ œλœ 청크λ₯Ό 가리킀고 μžˆλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. κ·Έλž˜μ„œ:

  • ν•΄λ‹Ή 크기의 μƒˆλ‘œμš΄ 청크가 ν• λ‹Ήλ˜λ©΄ ν—€λ”λŠ” μ‚¬μš©ν•  수 μžˆλŠ” 무료 청크λ₯Ό κ°€λ¦¬ν‚΅λ‹ˆλ‹€. 이 무료 청크가 λ‹€μŒ μ‚¬μš©ν•  청크λ₯Ό κ°€λ¦¬ν‚€λ―€λ‘œ 이 μ£Όμ†Œκ°€ 헀더에 μ €μž₯λ˜μ–΄ λ‹€μŒ 할당이 μ‚¬μš© κ°€λŠ₯ν•œ 청크λ₯Ό μ–΄λ””μ„œ κ°€μ Έμ˜¬μ§€ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 청크가 ν•΄μ œλ˜λ©΄ 무료 μ²­ν¬λŠ” ν˜„μž¬ μ‚¬μš© κ°€λŠ₯ν•œ 청크에 λŒ€ν•œ μ£Όμ†Œλ₯Ό μ €μž₯ν•˜κ³ , 이 μƒˆλ‘œ ν•΄μ œλœ 청크에 λŒ€ν•œ μ£Όμ†Œκ°€ 헀더에 λ„£μ–΄μ§‘λ‹ˆλ‹€.

μ—°κ²° 리슀트의 μ΅œλŒ€ ν¬κΈ°λŠ” 0x80이며, 크기 0x20의 μ²­ν¬λŠ” 인덱슀 0에, 크기 0x30의 μ²­ν¬λŠ” 인덱슀 1에 λ°°μΉ˜λ©λ‹ˆλ‹€β€¦

Caution

λΉ λ₯Έ 빈의 μ²­ν¬λŠ” μ‚¬μš© κ°€λŠ₯ μƒνƒœλ‘œ μ„€μ •λ˜μ§€ μ•ŠμœΌλ―€λ‘œ μ£Όλ³€μ˜ λ‹€λ₯Έ 무료 청크와 병합될 수 μžˆλŠ” λŒ€μ‹  일정 μ‹œκ°„ λ™μ•ˆ λΉ λ₯Έ 빈 청크둜 μœ μ§€λ©λ‹ˆλ‹€.

// 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 #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; }

8개의 λ™μΌν•œ 크기의 청크λ₯Ό ν• λ‹Ήν•˜κ³  ν•΄μ œν•˜λŠ” 방법에 μœ μ˜ν•˜μ—¬ tcacheλ₯Ό μ±„μš°κ³  μ—¬λŸ 번째 청크가 λΉ λ₯Έ 청크에 μ €μž₯λ©λ‹ˆλ‹€.

이λ₯Ό μ»΄νŒŒμΌν•˜κ³  `main` ν•¨μˆ˜μ˜ `ret` opcode에 쀑단점을 μ„€μ •ν•˜μ—¬ λ””λ²„κΉ…ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ `gef`λ₯Ό μ‚¬μš©ν•˜λ©΄ tcache 빈이 가득 μ°¨ 있고 ν•˜λ‚˜μ˜ 청크가 λΉ λ₯Έ λΉˆμ— μžˆλŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:
```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

μ •λ ¬λ˜μ§€ μ•Šμ€ 빈

μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ€ λ©”λͺ¨λ¦¬ 할당을 더 λΉ λ₯΄κ²Œ ν•˜κΈ° μœ„ν•΄ νž™ κ΄€λ¦¬μžμ— μ˜ν•΄ μ‚¬μš©λ˜λŠ” μΊμ‹œμž…λ‹ˆλ‹€. μž‘λ™ 방식은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€: ν”„λ‘œκ·Έλž¨μ΄ 청크λ₯Ό ν•΄μ œν•˜λ©΄, 이 청크가 tcache λ˜λŠ” λΉ λ₯Έ λΉˆμ— 할당될 수 μ—†κ³  μ΅œμƒμœ„ 청크와 μΆ©λŒν•˜μ§€ μ•ŠλŠ” 경우, νž™ κ΄€λ¦¬μžλŠ” μ¦‰μ‹œ 이λ₯Ό νŠΉμ • μž‘μ€ λΉˆμ΄λ‚˜ 큰 λΉˆμ— λ„£μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‹ , λ¨Όμ € μΈμ ‘ν•œ 무료 청크와 λ³‘ν•©ν•˜μ—¬ 더 큰 무료 λ©”λͺ¨λ¦¬ 블둝을 μƒμ„±ν•˜λ €κ³  μ‹œλ„ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ, 이 μƒˆλ‘œμš΄ 청크λ₯Ό β€œμ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆβ€œμ΄λΌκ³  λΆˆλ¦¬λŠ” 일반 λΉˆμ— λ°°μΉ˜ν•©λ‹ˆλ‹€.

ν”„λ‘œκ·Έλž¨μ΄ λ©”λͺ¨λ¦¬λ₯Ό μš”μ²­ν•˜λ©΄, νž™ κ΄€λ¦¬μžλŠ” μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ„ ν™•μΈν•˜μ—¬ μΆ©λΆ„ν•œ 크기의 청크가 μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. λ§Œμ•½ 찾으면 μ¦‰μ‹œ μ‚¬μš©ν•©λ‹ˆλ‹€. μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ—μ„œ μ ν•©ν•œ 청크λ₯Ό μ°Ύμ§€ λͺ»ν•˜λ©΄, 이 λͺ©λ‘μ˜ λͺ¨λ“  청크λ₯Ό 크기에 따라 ν•΄λ‹Ή 빈(μž‘μ€ 빈 λ˜λŠ” 큰 빈)으둜 μ΄λ™ν•©λ‹ˆλ‹€.

더 큰 청크가 두 개의 절반으둜 λ‚˜λ‰˜κ³  λ‚˜λ¨Έμ§€κ°€ MINSIZE보닀 크면, λ‹€μ‹œ μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ— λ°°μΉ˜λ©λ‹ˆλ‹€.

λ”°λΌμ„œ μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ€ μ΅œκ·Όμ— ν•΄μ œλœ λ©”λͺ¨λ¦¬λ₯Ό λΉ λ₯΄κ²Œ μž¬μ‚¬μš©ν•˜μ—¬ λ©”λͺ¨λ¦¬ ν• λ‹Ή 속도λ₯Ό 높이고, μ‹œκ°„ μ†Œλͺ¨μ μΈ 검색 및 λ³‘ν•©μ˜ ν•„μš”μ„±μ„ μ€„μ΄λŠ” λ°©λ²•μž…λ‹ˆλ‹€.

Caution

청크가 μ„œλ‘œ λ‹€λ₯Έ 범주에 μ†ν•˜λ”λΌλ„, μ‚¬μš© κ°€λŠ₯ν•œ 청크가 λ‹€λ₯Έ μ‚¬μš© κ°€λŠ₯ν•œ 청크와 μΆ©λŒν•˜λŠ” 경우(μ›λž˜ μ„œλ‘œ λ‹€λ₯Έ λΉˆμ— μ†ν•˜λ”λΌλ„) λ³‘ν•©λ©λ‹ˆλ‹€.

μ •λ ¬λ˜μ§€ μ•Šμ€ 청크 예제 μΆ”κ°€ ```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; }

9개의 λ™μΌν•œ 크기의 청크λ₯Ό ν• λ‹Ήν•˜κ³  ν•΄μ œν•˜λŠ” 방법에 μ£Όλͺ©ν•˜μ„Έμš”. 이 청크듀은 **tcacheλ₯Ό μ±„μš°κ³ **, μ—¬λŸ 번째 μ²­ν¬λŠ” **fastbin에 λΉ„ν•΄ λ„ˆλ¬΄ μ»€μ„œ** μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ— μ €μž₯λ©λ‹ˆλ‹€. 아홉 번째 μ²­ν¬λŠ” ν•΄μ œλ˜μ§€ μ•ŠμœΌλ―€λ‘œ 아홉 λ²ˆμ§Έμ™€ μ—¬λŸ 번째 μ²­ν¬λŠ” **μƒμœ„ 청크와 λ³‘ν•©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€**.

이λ₯Ό μ»΄νŒŒμΌν•˜κ³  `main` ν•¨μˆ˜μ˜ `ret` opcode에 쀑단점을 μ„€μ •ν•˜μ—¬ λ””λ²„κΉ…ν•˜μ„Έμš”. 그런 λ‹€μŒ `gef`λ₯Ό μ‚¬μš©ν•˜λ©΄ tcache 빈이 가득 μ°¨ 있고 ν•˜λ‚˜μ˜ 청크가 μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ— μžˆλŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:
```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*index (예: 인덱슀 5 -> 40)
  • κ°€μž₯ 큰 크기: 2*8*index (예: 인덱슀 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 #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; }

9개의 λ™μΌν•œ 크기의 청크λ₯Ό ν• λ‹Ήν•˜κ³  ν•΄μ œν•˜λŠ” 방법에 μœ μ˜ν•˜μ„Έμš”. 이 청크듀은 **tcacheλ₯Ό μ±„μš°κ³ **, μ—¬λŸ 번째 μ²­ν¬λŠ” **fastbin에 λΉ„ν•΄ λ„ˆλ¬΄ μ»€μ„œ** μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ— μ €μž₯λ©λ‹ˆλ‹€. 아홉 번째 μ²­ν¬λŠ” ν•΄μ œλ˜μ§€ μ•ŠμœΌλ―€λ‘œ 아홉 λ²ˆμ§Έμ™€ μ—¬λŸ 번째 μ²­ν¬λŠ” **top chunk와 λ³‘ν•©λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€**. 그런 λ‹€μŒ 0x110 크기의 더 큰 청크λ₯Ό ν• λ‹Ήν•˜λ©΄ **μ •λ ¬λ˜μ§€ μ•Šμ€ 빈의 청크가 μž‘μ€ 빈으둜 μ΄λ™ν•©λ‹ˆλ‹€**.

이λ₯Ό μ»΄νŒŒμΌν•˜κ³  `main` ν•¨μˆ˜μ˜ `ret` opcode에 쀑단점을 μ„€μ •ν•˜μ—¬ λ””λ²„κΉ…ν•˜μ„Έμš”. 그런 λ‹€μŒ `gef`λ₯Ό μ‚¬μš©ν•˜λ©΄ tcache 빈이 가득 μ°¨ 있고 ν•˜λ‚˜μ˜ 청크가 μž‘μ€ λΉˆμ— μžˆλŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:
```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 μ΄μƒμ˜ λͺ¨λ“  청크λ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.

λŒ€ν˜• λΉˆμ€ μž‘μ€ λΉˆμ— λΉ„ν•΄ μž‘λ™ 속도가 λŠλ¦½λ‹ˆλ‹€. μ™œλƒν•˜λ©΄ 할당에 κ°€μž₯ μ ν•©ν•œ 청크λ₯Ό μ°ΎκΈ° μœ„ν•΄ λ‹€μ–‘ν•œ 청크 크기의 λͺ©λ‘μ„ μ •λ ¬ν•˜κ³  검색해야 ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 청크가 λŒ€ν˜• λΉˆμ— μ‚½μž…λ  λ•Œ μ •λ ¬ν•΄μ•Ό ν•˜κ³ , λ©”λͺ¨λ¦¬κ°€ 할당될 λ•Œ μ‹œμŠ€ν…œμ€ μ˜¬λ°”λ₯Έ 청크λ₯Ό μ°Ύμ•„μ•Ό ν•©λ‹ˆλ‹€. 이 μΆ”κ°€ μž‘μ—…μœΌλ‘œ 인해 λŠλ €μ§€μ§€λ§Œ, λŒ€ν˜• 할당이 μž‘μ€ 할당보닀 덜 일반적이기 λ•Œλ¬Έμ— μ΄λŠ” 수용 κ°€λŠ₯ν•œ κ±°λž˜μž…λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 빈이 μžˆμŠ΅λ‹ˆλ‹€:

  • 64B λ²”μœ„μ˜ 32개 빈 (μž‘μ€ 빈과 좩돌)
  • 512B λ²”μœ„μ˜ 16개 빈 (μž‘μ€ 빈과 좩돌)
  • 4096B λ²”μœ„μ˜ 8개 빈 (일뢀 μž‘μ€ 빈과 좩돌)
  • 32768B λ²”μœ„μ˜ 4개 빈
  • 262144B λ²”μœ„μ˜ 2개 빈
  • λ‚˜λ¨Έμ§€ 크기λ₯Ό μœ„ν•œ 1개 빈
λŒ€ν˜• 빈 크기 μ½”λ“œ ```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>큰 청크 예제 μΆ”κ°€</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개의 큰 할당이 μˆ˜ν–‰λœ ν›„, ν•˜λ‚˜κ°€ ν•΄μ œλ˜μ–΄(μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ— 넣어짐) 더 큰 할당이 μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€(ν•΄μ œλœ 것이 μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ—μ„œ 큰 빈으둜 이동됨).

이λ₯Ό μ»΄νŒŒμΌν•˜κ³  main ν•¨μˆ˜μ˜ ret opcodeμ—μ„œ 쀑단점을 μ„€μ •ν•˜μ—¬ λ””λ²„κΉ…ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ gefλ₯Ό μ‚¬μš©ν•˜λ©΄ tcache 빈이 가득 μ°¨ 있고 ν•˜λ‚˜μ˜ 청크가 큰 λΉˆμ— μžˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€:

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 #include

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

`main`의 `ret` opcodeμ—μ„œ 쀑단점을 μ„€μ •ν•˜μ—¬ μ»΄νŒŒμΌν•˜κ³  λ””λ²„κΉ…ν•œ ν›„, malloc이 μ£Όμ†Œ `0xaaaaaaac12a0`λ₯Ό λ°˜ν™˜ν–ˆμœΌλ©°, λ‹€μŒμ€ μ²­ν¬λ“€μž…λ‹ˆλ‹€:
```bash
gef➀  heap chunks
Chunk(addr=0xaaaaaaac1010, size=0x290, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac1010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0xaaaaaaac12a0, size=0x20, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12a0     41 41 41 41 41 41 41 00 00 00 00 00 00 00 00 00    AAAAAAA.........]
Chunk(addr=0xaaaaaaac12c0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac12c0     41 64 64 72 65 73 73 20 6f 66 20 74 68 65 20 63    Address of the c]
Chunk(addr=0xaaaaaaac16d0, size=0x410, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[0x0000aaaaaaac16d0     41 41 41 41 41 41 41 0a 00 00 00 00 00 00 00 00    AAAAAAA.........]
Chunk(addr=0xaaaaaaac1ae0, size=0x20530, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)  ←  top chunk

상단 청크가 μ£Όμ†Œ 0xaaaaaaac1ae0에 μžˆλ‹€λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λ§ˆμ§€λ§‰μœΌλ‘œ ν• λ‹Ήλœ 청크가 0xaaaaaaac12a0에 크기 0x410으둜 μžˆμ—ˆκΈ° λ•Œλ¬Έμ— λ†€λΌμš΄ 일이 μ•„λ‹™λ‹ˆλ‹€. 0xaaaaaaac12a0 + 0x410 = 0xaaaaaaac1ae0μž…λ‹ˆλ‹€.
상단 청크의 청크 ν—€λ”μ—μ„œ 상단 청크의 길이λ₯Ό 확인할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€:

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

λ§ˆμ§€λ§‰ λ‚˜λ¨Έμ§€

malloc이 μ‚¬μš©λ˜κ³  청크가 λ‚˜λˆ„μ–΄μ§ˆ λ•Œ(예: μ •λ ¬λ˜μ§€ μ•Šμ€ λΉˆμ—μ„œ λ˜λŠ” μ΅œμƒμœ„ μ²­ν¬μ—μ„œ), λ‚˜λˆ„μ–΄μ§„ 청크의 λ‚˜λ¨Έμ§€ λΆ€λΆ„μ—μ„œ μƒμ„±λœ 청크λ₯Ό Last Remainder라고 ν•˜λ©°, κ·Έ ν¬μΈν„°λŠ” malloc_state ꡬ쑰체에 μ €μž₯λ©λ‹ˆλ‹€.

ν• λ‹Ή 흐름

λ‹€μŒ λ‚΄μš©μ„ ν™•μΈν•˜μ„Έμš”:

malloc & sysmalloc

ν•΄μ œ 흐름

λ‹€μŒ λ‚΄μš©μ„ ν™•μΈν•˜μ„Έμš”:

free

νž™ ν•¨μˆ˜ λ³΄μ•ˆ 검사

νž™μ—μ„œ 많이 μ‚¬μš©λ˜λŠ” ν•¨μˆ˜λ“€μ΄ μˆ˜ν–‰ν•˜λŠ” λ³΄μ•ˆ 검사λ₯Ό ν™•μΈν•˜μ„Έμš”:

Heap Functions Security Checks

μ°Έκ³  λ¬Έν—Œ

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°