在前一篇文章《DllMain()限入死锁问题分析 (一)》里,介绍了DllMain()死锁问题的理象及初步的调试分析结果。本章我们将继续分析导致死锁的详细原因。
之前我们看到,主线程和子线程因为都要访问一个变量ntdll!PebLdr,才导致进入了死锁。
0:000> x ntdll!pebldr
779f7880 ntdll!PebLdr = <no type information>
用x命令,只能看到这个变量的地址,看不到它的类型信息。但既然它的名字里有Peb,我们就不妨看看它和Peb有什么关联。
0:000> !peb
PEB at 7ffd3000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00230000
Ldr 779f7880
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 00251af8 . 00252c30
Ldr.InLoadOrderModuleList: 00251a58 . 00252c20
Ldr.InMemoryOrderModuleList: 00251a60 . 00252c28
!peb命令列出的信息里有一项叫Ldr的地址正好与PebLdr的地址完全吻合。看一下_PEB结构的定义。
0:000> dt _PEB 7ffd3000
LoadDll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0x1 ''
+0x003 SpareBool : 0x8 ''
+0x004 Mutant : 0xffffffff Void
+0x008 ImageBaseAddress : 0x00230000 Void
+0x00c Ldr : 0x779f7880 _PEB_LDR_DATA
其中果然有一个成员叫Ldr,类型是_PEB_LDR_DATA。再来看一下这个类型的定义。
0:000> dt _PEB_LDR_DATA 779f7880
LoadDll!_PEB_LDR_DATA
+0x000 Length : 0x30
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x251a58 - 0x252c20 ]
+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x251a60 - 0x252c28 ]
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x251af8 - 0x252c30 ]
+0x024 EntryInProgress : (null)
可以看到其中有三个Module List,根据它们的名字,可以想见它们存放的内容是一样的,只不过顺序不同。它们的类型都是_LIST_ENTRY,也就是每个都是一个双向链表。
0:000> dt _LIST_ENTRY
LoadDll!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
以其中第一个链表InLoadOrderModuleList为例,根据它列出的首尾指针,可以用dd命令依次遍历链表中每一个结点,结果发现这个链表有五个节点,分别是:
00251a58 -> 00251ae8 -> 00251e10 -> 00251f28 -> 00252c20
同时我们用lm命令列出已经加载的模块列表,刚好也是5个。