同样的,全局钩子也是用SetWindowHookEx来实现,只不过后面两个参数有些变化,并且一定要在一个DLL中实现钩子才能达到全局钩子的效果.
我们先新建一个项目,选择WIN32 DLL工程,先创建3个全局变量,再写一个钩子函数,之后分别实现他们的钩子子程序。
GetModuleHandle()函数是获取一个应用程序或动态链接库的模块句柄 。
HHOOK g_mouseHook;
HHOOK g_keyboardHook;
HWND g_hwnd=NULL;
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
) //鼠标钩子过程
{
return 1;
}
LRESULT CALLBACK KeyboardProc(
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)//键盘钩子过程
{
if(VK_F2==wParam)
{
::SendMessage(g_hwnd,WM_CLOSE,0,0);
::UnhookWindowsHookEx(g_mouseHook);
::UnhookWindowsHookEx(g_keyboardHook);
}
else
return 1;
}
void SetHook(HWND hwnd)
{
g_hwnd=hwnd;
g_mouseHook=::SetWindowsHookEx(WH_MOUSE,MouseProc,::GetModuleHandle(_T("dll_hook.dll")),0);
g_keyboardHook=::SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,::GetModuleHandle(_T("dll_hook.dll")),0);
}
之后我们添加def文件
LIBRARY "dll_hook"
EXPORTS
SetHook
编译下,编译通过之后可以看到 hook_dll.dll 和 hook_dll.lib文件。
接着我们写一个测试程序,新建MFC工程 选择对话框程序.
拷贝hook_dll.dll 和 hook_dll.lib到工程目录下,在链接器命令行加上 hook_dll.lib.
在OnInitDialog()函数之前 定义DLL函数:
_declspec(dllimport) void SetHook(HWND hwnd);
在OnInitDialog()内添加
SetHook(this->m_hWnd);
编译下,执行程序.
切换到其他程序下比如我的电脑 QQ 之类的任何程序发现点击鼠标或者按键盘都不起作用了,是的,所有的进程都被屏蔽了,只有我们留的后门F2键可以解除这种状态,然后我们会发现一个问题,就是当hookDll_test窗口为激活状态的时候按F2键才能解除这种状态,当hookDll_test不是活动状态的时候按下F2键没有任何反映。这是因为操作系统为了保护DLL中的共享数据设置的一种机制,
先看一下DLL与进程之间的工作方式 如图: (自己用WINDOWS自带的画图程序写的,不要鄙视 -.-)
当两个进程同时读同一份DLL中的数据时DLL中的数据不变,假设DLL中数据页1的内容为一个指针变量,进程二试图改变这个指针,但如果改变了这个指针进程一可能会崩溃,其实我们大可以放心,进程1永远不会崩溃,因为当进程2试图改变数据页1的时候并不会直接改变数据页1,而是在新页面中拷贝数据页1,也就是进程二改变的是新页面中的值,根本不会影响到进程一的使用。
如图:
知道了这个原因我们再来分析刚才的问题,显然dllHook中的g_hwnd即为这个被更改的数据,当我们在dllHoook_test测试程序中测试的时候改变了这个值但在DLL内部,改变的是上图中 新数据页1中的内容,而其他进程仍然使用的是数据页1中的内容,所以当dllHook_test窗口没在活动状态时,我们按F2键是没有用的.
知道原因之后就容易解决这个问题了,我们可以在dllHook.dll中自己写一个节 (section)。
方法:
在写全局变量g_hwnd的时候在前后加上如下代码,(具体#pragma的用法可以在msdn中查到)
#pragma data_seg("MySec"); //起名的时候最多为8个字符,否则取前8个字符
HWND g_hwnd=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:MySec,RWS") //设置链接器,使MySec节拥有属性为 RWS (READ WRITE SHARED )
这时在编译下,成功之后重新在把hook_dll.dll 和 hook_dll.lib拷贝到测试工程目录下,再编译dllHook_test,启动程序会发现
之前的问题已经解决了,无论在任何窗口下按F2键都可以退出这个钩子程序,使得鼠标和键盘变为可用状态。
最后我加上一些代码让它变成一个我们常遇到的愚人节程序(就是占着满屏幕,不能动鼠标不能动键盘,只能重启)。
仍然是用这个DLL ,和这个DLL测试程序,加上如下代码:
在OnInitDialog()函数中写入:
int cxScreen,cyScreen;
cxScreen=::GetSystemMetrics(SM_CXSCREEN); //获取屏幕宽度 (以像素为单位)
cyScreen=::GetSystemMetrics(SM_CYSCREEN);//获取高度
SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);//设置窗口占满整个屏幕
SetHook(this->m_hWnd); //钩子函数
在OnPaint中写入:
CClientDC dc(this);
CFont font;
font.CreatePointFont(500,_T("宋体"),NULL);
CFont *oldFont=dc.SelectObject(&font);
dc.TextOutW(200,300,_T("Blue236146祝大家愚人节快乐!"));
dc.SelectObject(oldFont);
现在就完成了,运行程序如图:
因为我们无形之中把现在计算机运行的所有进程都注入了我们的DLL,这是病毒和木马经常做的事情,所以360会提示警告,我们可以无视之。
关掉360之后我们会发现屏幕一直被这个窗口占着,无法看到任何东西,鼠标和键盘都无法关闭这个窗口,当然我们留了后门F2键可以关闭这个窗口。 按下F2键退出程序。 计算机又可以正常工作了。
写到这吧,1点多了,睡觉!