明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

Windows2000平臺下混合編程屏蔽鍵盤事件

[摘要]關(guān)鍵詞Windows2000, VC++, C++Bulider,  Visual Basic, HOOK, DLL 引言在一些應(yīng)用場合,比如基于Windows2000(以下簡稱Win2K)下開發(fā)工...
關(guān)鍵詞

Windows2000, VC++, C++Bulider,  Visual Basic, HOOK, DLL



引言

在一些應(yīng)用場合,比如基于Windows2000(以下簡稱Win2K)下開發(fā)工控軟件需要,為了增強系統(tǒng)安全性,需要對鍵盤事件進(jìn)行監(jiān)控、屏蔽。滿足控制系統(tǒng)安全性要求。作為一個Win2K后臺監(jiān)控軟件的編寫,需要注意如下要點:HOOK(鍵盤掛鉤函數(shù)),DLL,MsgINA.dll,Shell_NotifyIcon(托盤函數(shù))。為了提高軟件編寫效率,可以采用混合編程方式,即采用VC++/ C++Bulider 6.0編寫DLL文件,采用Visual Basic編寫客戶端程序。



1 HOOK與DLL簡介

1) HOOK

HOOK是一種反調(diào)函數(shù)。是Windows系統(tǒng)為應(yīng)用程序提供用于監(jiān)控系統(tǒng)各種事件消息的類中斷程序。在系統(tǒng)消息機制里掛上用戶自定義消息處理鉤子(HOOK),達(dá)到對消息的過濾。Windows系統(tǒng)本身提供數(shù)個HOOK函數(shù),為實現(xiàn)在Win2K/NT平臺下的鍵盤屏蔽,要采用低級鍵盤HOOK,即WH_KEYBOARD_LL。此HOOK函數(shù)可以屏蔽Ctrl+Esc、Alt+Tab、和Alt+Esc等系統(tǒng)功能鍵,在WINNT SP3后的操作系統(tǒng)中都是支持的。設(shè)置HOOK需要用到SetWindowsHookEx()函數(shù),在程序退出后,必須用UnhookWindowsHookEx()函數(shù)卸載掉HOOK。



2) DLL與Msgina.dll

DLL(動態(tài)鏈結(jié)庫)是Microsoft Windows最重要組成之一。大多數(shù)與Windows相關(guān)程序,不是程式模塊組模式,就是動態(tài)鏈結(jié)庫模式。為實現(xiàn)對所有鍵盤事件的監(jiān)控,必須將HOOK函數(shù)放在DLL文件中。

Windows本身就是由許多的DLL組成的,它所有的庫模塊也都設(shè)計成DLL。在Win2K在,為了屏蔽Ctrl+Alt+Del組合鍵,必須了解Msgina.dll。在Win2K系統(tǒng)中,微軟采用Winlogon和GINA-Graphical Identification and Authentication提供交互式登錄支持。登錄成功后,按下Ctrl+Alt+Del組合鍵,系統(tǒng)將通過Winlogon調(diào)用Msgina.dll內(nèi)部函數(shù)WlxLoggedOnSAS。所以要屏蔽Ctrl+Alt+Del組合鍵,則可以寫一個新的GINA.dll,其中提供接口調(diào)用Msgina.dll,從而實現(xiàn)屏蔽。



3) Shell_NotifyIcon

客戶端程序應(yīng)該運行在后臺,所以可以將其最小化在系統(tǒng)托盤中。采用Shell_NotifyIcon API函數(shù)用來添加、刪除、更改系統(tǒng)托盤區(qū)(taskbar status area)的圖標(biāo)。



2 程序?qū)崿F(xiàn)

在本文中,采用VC++6.開發(fā)系統(tǒng)GINA DLL, C++Bulider 6.0開發(fā)低層HOOK DLL,VB6.0開發(fā)客戶端程序,實現(xiàn)混合編程。



1) 自定義GINA編寫

因為自定義GINA編寫資料較多,本文只簡要介紹。自定義GINA可以采用VC++6.0開發(fā)。下面給出Windows2000 的Msgina內(nèi)部函數(shù)表。表中函數(shù)將在自定義GINA中導(dǎo)入。



函數(shù)名
說明

WlxActivateUserShell
激活用戶外殼程序

