GINAによるCTRL+ALT+DELの無効化

注意:

サンプルプログラムの実行に失敗すると、Windowsが再起動しなくなる恐れがある。そのような事態に陥った場合、あらかじめレジストリをバックアップしておきそれによって復旧を行なうか、またはレジストリの変更に合わせてオリジナルのDLL(msgina.dll)を複製すればこれがGINAとして使用される。いずれにせよ再インストール覚悟で試して頂きたい。

なお、以下の記述はNT4.0SP5に基づいている。

GINAとは:

Windowsにおいて通常作業を行なっている時に表示されている画面は、「アプリケーションデスクトップ」と呼ばれている。Windowsにはその他に二つのデスクトップが存在する。一つ目はログオン画面の表示が行われたり、CTRL+ALT+DELの押下によってアプリケーションデスクトップから切り替わって表示されるデスクトップで、「Winlogonデスクトップ」と呼ばれる。二つ目はスクリーンセーバーを表示するためのデスクトップであり、「スクリーンセーバーデスクトップ」と呼ばれる。システムは、起動時にこれら三つのデスクトップを作成する。ただし、可視状態のデスクトップは常にそのうちの一つのみである。

Winlogonデスクトップは不正なアクセスから保護されている安全なデスクトップである。このため、ログイン時のパスワード入力や、パスワードの変更(ログオン後CTRL+ALT+DELによって表示されるダイアログ中の「パスワードの変更」ボタン)を行なうのに適している。

ユーザがアプリケーションデスクトップで作業を行なっている際にWinlogonデスクトップに移行するには、SAS(Secure Attention Sequence)と呼ばれる手順を実行する。標準では、SASはCTRL+ALT+DELキーの押下として定められている。

Windowsにおけるログオンセッションなどの管理は“winlogon.exe”によって行なわれているが、特にSASの検出などの処理はwinlogonの中の“GINA(Graphical Identification and Authentication)”と呼ばれる部位が担当している。GINAは開発者が任意に置き換え可能であり、そのための仕様が公開されている。従ってSASの変更などのカスタマイズを行ないたい場合は新規にGINAを作成し、これをシステムに登録するという方法が正攻法となる。

GINAの指定方法:

GINAはDLLとして実装されている。標準のDLLは“msgina.dll”である。winlogonはレジストリで指定されているDLLをGINAとしてロードし、使用する。winlogonが使用するGINAは、具体的には以下のレジストリエントリによって指定される。

場所 \HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon
値の名前 GinaDLL
内容 WinlogonがGINAとして使用するDLLのパス(REG_SZ)。

GINAとして使用されるDLLは、定められた関数を実装し、これをエクスポートしていなければならない。

SAS発生時の動作:

標準のGINAでは、アプリケーションデスクトップにおけるSASは以下のように処理される。

GINAはSASの発生を常時監視している。ユーザがアプリケーションデスクトップにおいてCTRL+ALT+DELを押下した場合、GINAはwinlogon.exeのWlxSasNotify()を呼び出し、SASが入力されたことをwinlogonに伝える。winlogonは通知を受け取ったらwinlogonデスクトップを可視化して、GINAのWlxLoggedOnSas()を呼び出す。GINAは、winlogonデスクトップ上にダイアログボックスを表示してユーザに問い合わせを行なうなどして「次の状態」を決定し、戻り値の形でwinlogonに通知する。“次の状態”は、例えば「アプリケーションデスクトップに戻る」「ワークステーションをロックする」「ログオフ」「シャットダウン」「パスワードの変更」「タスクマネージャの起動」などが挙げられる。

CTRL+ALT+DELを無効化したい場合は、例えば新規に独自のSAS監視処理を実装し、アプリケーションデスクトップ上でのSAS発生時にはWlxSasNotify()を呼ばないようにする、などが考えられる。または、WlxLoggedOnSasが呼び出された際に、次の状態として「アプリケーションデスクトップに戻る」を指定する、といった手段が考えられる(ただし、その場合は一瞬winlogonデスクトップが表示されることになる)。本ドキュメントでは後者を例に挙げてサンプルを示す。

CTRL+ALT+DELを無効化するサンプル:

後述のソースはCTRL+ALT+DELを無効化するサンプルである。サンプルはVBで作成するEXEと、VCで作成するDLLからなる。EXEのソースはForm1.frm、DLLのソースはmygina.cとmygina.defである。DLLはGINAとして使用される。

サンプルを実行するにはEXEとDLLをコンパイル後DLLをシステムディレクトリに置き、「GINAの変更」の節で述べた通りに、作成したDLLのパスをレジストリに書き込む。以上が終わったらマシンを再起動する。再起動後EXEを実行し、Command1ボタンを押下するとCTRL+ALT+DELが無効化される。Command2ボタンを押下すると再び有効化される。

標準のGINAに対して動作を変更する箇所はわずかであるため、サンプルのGINAはその処理のほとんどを標準のGINAに任せている。実際、mygina.c中の記述のうちWlxNegotiate()とWlxLoggedOnSAS()以外の関数は単にそのまま標準のGINAを呼び出しているだけである。

