[zz]Adventures In A 32-bit Minidump, Part 0

http://www.sevenforums.com/287662-post1.html

Yes, I realise it has "stacked", but how?!?

One of the items of information stored in a minidump is the "stack" of the crashing thread.

All threads have a stack, at all times, and the word "stack" is not used here in the slang sense (to crumple and fall). Since a thread's stack is fundamental to our understanding of its activity, including those times when that thread has caused a crash, let's examine a simple stack in more detail:

kd> k
ChildEBP RetAddr
f8b69dac 805c4cce nt!ExpWorkerThread+0x100
f8b69ddc 805411c2 nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16

The 'k' (stack unwind) command was used to display the current thread's stack. In the display above, there are 3 columns of information:

  1. ChildEBP: a pointer to a memory location which stores the address of the previous function on the stack ("stack frame").
  2. RetAddr: The "return address" where processing will resume once this function returns (finishes what it had to do).
  3. (not labeled) Function name: the module name and function names, according to the module symbols.

Focus first on the top frame of the current stack:

ChildEBP RetAddr
f8b69dac 805c4cce nt!ExpWorkerThread+0x100

The ChildEBP pointer is supposed to contain the address of the previous function's frame. Let's examine what is at that location using a variation of the debugger's 'd' (dump memory) command:

kd> dd f8b69dac L1
f8b69dac f8b69ddc

Notice that the address found there corresponds exactly to the previous (middle of the current stack) function's ChildEBP:

ChildEBP RetAddr
f8b69ddc 805411c2 nt!PspSystemThreadStartup+0x34

And now let's repeat the same procedure with the next ChildEBP value:

kd> dd f8b69ddc L1
f8b69ddc 00000000

The number found at memory address f8b69ddc is zero (0). That confirms what the last line from the full stack listing is telling us:

ChildEBP RetAddr
00000000 00000000 nt!KiThreadStartup+0x16

Since the function name is "KiThreadStartup" (Kernel Internal Thread Startup), it's understandable that the stack does not extend any further back, and KiThreadStartup was in fact the first function on the stack. Hence, the ChildEBP in that case was zero (0) - it wasn't called from another function.

What about RetAddr, the "return address"? Let's check the top frame's RetAddr:

ChildEBP RetAddr
f8b69dac 805c4cce nt!ExpWorkerThread+0x100
f8b69ddc 805411c2 nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16

The stack suggests that the ExpWorkerThread function was called by PspSystemThreadStartup, and it is logical to assume that control would be returned back to PspSystemThreadStartup once ExpWorkerThread has completed whatever task it was asked to perform. If we examine those memory addresses using the debugger's 'ln' ("list nearest" [function]) command:

kd> ln 805c4cce
(805c4c9a) nt!PspSystemThreadStartup+0x34

Yes, execution will resume precisely where we left off in PspSystemThreadStartup - at 0x34 (52 decimal) bytes from the start of that function. Likewise, we can expect that once PspSystemThreadStartup is completely finished, the OS will give control back to KiThreadStartup:

kd> ln 805411c2
(805411ac) nt!KiThreadStartup+0x16

=========================================

One potential question at this point is how does the debugger display the stack based on the contents of a memory dump - how does the 'k' command produce its output?

For each thread, the OS maintains an information block which keeps track of many thread attributes, including the locations in memory where the thread's stack starts and finishes. During a crash, the "information block" for the thread which directly caused the crash is recorded in the memory dump, and portions of it can be viewed with the '!thread' debugger command:

kd> !thread
THREAD 82265470 Cid 0004.0108 Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
DeviceMap e1005570
Owning Process 0 Image: <Unknown>
Attached Process 82154500 Image: winlogon.exe
Wait Start TickCount 38493 Ticks: 0
Context Switch Count 768
UserTime 00:00:00.000
KernelTime 00:00:00.020
Start Address nt!ExpWorkerThread (0x80533ee6)
Stack Init f8b6a000 Current f8b69d1c Base f8b6a000 Limit f8b67000 Call 0
Priority 12 BasePriority 12 PriorityDecrement 0 DecrementCount 0

Note the stored stack values, including the "current" stack pointer at the time. Based on a series of steps very similar to what we did above by following ChildEBP from a function back to its caller, the debugger "unwinds" and hence displays the crashing thread's stack in text format.

The function names at or near the top of the stack form an excellent starting point for web searches regarding the question of whether others have experienced identical or very similar crashes.

posted @ 2010-05-03 21:44  bettermanlu  阅读(301)  评论(0编辑  收藏  举报