全局 HOOK API (通过钩子) ,请大侠们帮帮忙

时间:2022-09-05 21:48:27
我上网参照一些神人的代码想写个全局API HOOK, 大致是这样实现的,做了一个全局钩子(WH_GETMESSAGE)来确保含有修改API代码的DLL被加载到很多进程中。DLL有个类,在加载的时候初始化,修改API(MessageBoxW). 现在好像DLL都被那些进程正确加载了,而且都进行了API的修改,但是为什么只对我本进程有效,其它进程都无效, 和COPY ON WRITE有关吗?

代码如下:
DLL部分:
---------------------------------------------
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) 

        switch (dwReason) 
        { 
        case DLL_PROCESS_ATTACH: 
                glhInstance = hInstance; 
                pCHC=new CHookClass("user32.dll","MessageBoxW",MessageBoxWProxy);   
        pCHC->HookOn();                        
                break ; 
        case DLL_PROCESS_DETACH: 
                delete pCHC;
                break; 
        case DLL_THREAD_ATTACH: 
        case DLL_THREAD_DETACH:
                break;
        } 
        return TRUE; 


// WH_GETMESSAGE钩子处理过程
LRESULT CALLBACK GetMsgHookProc(int nCode,WPARAM wparam,LPARAM lparam) 
{// 什么也不做(只是为了进入其它进程)
        return CallNextHookEx(glhMsgHook,nCode,wparam,lparam);
}

DLLEXPORT BOOL InstallHOOK( void ) 

        if (glhMsgHook == NULL ){ 
                glhMsgHook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)GetMsgHookProc,glhInstance,0 ); 
        } 
        if (glhMsgHook == NULL) {                 
                return  FALSE; 
        }  
        return  TRUE; 

// 卸载钩子
DLLEXPORT BOOL UninstallHOOK(void)
{
        BOOL bResult=FALSE; 
        if(glhMsgHook){ 
                bResult= UnhookWindowsHookEx(glhMsgHook); 
                if(bResult){ 
                        glhMsgHook=NULL; 
                }
        } 
        return bResult;
}


DLL头文件:
---------------------------------------------
class CHookClass{
        CHookClass(const CHookClass&);
        // 不准拷贝和赋值
        CHookClass &operator=(const CHookClass&);        
        // 栈对齐为1字节
#pragma pack(push,1)
        struct TJmpCode{
                char Jmp;
                DWORD Ptr;
        };
#pragma pack(pop)

public:
        CHookClass(const char *LibName,                // DLL名
                           const char *FuncName,        // 目标函数名
                           void *NewFuncPtr);                // 用于替换的函数指针
        ~CHookClass();

        void HookOn();                                                // 开启 API Hook
        void HookOff();                                                // 关闭 API Hook
        void Lock();                                                // 进入临界区
        void Unlock();                                                // 退出临界区

        bool IsHooked(){                                        
                return _IsHooked;                                // 取Hook状态
        }
private:
        TJmpCode _OrgCode;                                        // 原函数头5个字节
        TJmpCode _NewCode;                                        // 新函数头5个字节(jmp FuncPtr)

        void *_TagFuncPtr;                                        // 目标函数指针
        HANDLE _CurProcess;                                        // 当前进程
        bool _IsHooked;                                                // 显示是否被Hook了

        CRITICAL_SECTION _cs;                                // 临界区
};

// 构造
CHookClass::CHookClass(const char *LibName, const char *FuncName, void *NewFuncPtr)
        :_TagFuncPtr(NULL),_IsHooked(false),_CurProcess(0)
{
        // 取目标API地址
        HMODULE hModule=GetModuleHandle(LibName);        
        _TagFuncPtr=GetProcAddress(hModule, FuncName);
        if(_TagFuncPtr==NULL) throw(-1);

        // 保存原函数前五字节
        CopyMemory(&_OrgCode,_TagFuncPtr,5);       
        // 新的五字节为 jmp 偏移
        _NewCode.Jmp=0xe9;                                                        
        _NewCode.Ptr=DWORD(NewFuncPtr)-DWORD(_TagFuncPtr)-5; 
                
        // 以特权方式打开当前进程
        _CurProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE, GetCurrentProcessId()); 
        LogInfo("当前进程句柄%d",_CurProcess);

        InitializeCriticalSection(&_cs);
}
// 析构
CHookClass::~CHookClass()
{
        if(_IsHooked) HookOff();
        DeleteCriticalSection(&_cs);
}

