Raw Stack Dump of all threads
Sometimes we need to dump the whole thread stack data to find traces of hooks, printer drivers or just string fragments. This is usually done by finding the appropriate TEB and dumping the data between StackLimit and StackBase addresses, for example:
0:000> ~
. 0 Id: 106c.4e4 Suspend: 1 Teb: 7ffde000 Unfrozen
1 Id: 106c.4e0 Suspend: 1 Teb: 7ffdc000 Unfrozen
2 Id: 106c.1158 Suspend: 1 Teb: 7ffdb000 Unfrozen
3 Id: 106c.c3c Suspend: 1 Teb: 7ffd9000 Unfrozen
4 Id: 106c.1174 Suspend: 1 Teb: 7ffd8000 Unfrozen
5 Id: 106c.1168 Suspend: 1 Teb: 7ffd4000 Unfrozen
6 Id: 106c.1568 Suspend: 1 Teb: 7ffaf000 Unfrozen
7 Id: 106c.1574 Suspend: 1 Teb: 7ffad000 Unfrozen
8 Id: 106c.964 Suspend: 1 Teb: 7ffac000 Unfrozen
9 Id: 106c.1164 Suspend: 1 Teb: 7ffab000 Unfrozen
10 Id: 106c.d84 Suspend: 1 Teb: 7ffaa000 Unfrozen
11 Id: 106c.bf4 Suspend: 1 Teb: 7ffa9000 Unfrozen
12 Id: 106c.eac Suspend: 1 Teb: 7ffa8000 Unfrozen
13 Id: 106c.614 Suspend: 1 Teb: 7ffd5000 Unfrozen
14 Id: 106c.cd8 Suspend: 1 Teb: 7ffa7000 Unfrozen
15 Id: 106c.1248 Suspend: 1 Teb: 7ffa6000 Unfrozen
16 Id: 106c.12d4 Suspend: 1 Teb: 7ffa4000 Unfrozen
17 Id: 106c.390 Suspend: 1 Teb: 7ffa3000 Unfrozen
18 Id: 106c.764 Suspend: 1 Teb: 7ffa1000 Unfrozen
19 Id: 106c.f48 Suspend: 1 Teb: 7ff5f000 Unfrozen
20 Id: 106c.14a8 Suspend: 1 Teb: 7ff53000 Unfrozen
21 Id: 106c.464 Suspend: 1 Teb: 7ff4d000 Unfrozen
22 Id: 106c.1250 Suspend: 1 Teb: 7ffa5000 Unfrozen
23 Id: 106c.fac Suspend: 1 Teb: 7ff5c000 Unfrozen
24 Id: 106c.1740 Suspend: 1 Teb: 7ffd7000 Unfrozen
25 Id: 106c.ae4 Suspend: 1 Teb: 7ffd6000 Unfrozen
26 Id: 106c.a4c Suspend: 1 Teb: 7ffdd000 Unfrozen
27 Id: 106c.1710 Suspend: 1 Teb: 7ffda000 Unfrozen
28 Id: 106c.1430 Suspend: 1 Teb: 7ffa2000 Unfrozen
29 Id: 106c.1404 Suspend: 1 Teb: 7ff4e000 Unfrozen
30 Id: 106c.9a8 Suspend: 1 Teb: 7ff4c000 Unfrozen
31 Id: 106c.434 Suspend: 1 Teb: 7ff4b000 Unfrozen
32 Id: 106c.c8c Suspend: 1 Teb: 7ff4a000 Unfrozen
33 Id: 106c.4f0 Suspend: 1 Teb: 7ff49000 Unfrozen
34 Id: 106c.be8 Suspend: 1 Teb: 7ffae000 Unfrozen
35 Id: 106c.14e0 Suspend: 1 Teb: 7ff5d000 Unfrozen
36 Id: 106c.fe0 Suspend: 1 Teb: 7ff5b000 Unfrozen
37 Id: 106c.1470 Suspend: 1 Teb: 7ff57000 Unfrozen
38 Id: 106c.16c4 Suspend: 1 Teb: 7ff5e000 Unfrozen
0:000> !teb 7ffad000
TEB at 7ffad000
ExceptionList: 0181ff0c
StackBase: 01820000
StackLimit: 0181c000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffad000
EnvironmentPointer: 00000000
ClientId: 0000106c . 00001574
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: c000000d
Count Owned Locks: 0
HardErrorMode: 0
0:000> dps 0181c000 01820000
0181c000 00000000
0181c004 00000000
0181c008 00000000
0181c00c 00000000
0181c010 00000000
0181c014 00000000
0181c018 00000000
0181c01c 00000000
0181c020 00000000
0181c024 00000000
...
...
...
0181ffb8 0181ffec
0181ffbc 77e6608b kernel32!BaseThreadStart+0x34
0181ffc0 00f31eb0
0181ffc4 00000000
0181ffc8 00000000
0181ffcc 00f31eb0
0181ffd0 8a38f7a8
0181ffd4 0181ffc4
0181ffd8 88a474b8
0181ffdc ffffffff
0181ffe0 77e6b7d0 kernel32!_except_handler3
0181ffe4 77e66098 kernel32!`string'+0x98
0181ffe8 00000000
0181ffec 00000000
0181fff0 00000000
0181fff4 7923a709
0181fff8 00f31eb0
0181fffc 00000000
01820000 ????????
However, if our process has many threads, like in the example above, and we want to dump stack data from all of them, we need to automate this process. After several attempts I created the following simple script which can be copy-pasted into WinDbg command window or saved in a text file to be loaded and executed later via WinDbg $$>< command. The script takes the advantage of the following command
~e (Thread-Specific Command)
The ~e command executes one or more commands for a specific thread or for all threads in the target process.
(from WinDbg help)
Here is the script:
~*e r? $t1 = ((ntdll!_NT_TIB *)@$teb)->StackLimit; r? $t2 = ((ntdll!_NT_TIB *)@$teb)->StackBase; !teb; dps @$t1 @$t2
Raw stack data from different stacks is separated by !teb output for clarity, for example:
0:000> .logopen rawdata.log
0:000> ~*e r? $t1 = ((ntdll!_NT_TIB *)@$teb)->StackLimit; r? $t2 = ((ntdll!_NT_TIB *)@$teb)->StackBase; !teb; dps @$t1 @$t2
TEB at 7ffde000
ExceptionList: 0007fd38
StackBase: 00080000
StackLimit: 0007c000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffde000
EnvironmentPointer: 00000000
ClientId: 0000106c . 000004e4
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
0007c000 00000000
0007c004 00000000
0007c008 00000000
0007c00c 00000000
0007c010 00000000
0007c014 00000000
0007c018 00000000
0007c01c 00000000
0007c020 00000000
0007c024 00000000
...
...
...
...
...
...
...
0977ffb4 00000000
0977ffb8 0977ffec
0977ffbc 77e6608b kernel32!BaseThreadStart+0x34
0977ffc0 025c3728
0977ffc4 00000000
0977ffc8 00000000
0977ffcc 025c3728
0977ffd0 a50c4963
0977ffd4 0977ffc4
0977ffd8 000a5285
0977ffdc ffffffff
0977ffe0 77e6b7d0 kernel32!_except_handler3
0977ffe4 77e66098 kernel32!`string'+0x98
0977ffe8 00000000
0977ffec 00000000
0977fff0 00000000
0977fff4 77bcb4bc msvcrt!_endthreadex+0x2f
0977fff8 025c3728
0977fffc 00000000
09780000 ????????
TEB at 7ffae000
ExceptionList: 0071ff64
StackBase: 00720000
StackLimit: 0071c000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffae000
EnvironmentPointer: 00000000
ClientId: 0000106c . 00000be8
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: c000000d
Count Owned Locks: 0
HardErrorMode: 0
0071c000 00000000
0071c004 00000000
0071c008 00000000
0071c00c 00000000
0071c010 00000000
0071c014 00000000
0071c018 00000000
0071c01c 00000000
0071c020 00000000
0071c024 00000000
0071c028 00000000
0071c02c 00000000
0071c030 00000000
0071c034 00000000
0071c038 00000000
0071c03c 00000000
0071c040 00000000
0071c044 00000000
0071c048 00000000
0071c04c 00000000
0071c050 00000000
0071c054 00000000
...
...
...
...
...
...
...
0:000> .logclose
Instead of (or in addition to) dps command used in the script we can use dpu or dpa commands to dump all strings that are pointed to by stack data or create an even more complex script that does triple dereference.
- Dmitry Vostokov @ DumpAnalysis.org -
December 24th, 2007 at 7:53 pm
[…] the previous part I used WinDbg scripting to get raw stack data from user process dump. However the script needs to be modified if the dump is complete memory dump. Here I use […]
April 29th, 2008 at 11:28 am
[…] a NULL thread procedure pointer to CreateThread function. We might expect to see little in the raw stack data because there was no user-supplied thread code. In reality, if we dump it we would see lots of […]
August 5th, 2008 at 12:24 pm
If you get this error
Symbol ntdll!_NT_TIB not found.
try to omit ntdll because sometimes the structure is imported into an application module or another DLL:
0:071> dt _NT_TIB
Application!_NT_TIB
+0×000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0×004 StackBase : Ptr32 Void
+0×008 StackLimit : Ptr32 Void
+0×00c SubSystemTib : Ptr32 Void
+0×010 FiberData : Ptr32 Void
+0×010 Version : Uint4B
+0×014 ArbitraryUserPointer : Ptr32 Void
+0×018 Self : Ptr32 _NT_TIB
August 10th, 2008 at 11:27 am
[…] stack output of dds command reveals them but beware of Coincidental Symbolic Information. See also Raw Stack Dump of all threads (process dumps) and Raw Stack Dump of all threads (part 2) (complete memory […]
May 11th, 2009 at 3:16 pm
[…] the script featured in part 1 doesn’t work because of the lack of symbols or […]
October 8th, 2010 at 3:34 pm
[…] Experts Magazine Online The previously published script to dump raw stack of all threads dumps only 64-bit raw stack from 64-bit WOW64 process memory dumps (a 32-bit process saved in a […]