DllMain中不当操作导致死锁问题的分析--加载卸载DLL与DllMain死锁的关系

        前几篇文章一直没有在源码级证明:DllMain在收到DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH时会进入临界区。这个论证非常重要,因为它是使其他线程不能进入临界区从而导致死锁的关键。我构造了在DLL被映射到进程地址空间的场景,请看死锁时加载DLL的线程的堆栈(转载请指明出于breaksoftware的csdn博客)

        如果仔细看过《DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2》,应该得知第14步就是进入临界区的点。

        我们可以看到LdrLoadDll内部调用了LdrLockLoaderLock。LdrLockLoaderLock内部进入临界区,我们用IDA查看LdrLoadDll函数

int __stdcall LdrLoadDll(int a1, int a2, int a3, int a4)
{
    ……
    LdrLockLoaderLock(1, 0, &v10);
    ……
    v6 = LdrpLoadDll(v9, a1, a2, v17, a4, 1);
    ……
    if ( v8 >= 0 )
    {
        ms_exc.disabled = -1;
        sub_7C936587(ebp0, v7);
        v6 = 0;
        goto LABEL_6;
    }

}

int __usercall sub_7C936587<eax>(int a1<ebp>, int a2<esi>)
{
    LdrpTopLevelDllBeingLoaded = a2;
    return LdrUnlockLoaderLock(1, *(_DWORD *)(a1 - 572));
}

        我们看到在LdrpLoadDll是在临界区中执行的。其实在LdrpLoadDll中也会进入该临界区,但是我们不必关注了。因为只要一次没出临界区就可以满足死锁的条件了。

        我们再看下卸载DLL时发生的进入临界区场景,请看堆栈


        我们将关注FreeLibrary和LdrpCallInitRoutine之间的代码逻辑。我们用IDA查看LdrUnLoadDll

int __stdcall LdrUnloadDll(int a1)
{
    ……
        v73 = 0;
    v70 = *(_DWORD *)(*MK_FP(__FS__, 24) + 48);
    v71 = 0;
    ms_exc.disabled = 0;
    if ( !LdrpInLdrInit )
        RtlEnterCriticalSection(&LdrpLoaderLock);
    ++LdrpActiveUnloadCount;
    if ( !LdrpShutdownInProgress )
    {
        if ( LdrpCheckForLoadedDllHandle(a1, (int)&v78) )
        {
            if ( *(_WORD *)(v78 + 56) != -1 )
            {
                ……
                if ( (unsigned __int8)LdrpActiveUnloadCount <= 1u )
                {
                    ……
                    v15 = (int *)LdrpUnloadHead;
                    v77 = (int *)LdrpUnloadHead;
                    while ( v15 != &LdrpUnloadHead )
                    {
                        ……
                        LdrpCallInitRoutine((int (__stdcall *)(_DWORD, _DWORD, _DWORD))v20, *(_DWORD *)(v78 + 24), 0, 0);
                        ……
                        v15 = (int *)LdrpUnloadHead;
                        v77 = (int *)LdrpUnloadHead;
                        ms_exc.disabled = 0;
                        v3 = 0;
                    }
                   ……
                }
            }
        }
        else
        {
            v71 = 0xC0000135u;
        }
    }
    ms_exc.disabled = -1;
    sub_7C937424();
……
    return v71;
}

int __cdecl sub_7C937424()
{
    int result; // eax@3

    --LdrpActiveUnloadCount;
    if ( !LdrpInLdrInit )
        result = RtlLeaveCriticalSection(&LdrpLoaderLock);
    return result;
}

        我们看到LdrUnloadDll几乎所有操作都是在临界区执行的。

        以上两段从源码级证明了加载和卸载DLL导致的DllMain的调用(以及不调用)都是在临界区中完成的。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

breaksoftware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值