大家知道,Ctrl+Alt+Del是Win2k/NT操作系統默認的系統登錄/注銷組合鍵序列,系統級別很高。在應用程序中,想要屏蔽掉該鍵序列的響應或得到這個“按下”事件,難度是相當大的。本文介紹了一種簡單易行的方法,實現在用戶登錄成功後,按下Ctrl+Alt+Del不再彈出“Windows安全”對話框。
關鍵詞:GINA(Graphical Identification aNd Authentication)
SAS(Secure Attention Sequence)
一.開發原理
首先介紹一下Winlogon。Windows 2000/NT有三種系統狀態:沒有用戶登錄狀態、用戶成功登錄狀態以及工作站鎖定狀態。Winlogon是Windows 2000/NT操作系統提供交互式登錄支持的組件。Winlogon有三個組成部分:可執行文件winlogon.exe,提供圖形界面認證功能的動態庫Gina Dll,以及一些網絡服務提供動態庫Network Provider Dll。參考模型如下:
winlogon.exe處理一些下層導出的接口函數,而認證策略是在Gina Dll中是獨立設計的。在系統啟動時,Gina Dll被winlogon.exe裝載。Microsoft提供了一個默認的Gina Dll——Winnt\system32\msgina.dll,提供了標准的用戶名、密碼認證模式。Gina Dll是可替換的,用戶可以設計自己的Gina Dll,以提供其他如智能卡、視網膜、指紋或其他一些認證機制。
開發自定義的Gina Dll。必須實現並導出與winlogon.exe交互的18個標准函數接口,包括WlxNegotiate、WlxInitialize、WlxLoggedOnSAS等(其他函數接口請參考Msdn)。其中WlxNegotiate是winlogon.exe調用的第一個接口函數,進行必要的版本判斷,隨後調用的是WlxInitialize,主要完成winlogon.exe特定版本的函數分派表向Gina Dll的傳遞。筆者還要說明的是WlxLoggedOnSAS函數,這個函數主要的功能是,當winlogon在登錄成功狀態下,接收到SAS事件,於是調用這個函數進行SAS事件的識別以及進行各事件的相應處理。
自定義Gina Dll的使用。比如開發的Gina Dll文件名為MyGina.dll。將該文件放到以下路徑:Winnt\system32。並修改注冊表,如下:
Key Name: \HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\ Winlogon
Value Name: GinaDLL
Value Type: [REG_SZ]
Value: MyGina.dll
重新啟動計算機MyGina.dll即投入使用。
二.應用實例
應用要求:在用戶登錄成功狀態下,按下Ctrl+Alt+Del時系統不再彈出“Widows安全”對話框。由於並不需要改變用戶名、密碼這種標准的認證模式,所以可以仍然使用msgina.dll中導出的函數接口,而對WlxLoggedOnSAS函數的實現進行必要的改變。
開發環境:Windows 2000,PII 400
開發工具:Microsoft Visual C++ 6.0
開發步驟:
1.新建項目,選擇MFC AppWizard(dll),項目名輸入為MyGina。按下“OK”後,選擇Regular DLL with MFC statically linked,按下“Finish”。
2.使用View->ClassWizard為CmyGinaApp增加InitInstance和ExitInstance兩個函數的覆蓋。注意在Stdafx.h中加入#include <Winwlx.h>。
3.由於要導入msgina.dll的接口函數,所以在MyGina.h中定義接口函數變量類型,如下:
typedef (WINAPI * NEGOTIATE) (DWORD,PDWORD);
typedef (WINAPI * INITIALIZE) (LPWSTR,HANDLE,PVOID,PVOID,PVOID *);
typedef (WINAPI * ACTIVATE_USHELL) (PVOID,PWSTR,PWSTR,PVOID);
typedef (WINAPI * PARAM_PVOID) (PVOID);
typedef (WINAPI * DISP_STATUS) (PVOID,HDESK,DWORD,PWSTR,PWSTR);
typedef (WINAPI * GET_STATUS) (PVOID,DWORD *,PWSTR,DWORD);
typedef (WINAPI * LOGON_SAS) (PVOID,DWORD,PVOID);
typedef (WINAPI * LOGOUT_SAS) (PVOID,DWORD,PLUID,PSID,PDWORD, PHANDLE,WLX_MPR_NOTIFY_INFO,PVOID *);
typedef (WINAPI * NETWORK_LOAD) (PVOID,PWLX_MPR_NOTIFY_INFO);
typedef (WINAPI * SCR_SAVER) (PVOID,BOOL *);
typedef (WINAPI * SHUT_DOWN) (PVOID,DWORD);
typedef (WINAPI * START_APP) (PVOID,PWSTR,PVOID,PWSTR);
typedef (WINAPI * LOCKED_SAS) (PVOID,DWORD);
並在類CmyGinaApp中定義成員變量,如下:
private:
HMODULE hMsDll;
public:
NEGOTIATE MyWlxNegotiate;
INITIALIZE MyWlxInitialize;
ACTIVATE_USHELL MyWlxActivateUserShell;
PARAM_PVOID MyWlxDisplayLockedNotice;
PARAM_PVOID MyWlxDisplaySASNotice;
DISP_STATUS MyWlxDisplayStatusMessage;
GET_STATUS MyWlxGetStatusMessage;
PARAM_PVOID MyWlxIsLockOk;
PARAM_PVOID MyWlxIsLogoffOk;
LOGON_SAS MyWlxLoggedOnSAS;
LOGOUT_SAS MyWlxLoggedOutSAS;
PARAM_PVOID MyWlxLogoff;
NETWORK_LOAD MyWlxNetworkProviderLoad;
PARAM_PVOID MyWlxRemoveStatusMessage;
SCR_SAVER MyWlxScreenSaverNotify;
SHUT_DOWN MyWlxShutdown;
START_APP MyWlxStartApplication;
LOCKED_SAS MyWlxWkstaLockedSAS;
注意在MyGina.h中說明extern CMyGinaApp theApp;以便於程序其他地方對theApp的引用。
4.在MyGina.cpp中,實現InitInstance如下:
// 得到默認的gina dll
if (hMsDll == NULL)
{
hMsDll = ::LoadLibrary("msgina.dll");
}
// 導入各個接口函數
if (hMsDll != NULL)
{
MyWlxNegotiate = (NEGOTIATE) GetProcAddress(hMsDll,"WlxNegotiate");
MyWlxInitialize = (INITIALIZE) GetProcAddress(hMsDll,"WlxInitialize");
MyWlxActivateUserShell=(ACTIVATE_USHELL) GetProcAddress(hMsDll,"WlxActivateUserShell");
MyWlxDisplayLockedNotice=(PARAM_PVOID) GetProcAddress(hMsDll,"WlxDisplayLockedNotice");
MyWlxDisplaySASNotice = (PARAM_PVOID) GetProcAddress(hMsDll,"WlxDisplaySASNotice");
MyWlxDisplayStatusMessage=(DISP_STATUS) GetProcAddress(hMsDll,"WlxDisplayStatusMessage");
MyWlxGetStatusMessage = (GET_STATUS) GetProcAddress(hMsDll,"WlxGetStatusMessage");
MyWlxIsLockOk = (PARAM_PVOID) GetProcAddress(hMsDll,"WlxIsLockOk");
MyWlxIsLogoffOk = (PARAM_PVOID) GetProcAddress(hMsDll,"WlxIsLogoffOk");
MyWlxLoggedOnSAS = (LOGON_SAS) GetProcAddress(hMsDll,"WlxLoggedOnSAS");
MyWlxLoggedOutSAS = (LOGOUT_SAS) GetProcAddress(hMsDll,"WlxLoggedOutSAS");
MyWlxLogoff = (PARAM_PVOID) GetProcAddress(hMsDll,"WlxLogoff");
MyWlxNetworkProviderLoad=(NETWORK_LOAD)GetProcAddress(hMsDll,"WlxNetworkProviderLoad");
MyWlxRemoveStatusMessage=(PARAM_PVOID) GetProcAddress(hMsDll,"WlxRemoveStatusMessage");
MyWlxScreenSaverNotify = (SCR_SAVER) GetProcAddress(hMsDll,"WlxScreenSaverNotify");
MyWlxShutdown = (SHUT_DOWN) GetProcAddress(hMsDll,"WlxShutdown");
MyWlxStartApplication = (START_APP) GetProcAddress(hMsDll,"WlxStartApplication");
MyWlxWkstaLockedSAS = (LOCKED_SAS) GetProcAddress(hMsDll,"WlxWkstaLockedSAS");
}
實現ExitInstance如下:
// 卸載dll
if (hMsDll != NULL)
{
::FreeLibrary(hMsDll);
hMsDll = NULL;
}
5.實現接口函數。由於本應用仍然保持msgina.dll的大部分操作,所以MyGina.dll的接口函數的實現較為簡單。重點需要注意的是WlxLoggedOnSAS函數的實現。當在成功登錄狀態下,不管接收到什麼SAS事件,該函數直接返回WLX_SAS_ACTION_NONE而不做其他處理。由於實現的函數較多(必須的18個),筆者僅列出代表性的五個,其余的依理類推。
// Winlogon.exe調用的gina dll中的第一個函數
// 使gina dll確認是否支持當前版本的Winlogon.exe
// 傳遞給winlogon.exe需要那個版本的接口函數
BOOL WINAPI WlxNegotiate(DWORD dwWinLogonVersion, PDWORD pdwDllVersion)
{
// 直接調用從msgina.dll中導入的函數
return theApp.MyWlxNegotiate(dwWinLogonVersion,pdwDllVersion);
}
// 初始化,winlogon.exe向gina dll傳遞需要版本的接口函數分配表
BOOL WINAPI WlxInitialize(LPWSTR lpWinsta,
HANDLE hWlx,
PVOID pvReserved,
PVOID pWinlogonFunctions,
PVOID * pWlxContext
)
{
// 直接調用從msgina.dll中導入的函數
return theApp.MyWlxInitialize(lpWinsta,hWlx,pvReserved,pWinlogonFunctions,pWlxContext);
}
// 當系統處於鎖定狀態時,Winlogon.exe調用該函數
// 顯示一些信息,如鎖定者、鎖定時間等
VOID WINAPI WlxDisplayLockedNotice(PVOID pWlxContext)
{
theApp.MyWlxDisplayLockedNotice(pWlxContext);
}
// 在系統關閉之前,Winlogon.exe調用該函數
// 允許gina dll處理一些系統關閉前的處理
VOID WINAPI WlxShutdown(PVOID pWlxContext, DWORD ShutdownType)
{
theApp.MyWlxShutdown(pWlxContext,ShutdownType);
}
// 當系統處於登陸成功,沒有鎖定的狀態下
// Winlogon接收到SAS事件,於是調用該函數
// 現屏蔽所有事件,直接返回
int WINAPI WlxLoggedOnSAS(PVOID pWlxContext,
DWORD dwSasType,
PVOID pReserved)
{
return WLX_SAS_ACTION_NONE;
}
6.將MyGina.dll中實現的所有接口函數,在MyGina.def中定義導出。