Windows 8の内部ASLR

ASLRは、アドレス空間レイアウトのランダム化、アドレス空間のランダム化です。 これは、攻撃に敏感なさまざまなデータ構造の仮想メモリアドレスのランダム化を含むセキュリティメカニズムです。 ターゲット構造のメモリ内の場所を予測するのは難しいため、攻撃者が成功する可能性はわずかです。



Windows ASLRの実装は、実行可能イメージの再配置メカニズムと密接に関連しています。 再配置により、固定優先ベースだけでなくPEファイルをロードできます。 PEファイルの再配置セクションは、イメージを移動するときの重要な構造です。 異なるベースアドレスでアプリケーションが正しく機能するようにするために、コードとデータの特定の要素にどのような変更を加える必要があるかについて説明します。



ASLRの作業における重要な役割は、乱数ジェネレーターと、読み込まれたPEファイルのベースアドレスを変更するいくつかの関数によって果たされます。



Windows 8は、乱数ジェネレーターに依存しています。これは、基本的にパラメーターj = 24およびk = 55の遅延フィボナッチジェネレーターです。 そのカーネルは、システムの起動時にwinload.exeモジュールで初期化されます。 Winload.exeは、レジストリキー、TPM、現在の時刻、ACPI、および新しいrdrand命令の使用など、さまざまなソースからエントロピーを収集します。 核乱数発生器の初期化は、ソース[1]で詳細に説明されています。



新しいrdrandステートメントを詳しく見てみましょう。 Ivy Bridgeアーキテクチャに基づくプロセッサでは、高品質の擬似乱数を生成するためにIntel Secure Keyテクノロジーが導入されました。 これは、ハードウェアのデジタル乱数ジェネレーター(DRNG)と、プログラムから値を抽出するためのrdrand命令を使用して実装されます。



ハードウェアの観点から見ると、DRNGはプロセッサチップ上の独立したモジュールです。 3 GHzのプロセッサコアと非同期で動作します。 DRNGは、エントロピーのソースとして熱ノイズを使用します。 さらに、抽出されたランダム値の一連の品質チェックを実行する統合テストシステムを備えています。 テスト結果が不十分な場合、DRNGはランダムな値の生成を停止します。



rdrand命令は、DRNGから乱数を抽出するために使用されます。 そのドキュメントでは、理論的には、テスト結果が不十分な場合、またはランダムな値の空の内部キューがある場合、DRNGはゼロ値を返す可能性があることに注意しました。 ただし、実際には、DRNGを空にすることはできませんでした。



Intel Secure Keyは、非常に高速で高品質の乱数値を生成する強力な乱数ジェネレーターです。 (他のエントロピーのソースとは異なり)rdrand命令を使用して初期化されたジェネレーターの初期状態を予測することはほとんど不可能です。



核PRNGの内部インターフェイス関数はExGenRandom()です。 エクスポートされたラッパー関数RtlRandomEx()もあります。 Windows 8のASLRはExGenRandom()を使用します-rdtsc命令に依存していた以前のバージョンとは異なります。 後者はCPU上で時間カウンターを取得するために使用されますが、これは線形に変化するため、生成された値の適切な品質を提供できず、安全ではありません。



ASLRメカニズムの主な機能はMiSelectImageBase()です。 Windows 8では、次の擬似コードで説明できます。



