Win7 x64鼠标键盘锁(SDK)

Win7 x64鼠标键盘锁(SDK)_第1张图片

曾经,学习孙鑫老师的《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

你可能感兴趣的:(Win7 x64鼠标键盘锁(SDK))