查看原文
其他

Win7 x64鼠标键盘锁(SDK)

Thvoifar 看雪学院 2019-05-26

曾经,学习孙鑫老师的《VC++深入详解》时,接触到了钩子HOOK原理,一度对HOOK技术特别感兴趣,便自然而然产生了实现“鼠标键盘锁”的想法。通过查资料和尝试,基本实现了鼠标和键盘的屏蔽,当然,会被叁陆灵等杀软拦截,本人小白,大神勿喷!!!

       

开始写程序,安装全局钩子,WH_KEYBOARD和WH_MOUSE,运行,毫无疑问,被叁陆灵立马拦截,暂时退出叁陆灵的保护后,重新运行,鼠标可以移动,但是点击没有反应,键盘字母按键按下没反应,达到了一定的效果。然而,还是有问题,屏蔽了鼠标,但是键盘并没有完全“钩住”,包括Win+L、Ctrl+Alt+Del、WIN、 WIN+Tab、Ctrl+ESC、Ctrl+Shift+Esc、Ctrl+Alt+Tab、Ctrl+WIN+Tab、WIN+U WIN+D、WIN+E等等系统组合键都是可以用的。于是接着查资料,如何屏蔽系统键,通过底层钩子WH_KEYBOARD_LL可以屏蔽除了Ctrl+Alt+Del和Win+L的其他系统键,通过底层钩子WH_MOUSE_LL可以截获整个系统的鼠标事件。在DLL中设置共享节,保存调用进程的窗口自句柄,则在屏蔽了鼠标键盘后通过按下自己设置的组合键(如Alt+Q)后,用SendMessage向主进程发送WM_CLOSE消息实现退出。关于动态链接库的相关知识不是本文重点,在此不多赘述。KeymsLockHook.dll代码实现:


全局变量和初始化

HHOOK g_hMouse = NULL;// 鼠标钩子句柄
HHOOK g_hLowlevelMouse = NULL; // 底层鼠标钩子句柄
HHOOK g_hKeyboard = NULL;// 键盘钩子句柄
HHOOK g_hLowlevelKeyboard = NULL;// 底层键盘钩子句柄
#pragma data_seg("SharedSec")// 设置共享节
HWND g_hWnd = NULL;// 传递调用进程的主窗口句柄
#pragma data_seg()


钩子DLL中导出函数:

extern "C" _declspec(dllexport) BOOL __stdcall SetKeymsHook(HWND hwnd)
{
   g_hWnd = hwnd;
   HMODULE hModule = GetModuleHandle(_T("KeymsLockHook.dll")); //获取动态链接库KeymsLockHook.dll模块句柄
   g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, hModule, 0);
   if (NULL == g_hMouse)
   {
       MessageBox(NULL, _T("安装鼠标钩子出错1!"), _T("error"), 0);
       return FALSE;
   }
   g_hLowlevelMouse=SetWindowsHookEx(WH_MOUSE_LL,LowLevelMouseProc, hModule, 0);
   if (NULL == g_hLowlevelMouse)
   {
       MessageBox(NULL, _T("安装鼠标钩子出错2!"), _T("error"), 0);
       return FALSE;
   }
   g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hModule, 0);
   if (NULL == g_hKeyboard)
   {
       MessageBox(NULL, _T("安装键盘钩子出错1!"), _T("error"), 0);
       return FALSE;
   }
   g_hLowlevelKeyboard = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hModule, 0);
   if (NULL == g_hLowlevelKeyboard)
   {
       MessageBox(NULL, _T("安装键盘钩子出错2!"), _T("error"), 0);
       return FALSE;
   }  
   return TRUE;
}


钩子过程函数体

LRESULT CALLBACK MouseProc(
   int code,       // hook code
   WPARAM wParam,  // virtual-key code
   LPARAM lParam   // keystroke-message information
)
{
   return 1;
}
LRESULT CALLBACK LowLevelMouseProc(
   int nCode,
   WPARAM wParam,
   LPARAM lParam
)
{
   return 1;
}
LRESULT CALLBACK KeyboardProc(
   int code,       // hook code
   WPARAM wParam,  // virtual-key code
   LPARAM lParam   // keystroke-message information
)
{
   return 1;
}
LRESULT CALLBACK LowLevelKeyboardProc(
   int nCode,     // hook code
   WPARAM wParam, // message identifier
   LPARAM lParam  // pointer to structure with message data
)
{
   PKBDLLHOOKSTRUCT pKey;
   pKey = (PKBDLLHOOKSTRUCT)lParam;
   // 屏蔽WIN CTRL ESC WIN+Tab Ctrl+ESC Ctrl+Shift+Esc Ctrl+Alt+Tab Ctrl+WIN+Tab WIN+U WIN+P WIN+X WIN+D WIN+E等键
   if (pKey->vkCode == VK_LWIN || pKey->vkCode == VK_RWIN || (GetAsyncKeyState(VK_CONTROL) & 0x8000) || (GetAsyncKeyState(VK_ESCAPE) & 0x8000))// 屏蔽WIN键,Ctrl+ESC组合键
   {
       return 1;
   }
   // 屏蔽Alt相关键 Alt+Tab
   if (pKey->flags & LLKHF_ALTDOWN) // Alt按下
   {
       if ('Q' == pKey->vkCode) // Alt+Q退出
       {
           UnhookWindowsHookEx(g_hMouse);
           UnhookWindowsHookEx(g_hLowlevelMouse);
           UnhookWindowsHookEx(g_hKeyboard);
           UnhookWindowsHookEx(g_hLowlevelKeyboard);
           SendMessage(g_hWnd, WM_CLOSE, 0, 0);
       }
       return 1;
   }
   return CallNextHookEx(g_hLowlevelKeyboard, nCode, wParam, lParam);
}