GINAのWlxNegotiate()はwinlogon.exeによって最初に呼ばれる関数である。mygina.cではこの関数内で標準のGINAをロードしておき、他の関数が標準のGINAを呼び出せるようにするための準備を行なう。

実際にCTRL+ALT+DELの入力を無効化している箇所はWlxLoggedOnSAS()である。標準のGINAはCTRL+ALT+DELを検出するとwinlogonのWlxSasNotify()を呼び出し、SASを検出したことをwinlogonに通知する。この際、SASのタイプとしてWLX_SAS_TYPE_CTRL_ALT_DELという値を引数に渡す。winlogonは、現在の状態がログオン状態であるならば、winlogonデスクトップを可視化してからmygina.cのWlxLoggedOnSas()を呼び出す。この際、標準のGINAから渡されたSASのタイプがそのままWlxLoggedOnSas()に渡される。mygina.cのWlxLoggedOnSAS()は、SASのタイプがWLX_SAS_TYPE_CTRL_ALT_DELであり、かつ“mygina mutex”という名前のミューテックスオブジェクトが存在していたら、WLX_SAS_ACTION_NONEを戻り値として返却することによって、可視デスクトップをアプリケーションデスクトップに戻すようにwinlogonに指示する。

サンプルのEXEは、単に上記ミューテックスオブジェクトの作成・破棄を行なう。これによって、WlxLoggedOnSas呼び出し時にWLX_SAS_ACTION_NONEを返すかどうかの制御を行なう。

付記:

「SAS発生時の動作」の節では、SASの監視はGINAが行ない、SASを検出したらWlxSasNotify()を呼び出してSASの発生をwinlogonに通知する、と述べた。実際には、CTRL+ALT+DELの入力をSASとして採用するGINAのためにwinlogonは特別なサービスを提供しており、動作が異なる可能性がある。winlogonのWlxUseCtrlAltDel()を呼び出すと、以降CTRL+ALT+DELの入力が自動的にSASとみなされ、winlogon内で監視を行なってくれるようになる。WlxSasNotify()の呼び出しもwinlogonが面倒を見てくれるため、GINAはWlxSasNotify()を呼び出す必要がなくなる。標準のGINAはこれを利用している可能性が高い。

ソースコード:

-------------------------------- Form1.frm --------------------------------
Private Declare Function CreateMutex Lib "kernel32" Alias "CreateMutexA" ( _
    ByVal pSecurity As Long, ByVal fInitialOwner As Long, ByVal MutexName As String) As Long

Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private hMutex As Long

Private Sub Command1_Click()

    hMutex = CreateMutex(0, 0, "mygina mutex")

End Sub


Private Sub Command2_Click()

    CloseHandle hMutex

End Sub

-------------------------------- mygina.c --------------------------------
#include <windows.h>
#include <winwlx.h>

struct { 
	HMODULE hDll;
	BOOL (WINAPI *WlxNegotiate)();
	BOOL (WINAPI *WlxInitialize)();
	VOID (WINAPI *WlxDisplaySASNotice)();
	int (WINAPI *WlxLoggedOutSAS)();
	BOOL (WINAPI *WlxActivateUserShell)();
	int (WINAPI *WlxLoggedOnSAS)();
	VOID (WINAPI *WlxDisplayLockedNotice)();
	int (WINAPI *WlxWkstaLockedSAS)();
	BOOL (WINAPI *WlxIsLockOk)();
	BOOL (WINAPI *WlxIsLogoffOk)();
	VOID (WINAPI *WlxLogoff)();
	VOID (WINAPI *WlxShutdown)();
	BOOL (WINAPI *WlxScreenSaverNotify)();
	BOOL (WINAPI *WlxStartApplication)();
	BOOL (WINAPI *WlxNetworkProviderLoad)();
} MsGina;

BOOL WINAPI WlxNegotiate(DWORD dwWinlogonVersion, PDWORD pdwDllVersion)
{
	MsGina.hDll = LoadLibrary("msgina.dll");
	if(MsGina.hDll == NULL) return FALSE;

	MsGina.WlxActivateUserShell = GetProcAddress(MsGina.hDll, "WlxActivateUserShell");
	MsGina.WlxDisplayLockedNotice = GetProcAddress(MsGina.hDll, "WlxDisplayLockedNotice");
	MsGina.WlxDisplaySASNotice = GetProcAddress(MsGina.hDll, "WlxDisplaySASNotice");
	MsGina.WlxInitialize = GetProcAddress(MsGina.hDll, "WlxInitialize");
	MsGina.WlxIsLockOk = GetProcAddress(MsGina.hDll, "WlxIsLockOk");
	MsGina.WlxIsLogoffOk = GetProcAddress(MsGina.hDll, "WlxIsLogoffOk");
	MsGina.WlxLoggedOnSAS = GetProcAddress(MsGina.hDll, "WlxLoggedOnSAS");
	MsGina.WlxLoggedOutSAS = GetProcAddress(MsGina.hDll, "WlxLoggedOutSAS");
	MsGina.WlxLogoff = GetProcAddress(MsGina.hDll, "WlxLogoff");
	MsGina.WlxNegotiate = GetProcAddress(MsGina.hDll, "WlxNegotiate");
	MsGina.WlxScreenSaverNotify = GetProcAddress(MsGina.hDll, "WlxScreenSaverNotify");
	MsGina.WlxShutdown = GetProcAddress(MsGina.hDll, "WlxShutdown");
	MsGina.WlxStartApplication = GetProcAddress(MsGina.hDll, "WlxStartApplication");
	MsGina.WlxWkstaLockedSAS = GetProcAddress(MsGina.hDll, "WlxWkstaLockedSAS");

	return MsGina.WlxNegotiate(dwWinlogonVersion, pdwDllVersion);
}