WlxDisPlayLockedNotice
允許GINA dll顯示鎖定信息

WlxDisPlaySASNotice
當(dāng)沒有用戶登錄時,winlogon調(diào)用此函數(shù)

WlxDisPlayStatusMessage
Winlogon用一個狀態(tài)信息調(diào)用此函數(shù)進(jìn)行顯示

WlxGetConsoleSwitchCredentials
Winlogon調(diào)用此函數(shù)讀取當(dāng)前登錄用戶的信任信息,并透明的將它們傳到目標(biāo)會話

WlxGetStatusMessage
Winlogon調(diào)用此函數(shù)獲取當(dāng)前狀態(tài)信息

WlxIntialize
針對指定的窗口位置進(jìn)行GINA dll初始化

WlxIsLockOk
驗證工作站正常鎖定

WlxIslogoffOk
驗證注銷正常

WlxLoggedOnSAS
用戶已登錄并且工作站沒有被加鎖,若此時接收到SAS事件,則Winlogon調(diào)用此函數(shù)

WlxLoggedOutSAS
沒有用戶登錄,若此時接收到SAS事件,則Winlogon調(diào)用此函數(shù)

WlxLogoff
請求注銷操作時通知GINA dll

WlxNegotiate
表示當(dāng)前的winlogon版本是否能使用GINA dll

WlxNetworkProviderLoad
在加載網(wǎng)絡(luò)服務(wù)提供程序收集了身份和認(rèn)證信息后,Winlogon調(diào)用此函數(shù)

WlxRemoveStatusMessage
Winlogon調(diào)用此函數(shù)告訴GINA dll停止顯示狀態(tài)信息

WlxScreensaverNotify
允許GINA與屏幕保護(hù)操作交互

WlxShutdown
在關(guān)閉之前Winlogon調(diào)用此函數(shù),允許GINA實現(xiàn)任何關(guān)閉任務(wù),例如從讀卡器中退出智能卡

WlxStartApplication
當(dāng)系統(tǒng)需要在用戶的上下文中啟動應(yīng)用程序時調(diào)用此函數(shù)

WlxWkstalockedSAS
當(dāng)工作站被鎖定,如果接收到一個SAS,則Winlogon調(diào)用此函數(shù)




我們需要注意的是WlxLoggedOnSAS函數(shù)。屏蔽Ctrl+Alt+Del組合鍵代碼將在調(diào)用該函數(shù)時添加。我們采用讀取注冊表鍵值來判斷是否屏蔽,而該鍵值將在客戶端程序中被操作。



// 當(dāng)系統(tǒng)處于登陸成功,沒有鎖定的狀態(tài)下

// Winlogon接收到SAS事件,于是調(diào)用該函數(shù)

int WINAPI WlxLoggedOnSAS(PVOID pWlxContext, DWORD dwSasType, PVOID pReserved)

{

HKEY hKey;

DWORD dwType=REG_DWORD; //定義讀取數(shù)據(jù)類型:雙字節(jié)

char content[4]; //所查詢注冊表鍵值的內(nèi)容

DWORD dwLength=4;

//打開注冊表鍵

if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\FCSKBLock\\KBConfig",

0,KEY_READ,&hKey)

==ERROR_SUCCESS)

{ //讀取CtrlAltDel鍵值

if(RegQueryValueEx(hKey,"CtrlAltDel",NULL,&dwType,(unsigned char *)content,&dwLength)

==ERROR_SUCCESS)

{

if(* content==1)

return WLX_SAS_ACTION_NONE;//直接返回桌面程序,實現(xiàn)屏蔽

}

}

return theApp.MyWlxLoggedOnSAS(pWlxContext,dwSasType,pReserved ) ;

}





開發(fā)完成的自定義GINA.dll要放到Wint\system32文件夾中。并修改注冊表:

鍵項名
\HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Winlogon

子鍵名
myGina(任意名稱均可)

子鍵類型
[REG_SZ]

子鍵值
myGina(自定義Gina的名稱)


若GINADLL不存在,新建即可。

再重啟計算機后myGina即為系統(tǒng)使用。



2) 全局HOOK、DLL編寫

