malloc & sysmalloc

ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Allocation Order Summary

(この要約ではチェックは説明されておらず、簡潔さのため一部のケースが省略されています)

  1. __libc_malloc は、tcache からチャンクを取得しようとします。取得できない場合は _int_malloc を呼び出します。

  2. _int_malloc

    1. アリーナを生成しようとします(存在しない場合)

    2. 正しいサイズの任意のファストビンチャンクがあれば使用します

      1. 他のファストチャンクで tcache を埋めます

    3. 正しいサイズの任意のスモールビンチャンクがあれば使用します

      1. そのサイズの他のチャンクで tcache を埋めます

    4. 要求されたサイズがスモールビン用でない場合、ファストビンをアンソートビンに統合します

    5. アンソートビンをチェックし、十分なスペースがある最初のチャンクを使用します

      1. 見つかったチャンクが大きい場合、一部を返却するために分割し、残りをアンソートビンに追加します

      2. サイズが要求されたサイズと同じ場合、それを返却する代わりに tcache を埋めるために使用します(tcache がいっぱいになるまで、次のものを返却します)

      3. チェックされたサイズよりも小さいサイズの各チャンクについて、それぞれ対応するスモールビンまたはラージビンに配置します

    6. 要求されたサイズのインデックスでラージビンをチェックします

      1. 要求されたサイズよりも大きい最初のチャンクから検索を開始し、見つかった場合はそれを返却し、残りをスモールビンに追加します

    7. 最後までの次のインデックスからラージビンをチェックします

      1. 次の大きなインデックスから、チャンクを検索し、最初に見つかったチャンクを使用して要求されたサイズに使用し、残りをアンソートビンに追加します

    8. 前のビンで何も見つからない場合、トップチャンクからチャンクを取得します

    9. トップチャンクが十分に大きくない場合は、sysmalloc で拡張します

__libc_malloc

malloc 関数は実際には __libc_malloc を呼び出します。この関数は、tcache をチェックして、必要なサイズの利用可能なチャンクがあるかどうかを確認します。利用可能な場合はそれを使用し、そうでない場合はシングルスレッドであるかどうかを確認し、その場合はメインアリーナの _int_malloc を呼び出し、そうでない場合はスレッドのアリーナの _int_malloc を呼び出します。

__libc_malloc コード

```c // From https://github.com/bminor/glibc/blob/master/malloc/malloc.c

#if IS_IN (libc) void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim;

_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX");

if (!__malloc_initialized) ptmalloc_init (); #if USE_TCACHE /* int_free also calls request2size, be careful to not pad twice. */ size_t tbytes = checked_request2size (bytes); if (tbytes == 0) { __set_errno (ENOMEM); return NULL; } size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache != NULL && tcache->counts[tc_idx] > 0) { victim = tcache_get (tc_idx); return tag_new_usable (victim); } DIAG_POP_NEEDS_COMMENT; #endif

if (SINGLE_THREAD_P) { victim = tag_new_usable (_int_malloc (&main_arena, bytes)); assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || &main_arena == arena_for_chunk (mem2chunk (victim))); return victim; }

arena_get (ar_ptr, bytes);

victim = _int_malloc (ar_ptr, bytes); /* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }

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

victim = tag_new_usable (victim);

assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || ar_ptr == arena_for_chunk (mem2chunk (victim))); return victim; }

</details>

以下は、常に返されたポインタに `tag_new_usable` を付ける方法についてのコードです:
```c
void *tag_new_usable (void *ptr)

Allocate a new random color and use it to color the user region of
a chunk; this may include data from the subsequent chunk's header
if tagging is sufficiently fine grained.  Returns PTR suitably
recolored for accessing the memory there.

_int_malloc

これは、他のビンとトップチャンクを使用してメモリを割り当てる関数です。

  • 開始

いくつかの変数を定義し、リクエストされたメモリスペースが実際に必要とするサイズを取得することから始まります:

ファストビン

必要なサイズがファストビンのサイズ内にある場合は、ファストビンからチャンクを使用しようとします。基本的に、サイズに基づいて、有効なチャンクが配置されているべきファストビンのインデックスを見つけ、存在すればそのうちの1つを返します。 さらに、tcacheが有効になっている場合、そのサイズのtcacheビンをファストビンで埋めます。

これらのアクションを実行する際に、ここでいくつかのセキュリティチェックが実行されます:

  • チャンクがアライメントされていない場合: malloc(): unaligned fastbin chunk detected 2

  • フォワードチャンクがアライメントされていない場合: malloc(): unaligned fastbin chunk detected

  • 返されたチャンクが、ファストビン内のインデックスのために正しいサイズでない場合: malloc(): memory corruption (fast)

  • tcacheを埋めるために使用されるチャンクがアライメントされていない場合: malloc(): unaligned fastbin chunk detected 3

Last updated