BOOL WINAPI WlxInitialize(
	LPWSTR lpWinsta,
	HANDLE hWlx,
	PVOID pvReserved,
	PVOID pWinlogonFunctions,
	PVOID *pWlxContext)
{
	return MsGina.WlxInitialize(
		lpWinsta,
		hWlx,
		pvReserved,
		pWinlogonFunctions,
		pWlxContext);
}

VOID WINAPI WlxDisplaySASNotice(PVOID pWlxContext)
{
	MsGina.WlxDisplaySASNotice(pWlxContext);
}

int WINAPI WlxLoggedOutSAS(
	PVOID pWlxContext,
	DWORD dwSasType,
	PLUID pAuthenticationId,
	PSID pLogonSid,
	PDWORD pdwOptions,
	PHANDLE phToken,
	PWLX_MPR_NOTIFY_INFO pNprNotifyInfo,
	PVOID *pProfile)
{
	return MsGina.WlxLoggedOutSAS(
		pWlxContext,
		dwSasType,
		pAuthenticationId,
		pLogonSid,
		pdwOptions,
		phToken,
		pNprNotifyInfo,
		pProfile);
}

BOOL WINAPI WlxActivateUserShell(
	PVOID pWlxContext,
	PWSTR pszDesktopName,
	PWSTR pszMprLogonScript,
	PVOID pEnvironment)
{
	return MsGina.WlxActivateUserShell(
		pWlxContext,
		pszDesktopName,
		pszMprLogonScript,
		pEnvironment);
}

int WINAPI WlxLoggedOnSAS(PVOID pWlxContext, DWORD dwSasType, PVOID pReserved)
{
	HANDLE hMutex;

	if(dwSasType == WLX_SAS_TYPE_CTRL_ALT_DEL) {
		hMutex = OpenMutex(MUTANT_QUERY_STATE, FALSE, "mygina mutex");
		if(hMutex != NULL) {
			CloseHandle(hMutex);
			return WLX_SAS_ACTION_NONE;
		}
	}

	return MsGina.WlxLoggedOnSAS(pWlxContext, dwSasType, pReserved);
}

VOID WINAPI WlxDisplayLockedNotice(PVOID pWlxContext)
{
	MsGina.WlxDisplayLockedNotice(pWlxContext);
}

int WINAPI WlxWkstaLockedSAS(PVOID pWlxContext, DWORD dwSasType)
{
	return MsGina.WlxWkstaLockedSAS(pWlxContext, dwSasType);
}

BOOL WINAPI WlxIsLockOk(PVOID pWlxContext)
{
	return MsGina.WlxIsLockOk(pWlxContext);
}

BOOL WINAPI WlxIsLogoffOk(PVOID pWlxContext)
{
	return MsGina.WlxIsLogoffOk(pWlxContext);
}

VOID WINAPI WlxLogoff(PVOID pWlxContext)
{
	MsGina.WlxLogoff(pWlxContext);
}

VOID WINAPI WlxShutdown(PVOID pWlxContext, DWORD ShutdownType)
{
	MsGina.WlxShutdown(pWlxContext, ShutdownType);
}

BOOL WINAPI WlxScreenSaverNotify(PVOID pWlxContext, BOOL *pSecure)
{
	return MsGina.WlxScreenSaverNotify(pWlxContext, pSecure);
}

BOOL WINAPI WlxStartApplication(
	PVOID pWlxContext,
	PWSTR pszDesktopName,
	PVOID pEnvironment,
	PWSTR pszCmdLine)
{
	return MsGina.WlxStartApplication(pWlxContext, pszDesktopName, pEnvironment, pszCmdLine);
}

BOOL WINAPI WlxNetworkProviderLoad(PVOID pWlxContext, PWLX_MPR_NOTIFY_INFO pNprNotifyInfo)
{
	return MsGina.WlxNetworkProviderLoad(pWlxContext, pNprNotifyInfo);
}

-------------------------------- mygina.def --------------------------------
EXPORTS
	WlxActivateUserShell
	WlxDisplayLockedNotice
	WlxDisplaySASNotice
	WlxInitialize
	WlxIsLockOk
	WlxIsLogoffOk
	WlxLoggedOnSAS
	WlxLoggedOutSAS
	WlxLogoff
	WlxNegotiate
	WlxScreenSaverNotify
	WlxShutdown
	WlxStartApplication
	WlxWkstaLockedSAS
(original text:2000/05/18 更新)

本ドキュメントの内容は保証しません。本ドキュメントによって生じた結果について、一切の責任を負いません。