Translated ['binary-exploitation/heap/README.md'] to jp

This commit is contained in:
Translator 2024-05-09 18:09:08 +00:00
parent 1ff24c9822
commit 72f5cfdb38
7 changed files with 209 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -1 +1,210 @@
# ヒープ
## ヒープの基礎
ヒープは、プログラムが**`malloc`**、`calloc`などの関数を呼び出してデータを要求するときにデータを格納できる場所です。さらに、このメモリが不要になった場合は、**`free`** 関数を呼び出すことで利用可能になります。
バイナリがメモリにロードされる直後にあることが示されています(`[heap]` セクションを確認):
<figure><img src="../../.gitbook/assets/image (1241).png" alt=""><figcaption></figcaption></figure>
### ベーシック チャンクの割り当て
ヒープにデータを格納するよう要求されると、ヒープの一部がそれに割り当てられます。このスペースはビンに属し、要求されたデータ + ビンヘッダのスペース + 最小ビンサイズオフセットだけがチャンクのために予約されます。目標は、各チャンクの場所を見つけるのが複雑にならないように、可能な限り最小限のメモリを予約することです。そのために、メタデータチャンク情報が使用され、どこに各チャンクがあるかを知るために使用されます。
使用されるビンによって主に異なる方法でスペースを予約する方法がありますが、一般的な方法論は次のとおりです:
- プログラムは一定量のメモリを要求して開始します。
- チャンクのリストに、要求を満たすのに十分な大きさの利用可能なチャンクがあれば、それが使用されます。
- これは、利用可能なチャンクの一部がこの要求に使用され、残りがチャンクリストに追加されることさえ意味するかもしれません。
- リストに利用可能なチャンクがない場合でも、割り当てられたヒープメモリにはまだスペースがある場合、ヒープマネージャは新しいチャンクを作成します。
- 新しいチャンクを割り当てるためのヒープスペースが十分でない場合、ヒープマネージャはカーネルにヒープに割り当てられたメモリを拡張するように要求し、そのメモリを使用して新しいチャンクを生成します。
- すべてが失敗した場合、`malloc` は null を返します。
要求されたメモリが**しきい値を超える**場合は、**`mmap`** が要求されたメモリをマップするために使用されます。
### アリーナ
**マルチスレッド** アプリケーションでは、ヒープマネージャはクラッシュにつながる可能性のある**競合状態**を防止する必要があります。最初は、**グローバルミューテックス**を使用して、一度に1つのスレッドだけがヒープにアクセスできるようにすることでこれを行っていましたが、これにより**パフォーマンスの問題**が発生しました。
これを解決するために、ptmalloc2 ヒープアロケータは「アリーナ」を導入しました。**各アリーナ**は**独自の**データ**構造**と**ミューテックス**を持つ**別々のヒープ**として機能し、異なるアリーナを使用する限り、複数のスレッドが互いに干渉せずにヒープ操作を実行できます。
デフォルトの「メイン」アリーナは、単一スレッドアプリケーションのヒープ操作を処理します。**新しいスレッド**が追加されると、ヒープマネージャは競合を減らすためにそれらに**セカンダリアリーナ**を割り当てます。まず、新しいスレッドを未使用のアリーナにアタッチしようとし、必要に応じて新しいアリーナを作成し、32ビットシステムでは CPU コア数の2倍、64ビットシステムでは8倍の制限まで増やします。制限に達すると、**スレッドはアリーナを共有**する必要があり、競合が発生する可能性があります。
メインアリーナが`brk`システムコールを使用して拡張するのに対し、セカンダリアリーナは、`mmap` と `mprotect` を使用して「サブヒープ」を作成し、ヒープの動作をシミュレートしてメモリをマルチスレッド操作のために柔軟に管理します。
### サブヒープ
サブヒープは、マルチスレッドアプリケーションのセカンダリアリーナにとってのメモリリザーブであり、メインヒープとは別に独自のヒープ領域を成長させ、管理することを可能にします。以下は、サブヒープが初期ヒープとどのように異なり、どのように動作するかです:
1. **初期ヒープ vs. サブヒープ**:
- 初期ヒープは、プログラムのバイナリの直後にあり、`sbrk` システムコールを使用して拡張されます。
- セカンダリアリーナが使用するサブヒープは、指定されたメモリ領域をマップする `mmap` を介して作成されます。
2. **`mmap` を使用したメモリリザーブ**:
- ヒープマネージャがサブヒープを作成すると、`mmap` を介して大きなメモリブロックを予約します。この予約はメモリを直ちに割り当てるのではなく、他のシステムプロセスや割り当てが使用しない領域を指定するだけです。
- デフォルトでは、32ビットプロセスのサブヒープの予約サイズは1 MB、64ビットプロセスの場合は64 MB です。
3. **`mprotect` を使用した段階的な拡張**:
- 予約されたメモリ領域は最初は `PROT_NONE` としてマークされ、カーネルはこのスペースに物理メモリを割り当てる必要がないことを示します。
- サブヒープを「成長」させるために、ヒープマネージャは `mprotect` を使用してページの権限を `PROT_NONE` から `PROT_READ | PROT_WRITE` に変更し、カーネルに以前に予約されたアドレスに物理メモリを割り当てるよう促します。この段階的なアプローチにより、サブヒープは必要に応じて拡張されます。
- サブヒープ全体が使い切られると、ヒープマネージャは新しいサブヒープを作成して割り当てを続行します。
### メタデータ
以前にコメントしたように、これらのチャンクにはメタデータもあり、この画像で非常によく表現されています:
<figure><img src="../../.gitbook/assets/image (1242).png" alt=""><figcaption><p><a href="https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png">https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png</a></p></figcaption></figure>
メタデータは通常、現在のチャンクサイズを示す 0x08B であり、最後の 3 ビットを使用して次のように示されます:
- `A`: 1 の場合、サブヒープから来ており、0 の場合はメインアリーナにある
- `M`: 1 の場合、このチャンクは `mmap` で割り当てられたスペースの一部であり、ヒープの一部ではない
- `P`: 1 の場合、前のチャンクが使用中
その後、ユーザーデータのスペース、最後にチャンクが利用可能な場合に前のチャンクサイズを示すための 0x08B があります(または割り当てられた場合はユーザーデータを格納するため)。
さらに、利用可能な場合、ユーザーデータはいくつかのデータも含むように使用されます:
- 次のチャンクへのポインタ
- 前のチャンクへのポインタ
- リスト内の次のチャンクのサイズ
- リスト内の前のチャンクのサイズ
<figure><img src="../../.gitbook/assets/image (1243).png" alt=""><figcaption><p><a href="https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png">https://azeria-labs.com/wp-content/uploads/2019/03/chunk-allocated-CS.png</a></p></figcaption></figure>
リストをこのようにリンクさせることで、すべてのチャンクを登録する必要がないため、配列を持つ必要がないことに注意してください。
## フリー保護
フリー関数の意図しないまたは意図的な乱用から保護するために、そのアクションを実行する前にいくつかのチェックを行います:
- アドレスが 8 バイトまたは 64 ビット境界でアラインされているかどうかをチェックします(`(address % 16) == 0`)、なぜなら _malloc_ はすべての割り当てがアラインされていることを保証しているからです。
- チャンクのサイズフィールドが不可能でないことをチェックします—小さすぎる、大きすぎる、アラインされていないサイズ、またはプロセスのアドレススペースの終わりをオーバーラップする可能性があるためです。
- チャンクがアリーナの境界内にあることをチェックします。
- チャンクが既に解放されていないことをチェックします。これは、次のチャンクの先頭にあるメタデータ内の対応する「P」ビットをチェックすることで行われます。
## ビン
チャンクの格納効率を向上させるために、各チャンクは1つのリンクリストにだけではなく、複数のタイプが存在します。これらがビンであり、5種類のビンがあります: [62](https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=blob;f=malloc/malloc.c;h=6e766d11bc85b6480fa5c9f2a76559f8acf9deb5;hb=HEAD#l1407) small bins、63 large bins、1 unsorted bin、10 fast bins、そしてスレッドごとに64個のtcache binsがあります。
未整列、small、largeの各ビンの初期アドレスは同じ配列内にあります。インデックス0は未使用で、1は未整列ビン、2から64はsmall bins、65から127はlarge binsです。
### Small Bins
Small binsはlarge binsよりも速く、fast binsよりも遅いです。
62の各ビンには**同じサイズのチャンク**があります: 16、24、... (32ビットでは最大504バイト、64ビットでは1024バイト)。これにより、空間を割り当てるべきビンを見つける速度や、これらのリストへのエントリの挿入と削除の速度が向上します。
### Large Bins
固定サイズのチャンクを管理するsmall binsとは異なり、各**large binはチャンクサイズの範囲を扱います**。これにより、システムが**さまざまなサイズ**を別々のビンなしで収容できる柔軟性があります。
メモリアロケーターでは、large binsはsmall binsが終わる場所から始まります。large binsの範囲は徐々に大きくなり、最初のビンは512から576バイトのチャンクをカバーし、次のビンは576から640バイトをカバーします。このパターンが続き、最大のビンには1MB以上のすべてのチャンクが含まれます。
large binsは、**最適な割り当て先を見つけるためにさまざまなチャンクサイズのリストをソートして検索**する必要があるため、small binsよりも操作が遅くなります。チャンクがlarge binに挿入されるときはソートする必要があり、メモリが割り当てられるときはシステムが適切なチャンクを見つける必要があります。この追加作業により、large binsは**遅く**なりますが、大きな割り当ては小さな割り当てよりも一般的であるため、これは許容できるトレードオフです。
以下があります:
* 64B範囲の32ビン
* 512B範囲の16ビン
* 4096B範囲の8ビン
* 32768B範囲の4ビン
* 262144B範囲の2ビン
* 残りのサイズ用の1ビン
### Unsorted Bin
未整列ビンは、メモリ割り当てを迅速にするためにヒープマネージャーが使用する**高速キャッシュ**です。動作は次のとおりです: プログラムがメモリを解放するとき、ヒープマネージャーはそれをすぐに特定のビンに入れません。代わりに、隣接する空きチャンクとマージしてより大きな空きメモリブロックを作成しようとします。その後、この新しいチャンクを「未整列ビン」と呼ばれる一般的なビンに配置します。
プログラムがメモリを要求するとき、ヒープマネージャーは**まず未整列ビンをチェック**して適切なサイズのチャンクがあるかどうかを確認します。見つかればすぐに使用され、他のビンを検索するよりも速くなります。適切なチャンクが見つからない場合は、解放されたチャンクをサイズに基づいて正しいビンsmallまたはlargeに移動します。
したがって、未整列ビンは、最近解放されたメモリを迅速に再利用し、時間のかかる検索とマージの必要性を減らすことでメモリ割り当てを高速化する方法です。
{% hint style="danger" %}
異なるカテゴリのチャンクであっても、時々、利用可能なチャンクが他の利用可能なチャンクと衝突している場合(カテゴリが異なっていても)、それらはマージされます。
{% endhint %}
### Fast Bins
Fast binsは、最近解放されたチャンクを迅速にアクセスする構造に保持することで、小さなチャンクのメモリ割り当てを**高速化する**ように設計されています。これらのビンは、最後に解放されたチャンクが新しい割り当て要求があるときに**最初に再利用される**Last-In, First-OutLIFOアプローチを使用します。この動作は、スタックLIFOの先頭から挿入および削除を行うのが、キューFIFOよりも速いため、速度的に有利です。
さらに、**fast binsは単方向リンクリスト**を使用し、ダブルリンクリストではないため、速度が向上します。fast binsのチャンクは隣接するチャンクとマージされないため、中央からの削除を許可する複雑な構造は不要です。単方向リンクリストはこれらの操作に対してより単純で速いです。
基本的に、ここで起こることは、ヘッダー(チェックする最初のチャンクへのポインタ)が常にそのサイズの最新の解放されたチャンクを指すようになっていることです。したがって:
* そのサイズの新しいチャンクが割り当てられると、ヘッダーは使用する空きチャンクを指すようになります。この空きチャンクが次に使用するチャンクを指しているため、このアドレスはヘッダーに保存され、次の割り当てがどこから利用可能なチャンクを取得するかを知ることができます。
* チャンクが解放されると、空きチャンクは現在の利用可能なチャンクへのアドレスを保存し、この新しく解放されたチャンクへのアドレスがヘッダーに入れられます。
{% hint style="danger" %}
fast binsのチャンクは自動的に利用可能に設定されないため、他のチャンクとマージできる代わりに一定時間fast binチャンクとして保持されます。
{% endhint %}
### TcacheスレッドごとのキャッシュBins
スレッドが独自のヒープを持とうとしますが([Arenas](./#arenas)と[Subheaps](./#subheaps)を参照、多くのスレッドWebサーバーなどを持つプロセスは**他のスレッドとヒープを共有する可能性**があります。この場合、主な解決策は**ロッカー**の使用であり、これによりスレッドの**遅延が著しく増加**します。
したがって、tcacheは、スレッドごとのfast binのように、チャンクをマージしない**単方向リンクリスト**であります。各スレッドには**64個の単方向リンクtcache bins**があります。各ビンには[64ビットシステムでは24から1032B、32ビットシステムでは12から516B](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l315)の最大[同じサイズのチャンクが7つ](https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=2527e2504761744df2bdb1abdc02d936ff907ad2;hb=d5c3fafc4307c9b7a4c7d5cb381fcdbfad340bcc#l323)入ることができます。
スレッドが**チャンクを解放**するとき、それがtcacheに割り当てるには**大きすぎない**かつ対応するtcache binが**満杯でない**すでに7つのチャンクがある必要があります。tcacheに入れることができない場合は、グローバルなビンで解放操作を行うためにヒープロックを待つ必要があります。
チャンクが**割り当てられる**とき、**Tcacheに必要なサイズの空きチャンクがあれば使用**され、そうでない場合は、グローバルビンで見つけるためにヒープロックを待つ必要があります。\
また、この場合の最適化があり、ヒープロックを取得する間に、スレッドは要求されたサイズのヒープチャンク7つでTcacheを埋めるため、必要があればTcacheでそれらを見つけることができます。
### ビンの順序
#### 割り当てるために:
1. そのサイズのTcacheに利用可能なチャンクがある場合、Tcacheを使用します
2. それが非常に大きい場合、mmapを使用します
3. アリーナヒープロックを取得し、次の操作を行います:
1. 要求されたサイズの利用可能な小さなサイズのファストビンチャンクがある場合、それを使用し、ファストビンからTcacheを事前に埋めます
2. 要求されたサイズよりも大きい1つのチャンクを探して未整列リスト内の各エントリをチェックし、可能であればTcacheを事前に埋めます
3. 要求されたサイズに応じて、小さなビンまたは大きなビンをチェックし、可能であればTcacheを事前に埋めます
4. 利用可能なメモリから新しいチャンクを作成します
1. 利用可能なメモリがない場合、`sbrk`を使用してさらに取得します
2. メインヒープメモリをさらに拡張できない場合、mmapを使用して新しいスペースを作成します
5. 何もうまくいかない場合、nullを返します
**解放するために:**
1. ポインタがNullの場合、終了します
2. チャンク内の`free`セーフティチェックを実行して、正当なチャンクであるかを検証しようとします
1. 十分に小さくてTcacheがいっぱいでない場合、そこに配置します
2. ビットMが設定されている場合ヒープでない場合、`munmap`を使用します
3. アリーナヒープロックを取得します:
1. ファストビンに収まる場合、そこに配置します
2. チャンクが64KBを超える場合、すぐにファストビンを統合し、結果としてマージされたチャンクを未整列ビンに配置します
3. 隣接する解放されたチャンクと後方および前方にチャンクをマージします。これは、小さな、大きな、および未整列のビンに存在する場合です
4. ヘッドのトップにある場合、未使用のメモリにマージします
5. これら以外の場合、未整列リストに保存します
\
クイックヒープの例は、[https://guyinatuxedo.github.io/25-heap/index.html](https://guyinatuxedo.github.io/25-heap/index.html) からarm64で提供されています:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{
char *ptr;
ptr = malloc(0x10);
strcpy(ptr, "panda");
}
```
main関数の最後にブレークポイントを設定し、情報が格納されている場所を見つけましょう
<figure><img src="../../.gitbook/assets/image (1239).png" alt=""><figcaption></figcaption></figure>
`0xaaaaaaac12a0`に文字列pandaが格納されていることがわかりますこれは`x0`内のmallocによって返されたアドレスでした。その前の0x10バイトをチェックすると、`0x0`が**前のチャンクが使用されていないこと**を示し、このチャンクの長さが`0x21`であることがわかります。
余分なスペース0x21-0x10=0x11は、**追加されたヘッダー**0x10から来ており、0x1は0x21Bが予約されたことを意味するのではなく、現在のヘッダーの長さの最後の3ビットに特別な意味があることを示しています。長さは常に16バイトに整列されるため64ビットマシンでは、これらのビットは実際には長さ番号によって使用されることはありません。
```
0x1: Previous in Use - Specifies that the chunk before it in memory is in use
0x2: Is MMAPPED - Specifies that the chunk was obtained with mmap()
0x4: Non Main Arena - Specifies that the chunk was obtained from outside of the main arena
```
##
## 参考文献
* [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/)