EternalWindows
GINA / GINAフック

GINAがメッセージボックスを表示する必要があるような場合は、 できるだけディスパッチテーブルのWlxMessageBoxを使うことが推奨されますが、 そのような傾向は何もメッセージボックスに限ったことではありません。 ディスパッチテーブルには、ダイアログの表示をサポートするWlxDialogBoxや WlxDialogBoxParamといった関数も用意されているため、ダイアログの表示に関しては、 これらの関数を呼び出すように意識しなければなりません。 現に、スタブが提供しているダイアログは全てWlxDialogBoxParamを通じて表示されており、 今回提案するGINAフックという方法もこのディスパッチテーブルの仕組みを利用しています。 具体的には、WlxDialogBoxParamに格納されているアドレスをオリジナルのGINAが用意した自作関数 のアドレスに書き換え、スタブによるWlxDialogBoxParamの呼び出しを監視しようという狙いです。 WlxDialogBoxParamは、次のように定義されています。

int WlxDialogBoxParam(
  HANDLE hWlx,          
  HANDLE hInst,         
  LPWSTR lpszTemplate,  
  HWND hwndOwner,       
  DLGPROC dlgprc,       
  LPARAM dwInitParam    
);

基本的に関数の呼び出しを監視することで得られるものは、その関数に渡された引数を 確認したり、独自に用意した引数を変わりに指定するぐらいのことですが、 WlxDialogBoxParamにはダイアログプロシージャという大変重要な引数があります。 このプロシージャには、そのダイアログの全てのメッセージが送られるわけですから、 オリジナルのGINAが用意した関数のアドレスを代わりのプロシージャとして指定すれば、 そのダイアログのメッセージを完全にフックすることができるようになります。 これにより、オリジナルのGINAはスタブによるエクスポート関数の処理結果のみならず、 ダイアログプロシージャでの処理結果をも確認できるようになります。

それでは、具体的な手順を見ていきましょう。 まず、最初にすべきことはWlxDialogBoxParamのアドレスを書き換えることです。 書き換える関数はMyWlxDialogBoxParamとし、WlxInitializeで設定することにします。

BOOL WINAPI WlxInitialize(LPWSTR lpWinsta, HANDLE hWlx, PVOID pvReserved, PVOID pWinlogonFunctions, PVOID *pWlxContext)
{
	g_hWlx = hWlx;
	g_pDispatchTable = (PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions;

	g_lpfnDefWlxDialogBoxParam = pDispatchTable->WlxDialogBoxParam;
	g_pDispatchTable->WlxDialogBoxParam = MyWlxDialogBoxParam;

	return g_lpfnWlxInitialize(lpWinsta, hWlx, pvReserved, pWinlogonFunctions, pWlxContext);
}

lpfnDefWlxDialogBoxParamに、元のWlxDialogBoxParamのアドレスを保存しているのが重要です。 オリジナルのGINAがWlxDialogBoxParamの呼び出しを監視する理由は、 独自のダイアログプロシ−ジャを指定してWlxDialogBoxParamを呼ぶことにありますから、 どのみちWlxDialogBoxParamのアドレスは必要になるのです。

MyWlxDialogBoxParamは、全てのダイアログの呼び出しを監視しているため、 少なくとも開発者は、そのダイアログがどのようなものであるかは特定しなければなりません。 単純に独自の関数をダイアログプロシージャの代わりと指定するだけでは、 フックするつもりのないダイアログのプロシージャも置き換えてしまうことになるため、 メッセージ処理やデータの整合性の面でも問題が起きやすくなってしまいます。 次の表は、筆者がWindowsXP Homeで調べたダイアログと識別子の関係です。

識別子 関連するダイアログ
1400 Ctrl + Alt + Del ダイアログ
1500 ログオンダイアログ
1700 パスワードダイアログ
1800 セキュリティダイアログ
1900 ディスプレイロックダイアログ
1950 ワークステーションロックダイアログ
2200 シャットダウンダイアログ
2250 ログオフダイアログ

たとえば、ログオンダイアログボックスのプロシージャを書き換えたい場合は、 ダイアログの識別子が1500かどうかを調べ、もしそうならばg_lpfnDefWlxDialogBoxParamの プロシージャ引数に独自の関数のアドレスを指定することになります。 ただ、ここで再三注意しておかなければならないのは、 このダイアログ識別子の値がWindowsのバージョンによって統一性がないということです。 上の表で示した識別子はあくまでWindowsXP上での話であり、 他のWindowsのバージョンでは上記のIDを頼りにするわけにはいきません。 また、一口にWindowsといっても、サービスパックのインストールの有無などもあるため、 先に示した表の内容を参考する場合は十分に注意してください。 次に、MyWlxDialogBoxParamのコードを示します。

int WINAPI MyWlxDialogBoxParam(HANDLE hWlx, HANDLE hInst, LPWSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc, LPARAM dwInitParam)
{
	int nDlgId = LOWORD(lpszTemplate);

	if (nDlgId == IDD_DIALOG) {
		g_lpfnDefDlgProc = dlgprc;
		return g_lpfnDefWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner, MyDlgProc, dwInitParam);
	}

	return g_lpfnDefWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner, dlgprc, dwInitParam);
}

この関数の引数は、WlxDialogBoxParamの引数と完全に一致しなければならないません。 WlxDialogBoxParamの第3引数はダイアログテンプレートと呼ばれ、 そこにはテンプレートを表す文字列かリソース識別子を指定できるようになっています。 どうやら、スタブでは全てのダイアログにおいてテンプレートを識別子で指定しているようなので、 識別子が下位ワードに格納されるという点を考慮し、LOWORDマクロで取得します。 また、識別子の上位ワードは常に0なるという決まりがあるため、 HIWORDマクロを利用してそれを確認するのもよいかもしれません。 取得した識別子が目的のダイアログの識別子と一致したら、 まずは、そのダイアログの本来のプロシージャのアドレス変数に保存します。 独自のプロシージャで行うことは基本的にメッセージの監視ですから、 メッセージの処理自体は本来のプロシージャに任せなければなりません。 そして、g_lpfnDefWlxDialogBoxParamの呼び出しでは、 独自のプロシージャを指定するために第5引数のみを変更しています。

BOOL WINAPI MyDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	// 必要のあるメッセージを捕らえて処理する。

	default:
		break;
	
	}

	return g_lpfnDefDlgProc(hwndDlg, uMsg, wParam, lParam);
}

自分にとって必要なメッセージを捕らえるというスタイルは通常の ダイアログプロシージャと変わりませんから、特に難しいところはないと思われます。 また、本来のダイアログプロシージャに処理をさせたくないような場合は、 一般のダイアログプロシージャの戻り値に則ってTRUEを返すとよいでしょう。 次節は、GINAフックを利用した簡単なプログラムを紹介します。


戻る