In addition to stack trace collection (Volume 1, page 409) we often are interested in Module Collection (we initially called this pattern Vendor Collection), especially if we would like to check whether a particular vendor DLL is present in some process address space in a complete memory dump (kernel module list or module list from a process memory dump is trivial). Or we need to check for some vendor information from a problem description (lmv command). If we have a complete memory dump from x64 system then listing modules for each process is not enough. For example, we might have this:
0: kd> lmu start end module name 00000000`00ab0000 00000000`00ae8000 AppA (deferred) 00000000`74fe0000 00000000`7502e000 wow64win (deferred) 00000000`75030000 00000000`75075000 wow64 (deferred) 00000000`750c0000 00000000`750c9000 wow64cpu (deferred) 00000000`77b70000 00000000`77cf7000 ntdll (pdb symbols)
AppA is a 32-bit process and has an additional 32-bit module list that is more useful. We can set x86 context for a thread from that process and get the list of 32-bit modules:
0: kd> .load wow64exts 0: kd> .thread /w fffffa800e372060 Implicit thread is now fffffa80`0e372060 x86 context set 0: kd:x86> .reload Loading Kernel Symbols Loading User Symbols Loading unloaded module list Loading Wow64 Symbols
0: kd:x86> lmu start end module name 00000000`00ab0000 00000000`00ae8000 AppA (deferred) 00000000`73490000 00000000`73515000 COMCTL32 (deferred) 00000000`73520000 00000000`735c3000 MSVCR90 (deferred) 00000000`735d0000 00000000`7365e000 MSVCP90 (deferred) 00000000`74920000 00000000`7493e000 USERENV (deferred) 00000000`74940000 00000000`74ade000 comctl32_74940000 (deferred) 00000000`74af0000 00000000`74b02000 MSASN1 (deferred) 00000000`74b10000 00000000`74c03000 CRYPT32 (deferred) 00000000`74dc0000 00000000`74e5b000 MSVCR80 (deferred) 00000000`74f60000 00000000`74fd6000 NETAPI32 (deferred) 00000000`74fe0000 00000000`7502e000 wow64win (deferred) 00000000`75030000 00000000`75075000 wow64 (deferred) 00000000`750b0000 00000000`750ba000 WTSAPI32 (deferred) 00000000`750c0000 00000000`750c9000 wow64cpu (deferred) 00000000`75cf0000 00000000`75d50000 Secur32 (deferred) 00000000`75d50000 00000000`76861000 SHELL32 (deferred) 00000000`76a10000 00000000`76aa0000 GDI32 (deferred) 00000000`76b30000 00000000`76b90000 IMM32 (deferred) 00000000`76be0000 00000000`76cf0000 kernel32 (deferred) 00000000`76e30000 00000000`76f75000 ole32 (deferred) 00000000`76f80000 00000000`7702a000 msvcrt (deferred) 00000000`77030000 00000000`77037000 PSAPI (deferred) 00000000`77040000 00000000`77110000 USER32 (deferred) 00000000`77110000 00000000`77169000 SHLWAPI (deferred) 00000000`77170000 00000000`771ed000 USP10 (deferred) 00000000`77380000 00000000`7740d000 OLEAUT32 (deferred) 00000000`77640000 00000000`77649000 LPK (deferred) 00000000`776e0000 00000000`777d0000 RPCRT4 (deferred) 00000000`777d0000 00000000`77898000 MSCTF (deferred) 00000000`778a0000 00000000`77966000 ADVAPI32 (deferred) 00000000`77b70000 00000000`77cf7000 ntdll (pdb symbols) 00000000`77d30000 00000000`77e90000 ntdll_77d30000 # (pdb symbols)
So it looks like we need to dump modules for each thread. However, the output would be enormous unless we skip threads having the same PID. After some tinkering we wrote this WinDbg script with moderate output volume:
.load wow64exts !for_each_thread ".thread @#Thread; .if (@$t0 != @@c++(@$thread- >Cid.UniqueProcess)) {.reload /user;lmvu;.thread /w @#Thread;.reload /user;lmvu;r $t0 = @@c++(@$thread->Cid.UniqueProcess);.effmach AMD64; }"