#define MI_64K_ALIGN(x) (x + 0x0F) >> 4 #define MmHighsetUserAddress 0x7FFFFFEFFFF typedef PIMAGE_BASE ULONG_PTR; typedef enum _MI_MEMORY_HIGHLOW { MiMemoryHigh = 0, MiMemoryLow = 1, MiMemoryHighLow = 2 } MI_MEMORY_HIGHLOW, *PMI_MEMORY_HIGHLOW; MI_MEMORY_HIGHLOW MiSelectBitMapForImage(PSEGMENT pSeg) { if (!(pSeg->SegmentFlags & FLAG_BINARY32)) // WOW binary { if (!(pSeg->ImageInformation->ImageFlags & FLAG_BASE_BELOW_4GB)) { if (pSeg->BasedAddress > 0x100000000) { return MiMemoryHighLow; } else { return MiMemoryLow; } } } return MiMemoryHigh; } PIMAGE_BASE MiSelectImageBase(void* a1<rcx>, PSEGMENT pSeg) { MI_MEMORY_HIGHLOW ImageBitmapType; ULONG ImageBias; RTL_BITMAP *pImageBitMap; ULONG_PTR ImageTopAddress; ULONG RelocationSizein64k; MI_SECTION_IMAGE_INFORMATION *pImageInformation; ULONG_PTR RelocDelta; PIMAGE_BASE Result = NULL; // rsi = rcx // rcx = rdx // rdi = rdx pImageInformation = pSeg->ImageInformation; ImageBitmapType = MiSelectBitMapForImage(pSeg); a1->off_40h = ImageBitmapType; if (ImageBitmapType == MiMemoryLow) { // 64-bit executable with image base below 4 GB ImageBias = MiImageBias64Low; pImageBitMap = MiImageBitMap64Low; ImageTopAddress = 0x78000000; } else { if (ImageBitmapType == MiMemoryHighLow) { // 64-bit executable with image base above 4 GB ImageBias = MiImageBias64High; pImageBitMap = MiImageBitMap64High; ImageTopAddress = 0x7FFFFFE0000; } else { // MiMemoryHigh 32-bit executable image ImageBias = MiImageBias; pImageBitMap = MiImageBitMap; ImageTopAddress = 0x78000000; } } // pSeg->ControlArea->BitMap ^= (pSeg->ControlArea->BitMap ^ (ImageBitmapType << 29)) & 0x60000000; // or bitfield form pSeg->ControlArea.BitMap = ImageBitmapType; RelocationSizein64k = MI_64K_ALIGN(pSeg->TotalNumberOfPtes); if (pSeg->ImageInformation->ImageCharacteristics & IMAGE_FILE_DLL) { ULONG StartBit = 0; ULONG GlobalRelocStartBit = 0; StartBit = RtlFindClearBits(pImageBitMap, RelocationSizein64k, ImageBias); if (StartBit != 0xFFFFFFFF) { StartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 0); if (StartBit != 0xFFFFFFFF) { Result = ImageTopAddress - (((RelocationSizein64k) + StartBit) << 0x10); if (Result == (pSeg->BasedAddress - a1->SelectedBase)) { GlobalRelocStartBit = MiObtainRelocationBits(pImageBitMap, RelocationSizein64k, StartBit, 1); StartBit = (GlobalRelocStartBit != 0xFFFFFFFF) ? GlobalRelocStartBit : StartBit; Result = ImageTopAddress - (RelocationSizein64k + StartBit) << 0x10; } a1->RelocStartBit = StartBit; a1->RelocationSizein64k = RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = StartBit; pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k; return Result; } } } else { // EXE image if (a1->SelectedBase != NULL) { return pSeg->BasedAddress; } if (ImageBitmapType == MiMemoryHighLow) { a1->RelocStartBit = 0xFFFFFFFF; a1->RelocationSizein64k = (WORD)RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF; pSeg->ControlArea->ImageRelocationSizeIn64k = (WORD)RelocationSizein64k; return ((DWORD)(ExGenRandom(1) % (0x20001 - RelocationSizein64k)) + 0x7F60000) << 16; } } ULONG RandomVal = ExGenRandom(1); RandomVal = (RandomVal % 0xFE + 1) << 0x10; RelocDelta = pSeg->BasedAddress - a1->SelectedBase; if (RelocDelta > MmHighsetUserAddress) { return 0; } if ((RelocationSizein64k << 0x10) > MmHighsetUserAddress) { return 0; } if (RelocDelta + (RelocationSizein64k << 0x10) <= RelocDelta) { return 0; } if (RelocDelta + (RelocationSizein64k << 0x10) > MmHighsetUserAddress) { return 0; } if (a1->SelectedBase + RandomVal == 0) { Result = pSeg->BasedAddress; } else { if (RelocDelta > RandomVal) { Result = RelocDelta - RandomVal; } else { Result = RelocDelta + RandomVal; if (Result < RelocDelta) { return 0; } if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) > 0x7FFFFFDFFFF) { return 0; } if (((RelocationSizein64k << 0x10) + RelocDelta + RandomVal) < (RelocDelta + (RelocationSizein64k << 0x10)))) { return 0; } } } //random_epilog a1->RelocStartBit = 0xFFFFFFFF; a1->RelocationSizein64k = RelocationSizein64k; pSeg->ControlArea->ImageRelocationStartBit = 0xFFFFFFFF; pSeg->ControlArea->ImageRelocationSizeIn64k = RelocationSizein64k; return Result; }
      
      