采用BORLAND C++Bulider 6.0(以下簡稱BCB)編寫安裝全局HOOK的DLL文件。BCB是一款優(yōu)秀的C/C++語言開發(fā)工具,可以快速開發(fā)高質(zhì)量的Windows程序。下面介紹簡要步驟:

I. 利用BCB新建向?qū),建立一個DLL工程。在此DLL中我們將有條件的安放兩個HOOK。一個用于捕獲系統(tǒng)功能熱鍵并屏蔽,另一個用作客戶端程序的激活熱鍵;



II. 在cpp里添加如下代碼:



此段代碼用于申明全局變量和導(dǎo)出函數(shù)。因為此DLL文件將被VB編寫的客戶端程序所調(diào)用,所以聲明導(dǎo)出函數(shù)時需要將語句extern ”C” 放置在聲明處。另外在BCB中默認(rèn)的調(diào)用約定為__cdecl方式,而在VB中調(diào)用約定為__stdcall。

pragma argsused

//下面變量用于HOOK.cpp

static HHOOK hOldHook=0;/*記錄上一個注冊的鍵盤鉤子*/

static HHOOK hOldHook2=0;/*記錄上一個注冊的鍵盤鉤子*/

static HWND hProcWnd=0; /*記錄客戶程序的窗體*/

static HANDLE hInstance=0;/*DLL的句柄*/

//導(dǎo)出setHotKey

extern "C" __declspec(dllexport) char _stdcall ActivateKey(HWND hWnd,bool nCode,bool bWhich);



int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)

{

//用全局變量保存這個DLL的句柄

hInstance=hinst;

return 1;

}




因為客戶端程序是作為后臺運行的,所以我們需要給其安放個激活熱鍵,以便用戶在任何情況下通過熱鍵呼出。所以必須通過DLL文件安放一個全局HOOK,用作激活熱鍵。當(dāng)用戶按下激活熱鍵后,DLL會截獲消息并向指定的客戶端程序發(fā)送激活消息。

//客戶端程序熱鍵---------------------------------------------------------------------------

LRESULT CALLBACK HotKeyProc(int nCode,WPARAM wParam,LPARAM lParam)

{

bool fEatKeystroke = FALSE;

//PKBDLLHOOKSTRUCT p = NULL;

if (nCode == HC_ACTION) {

switch (wParam)

{

case WM_KEYDOWN: case WM_SYSKEYDOWN:

case WM_KEYUP: case WM_SYSKEYUP:

PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;

fEatKeystroke =

((p->flags & LLKHF_ALTDOWN) != 0)&& (p->vkCode==VK_F12);//自定義激活//熱鍵: Alt+F12

break;

}

if(fEatKeystroke)

SendMessage(hProcWnd,WM_USER+200,2000,0); //用于激活客戶程序的自定義消息

}

return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam,

lParam));

}






此處為捕獲、屏蔽系統(tǒng)功能熱鍵的回調(diào)函數(shù),用戶可根據(jù)需要添加修改需要屏蔽的按鍵。

//屏蔽Ctrl+Esc/Alt+Tab/Win/F1/Alt+Esc等功能按鍵------------------------------------------------------------------

LRESULT CALLBACK ShieldKeyProc(int nCode,WPARAM wParam,LPARAM lParam)

{

bool fEatKeystroke = FALSE;

//PKBDLLHOOKSTRUCT p = NULL;

if (nCode == HC_ACTION)

{

switch (wParam) {

case WM_KEYDOWN: case WM_SYSKEYDOWN:

case WM_KEYUP: case WM_SYSKEYUP:

PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;

fEatKeystroke =

(p->vkCode==VK_F1) //F1

((p->vkCode == VK_TAB) && ((p->flags & LLKHF_ALTDOWN) != 0)) //Alt+Tab

((p->vkCode == VK_ESCAPE) && ((p->flags & LLKHF_ALTDOWN) != 0)) //Alt+Esc

((p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0)) //Ctrl+Esc

(((GetKeyState(VK_CONTROL) & 0x8000) != 0) && (p->vkCode == VK_SPACE)) //Ctrl+Space

(((GetKeyState(VK_CONTROL) & 0x8000) != 0) && ((GetKeyState(VK_SHIFT) & 0x8000) != 0));

break;

}

}

return(fEatKeystroke ? 1 : CallNextHookEx(NULL, nCode, wParam,

lParam));

}




