欢迎与李马交流:titilima@163.com。
> 昨天才看到你的博客, 有点相见恨晚的感觉. 很多文章都让我受益良多, 所以立刻加了你的QQ:)
> 我看你对DLL的知识非常熟悉, 所以有个问题困惑已久, 特来请教.
> 我有一个项目是对IE等浏览器进行嗅探, 采用hook给IE注入dll. 然后在DLL_PROCESS_ATTACH时调用detours库对IE的send和recv之类的函数进行api hook. 已经可以成功的嗅探了, 但是在退出时有一个问题.
> 如果退出时有某个recv还在堵塞中, 也就是没有退出它相应的线程的话. 我这边卸载了detours和dll, 之后会造成IE崩溃(应该是dll卸载后, 嗅探函数地址已经不存在了, 造成的访问错误吧).
> 我尝试了一些方法(比如等待recv结束, 关闭socket, 或者terminatethread), 都没有获得理想的效果. 目前采取了在退出程序的同时关闭IE的方法, 但是这个方法显然过于"粗暴"了.
> 不知道马兄是否有比较好的点子啊?
> 还望多多指教:)
Hi,
可以试试引用计数来管理 recv 的生命期。
当流程转入 recv 后,引用计数 + 1,返回后 – 1。
当要退出时,等待引用计数减至 0,然后安全卸载。
辅以线程同步对象可能会收到更好的效果。
祝你好运。
> 谢谢马兄回复:)
> 我尝试过等待的方式, 如果g_nThread不为0则等待, 但是长时间无法等待到g_nThread为0.
> 但是也有一些网站, g_nThread能够为0, 从而顺利卸载不崩溃.
> 如果dll能在IE退出时卸载就好了. 我使用的Windows钩子的方式注入dll, 貌似就算主程序不执行Unhook函数, 只要主程序退出, 还是会导致IE中挂钩的dll退出.
> 或者有没有更好的嗅探方式.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | //API hook 替换的函数 int WINAPI RecvMonitor(SOCKET s, char* buf, int len, int flags) { ++g_nThread; int iRet = RealRecv(s, buf, len, flags); --g_nThread; // 一些分析代码 // … return iRet; } //UnHook时会导致DLL卸载 case DLL_PROCESS_DETACH: { if (g_bMonitor) { // 用临界区防止新开启了其他的send或recv线程 EnterCriticalSection(&g_cs); // 这里检查g_nThread的值, 实际情况是, 如果为0, 则不会崩溃, 如果不为0, 则会崩溃. // 但是如果等待g_nThread为0, 却不会成功, 并且IE会一直无响应 for (;;) { if (g_nThread == 0) break; } // 将API hook还原, 即IE会直接进入RealRecv, 而不再进入RecvMonitor. UnMonitorSocket(); LeaveCriticalSection(&g_cs); } } |
Hi,
试试这样的方法,可以避免 busy loop 无响应的方式。
大概写了一下,未经语法验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class CLock { public: CLock(void) { m_refcnt = 0; m_hEvent = ::CreateEvent(...); } ~CLock(void) { ::CloseHandle(m_hEvent); } void Enter(void) { if (0 == m_refcnt) ::ResetEvent(m_hEvent); ++m_refcnt; } void Leave(void) { –m_refcnt; if (0 == m_refcnt) ::SetEvent(m_hEvent); } void Wait(void) { ::WaitForSingleObject(m_hEvent, INFINITE); } private: int m_refcnt; HANDLE m_hEvent; }; |
进入挂钩函数时调用 Enter,返回时调用 Leave。
当准备结束挂钩时则调用 Wait,Wait 之后则可以释放钩子。
看了一下,几个粗浅的建议:)
1)for死循环一定要加sleep,避免其他进程线程无法正常响应
2)采用CCriticalSection对象和CSingleLock对象来对代码区进行临界保护。避免程序出现某异常没有正常LeaveCriticalSection时,造成下次进入失败
3)注意一下WaitForSingleObject的第三中返回的情况。可以帮助分析问题
4)注意vista、win7平台下创建的内核对象的权限问题。如mutex、enent、文件读写等。降低它们的权限,可以让ie或其他进程能正常访问,否则一定出现问题
5)如果是全局钩子,注意该dll被注入到其他进程时的引用计数,不等dll分发计数清零而卸载,将导致其他一些被注入的进程发生崩溃等异常情况