遮蔽系統熱鍵的方法總結
在編寫程式的過程中,我們有時需要實現遮蔽作業系統一些熱鍵的功能,如(ctrl+delete+alt,ctrl+shift+esc等)。網路上有很多關於這方面的資料,總結了一下,一般有如下兩個方法:
1. 通過載入低階鍵盤鉤子(WH_KEYBOARD_LL)截獲大部分的系統熱鍵,並遮蔽它。這個方法比較簡單,但有個缺陷,那就是對ctrl+alt+delete沒有辦法。
2. 通過遠端注入dll到winlogon程序,修改winlogon桌面下SAS視窗的回撥函式,從而捕獲該視窗的WM_HOTKEY訊息,並遮蔽它,可以實現遮蔽ctrl+delete+alt。這個方法相對複雜,可以解決第一點中存在的問題,但是也有缺陷,那就是除了ctrl+delete+alt外,大多數的其它系統熱鍵,(包括alt+tab,ctrl+esc及左右兩個windows鍵)都無法遮蔽。
所以如果我們的程式需要遮蔽大量的系統熱鍵,就應當將以上兩個方法結合起來使用。dll注入有很多好處,包括可以實現對我們所執行程序的隱藏,這非常有用。當我們的程序執行後,遮蔽掉了系統熱鍵,當然不想使用者隨便在程序管理器裡面就kill掉,因此將以上兩個方法結合的辦法就是把程式碼通通寫到dll裡面,然後再一起注入到winlogon程序中。
對於上面所講的第二點,注入到winlogon程序,沒問題,可以很好的完成功能(網上有很多相關的文章及程式碼)。但對於第一點,如果在一個GUI程式中載入鉤子,也沒有問題。但現在載入的物件是winlogin程序,這個程序很特殊,它不是GUI程序,也不在系統應用程式所處的Default桌面下,因此在這個程序中載入鉤子,需要注意以下幾點:
1. 在需要注入的dll程式碼中的DLL_PROCESS_ATTACH後面開啟一個新執行緒,並在該執行緒中實現修改SAS視窗回撥函式的程式碼以及設定低階鍵盤鉤子。
2. 由於鉤子所在的執行緒為非GUI執行緒,因此,必須在該執行緒成功設定鉤子以後主動接收並分發收到的訊息,否則鉤子將不會鉤到任何訊息:
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
3. 由於該執行緒建立時預設與winlogon同屬一個桌面(winlogon桌面),而其它包括explorer.exe在內的GUI程式都處在Default桌面,在windows中規定程式只能獲得針對同一桌面上建立的視窗訊息。所以,要讓該執行緒能接收到使用者在Default桌面下操作所產生的訊息,必須在該執行緒中使用如下程式碼將它的桌面設定為Default桌面:
HDESK hDesk = OpenDesktop("Default",0,FALSE,MAXIMUM_ALLOWED);
SetThreadDesktop(hDesk);
CloseHandle(hDesk);
我的程式在解決了以上問題之後,能正確將所編寫的dll注入到winlogon程序,並在dll載入的時候開啟執行緒,設定鉤子,替換SAS視窗回撥函式。從而實現遮蔽了我所能想到的所有系統熱鍵。大家如果有任何問題或建議,歡迎在我的BLOG:http://blog.csdn.net/tabby 給我留言。
附:DLL的完整原始碼(注入該DLL到winlogon程序的原始碼大家可以在網上找到)
#define _WIN32_WINNT 0x0500 //Use WH_KEYBOARD_LL
#include <windows.h>
#include <stdio.h>
//SAS window控制代碼
HWND hSASWnd = NULL;
//原有SAS window回撥函式地址
FARPROC FOldProc = NULL;
//起遮蔽作用的新SAS window回撥函式
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//列舉所有窗體控制代碼的回撥函式
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);
//Dll所建立執行緒的控制代碼
HANDLE hThread = NULL;
//Dll所建立執行緒的ID
DWORD dwThreadId = 0;
//Dll所建立執行緒的執行緒函式
DWORD WINAPI ThreadFunc();
//_H鉤子控制代碼
HHOOK hHook = NULL;
//_H低階鍵盤鉤子回撥函式
LRESULT CALLBACK KeyboardProc(int,WPARAM,LPARAM);
//對外輸出字串
char szOutput[36];
BOOL APIENTRY DllMain(HANDLE hMoudle, DWORD dwReason, LPVOID lpReserved)
{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
sprintf(szOutput,"Dll成功加載於 %d 號程序。",GetCurrentProcessId());
OutputDebugString(szOutput);
//建立更替SAS window回撥函式的執行緒
if(FOldProc == NULL)
hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
break;
case DLL_PROCESS_DETACH:
sprintf(szOutput,"Dll成功解除安裝。",GetCurrentProcessId());
//MessageBox(NULL, szOutput, "ZZ", MB_ICONINFORMATION | MB_OK);
OutputDebugString(szOutput);
//恢復原有SAS window的回撥函式
if(FOldProc != NULL)
SetWindowLong(hSASWnd,GWL_WNDPROC,long(FOldProc));
//_H解除安裝低階鍵盤鉤子
if(hHook != NULL)
{
if(!UnhookWindowsHookEx(hHook))
{
OutputDebugString("Unhook failed..");
//__leave;
break;
}
OutputDebugString("鍵盤鉤子成功取消");
}
TerminateThread(hThread,1);
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
//Dll所建立執行緒的執行緒函式
DWORD WINAPI ThreadFunc()
{
//開啟Winlogon桌面
HDESK hDesk = OpenDesktop("Winlogon",0,FALSE,MAXIMUM_ALLOWED);
//列舉桌面所有窗體
EnumDesktopWindows(hDesk,(WNDENUMPROC)EnumWindowsProc,0);
//修改SAS window的回撥函式
if(hSASWnd != NULL)
{
FOldProc = (FARPROC)SetWindowLong(hSASWnd,GWL_WNDPROC,long(SASWindowProc));
}
CloseHandle(hDesk);
//_H同一桌面上程序之間只能傳送視窗訊息。無法跨程序與其他桌面傳送它們。
//_H同樣,Windows訊息是限制應用程式定義掛鉤。
//_H特定桌面中執行的程序掛鉤過程將〈〈只獲得針對同一桌面上建立視窗訊息。〉〉
//_H詳見http://support.microsoft.com/kb/171890/zh-cn
//_H所以,這裡必須設定鉤子所線上程的桌面為Default桌面
//_H才能使得鉤子所線上程能接收到Default桌面的訊息
hDesk = OpenDesktop("Default",0,FALSE,MAXIMUM_ALLOWED);
SetThreadDesktop(hDesk);
CloseHandle(hDesk);
//_H設定低階鍵盤鉤子,遮蔽非SAS window的熱鍵
//_H需要#define _WIN32_WINNT 0x0500
hHook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardProc,GetModuleHandle(NULL),0);
if (hHook == NULL)
{
OutputDebugString("Set hook failed..");
//__leave;
return 1;
}
OutputDebugString("鍵盤鉤子成功設定");
//_H在非GUI執行緒中使用訊息鉤子必須主動接收並分發收到的訊息
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
//列舉所有窗體控制代碼的回撥函式
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
char ClassBuf[128];
//獲得當前窗體的顯示文字
GetWindowText(hwnd,ClassBuf,sizeof(ClassBuf));
//在"Winlogon"桌面中查詢視窗"SAS window"。
if(strstr(ClassBuf,"SAS window")!=NULL)
{
//返回SAS window控制代碼
hSASWnd = hwnd;
return FALSE;
}
return TRUE;
}
//起遮蔽作用的新SAS window回撥函式
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if(uMsg == WM_HOTKEY)
{
//遮蔽所有WM_HOTKEY訊息
OutputDebugString("All SAS window's hotkeys are disabled");
return 1;
WORD wKey = HIWORD(lParam);
WORD wModifier = LOWORD(lParam);
bool IsCtrlDown = ((wModifier & VK_CONTROL) != 0);
bool IsAltDown = ((wModifier & VK_MENU) != 0);
bool IsShiftDown = ((wModifier & VK_SHIFT) != 0);
//Ctrl + Alt + Del組合鍵
if(IsCtrlDown && IsAltDown && wKey == VK_DELETE)
{
return 1; //遮蔽
}
//Ctrl + Shift + Esc組合鍵,這個組合鍵將顯示工作管理員,可根據需要是否遮蔽。
else if(IsCtrlDown && IsShiftDown && wKey == VK_ESCAPE)
{
// Do nothing
}
}
return CallWindowProc((WNDPROC)FOldProc,hwnd,uMsg,wParam,lParam);
}
//_H低階鍵盤鉤子回撥函式
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_KEYDOWN: case WM_SYSKEYDOWN:
//case WM_KEYUP: case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
if (p->vkCode == VK_F12)
{
//實現模擬按鍵程式碼
MessageBox(GetForegroundWindow(),"I'm in position..","ZZ",MB_OK);
}
//遮蔽ALT+TAB
else if ((p->vkCode == VK_TAB) && ((p->flags & LLKHF_ALTDOWN) != 0))
{
OutputDebugString("ALT+TAB is disabled");
return 1;
}
//遮蔽ALT+ESC
else if ((p->vkCode == VK_ESCAPE) && ((p->flags & LLKHF_ALTDOWN) != 0))
{
OutputDebugString("ALT+ESC is disabled");
return 1;
}
//遮蔽CTRL+ESC
else if ((p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
{
OutputDebugString("CTRL+ESC is disabled");
return 1;
}
//遮蔽CTRL+SHIFT+ESC,(SAS window中也已遮蔽)
else if ((p->vkCode == VK_ESCAPE) &&
((GetKeyState(VK_CONTROL) & 0x8000) != 0) &&
((GetKeyState(VK_SHIFT) & 0x8000) != 0))
{
OutputDebugString("CTRL+SHIFT+ESC is disabled");
return 1;
}
//遮蔽左右windows鍵
else if (p->vkCode == VK_LWIN || p->vkCode == VK_RWIN)
{
OutputDebugString("windows key is disabled");
return 1;
}
//此處無法遮蔽CTRL+ALT+DEL,已在SAS window中遮蔽
else if ((p->vkCode == VK_DELETE) &&
((GetKeyState(VK_CONTROL) & 0x8000) != 0) &&
((GetKeyState(VK_MENU) & 0x8000) != 0 ))
return 1;
break;
}
}
return CallNextHookEx(hHook,nCode,wParam,lParam);
}