首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络基础 >

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

2012-11-13 
DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子有了前面两节的基础,我们现在切入

DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子

        有了前面两节的基础,我们现在切入正题:研究下DllMain为什么会因为不当操作导致死锁的问题。首先我们看一段比较经典的“DllMain中死锁”代码。(转载请指明出于breaksoftware的csdn博客)


        后就停住了,光标在闪动,貌似还是在等待我们输入。可是我们怎么敲击键盘都没有用:它死锁了。

        我是在VS2005中调试该程序,于是我们可以Debug->Break All来冻结所有线程。

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        我们先查看主线程(3096)的堆栈

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子           堆栈不长,我全部列出来

17ntdll.dll!_KiFastSystemCallRet@0()16ntdll.dll!_NtWaitForSingleObject@12()15kernel32.dll!_WaitForSingleObjectEx@12()14kernel32.dll!_WaitForSingleObject@8()13DllWithoutDisableThreadLibraryCalls_A.dll!DllMain(HINSTANCE__ * hModule=0x10000000, unsigned long ul_reason_for_call=1, void * lpReserved=0x00000000)12DllWithoutDisableThreadLibraryCalls_A.dll!__DllMainCRTStartup(void * hDllHandle=0x10000000, unsigned long dwReason=1, void * lpreserved=0x00000000)11DllWithoutDisableThreadLibraryCalls_A.dll!_DllMainCRTStartup(void * hDllHandle=0x10000000, unsigned long dwReason=1, void * lpreserved=0x00000000)10ntdll.dll!_LdrpCallInitRoutine@16()9ntdll.dll!_LdrpRunInitializeRoutines@4()8ntdll.dll!_LdrpLoadDll@24()7ntdll.dll!_LdrLoadDll@16()6kernel32.dll!_LoadLibraryExW@12()5kernel32.dll!_LoadLibraryExA@12()4kernel32.dll!_LoadLibraryA@4()3DllMainSerial.exe!wmain(int argc=3, wchar_t * * argv=0x003b7000)2DllMainSerial.exe!__tmainCRTStartup()1DllMainSerial.exe!wmainCRTStartup()0kernel32.dll!_BaseProcessStart@4()

      我们看下这个堆栈。大致我们可以将我们程序分为4段:

        0 启动启动我们程序

        1~6 我们加载Dll。

        7~10 系统为我们准备DLL的加载。

        11~17 DLL内部代码执行。

        我们关注一下14~17这段对WaitForSingleObject的调用逻辑。15、16步这个过程显示了Kernel32中的WaitForSingleObjectEx在底层是调用了NtDll中的NtWaitForSingleObject。在NtWaitForSingleObject内部,即17步,我们看到的“_KiFastSystemCallRet@0”。这儿要说明下,这个并不是意味着我们程序执行到这个函数。我们看下这个函数的代码

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        KiFastSystemCallRet函数是内核态(Ring0层)逻辑回到用户态(Ring3层)的着陆点。与之相对应的KiFastSystemCall函数是用户态进入内核态必要的调用方法。因为内核态代码我们是无法查看的,所以动态断点只能设置到KiFastSystemCallRet开始处。所以实际死锁是因为NtWaitForSingleObject在底层调用了KiFastSystemCall进入内核,在内核态中死锁的。

        我们在《DllMain中不当操作导致死锁问题的分析--死锁介绍》中介绍过,死锁存在的条件是相互等待。主线程中,我们发现其等待的是工作线程结束。那么工作线程在等待主线程什么呢?我们看下工作线程的调用堆栈

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        我们对这个堆栈进行编号

6ntdll.dll!_KiFastSystemCallRet@0()5ntdll.dll!_NtWaitForSingleObject@12()  + 0xc bytes4ntdll.dll!_RtlpWaitForCriticalSection@4()  + 0x8c bytes3ntdll.dll!_RtlEnterCriticalSection@4()  + 0x46 bytes2ntdll.dll!__LdrpInitialize@12()  + 0xb4bf bytes1ntdll.dll!_KiUserApcDispatcher@20()  + 0x7 bytes0ntdll.dll!_RtlAllocateHeap@12()  + 0x9b48 bytes

       我们看到倒数两步(5、6)和主线程中最后两步(16、17)是相同的,即工作线程也是在进入内核态后死锁的。我们知道主线程在等工作线程结束,那么工作线程在等什么呢?我们追溯栈,请关注“ntdll.dll!__LdrpInitialize@12() + 0xb4bf bytes”处的代码 

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        我们看到,是因为_RtlEnterCriticalSection在底层调用了NtWaitForSingleObject。那么我们关注下_RtlEnterCriticalSection的参数_LdrpLoaderLock,它是什么?我们借助下IDA查看下LdrpInitialize反编译代码


        我们查看TIB结构体去匹配该地址指向的空间的。

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        可以看到7ffdb000所指向的空间的各字段的值和FS:[0]指向的空间的值一致。但是如果我们这样输入就会失败

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        介绍完这些后,我们再回到IDA反汇编的代码中。v4 = *(_DWORD*)(*MK_FP(__FS__, 0x18) + 0x30);这段中MK_FP不是一个函数,是一个宏。它的作用是在基址上加上偏移得出一个地址。于是MK_FP(__FS__, 0x18)就是FS:[0x18],即TIB的Self字段。在该地址再加上0x30得到的地址已经超过了TIB空间,于是我们继续查看TEB结构体

DllMain中不当操作导致死锁有关问题的分析-导致DllMain中死锁的关键隐藏因子

        发现0x30偏移的是PEB(Process Environment Block)。


        

        其中还有段关于这个锁的介绍

HANDLE hThread = CreateThread(NULL, 0, ThreadCreateInDllMain, NULL, 0, NULL);WaitForSingleObject(hThread, INFINITE);
        主线程进入临界区去调用DllMain时进入了临界区,而工作线程也要进入临界区去执行DllMain。但是此时临界区被主线程占用,工作线程便进入等待状态。而主线程却等待工作线程退出才退出临界区。于是这就是死锁产生的原因。


热点排行
Bad Request.