1. 程式人生 > >遮蔽系統熱鍵的方法總結

遮蔽系統熱鍵的方法總結

    在編寫程式的過程中,我們有時需要實現遮蔽作業系統一些熱鍵的功能,如(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);
}