在调用主进程中,通过动态加载,调用以上DLL导出的函数SetKeymsHook安装全局钩子,创建互斥对象,仅允许主程序运行一个实例,调用程序主要代码实现:

typedef BOOL(__stdcall *HookFun)(HWND hwnd);//定义函数指针

HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
     CW_USEDEFAULT, 0, 0, 0, nullptr, nullptr, hInstance, nullptr);
HANDLE hMutex = CreateMutex(NULL, TRUE, _T("KeymsLock")); //只允许运行一个实例
if (hMutex)
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
return FALSE;
   }
}
HookFun hookfun = 0;
HMODULE hDll = LoadLibrary(_T("KeymsLockHook.dll"));
if (hDll)
{
hookfun = (HookFun)GetProcAddress(hDll, "SetKeymsHook");
if (!hookfun)
{
MessageBox(hWnd, _T("获取模块地址失败!"), _T("提示"), 0);
return FALSE;
}
}
else
{
   MessageBox(hWnd, _T("加载Dll失败!"), _T("提示"), 0);
   return FALSE;
}
if (!hookfun(hWnd))    return FALSE; // - 安装鼠标键盘钩子


至此,除了Ctrl+Alt+Del、Win+L组合键之外,其他能想到的都屏蔽了,对于这两个组合键的屏蔽,论坛中有位前辈很早之前就写了一篇文章,但是不知道为什么帖子被删了,还好有幸找到了:

https://blog.csdn.net/linfei2707/article/details/25237671


文章提到四种想法,其中第四种方法堪称完美,好像这也是不用驱动实现屏蔽这两个组合键的最佳方法了。若想看具体原理和调试过程方法,请看该前辈的帖子,笔者win7 64位系统winlogon.exe中主要部分如下:

; Win+L的ID为5、Ctrl+Shift+Esc的ID为4、Ctrl+Alt+Del的ID为0
.text:000000010001484C    83 FB 04                 cmp     ebx, 4
.text:000000010001484F    0F 84 18 2C 00 00          jz      loc_10001746D
.text:0000000100014855    83 FB 05                  cmp     ebx, 5
.text:0000000100014858    0F 84 60 2C 00 00          jz      loc_1000174BE
.text:000000010001485E    83 FB 06                  cmp     ebx, 6
.text:0000000100014861    0F 84 87 2C 00 00          jz      loc_1000174EE
.text:0000000100014867    83 FB 07                  cmp     ebx, 7
.text:000000010001486A    0F 84 B9 2C 00 00          jz      loc_100017529
.text:0000000100014870    83 FB 08                  cmp     ebx, 8
.text:0000000100014873    0F 84 DE 2C 00 00          jz      loc_100017557
.text:0000000100014879    83 FB 09                  cmp     ebx, 9
.text:000000010001487C    0F 84 03 2D 00 00          jz      loc_100017585
.text:0000000100014882    85 DB                    test    ebx, ebx
;  3B DB  ====>  cmp ebx, ebx
.text:0000000100014884    75 45                     jnz     short loc_1000148CB


其中,Win+L很好屏蔽,将cmp ebx,5中的立即数改为其他比较大的值即可实现;Ctrl+Alt+Del有两个语句

test ebx,ebx                           ;85 DB
jnz shrort loc_1000148CB               ;75 45


按下Ctrl+Alt+Del时,ebx=0;所以可以通过85改为3B,75改为74改动两个字节即可,按下Alt+Q退出时可以还原。

cmp ebx,ebx                            ;3B DB
jz shrort loc_1000148CB                  ;74 45

具体实现笔者用远线程注入到winlogon.exe中进行修改,远线程注入DLL中代码如下:

