Windows カーネルグラフィック
ドライバの攻撃面
イリヤ・ファン・スプルンデル
自己紹介
•
イリヤ・ファン・スプルンデル
•
[email protected]
•
ペネトレーションテスト
ディレクター at IOActive
•
ペネトレーションテスト
•
コードレビュー
•
趣味と実益のコード破壊
☺
アジェンダ
•
今回お話すること
•
Windows グラフィックドライバ
•
WDDM kmd ドライバ
– 排他制御 – エントリポイント•
これらと通信するためのユーザーランドで完結したプログラム
•
プライベートデータの探索と窃取
•
まとめ
– ファジング – リバースエンジニアリング今回お話すること
•
Windows
®WDDM ドライバ
– 実装上のセキュリティ – カーネルドライバに関して•
想定される聴衆
– 解析者(どこを解析すべきか) – グラフィックドライバの開発者 (すべきでないこと、注意を払うべき箇所) – ドライバの内部構造に興味を持った奇人変人•
前提知識
– Windows ドライバに関する基礎知識(IRP、イベントハンドラ、データの受け渡し…)
Windows グラフィックドライバ
•
過去のモデル
– XDDM /XPDM – Windows 2000/XP – Windows 8 ではサポートされていない – 本セッションでは対象としない•
WDDM (Windows Display Driver Model)
– Vista 以降のモデル • v1 – vista • v1.1 – win 7 • v1.2 – win 8 • V1.3 – win 8.1 – セキュリティの観点から興味深い箇所について紹介する
Windows グラフィックドライバ
•
誰が、何のためにドライバを開発しているのか?
– 独立系ハードウェアベンダ (Intel, NVIDIA, AMD, Qualcomm, PowerVR, VIA, Matrox, …)
• 非常に高機能なドライバ
– 基本的なフォールバック (基本レンダラ、基本ディスプレイ) • 最小限の実装
– 仮想化 (VMware, Virtual Box, Parallels guest drivers) • 特殊な目的のためのドライバ
– リモートデスクトップの実現 (XenDesktop, RDP, …) • 特殊な目的のためのドライバ
– 仮想ディスプレイ (intelligraphics, extramon, …) • 特殊な目的のためのドライバ
Windows グラフィックドライバ
• WDDM モデルはユーザーモードとカーネルモードに分けられている • 安定性と信頼性のために、ユーザーモードへとコンポーネントが分割された – Vista 以前のブルースクリーンは、その原因の大部分をグラフィックドライバに負っていた (MSDN から引 用): “Windows XP のグラフィックドライバは巨大かつ複雑で、システムを不安定にする原因でもあった。 これらのドライバは完全にカーネルモードで動作するものであり(i.e., システムの深部)、したがって、ドライ バの内部で問題が発生した場合、システムは再起動を余儀なくされる。Windows XP が主流だった時期 に収集されたクラッシュダンプ解析によると、ブルースクリーンにおける原因の最大 20 パーセントはグラ フィックドライバによるものだった。•
ユーザーモードにおいては、プロセスに読み込まれる
DLLとして実行される
– こちらについても、興味深い攻撃面が見受けられる • エンコーダ/デコーダ • バイナリプランティング • いくつかのAPIは、部分的に(間接的に)リモートからの攻撃に晒されうる (e.g. WebGL) • 本発表はユーザーモードを対象とせず、カーネルモードにおける攻撃面を取り上げるWDDM kmd ドライバ
•
では、
WDDM kmd ドライバとはどのようなものか?
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { ... DRIVER_INITIALIZATION_DATA DriverInitializationData; ... DriverInitializationData.DxgkDdiEscape = DDIEscape; ... Status = DxgkInitialize(DriverObject, RegistryPath, &DriverInitializationData); ... }
WDDM kmd ドライバ
•
DriverEntry() はあらゆるカーネルドライバのエントリポイントである
•
DRIVER_INITIALIZATION_DATA 構造体を埋める
– 無数のコールバック関数を含む – 「動的な」構造体 • win7 では肥大化 (vs vista) • win8 ではより肥大化 • win 8.1 ではさらに肥大化 • 末尾に新たな要素が追加されていく•
DxgkInitialize() の呼び出し
–dxgkernel にドライバ自身とそのコールバック関数を通知する
WDDM kmd ドライバ
•
これまでのものとよく似ている
•
DxgkInitialize() の代わりに DxgkInitializeDisplayOnlyDriver() を呼び出す
•
PKMDDOD_INITIALIZATION_DATA 構造体を利用
•
これまでのものと似ているが、カーネルモードの
display only driverのみが用
いる
WDDM kmd ドライバ
•
DRIVER_INITIALIZATION_DATA はあらゆるコールバック関数を含む
•
攻撃面の観点から、大きく
3つのグループに分けることができる
– 攻撃者が全く制御できない、あるいは少ししか制御できない箇所 – 攻撃者が(間接的に)制御できる箇所 – 攻撃者がコールバック関数への重要な入力を制御できる箇所•
我々は後者を問題視している
WDDM kmd ドライバ – 排他制御
•
WDDM はスレッドモデルであり、そのコールバック関数は4つのレベルで構
成される(コールバック関数はこれらのいずれかに属する):
•
レベル
3
– 単一スレッドのみ – GPU はアイドル状態である必要がある – DMA バッファは処理されない – ビデオメモリは CPU 側のメモリに追い出される•
レベル
2
– ビデオメモリの追い出しを除き、上記と同じWDDM kmd ドライバ – 排他制御
•
レベル
1
– クラスに分類された呼び出し。各クラスにつき 1 つのスレッドのみ、同
時にコールバック関数を呼び出すことができる
•
レベル
0
– リエントラント(再入可能)•
マルチスレッドの実行が許可される場合であっても、
2 つのスレッドが同時に
単一のプロセスに属することはできない
•
レースコンディションによる攻撃シナリオを考えるにあたっては、このことを念
頭に置いておく必要がある
WDDM kmd ドライバ エントリポイント
•
ユーザーランドから重要な入力を受け取るコールバック関数は、かなり少数である:
– エスケープ機能 – 描画 – メモリ割り当て – クエリアダプタ•
興味深いコールバック関数に辿り着く前に、我々はドライバを適切に初期化しなければならない
– まずは初期化の部分を見てみよう • それから、コールバック関数を見てみようWDDM kmd ドライバ エントリポイント – 初期化
•
ユーザーランドからエントリポイントに到達する前に、デバイスを初期化しなけ
ればならない
•
我々は
HDC(デバイスコンテキストのハンドル)を持ってGDI(グラフィックデバ
イスインターフェイス)の世界からやってくると仮定する
•
簡潔に、これらは
3つのステップを伴う
– HDC を WDDM が扱えるハンドルとして変換する – 変換したハンドルから WDDM のデバイスハンドルを取得する – デバイスのためのコンテキストを作成するWDDM kmd ドライバ エントリポイント – 初期化
– HDC を WDDM が扱えるハンドルとして変換する
– D3DKMT_OPENADAPTERFROMHDC 構造体を埋める
– D3DKMTOpenAdapterFromHdc の呼び出し
D3DKMT_OPENADAPTERFROMHDC oafh; memset(&oafh, 0x00, sizeof(oafh)); oafh.hDc = GetDC(NULL); D3DKMTOpenAdapterFromHdc(&oafh);WDDM kmd ドライバ エントリポイント – 初期化
– 変換したハンドルからデバイスハンドルを取得する
– D3DKMT_CREATEDEVICE 構造体を埋める
– D3DKMTCreateDevice の呼び出し
D3DKMT_CREATEDEVICE cdev; memset(&cdev, 0x00, sizeof(cctx)); cdev.hAdapter = oafh.hAdapter; D3DKMTCreateDevice(&cdev);WDDM kmd ドライバ エントリポイント – 初期化
– デバイスのためのコンテキストを作成する
– 先程取得したデバイスハンドルは、WDDM ドライバに通信するためのユーザーラン
ド
API の殆どに渡されるハンドルである
– 何をするにもまずデバイスコンテキストを作成する必要がある
• WDDM ドライバに渡すことができるコマンドバッファを設定する • ここに攻撃面がある。ユーザーランドから任意のデータ (pPrivateDriverData) (とその ポインタが指すデータ長 PrivateDriverDataSize) を WDDM ドライバに渡すことができ るのだ – とはいえ、これは完全にドライバ依存であり、時と場合によるものであるIOActive, Inc. Copyright ©2014. All Rights Reserved.
WDDM kmd ドライバ エントリポイント – 初期化
– デバイスのためのコンテキストを作成する
– D3DKMT_CREATECONTEXT 構造体を埋める
– D3DKMTCreateContext の呼び出し
– DxgkDdiCreateContext カーネルエントリポイント
D3DKMT_CREATECONTEXT cctx; memset(&cctx, 0x00, sizeof(cctx)); cctx.hDevice = cdev.hDevice; r = pfnKTCreateContext(&cctx);
typedef struct _D3DKMT_CREATECONTEXT
{ D3DKMT_HANDLE hDevice; UINT NodeOrdinal; UINT EngineAffinity;
D3DDDI_CREATECONTEXTFLAGS Flags;
VOID *pPrivateDriverData; UINT
PrivateDriverDataSize; D3DKMT_CLIENTHINT ClientHint; D3DKMT_HANDLE hContext;
VOID *pCommandBuffer; UINT CommandBufferSize; D3DDDI_ALLOCATIONLIST *pAllocationList; UINT AllocationListSize; D3DDDI_PATCHLOCATIONLIST *pPatchLocationList; UINT PatchLocationListSize; D3DGPU_VIRTUAL_ADDRESS CommandBuffer; } D3DKMT_CREATECONTEXT;
WDDM kmd ドライバ エントリポイント – 初期化
– デバイスのためのコンテキストを作成する
– 構造体が出力する興味深い要素
– コマンドバッファや patchlocationlist は、いずれもあ
なたに代わって
WDDM によって割り当てられる
– ユーザーモードでは WDDM ドライバとの通信に用
いられる
typedef struct _D3DKMT_CREATECONTEXT
{ D3DKMT_HANDLE hDevice; UINT NodeOrdinal; UINT EngineAffinity;
D3DDDI_CREATECONTEXTFLAGS Flags; VOID *pPrivateDriverData; UINT
PrivateDriverDataSize; D3DKMT_CLIENTHINT ClientHint; D3DKMT_HANDLE hContext;
VOID *pCommandBuffer; UINT
CommandBufferSize; D3DDDI_ALLOCATIONLIST *pAllocationList; UINT AllocationListSize; D3DDDI_PATCHLOCATIONLIST *pPatchLocationList; UINT PatchLocationListSize; D3DGPU_VIRTUAL_ADDRESS CommandBuffer; } D3DKMT_CREATECONTEXT;
WDDM kmd ドライバ エントリポイント – 初期化
•
DxgkDdiEscape
•
グラフィックドライバにおける
IOCTL に相当する
•
「過去の」
extEscape に酷似している
•
しかしながら、
エスケープ機能が呼ばれることはない
•
プライベートデータへのポインタとそれが指すデータ長が格納される
•
MSDN は「DxgkDdiEscape 関数は、ユーザーモードとディスプレイドライバが情報を共有する
ためのものである」と述べている
•
ドライバは、どのような方式でも、この仕組みを実装して構わない
•
データは全くと言って標準化されていない
– めちゃくちゃなデータであっても、ドライバ間で送受信することができる•
スレッドレベル
2
WDDM kmd ドライバ エントリポイント – エスケープ機能
•
では、
DxgkDdiEscape とはどのようなものか?
NTSTATUS APIENTRY DxgkDdiEscape(
__in const HANDLE hAdapter, __in const DXGKARG_ESCAPE *pEscape ) { ... } typedef struct _DXGKARG_ESCAPE { HANDLE hDevice; D3DDDI_ESCAPEFLAGS Flags; VOID *pPrivateDriverData; UINT PrivateDriverDataSize; HANDLE hContext; } DXGKARG_ESCAPE;
WDDM kmd ドライバ エントリポイント – エスケープ機能
•
pPrivateDriverData はハンドリングされ、キャプチャされる
•
長さの制限なし (e.g. 4 ギガバイトまで可能)
•
ユーザーランドから完全に制御することができる
•
埋め込まれたポインタは全て
try/except によってハンドリングされる必要があ
る
WDDM kmd ドライバ エントリポイント – エスケープ機能
•
どのようにしてユーザーランドから通信するか?
•
システムコールとして正式に文書化されている
NTSTATUSD3DKMTEscape( _In_ const D3DKMT_ESCAPE *pData );
typedef struct _D3DKMT_ESCAPE { D3DKMT_HANDLE hAdapter; D3DKMT_HANDLE hDevice; D3DKMT_ESCAPETYPE Type; D3DDDI_ESCAPEFLAGS Flags; VOID *pPrivateDriverData; UINT PrivateDriverDataSize; D3DKMT_HANDLE hContext; } D3DKMT_ESCAPE;
WDDM kmd ドライバ エントリポイント – 描画
•
DxgkDdiRender
•
このコールバック関数はレンダリングの心臓部である
•
ユーザーモードから
GPU に対し、コマンドバッファから描画させる
WDDM kmd ドライバ エントリポイント – 描画
•
では、
DxgkDdiRender とはどのような
ものか?
NTSTATUS APIENTRY DxgkDdiRender(
_In_ const HANDLE hContext, _Inout_ DXGKARG_RENDER *pRender
) { ... }
typedef struct _DXGKARG_RENDER { const VOID CONST *pCommand; const UINT CommandLength; VOID *pDmaBuffer; UINT DmaSize; VOID *pDmaBufferPrivateData; UINT DmaBufferPrivateDataSize; DXGK_ALLOCATIONLIST *pAllocationList; UINT AllocationListSize; D3DDDI_PATCHLOCATIONLIST *pPatchLocationListIn; UINT PatchLocationListInSize; D3DDDI_PATCHLOCATIONLIST *pPatchLocationListOut; UINT PatchLocationListOutSize; UINT MultipassOffset; UINT DmaBufferSegmentId; PHYSICAL_ADDRESS DmaBufferPhysicalAddress; } DXGKARG_RENDER;
WDDM kmd ドライバ エントリポイント – 描画
•
pCommand バッファはユーザーランドから送られるポインタである
•
pPatchLocationListIn はユーザーランドから送られるポインタである
•
MSDN はこのように述べている:
「サブシステムとしてユーザーモードで動作するディスプレイドライバが生成するデータである
pCommand コマンドバッファと patch-location list の入力 pPatchLocationListIn は、いず
れもユーザーモードのアドレス空間に割り当てられ、手を加えられないまま、ディスプレイミニ
ポートドライバを経由する。ディスプレイミニポートドライバはバッファやリストを参照するにあ
たって必ず
__try/__except を用いる必要があり、それぞれのカーネルバッファにバッファやリス
トをコピーする前に、その内容をバリデーションする必要がある」
WDDM kmd ドライバ エントリポイント – 描画
__try {
for (Index = 0; Index < AllocationListInSize; AllocationTable++, AllocationListIn++, AllocationListOut++, Index++)
{
D3DKMT_HANDLE AllocationHandle = AllocationListIn->hAllocation; . . . } } __except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INVALID_PARAMETER;
SAMPLE_LOG_ERROR( "Exception occurred accessing … Status=0x%I64x", Status); goto cleanup;
WDDM kmd ドライバ エントリポイント – 描画
•
ユーザーランドが実際にコマンドバッファやパッチリストのアドレスを指定してい
るわけではない
•
D3DKMTCreateContext を呼び出す際に、Dxgkernel はあなたに代わってバ
アドレスをユーザーランドに割り当てる
•
そのため、ドライバのバックグラウンドで(
VirtualFree によって)アンマップするこ
とができる
•
したがって、
try/except が必要とされる
•
コマンド、パッチリストのアドレスともユーザーランドに存在していることを考える
と、ダブルフェッチに気をつけなければならない
– フェッチ 1: デリファレンスとバリデーション – ユーザーランドからのデータ改変 – フェッチ 2: デリファレンスとデータの利用、ダブルフェッチのバグ、先ほどのバリ デーションの無効化
WDDM kmd ドライバ エントリポイント – 描画
•
try /except が存在しない例
static NTSTATUS APIENTRY DxgkDdiRenderNew( CONST HANDLE hContext, DXGKARG_RENDER *pRender) { if (pRender->CommandLength < sizeof (VBOXWDDM_DMA_PRIVATEDATA_BASEHDR))
{
return STATUS_INVALID_PARAMETER; }
PVBOXWDDM_DMA_PRIVATEDATA_BASEHDR pInputHdr = (PVBOXWDDM_DMA_PRIVATEDATA_BASEHDR)pRender->pCommand;
NTSTATUS Status = STATUS_SUCCESS;
VBOXCMDVBVA_HDR* pCmd = (VBOXCMDVBVA_HDR*)pRender->pDmaBufferPrivateData; switch (pInputHdr->enmCmd) ← try/except が存在しない
{ ... } ... return STATUS_SUCCESS; }
WDDM kmd ドライバ エントリポイント – 描画
•
ダブルフェッチの例
static NTSTATUS APIENTRY DxgkDdiRenderNew( CONST HANDLE hContext, DXGKARG_RENDER *pRender) { …
PVBOXWDDM_DMA_PRIVATEDATA_BASEHDR pInputHdr =
(PVBOXWDDM_DMA_PRIVATEDATA_BASEHDR)pRender->pCommand; ...
PVBOXWDDM_DMA_PRIVATEDATA_UM_CHROMIUM_CMD pUmCmd = pInputHdr; …
PVBOXWDDM_UHGSMI_BUFFER_UI_SUBMIT_INFO pSubmUmInfo = pUmCmd->aBufInfos; …
if (pSubmUmInfo->offData >= pAlloc->AllocData.SurfDesc.cbSize
|| pSubmUmInfo->cbData > pAlloc->AllocData.SurfDesc.cbSize
|| pSubmUmInfo->offData + pSubmUmInfo->cbData > pAlloc->AllocData.SurfDesc.cbSize) { WARN(("invalid data")); return STATUS_INVALID_PARAMETER; } … pSubmInfo->cbBuffer = pSubmUmInfo->cbData; バリ デート 使用
WDDM kmd ドライバ エントリポイント – 描画
•
どのようにしてユーザーモードから通
信する?
NTSTATUS APIENTRY D3DKMTRender( _Inout_ D3DKMT_R ENDER *pData );typedef struct _D3DKMT_RENDER { union { D3DKMT_HANDLE hDevice; D3DKMT_HANDLE hContext; }; UINT CommandOffset; UINT CommandLength; UINT AllocationCount; UINT PatchLocationCount; VOID *pNewCommandBuffer; UINT NewCommandBufferSize; D3DDDI_ALLOCATIONLIST *pNewAllocationList; UINT NewAllocationListSize; D3DDDI_PATCHLOCATIONLIST *pNewPatchLocationList; UINT NewPatchLocationListSize; D3DKMT_RENDERFLAGS Flags; ULONGLONG PresentHistoryToken; ULONG BroadcastContextCount; D3DKMT_HANDLE BroadcastContext[D3DDDI_MAX_BROADCAST_CONTEXT]; ULONG QueuedBufferCount; D3DGPU_VIRTUAL_ADDRESS NewCommandBuffer; VOID *pPrivateDriverData; UINT PrivateDriverDataSize; } D3DKMT_RENDER;
WDDM kmd ドライバ エントリポイント – メモリ割り当て
•
DxgkDdiCreateAllocation
•
Dxgkernel は、ユーザーランドからのメモリ割り当て要求に対し、このコール
バック関数を呼び出す
WDDM kmd ドライバ エントリポイント – メモリ割り当て
•
では、
DxgkDdiCreateAllocation とはどのようなものか?
NTSTATUS APIENTRY DxgkDdiCreateAllocation( const HANDLE hAdapter,
DXGKARG_CREATEALLOCATION *pCreateAllocation ) { ... } typedef struct _DXGKARG_CREATEALLOCATION {
const VOID *pPrivateDriverData; UINT PrivateDriverDataSize; UINT NumAllocations; DXGK_ALLOCATIONINFO *pAllocationInfo; HANDLE hResource; DXGK_CREATEALLOCATIONFLAGS Flags; } DXGKARG_CREATEALLOCATION;
IOActive, Inc. Copyright ©2014. All Rights Reserved.
WDDM kmd ドライバ エントリポイント – メモリ割り当て
•
では、
DxgkDdiCreateAllocation とはどのようなものか?(cont.)
typedef struct _DXGK_ALLOCATIONINFO { VOID *pPrivateDriverData; UINT PrivateDriverDataSize; UINT Alignment; SIZE_T Size; SIZE_T PitchAlignedSize; DXGK_SEGMENTBANKPREFERENCE HintedBank; DXGK_SEGMENTPREFERENCE PreferredSegment; UINT SupportedReadSegmentSet; UINT SupportedWriteSegmentSet; UINT EvictionSegmentSet; UINT MaximumRenamingListLength; HANDLE hAllocation; DXGK_ALLOCATIONINFOFLAGS Flags; DXGK_ALLOCATIONUSAGEHINT *pAllocationUsageHint; UINT AllocationPriority; } DXGK_ALLOCATIONINFO;
WDDM kmd ドライバ エントリポイント – メモリ割り当て
•
プライベートドライバデータは、カーネルがユーザーランドから取得する
•
ユーザーランドから渡すための NumAllocations DXGK_ALLOCATIONINFO
構造体がある
•
DXGK_ALLOCATIONINFO のプライベートデータは、カーネルがユーザーラ
ンドから取得する
•
DxgkDdiOpenAllocation を直接ユーザーランドから呼び出すことはできない
が、
そのプライベートドライバデータはここで提供されるものと同じである
WDDM kmd ドライバ エントリポイント – メモリ割り当て
•
どのようにしてユーザーランドから通信す
る?
NTSTATUS APIENTRY D3DKMTCreateAllocation( D3DKMT_CREATEALLOCATION *pData );typedef struct _D3DKMT_CREATEALLOCATION { D3DKMT_HANDLE hDevice;
D3DKMT_HANDLE hResource; D3DKMT_HANDLE hGlobalShare; const VOID *pPrivateRuntimeData; UINT PrivateRuntimeDataSize; const VOID *pPrivateDriverData; UINT PrivateDriverDataSize; UINT NumAllocations; D3DDDI_ALLOCATIONINFO *pAllocationInfo; D3DKMT_CREATEALLOCATIONFLAGS Flags; HANDLE hPrivateRuntimeResourceHandle; } D3DKMT_CREATEALLOCATION;
WDDM kmd ドライバ エントリポイント – クエリアダプタ
•
実際の
nr の型はユーザーとドライバで異
なっている
•
Dxgkernel は何らかの変換を行う
•
いずれのフォーマットも定義されている
•
データ長についても定義されている
•
DXGKQAITYPE_UMDRIVERPRIVATE
を除いて、
•
ドライバはこれらをどのように扱っても良い
typedef enum _DXGK_QUERYADAPTERINFOTYPE { DXGKQAITYPE_UMDRIVERPRIVATE = 0, DXGKQAITYPE_DRIVERCAPS = 1, DXGKQAITYPE_QUERYSEGMENT = 2,
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN7) DXGKQAITYPE_ALLOCATIONGROUP = 3,
DXGKQAITYPE_QUERYSEGMENT2 = 4, #endif
#if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8) DXGKQAITYPE_QUERYSEGMENT3 = 5, DXGKQAITYPE_NUMPOWERCOMPONENTS = 6, DXGKQAITYPE_POWERCOMPONENTINFO = 7, DXGKQAITYPE_PREFERREDGPUNODE = 8, #endif #if (DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WDDM1_3) DXGKQAITYPE_POWERCOMPONENTPSTATEINFO = 9, DXGKQAITYPE_HISTORYBUFFERPRECISION = 10 #endif } DXGK_QUERYADAPTERINFOTYPE;
WDDM kmd ドライバ エントリポイント – クエリアダプタ
•
では、
DxgkDdiQueryAdapterInfo とはどのようなものか?
NTSTATUS APIENTRY DxgkDdiQueryAdapterInfo( HANDLE hAdapter, DXGKARG_QUERYADAPTERINFO *pQueryAdapterInfo ) { ... } typedef struct _DXGKARG_QUERYADAPTERINFO { DXGK_QUERYADAPTERINFOTYPE Type; VOID *pInputData; UINT InputDataSize; VOID *pOutputData; UINT OutputDataSize; } DXGKARG_QUERYADAPTERINFO;WDDM kmd ドライバ エントリポイント – クエリアダプタ
•
興味深いエントリ- イグジットポイントを持っている
•
エントリポイント:
–
ユーザーランドを経由してきたデータ
–このための最も興味深い型は
DXGKQAITYPE_UMDRIVERPRIVATE で
ある
•
イグジットポイント:
– ユーザーからカーネルに巨大な構造体を返すクエリ API が存在しており、情報が漏 洩する可能性がある •構造体がスタック
/ヒープ領域にあり、かつ memsetが行われておら
ず、かつ
1 つ以上のメンバが初期化されていないとき(e.g. NULL 終
端文字列)
WDDM kmd ドライバ エントリポイント – クエリアダプタ
•
どのようにしてユーザーランドから通信する?
NTSTATUS D3DKMTQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *pData ); typedef struct _D3DKMT_QUERYADAPTERINFO { D3DKMT_HANDLE hAdapter; KMTQUERYADAPTERINFOTYPE Type; VOID *pPrivateDriverData; UINT PrivateDriverDataSize; } D3DKMT_QUERYADAPTERINFO;WDDM kmd ドライバ エントリポイント – 最善の方法
•
境界外の読み取り
← 極めて一般的
– これはカーネルでのブルースクリーンを意味する – 境界外のたった 1 バイトを読み取った場合でも発生しうる•
デバッグコードを出荷するな
– DbgPrint の除去 – および周辺のコード (e.g. 書式指定文字列によって出力されうるデータ) • DbgPrint を除去したところで、周辺のコードはバイナリに含まれるため、悪 用されうるバグが含まれてしまうかもしれない – #ifdef debug の除去
•
カーネル用の安全な整数ライブラリ関数を用いること
(e.g. RtlUIntAdd)
– 我流のコードを書かないように…これらと通信するためのユーザーランドで完結したプログラム
•
少しばかり見た目より難しい
•
API は MSDN で文書化されており、 gdi32.dll からエクスポートされている
•
データ構造についても
MSDN で文書化されている
•
OpenGL ICD (インストール可能なクライアントドライバ) ドライバのためのもの
– これらのためのヘッダが存在しない: • LoadLibrary/GetProcAddress が必要となる • 開発キットはあるものの…MSDN は述べている:「OpenGL ICD 開発キット のライセンスを得る際にはOpenGL Issues に連絡するよう 注意してくださ い」 • 文書化されていることを考えると、動作している(部分的な)実装を把握する ことは容易い • あるいは COM API を呼ぶこともできる ☺プライベートデータの探索と窃取
•
umd から kmd に送信されるデータは標準化されておらず、ドライバの実装に
依存するため、正常系におけるデータの送信を解析することによって、プロトコ
ルの構造を推測する必要がある
•
任意のドライバがどのようなプロトコルで通信しているか知るためには
•
API フック:
– D3DKMTEscape – D3DKMTRender – D3DKMTCreateAllocation – D3DKMTQueryAdapterInfoプライベートデータの探索と窃取
•
ツール
/デモ
•
公開!
まとめ
•
ファジング
まとめ
– ファジング
•
ミューテーションファザー
•
送受信データの傍受(ドライバごとにテンプレートを作成)
•
テンプレートをもとに改変したデータを送信
•
ループ
•
リバースエンジニアリングとの連携
– If (embedded_len != PrivateDataSize) bail; – Checksums
まとめ
– リバースエンジニアリング
Bruce Dang 曰く、
「
Windows ドライバのリバースエンジニアリングを職業として捉えるな
らば、その工程の
90% は Windows の仕組みを理解することであり、残
り
10% はアセンブリコードを理解することだと言えるだろう」
WDDM kmd ドライバ – リバースエンジニアリング
•
先に示したように、あらゆるドライバは初期化の一環として、
DxgkInitialize() あ
るいは
DxgkInitializeDisplayOnlyDriver() を呼び出す
•
そしてコールバックテーブル
(DRIVER_INITIALIZATION_DATA) に渡す
•
逆アセンブラによる静的解析では、これらの関数への
call が発見できなかった
•
なぜなら、これらの関数はインライン展開されるからである
WDDM kmd ドライバ – リバースエンジニアリング
•
では、シンボルとともに、どのようなものか見てみよう
WDDM kmd ドライバ – リバースエンジニアリング
•
Dxgkrnl.sys のロード(これは既にロードされているべきである)
•
デバイスオブジェクトへのポインタを取得する
•
ioctl 0x230043 を発行 (video device, function 10, method neither,
FILE_ANY_ACCESS)
•
返された関数ポインタは、コールバック関数の登録に用いられる
•
関数ポインタの呼び出しには、
DRIVER_INITIALIZATION_DATA または
PKMDDOD_INITIALIZATION_DATA 構造体を引数として与える
WDDM kmd ドライバ – リバースエンジニアリング
•
テーブル自身はいくつかの異なった方法で作成
/記録される
– グローバル空間 – スタック上 – 特定の関数が DRIVER_INITIALIZATION_DATA に記録する•
あるいは
DxgkInitialize() を呼び出す前にローカルスタックに書き込まれたバッ
ファ
•
これらのコードの発見は非常に簡単である。これらの処理は、ドライバの早い
段階で行われるためである(通常は
DriverEntry() か、それが呼び出す関数)
•
このように見える傾向がある:
WDDM kmd ドライバ – リバースエンジニアリング
•
C 言語によって表現した構造体を示す:
•
IDA の逆アセンブルコードに構造体を反映させることで、名前マングリングが
容易になる
typedef struct _DRIVER_INITIALIZATION_DATA { ULONG Version; PDXGKDDI_ADD_DEVICE DxgkDdiAddDevice; PDXGKDDI_START_DEVICE DxgkDdiStartDevice; PDXGKDDI_STOP_DEVICE DxgkDdiStopDevice; PDXGKDDI_REMOVE_DEVICE DxgkDdiRemoveDevice; ... } DRIVER_INITIALIZATION_DATA, *PDRIVER_INITIALIZATION_DATA;