這個函數(shù)負(fù)責(zé)根據(jù)客戶端調(diào)用參數(shù),向系統(tǒng)注冊、注銷HOOK。HOOK必須在不要的時候卸載!

//HWND hWnd:客戶端程序調(diào)用窗體的句柄,bool nCode:掛還是不掛HOOK的標(biāo)志,bool bWhich:掛哪個HOOK的標(biāo)志

char _stdcall ActivateKey(HWND hWnd,bool nCode,bool bWhich)

{

if (bWhich)

{

if(nCode) // 安放底層HOOK

{

hProcWnd=hWnd;//記錄下這一個DLL是由哪個窗體調(diào)用的

hOldHook=SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)ShieldKeyProc,hInstance,0); //記錄下上一個DLL是由哪個窗體調(diào)用的

return(hOldHook != NULL? 1: 0 );

}

else // 卸下HOOK

UnhookWindowsHookEx(hOldHook);

}

else

{

if(nCode) // 安放HotHooK

{

hProcWnd=hWnd;//記錄下這一個DLL是由哪個窗體調(diào)用的

hOldHook2=SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)HotKeyProc,hInstance,0); //記錄下上一個DLL是由哪個窗體調(diào)用的

return(hOldHook2 !=NULL ? 1: 0);

}

else // 卸下HOOK

UnhookWindowsHookEx(hOldHook2);

}

return true;




III. 以Release方式編譯保存。

IV. 關(guān)于DLL調(diào)試可以參見有關(guān)文檔。



3) 客戶端程序

微軟的 Visual Basic 因為其編寫Windows界面程序的方便、靈活而成為我們開發(fā)客戶端程序的首選。我們采用Visual Basic 6.0 中文企業(yè)版(以下簡稱VB)進(jìn)行開發(fā)。VB本身并不直接支持DLL文件的開發(fā),但提供了對DLL的調(diào)用功能。作為客戶端程序,就是實現(xiàn)用戶操作與程序調(diào)用DLL,API函數(shù)的轉(zhuǎn)換。下面介紹簡要步驟:



I. 工程建立

新建三個窗體.分別命名為:Form1,frmLogin,Dialog.
Form1作為主窗體界面布置如圖<1>:

<1>

第一項采用API函數(shù)屏蔽任務(wù)欄;

第二項通過操作注冊表,實現(xiàn)屏蔽Ctrl+Alt+Del組合鍵;

第三項通過調(diào)用開發(fā)的底層鍵盤HOOK DLL實現(xiàn)功能鍵的屏蔽。

密碼設(shè)置項用于客戶端程序激活需要密碼情況。

frmLogin作為用戶設(shè)置密碼后,重新激活的登錄窗體,如圖<2>:

<2>

Dialog則作為”密碼設(shè)置”窗體,如圖<3>:

<3>



II. 代碼流程:

本文給出主流程圖。
說明:

i. 因為軟件是基于Windows2000平臺,所以啟動后首先要判斷系統(tǒng)平臺;

ii. 考慮系統(tǒng)安全性,程序要檢查是否已有遠(yuǎn)行實例;

iii. 因為要接受DLL文件發(fā)送的激活消息,所以可以在窗體加載事件中通過SetWindowLong函數(shù)在VB消息序列中添加自定義消息過濾函數(shù)。



SetWindowLong語法:

SetWindowLong (hwnd, GWL_WNDPROC, AddressOf SysMenuProc)
hwnd:當(dāng)前窗體的句柄

GWL_WNDPROC:設(shè)置一個新的窗口消息處理過程的地址

AddressOf SysMenuProc :取新的窗口消息處理過程名稱

返回值代表前個窗體消息處理過程。



SysMenuProc 函數(shù)是個回調(diào)函數(shù)。必須聲明定義在標(biāo)準(zhǔn)模塊中。

iv. 程序最小化在系統(tǒng)托盤區(qū)編程利用Shell_NotifyIcon函數(shù)。

Shell_NotifyIcon語法可以參見微軟的MSDN。添加系統(tǒng)托盤圖標(biāo)子程序放在窗體的Resize事件中。程序在退出時必須刪除圖標(biāo)。