ご覧のとおり、3つの異なる画像ビットマップがあります。 1つ目は32ビット実行可能アプリケーション用、2つ目は64ビット用、3つ目はベースが4 GBを超える64ビットアプリケーション用で、高いエントロピーを持つ仮想アドレスを提供します。



実行可能イメージのランダム化は、ベースアドレスに直接影響します。 ライブラリをロードする場合、ASLRはモジュールの再配置プロセスの一部です。新しいベースアドレスを選択するときのランダム変数は、システムの起動時に初期化されるImageBias変数です。



 VOID MiInitializeRelocations() { MiImageBias = ExGenRandom(1) % 256; MiImageBias64Low = ExGenRandom(1) % MiImageBitMap64Low.SizeOfBitMap; MiImageBias64High = ExGenRandom(1) % MiImageBitMap64High.SizeOfBitMap; return; }
      
      





実行可能イメージのビットマップは、現在のユーザープロセスのアドレス空間を表示します。 実行可能イメージはブート時にベースアドレスを取得し、同じベースアドレスの他のプロセスに再ロードされます。 ブートローダーのこの動作は、コピーオンライトメカニズムの実行可能イメージを使用するため、速度とメモリの節約に関して最も効果的です。



Windows 8の現在のASLR実装では、ランダム化をサポートしていないアプリケーションのベースアドレスをランダム化できます。 次の表は、ASLRに関連付けられているリンカーフラグの組み合わせに応じたブートローダーの動作を示しています。







* MSVSを使用してイメージをアセンブルすることはできません。これは、/ DYNAMICBASEフラグには/ FIXED:NOフラグが必要であり、再配置セクションが生成されるためです。



Windows 8で一般的なブートローダーの動作の変更に気付くかもしれません。実行可能イメージに再配置セクションがある場合、とにかくロードされます。 これは、ASLRと再配置メカニズムの関係のさらなる証拠です。



一般に、Windows 8での新しいASLR関数の実装はコードのロジックに大きな影響を与えないため、その中の有用な脆弱性を検出するのはかなり難しいと言えます。 さまざまなオブジェクトをランダム化するためのエントロピーを増やすことは、本質的にコード内の定数式の代わりになります。 さらに、コードグラフにはコード検査が含まれます。



ソース

[1] Valasek Ch。、Mandt T. Windows 8ヒープ内部。 2012年。

[2]ジョンソンK.、ミラーM. Windows 8での緩和策の改善。スライド、Black Hat USA、2012年。

[3] Intel。 Intel Digital Random Number Generator(DRNG):ソフトウェア実装ガイド。 Intel Corporation、2012年。

[4] Whitehouse O. Windows Vistaでのアドレス空間レイアウトのランダム化の分析。 Symantec Advances Threat Research、2007年。

[5] Sotirov A.、Dowd M.ブラウザーメモリ保護のバイパス。 2008年。



著者:Artyom Shishkin( 名誉 ボット )およびIlya Smith( 黒人 )、ポジティブリサーチセンター。



All Articles