曾经,学习孙鑫老师的《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 DBtest 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中的偏移也会有所不同,所以如果要写个具有普遍性的程序还需搜索找到要修改的偏移,因此还需进一步改进。请大神们轻喷,不对的地方,请指正,大神们有好的方法欢迎分享!
本文由看雪论坛 Thvoifar 原创
转载请注明来自看雪社区
原文链接:https://bbs.pediy.com/thread-228522.htm