在下利用微软的detours写了一个Hook全局MessageBox的dll,代码比较短,先贴下,然后再说问题:
#include"HookDll.h"
#include <detours.h>
#pragma comment(lib, "detours.lib")
#pragma data_seg("Shared")
HHOOK mHook = NULL;
HINSTANCE hInstance = NULL;
#pragma data_seg()
int (WINAPI* OLD_MessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = MessageBoxW;
int (WINAPI* OLD_MessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) = MessageBoxA;
//自定义的代替函数
int WINAPI NEW_MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
int ret = OLD_MessageBoxW(hWnd, L"已经被Hook!", L"Hook", uType);
return ret;
}
int WINAPI NEW_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
int ret = OLD_MessageBoxA(hWnd, "已经被Hook", "Hook", uType);
return ret;
}
//启动Hook
VOID Hook()
{
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
//Hook MessageBoxW和MessageBoxA
DetourAttach(&(PVOID&)OLD_MessageBoxW, NEW_MessageBoxW);
DetourAttach(&(PVOID&)OLD_MessageBoxA, NEW_MessageBoxA);
DetourTransactionCommit();
}
//撤销Hook
VOID UnHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
//撤销Hook
DetourDetach(&(PVOID&)OLD_MessageBoxW, NEW_MessageBoxW);
DetourDetach(&(PVOID&)OLD_MessageBoxA, NEW_MessageBoxA);
DetourTransactionCommit();
}
//挂钩函数
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(mHook, nCode, wParam, lParam);
}
//导出函数,安装一个针对WH_CALLWNDPROC的全局钩子
//WH_CALLWNDPROC:当目标线程调用SendMessage发送消息时,钩子函数被调用
extern "C" __declspec(dllexport) BOOL WINAPI Start()
{
mHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hInstance, 0);
return mHook != NULL;
}
//卸载钩子
extern "C" __declspec(dllexport) void WINAPI Stop()
{
::UnhookWindowsHookEx(mHook);
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH://当DLL被某个进程加载时,执行该case下的语句体
{
//在这里干两件事:1.启动Hook,Hook MessageBox
// 2.获取本DLL的句柄
//之所以只调用一次Hook,后面解释
static int num = 1;
if (1==num)
{
++num;
Hook();
}
hInstance = (HINSTANCE)hModule;
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH://当DLL将被卸载时,执行下面的语句体
{
//UnHook(); //如果这里加上UnHook(),那MessageBox就不能被Hook了,后面再详述
break;
}
}
return TRUE;
}
在下在另一个MFC程序中加载上面的DLL,并且调用Start启动钩子。
按照在下的想法,当一个程序(假设为A)调用SendMessage函数发送消息时,系统会强行将该DLL映射到程序A的地址空间,并且执行case DLL_PROCESS_ATTACH下的语句体,即调用Hook函数,Hook程序A的MessageBox。
但事实是这样的:
在下在另一个C++程序中调用MessageBoxW,发现如下情况:
1.如果在case DLL_PROCESS_DETACH的语句体中加上UnHook调用,那么,无论调用多少次MessageBoxW,没有一次是被Hook的。
2.鉴于1,在下猜想可能是在调用MessageBoxW前就先执行UnHook了,于是干脆就将UnHook注释掉了,这是在调用MessageBoxW,发现,第一次调用没有被Hook,而后面再调用MessageBoxW就被Hook了。但这只是在下写的C++程序的表现,对于其它程序,如记事本,还依然没被Hook。
3.修改上面的DLL,不Hook MessageBox了,而改为Hook CopyFileW和Hook CopyFileA,这时,电脑上的复制粘贴都没有被Hook,自己写程序调用CopyFileW也没有被Hook,但是,在调用CopyFileW前调用一次MessageBoxW,后面的CopyFileW就被Hook了。
综上,在下的理解是:程序的一些行为,如调用MessageBox,才会导致调用SendMessage,进而导致加载DLL,这时才Hook相应的API。
在下想要的效果是:只要我运行一个程序(比如前边的启动钩子的MFC程序),电脑上所有的MessageBox都被Hook了。
在下对Windows消息机制不是特别熟悉,不知道在下的想法有问题还是代码写的有问题,还请诸位大神指点一二。在下先行谢过!
首先,detour hook只能作用于加载了你这个模块的进程,所以你说的“运行一个程序(比如前边的启动钩子的MFC程序),电脑上所有的MessageBox都被Hook了,这个用你的程序是没办法实现的,你想hook整个系统,只能保证你的dll被所有进程加载,这个比较困难,除非你注入成为资源管理器的插件或者系统必加载的模块
而你hook SendMessage,不要所想替换整个系统的MessageBox,就是想替换你本进程的MessageBox都有问题,比如说你这个进程根本就不存在消息循环或者所有消息都是用PostMessage的方式发的,你根本hook不到
我给你一个方案,你做一个资源管理器插件,让系统一启动就加载你,DllMain里hook CreateProcess,在CreateProcess里让目标进程加载你这个dll,然后再hook messagebox,当然即使这样也很难办,因为还有很多程序不是从资源管理器启动的,比如服务
总而言之,你先搞清楚hook的本质,跨进程hook不是不可以,但是你要有目标进程的权限,把你的dll注入进去