なお、以下の記述はNT4.0SP5に基づいている。
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を作成し、これをシステムに登録するという方法が正攻法となる。
場所 | \HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon |
値の名前 | GinaDLL |
内容 | WinlogonがGINAとして使用するDLLのパス(REG_SZ)。 |
GINAとして使用されるDLLは、定められた関数を実装し、これをエクスポートしていなければならない。
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デスクトップが表示されることになる)。本ドキュメントでは後者を例に挙げてサンプルを示す。
サンプルを実行するには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を返すかどうかの制御を行なう。
-------------------------------- 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