BOOL SetHotkeyStatus(BOOL bFlag) // TRUE => 恢复CAD WL为可用状态; FALSE => 禁用CAD WL
{
   const BYTE btWLOri = 0x5; // Win+L 的ID为5
   const BYTE btCADJnzOri = 0x75;
   const BYTE btWLMod = 0x55;
   const BYTE btCADJnzMod = 0x74;
   const BYTE btCADTstOri = 0x85;
   const BYTE btCADTstMod = 0x3b;

   HMODULE hWlg = GetModuleHandle(0);
   LPVOID lpAddrWL = (LPVOID)((LPSTR)hWlg + 0x14857);
   LPVOID lpAddrCadTst = (LPVOID)((LPSTR)hWlg + 0x14882);
   LPVOID lpAddrCadJnz = (LPVOID)((LPSTR)hWlg + 0x14884);

   BYTE btReadWL = *(BYTE*)lpAddrWL;
   BYTE btReadCadTst = *(BYTE*)lpAddrCadTst;
   BYTE btReadCadJnz = *(BYTE*)lpAddrCadJnz;

   DWORD dwOldProtect = 0;
   if (bFlag)
   {
       if (btWLMod == btReadWL && btCADJnzMod == btReadCadJnz && btCADTstMod==btReadCadTst) // - 将CAD WL置为可用状态
       {
           VirtualProtect(lpAddrWL, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
           *(BYTE*)lpAddrWL = btWLOri;
           VirtualProtect(lpAddrWL, 1, dwOldProtect, &dwOldProtect);

           VirtualProtect(lpAddrCadTst, 3, PAGE_EXECUTE_READWRITE, &dwOldProtect);
           *(BYTE*)lpAddrCadTst = btCADTstOri;
           *(BYTE*)lpAddrCadJnz = btCADJnzOri;
           VirtualProtect(lpAddrCadTst, 3, dwOldProtect, &dwOldProtect);

           return TRUE;
       }
   }
   else
   {
       if (btWLOri == btReadWL && btCADJnzOri == btReadCadJnz && btCADTstOri==btReadCadTst) // - 将CAD WL置为禁用状态
       {
           VirtualProtect(lpAddrWL, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
           *(BYTE*)lpAddrWL = btWLMod;
           VirtualProtect(lpAddrWL, 1, dwOldProtect, &dwOldProtect);

           VirtualProtect(lpAddrCadTst, 3, PAGE_EXECUTE_READWRITE, &dwOldProtect);
           *(BYTE*)lpAddrCadTst = btCADTstMod;
           *(BYTE*)lpAddrCadJnz = btCADJnzMod;
           VirtualProtect(lpAddrCadTst, 3, dwOldProtect, &dwOldProtect);

           return TRUE;
       }
   }
   return FALSE;
}

BOOL WINAPI DllMain(HMODULE hModule,
   DWORD  dwReason,
   LPVOID lpReserved
)
{
   g_hInst = hModule;
   switch (dwReason)
   {
       case DLL_PROCESS_ATTACH:
       {
           if (SetHotkeyStatus(FALSE))
           {
               MessageBeep(MB_ICONWARNING);
           }
           return TRUE;
       }
       case DLL_THREAD_ATTACH:
           return FALSE;
       case DLL_THREAD_DETACH:
           return FALSE;
       case DLL_PROCESS_DETACH:
       {
           if (SetHotkeyStatus(TRUE))
           {
               MessageBeep(MB_ICONINFORMATION);
           }
       }
       return FALSE;
   }
   return FALSE;
}


调用主程序中,远线程注入和卸载部分主要参考《Windows核心编程》相关,实现代码如下:  

HINSTANCE g_hInst;
BOOL WINAPI InjectLib(DWORD dwProcessId, PCTSTR pszLibFile)
{
   BOOL bOk = FALSE;
   HANDLE hProcess = NULL, hThread = NULL;
   PTSTR pszLibFileRemote = NULL;
   ULONG_PTR dwWritten;

   __try
   {
       BOOL bRet = EnableDebugPrivilege(TRUE);
       if (!bRet) __leave;
       hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
           PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId);
       if (NULL == hProcess)    __leave;
       SIZE_T nSize = (1 + lstrlen(pszLibFile)) * sizeof(TCHAR);
       pszLibFileRemote = (PTSTR)VirtualAllocEx(hProcess, NULL, nSize, MEM_COMMIT, PAGE_READWRITE);
       if (NULL == pszLibFileRemote)    __leave;
       if (!WriteProcessMemory(hProcess, pszLibFileRemote, pszLibFile, nSize, &dwWritten))    __leave;
       FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(_T("Kernel32.dll")), bUncd ? "LoadLibraryW" : "LoadLibrary");
       if (NULL == pfnThreadRtn)    __leave;
       hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnThreadRtn,
           pszLibFileRemote, 0, NULL);
       if (NULL == hThread) __leave;
       WaitForSingleObject(hThread, INFINITE);
       bOk = TRUE;
   }
   __finally
   {
       if (pszLibFileRemote != NULL)
       {
           VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);
       }
       if (hThread != NULL)
       {
           CloseHandle(hThread);
       }
       if (hProcess != NULL)
       {
           CloseHandle(hProcess);
       }
   }
   return bOk;
}