void CHookClass::HookOn()
{// !开启 API Hook
        if(!_IsHooked){
                DWORD dwOldProtect;
                VirtualProtectEx(_CurProcess, _TagFuncPtr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); 
                WriteProcessMemory(_CurProcess, _TagFuncPtr, &_NewCode, 5, NULL);                                
                VirtualProtectEx(_CurProcess, _TagFuncPtr, 5, dwOldProtect, &dwOldProtect);
                _IsHooked=true;
                LogInfo("进程%d的API修改完成",GetCurrentProcessId());
        }
}
void CHookClass::HookOff()
{// !关闭 API Hook
        if(_IsHooked){
                DWORD dwOldProtect;
                VirtualProtectEx(_CurProcess, _TagFuncPtr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
                WriteProcessMemory(_CurProcess, _TagFuncPtr, &_OrgCode, 5, NULL);
                VirtualProtectEx(_CurProcess, _TagFuncPtr, 5, dwOldProtect, &dwOldProtect);
                _IsHooked=false;
        }
}

有查看调度日志时发现,很多进程都载入了APIHOOK.dll, 而且在HookOn时都有改写MessageBoxW,为什么其它进程无效呀? APIHOOK.DLL已和这些进程在同一地址空间了呀,郁闷。。。 望各位大侠不吝赐教!

12 个解决方案

#1


我没有仔细看你的逻辑,既然你说在其他进程已经加载并hook成功了,那么,你是否确定调用了MessageBoxWProxy这个函数呢?

#2


Windows消息分为队列消息和非队列消息,WH_GETMESSAGE不能取到非队列消息,用SendMessage发送的消息不经过队列,用PostMessage发送的消息被放入队列

#3


不懂C++语法,不过以前用汇编写过一个全局HOOK的DLL,流程与你的不同.

一,你_CurProcess句柄的获取,我是在DLL_PROCESS_ATTACH时调用GetCurrentProcess得到,参考自老罗的<<WINDOWS环境下32位汇编语言程序设计>>.这个函数只有在这个时候调用才能得到正确的句柄(书中的意思).

二,所有涉及修改的内存操作都使用ReadProcessMemory/WriteProcessMemory来完成.

三,DLL中有一个标志,用于判断是否已经被HOOK了.

四,HOOK的过程写在CALLBACK GetMsgHookProc中,通过判断上面的标志来决定是否HOOK,并与主进程建立通讯.因此在DLL被加载到目标进程后,当有一个消息到来时,才开始HOOK.

而你的代码是在DLL_PROCESS_ATTACH时就调用了HOOK的过程,这个时机不知道是否正确.

五,以上仅供参考......

#4


引用 2 楼 liuxiaoyi666 的回复:
Windows消息分为队列消息和非队列消息,WH_GETMESSAGE不能取到非队列消息,用SendMessage发送的消息不经过队列,用PostMessage发送的消息被放入队列

我不需要所有进程都挂上DLL,有消息循环的挂上即可。比如现在是我确定记事本已挂上了APIHOOK.DLL,而且代码 :LogInfo("进程%d的API修改完成",GetCurrentProcessId());已确实执行了。为什么会拦截不到MessageBoxW呢:(, 我用远程注入修改IAT的方式可以拦截这个函数的,说明记事本程序的确是调用了MessageBoxW。

#5


引用 3 楼 myjian 的回复:
不懂C++语法,不过以前用汇编写过一个全局HOOK的DLL,流程与你的不同.

一,你_CurProcess句柄的获取,我是在DLL_PROCESS_ATTACH时调用GetCurrentProcess得到,参考自老罗的<<WINDOWS环境下32位汇编语言程序设计>>.这个函数只有在这个时候调用才能得到正确的句柄(书中的意思).

二,所有涉及修改的内存操作都使用ReadProcessM……


关于宿主进程句柄的获取没有问题的:我的日志记录如下:
当前进程句柄1480
进程2060的API修改完成
当前进程句柄0
进程1044的API修改完成
当前进程句柄0
进程3556的API修改完成
进程3272的API修改完成
当前进程句柄4040
进程836的API修改完成
当前进程句柄4756
进程3984的API修改完成
当前进程句柄0
进程1236的API修改完成
当前进程句柄4412
进程2180的API修改完成
当前进程句柄2696
进程252的API修改完成
当前进程句柄0
进程2184的API修改完成
当前进程句柄0
进程980的API修改完成
当前进程句柄0
进程552的API修改完成
进程836的API修改完成
在DLL_PROCESS_ATTACH时就调用了HOOK的过程(这点不知道会不会有影响),但看起来象是没影响的,如上面的日志所示。

走过路过的大侠们,帮帮忙。

#6


那剩下的只有调试的问题了,这个基本上就是你自己的事了 全局 HOOK API (通过钩子) ,请大侠们帮帮忙

#7


楼主用hook注入啊,我都是用CreateRemoteThread注入的,感觉比hook好用的。

#8


CreateRemoteThread 我实现了,只是不明白用HOOK为什么实现不了,望大侠们不吝赐教!

#9


昨晚试了用VC6 生成, 居然一切OK, 天哪,偶要崩溃了!为什么用VS2008 或VS2010生成的就只能HOOK 自己。而VC6 生成的都可以HOOK ,explore,notepad都行. 高手们,大侠们,指点指点,谢谢了!

#10


建议楼主,先对一些简单的程序,进行钩子注入

#11


嗯,想写个全局Hook API啊,这可不是闹着玩的。
没有仔细看你的代码,但,估计你是遭遇到Windows的copy-on-write机制了,
建议看看这篇,或许对你有帮助。

绕过Copy-On-Write机制安装全局Hook
http://dev.firnow.com/course/3_program/c++/cppsl/20081117/151293.html

#12


引用 9 楼 wwstudio_cn 的回复:
昨晚试了用VC6 生成, 居然一切OK, 天哪,偶要崩溃了!为什么用VS2008 或VS2010生成的就只能HOOK 自己。而VC6 生成的都可以HOOK ,explore,notepad都行. 高手们,大侠们,指点指点,谢谢了!

我也遇到过类似经历,而且是Debug版,可以Release版通不过,也是一头雾水啊。不知道是不是跟VS2008的编译优化有关???

#1


我没有仔细看你的逻辑,既然你说在其他进程已经加载并hook成功了,那么,你是否确定调用了MessageBoxWProxy这个函数呢?

#2


Windows消息分为队列消息和非队列消息,WH_GETMESSAGE不能取到非队列消息,用SendMessage发送的消息不经过队列,用PostMessage发送的消息被放入队列

#3


不懂C++语法,不过以前用汇编写过一个全局HOOK的DLL,流程与你的不同.

一,你_CurProcess句柄的获取,我是在DLL_PROCESS_ATTACH时调用GetCurrentProcess得到,参考自老罗的<<WINDOWS环境下32位汇编语言程序设计>>.这个函数只有在这个时候调用才能得到正确的句柄(书中的意思).

二,所有涉及修改的内存操作都使用ReadProcessMemory/WriteProcessMemory来完成.

三,DLL中有一个标志,用于判断是否已经被HOOK了.

四,HOOK的过程写在CALLBACK GetMsgHookProc中,通过判断上面的标志来决定是否HOOK,并与主进程建立通讯.因此在DLL被加载到目标进程后,当有一个消息到来时,才开始HOOK.

而你的代码是在DLL_PROCESS_ATTACH时就调用了HOOK的过程,这个时机不知道是否正确.

五,以上仅供参考......

#4


引用 2 楼 liuxiaoyi666 的回复:
Windows消息分为队列消息和非队列消息,WH_GETMESSAGE不能取到非队列消息,用SendMessage发送的消息不经过队列,用PostMessage发送的消息被放入队列

我不需要所有进程都挂上DLL,有消息循环的挂上即可。比如现在是我确定记事本已挂上了APIHOOK.DLL,而且代码 :LogInfo("进程%d的API修改完成",GetCurrentProcessId());已确实执行了。为什么会拦截不到MessageBoxW呢:(, 我用远程注入修改IAT的方式可以拦截这个函数的,说明记事本程序的确是调用了MessageBoxW。

#5


引用 3 楼 myjian 的回复:
不懂C++语法,不过以前用汇编写过一个全局HOOK的DLL,流程与你的不同.

一,你_CurProcess句柄的获取,我是在DLL_PROCESS_ATTACH时调用GetCurrentProcess得到,参考自老罗的<<WINDOWS环境下32位汇编语言程序设计>>.这个函数只有在这个时候调用才能得到正确的句柄(书中的意思).

二,所有涉及修改的内存操作都使用ReadProcessM……


关于宿主进程句柄的获取没有问题的:我的日志记录如下:
当前进程句柄1480
进程2060的API修改完成
当前进程句柄0
进程1044的API修改完成
当前进程句柄0
进程3556的API修改完成
进程3272的API修改完成
当前进程句柄4040
进程836的API修改完成
当前进程句柄4756
进程3984的API修改完成
当前进程句柄0
进程1236的API修改完成
当前进程句柄4412
进程2180的API修改完成
当前进程句柄2696
进程252的API修改完成
当前进程句柄0
进程2184的API修改完成
当前进程句柄0
进程980的API修改完成
当前进程句柄0
进程552的API修改完成
进程836的API修改完成
在DLL_PROCESS_ATTACH时就调用了HOOK的过程(这点不知道会不会有影响),但看起来象是没影响的,如上面的日志所示。

走过路过的大侠们,帮帮忙。

#6


那剩下的只有调试的问题了,这个基本上就是你自己的事了 全局 HOOK API (通过钩子) ,请大侠们帮帮忙

#7


楼主用hook注入啊,我都是用CreateRemoteThread注入的,感觉比hook好用的。

#8


CreateRemoteThread 我实现了,只是不明白用HOOK为什么实现不了,望大侠们不吝赐教!

#9


昨晚试了用VC6 生成, 居然一切OK, 天哪,偶要崩溃了!为什么用VS2008 或VS2010生成的就只能HOOK 自己。而VC6 生成的都可以HOOK ,explore,notepad都行. 高手们,大侠们,指点指点,谢谢了!

#10


建议楼主,先对一些简单的程序,进行钩子注入

#11


嗯,想写个全局Hook API啊,这可不是闹着玩的。
没有仔细看你的代码,但,估计你是遭遇到Windows的copy-on-write机制了,
建议看看这篇,或许对你有帮助。

绕过Copy-On-Write机制安装全局Hook
http://dev.firnow.com/course/3_program/c++/cppsl/20081117/151293.html

#12


引用 9 楼 wwstudio_cn 的回复:
昨晚试了用VC6 生成, 居然一切OK, 天哪,偶要崩溃了!为什么用VS2008 或VS2010生成的就只能HOOK 自己。而VC6 生成的都可以HOOK ,explore,notepad都行. 高手们,大侠们,指点指点,谢谢了!

我也遇到过类似经历,而且是Debug版,可以Release版通不过,也是一头雾水啊。不知道是不是跟VS2008的编译优化有关???