browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

技术交流邮件(1)

Posted by on 2009 年 10 月 10 日

你可以任意转载本文,但请在转载后的文章中注明作者和原始链接。
媒体约稿请联系 titilima_AT_163.com(把“_AT_”换成“@”)。

欢迎与李马交流: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 之后则可以释放钩子。

订阅本站

一条评论

  • At 2009.10.14 12:07, 王永龙 said:

    看了一下,几个粗浅的建议:)
    1)for死循环一定要加sleep,避免其他进程线程无法正常响应
    2)采用CCriticalSection对象和CSingleLock对象来对代码区进行临界保护。避免程序出现某异常没有正常LeaveCriticalSection时,造成下次进入失败
    3)注意一下WaitForSingleObject的第三中返回的情况。可以帮助分析问题
    4)注意vista、win7平台下创建的内核对象的权限问题。如mutex、enent、文件读写等。降低它们的权限,可以让ie或其他进程能正常访问,否则一定出现问题
    5)如果是全局钩子,注意该dll被注入到其他进程时的引用计数,不等dll分发计数清零而卸载,将导致其他一些被注入的进程发生崩溃等异常情况

    (Required)
    (Required, will not be published)