BOOL WINAPI EjectLib(DWORD dwProcessId, PCTSTR pszLibFile)
{
   BOOL bOk = FALSE;
   HANDLE hSnapshot = NULL;
   HANDLE hProcess = NULL, hThread = NULL;
   __try
   {
       hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
       if (INVALID_HANDLE_VALUE == hSnapshot)   __leave;
       MODULEENTRY32 me32 = { sizeof(me32) };
       BOOL bFound = FALSE;
       if (!Module32First(hSnapshot, &me32))
       {
           CloseHandle(hSnapshot);
           return 0;
       }
       do
       {
           bFound = (lstrcmpi(me32.szModule, pszLibFile) == 0) || (lstrcmpi(me32.szExePath, pszLibFile) == 0);
           if (bFound) break;
       } while (Module32Next(hSnapshot, &me32));
       if (!bFound)   __leave;
       hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
           PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId);
       if (NULL == hProcess)    __leave;
       FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(_T("Kernel32.dll")), "FreeLibrary");
       if (NULL == pfnThreadRtn)    __leave;
       hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnThreadRtn,
           me32.modBaseAddr, 0, NULL);
       if (NULL == hThread) __leave;
       WaitForSingleObject(hThread, INFINITE);
       bOk = TRUE;
   }
   __finally
   {
       if (hSnapshot != NULL)
       {
           CloseHandle(hSnapshot);
       }
       if (hThread != NULL)
       {
           CloseHandle(hThread);
       }
       if (hProcess != NULL)
       {
           CloseHandle(hProcess);
       }
   }
   return bOk;
}


获取进程PID子函数GetProId

DWORD GetProcId(LPCTSTR pszName) // 根据进程名获取进程PID
{
   PROCESSENTRY32 pe32;
   HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
   if (INVALID_HANDLE_VALUE == hSnapshot)   return 0;
   pe32.dwSize = sizeof(PROCESSENTRY32);
   if (!Process32First(hSnapshot, &pe32))
   {
       CloseHandle(hSnapshot);
       return 0;
   }
   do
   {
       if (lstrcmpi(pe32.szExeFile, pszName) == 0)
       {
           CloseHandle(hSnapshot);
           return pe32.th32ProcessID;
       }
   } while (Process32Next(hSnapshot, &pe32));
   CloseHandle(hSnapshot);
   return 0;
}


获取winlogon.exe的进程ID,得到注入DLL路径,远线程注入

dwProcessId = GetProcId(_T("winlogon.exe"));
if (!dwProcessId)
{
   MessageBox(hWnd, _T("获取winlogon进程PID失败"), _T("提示"), 0);
   return FALSE;
}
TCHAR szExePath[_MAX_PATH] = { 0 };
TCHAR drive[_MAX_DRIVE] = { 0 };
TCHAR dir[_MAX_DIR] = { 0 };
GetModuleFileName(NULL, szExePath, _MAX_PATH);
_tsplitpath_s(szExePath, drive, _MAX_DRIVE, dir, _MAX_DIR, NULL, 0, NULL, 0);
_tcscat_s(szDllPath, _MAX_PATH, drive);
_tcscat_s(szDllPath, _MAX_PATH, dir);
_tcscat_s(szDllPath, _MAX_PATH, _T("InjwlgDll.dll"));
if (!InjectLib(dwProcessId, szDllPath))
{
   MessageBox(hWnd, _T("远线程注入失败!"), _T("提示"), 0);
   return FALSE;
}
ShowWindow(hWnd, SW_HIDE);


在主程序退出时,进行卸载DLL

case WM_DESTROY:
{
   while (EjectLib(dwProcessId, szDllPath))
   {
       EjectLib(dwProcessId, szDllPath);
   }
   PostQuitMessage(0);
}
break;

以上就是整个鼠标键盘锁的实现过程,纯SDK,没技术含量,功能虽然可以实现,但是这种方法有个问题,即系统不同,需要修改的字节在winlogon中的偏移也会有所不同,所以如果要写个具有普遍性的程序还需搜索找到要修改的偏移,因此还需进一步改进。请大神们轻喷,不对的地方,请指正,大神们有好的方法欢迎分享!





看雪ID:Thvoifar                           

https://bbs.pediy.com/user-681841.htm



本文由看雪论坛 Thvoifar 原创

转载请注明来自看雪社区



好书推荐:

立即购买!



热门技术文章推荐:






公众号ID:ikanxue
官方微博:看雪安全

商务合作:wsc@kanxue.com





    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存