Minidump Analysis 63 SYMBOLS AND IMAGES Suppose we have a minidump with a stack trace that involves our product driver and due to some reason WinDbg doesn’t pick symbols automatically
Trang 1Minidump Analysis 61
0: kd> lmv m dxg
start end module name
bf9c3000 bf9d4580 dxg (pdb symbols)
Loaded symbol image file: dxg.sys
Mapped memory image file: c:\websymbols\dxg.sys\41107B9311580\dxg.sys
Image path: dxg.sys
Image name: dxg.sys
Timestamp: Wed Aug 04 07:00:51 2004 (41107B93)
CheckSum: 0001D181
ImageSize: 00011580
File version: 5.1.2600.2180
Product version: 5.1.2600.2180
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 3.7 Driver
File date: 00000000.00000000
Translations: 0409.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft® Windows® Operating System
InternalName: dxg.sys
OriginalFilename: dxg.sys
ProductVersion: 5.1.2600.2180
FileVersion: 5.1.2600.2180 (xpsp_sp2_rtm.040803-2158)
FileDescription: DirectX Graphics Driver
LegalCopyright: © Microsoft Corporation All rights reserved
0: kd> lmv m win32k
start end module name
bf800000 bf9c2180 win32k # (pdb symbols)
Loaded symbol image file: win32k.sys
Mapped memory image file:
c:\websymbols\win32k.sys\45F013F61c2180\win32k.sys
Image path: win32k.sys
Image name: win32k.sys
Timestamp: Thu Mar 08 13:47:34 2007 (45F013F6)
CheckSum: 001C4886
ImageSize: 001C2180
File version: 5.1.2600.3099
Product version: 5.1.2600.3099
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 3.7 Driver
File date: 00000000.00000000
Translations: 0406.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft® Windows® Operativsystem
Trang 3Minidump Analysis 63
SYMBOLS AND IMAGES
Suppose we have a minidump with a stack trace that involves our product driver
and due to some reason WinDbg doesn’t pick symbols automatically and shows the
following stack trace and crash address pointing to driver.sys module:
start end module name
bfa9e000 bfb42a00 driver T (no symbols)
Loaded symbol image file: driver.sys
Image path: driver.sys
Image name: driver.sys
Timestamp: Thu Mar 01 20:50:04 2007 (45E73C7C)
CheckSum: 000A5062
ImageSize: 000A4A00
Translations: 0000.04b0 0000.04e0 0409.04b0 0409.04e0
We see that no symbols for driver.sys were found and this is also indicated by the
absence of function names and the presence of huge code offsets like 0x2df2a
Perhaps we don’t have a symbol server and store our symbol files somewhere Or we
got symbols from the developer of the recent fix that bugchecks and we want to apply
them In any case if we add a path to Symbol Search Path dialog (File -> Symbol File Path
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 4…) or use sympath WinDbg command we are able to get better stack trace and crash
point:
1: kd> reload
Loading Kernel Symbols
Loading User Symbols
Loading unloaded module list
Unable to load image driver.sys, Win32 error 0n2
*** WARNING: Unable to verify timestamp for driver.sys
1: kd> kL
ChildEBP RetAddr
ba0fd0c0 bfabc399 driver!ProcessBytes+0×18
ba0fd0e4 bfabd64b driver!ProcessObject+0xc9
Because WinDbg reports that it was unable to verify timestamp for driver.sys we
might want to double check the return address saved when ProcessBytes function was
called If symbols are correct then disassembling the return address backwards will most
Trang 5Minidump Analysis 65 likely show ProcessObject function code and “call” instruction with ProcessBytes
address Unfortunately minidumps don’t have code except for the currently executing
No code found, aborting
Therefore we need to point WinDbg to our driver.sys which contains executable
code This can be done by specifying a path in Executable Image Search Path dialog
(File -> Image File Path …) or using exepath WinDbg command
Now we get more complete stack trace and we are able to double check the
re-turn address:
1: kd> reload
Loading Kernel Symbols
Loading User Symbols
Loading unloaded module list
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 61: kd> kL
ChildEBP RetAddr
ba0fd0c0 bfabc399 driver!ProcessBytes+0×18
ba0fd0e4 bfabd64b driver!ProcessObject+0xc9
ba0fd104 bfac5aac driver!CacheBitBlt+0×13d
ba0fd114 bfac6840 driver!ProcessCommand+0×150
ba0fd140 bfac1878 driver!CheckSurface+0×258
ba0fd178 bfaba0ee driver!CopyBitsEx+0xfa
bfabc387 57 push edi
bfabc388 40 inc eax
bfabc389 50 push eax
bfabc38a e861fb0000 call driver!convert (bfacbef0)
bfabc38f ff7508 push dword ptr [ebp+8]
bfabc392 57 push edi
bfabc393 50 push eax
bfabc394 e879fb0000 call driver!ProcessBytes (bfacbf12)
Trang 7Minidump Analysis 67
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 8INTERRUPTS AND EXCEPTIONS EXPLAINED
EXCEPTIONS AB INITIO
Where do native exceptions come from? How do they propagate from hardware
and eventually result in crash dumps? I was asking these questions when I started doing
crash dump analysis more than four years ago and I tried to find answers using IA-32
Intel® Architecture Software Developer’s Manual, WinDbg and complete memory
dumps Let’s look at some findings
Trang 9Interrupts and Exceptions Explained 69
X86 INTERRUPTS
How do exceptions happen in the first place and how does the execution flow
reach KiDispatchException function? When some abnormal condition happens such as a
breakpoint, division by zero or memory protection violation then the normal CPU
execu-tion flow (code stream) is interrupted (I use the terms “interrupt” and “excepexecu-tion”
interchangeably here) The type of interrupt is specified by a number called interrupt
vector number CPU has to transfer execution to some procedure in memory to handle
that interrupt CPU has to find that procedure, theoretically either having one procedure
for all interrupts and specifying an interrupt vector number as a parameter or having a
table containing pointers to various procedures that correspond to different interrupt
vectors Intel x86 and x64 CPUs use the latter approach which is depicted on the
follow-ing diagram:
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 10KiTrap00 (808347ca)
KiTrap01 (8083494a) KiTrap02 (80834a3d)
808347d4 push ebx 808347d5 push esi 808347d6 push edi 808347d7 push fs 808347d9 mov ebx,30h 808347de mov fs,bx 808347e0 mov ebx,dword ptr fs:[0]
808347e7 push ebx
Trang 11Interrupts and Exceptions Explained 71
When an exception happens, for example, divide by zero, CPU gets the address
of the procedure table from IDTR (Interrupt Descriptor Table Register) This IDT
(Inter-rupt Descriptor Table) is a zero-based array of 8-byte descriptors (x86) or 16-byte
descriptors (x64) CPU calculates the location of the necessary procedure to call and
does some necessary steps like saving appropriate registers before the call
The same happens when some external I/O device interrupts For I/O devices the
term “interrupt” is more appropriate On the picture above I/O hardware interrupt
vec-tor numbers were taken from some crash dump These are OS and user-defined
num-bers The first 32 vectors are reserved by Intel Before Windows switches CPU to
pro-tected mode during boot process it creates IDT table in memory and sets IDTR to point
to it by using SIDT instruction
Let me now illustrate this by using a UML class diagram annotated by
code that shows what CPU does before calling the appropriate procedure The
pseudo-code on the diagram below is valid for interrupts and exceptions happening when
the current CPU execution mode is kernel For interrupts and exceptions
gener-ated when CPU executes code in user mode the picture is a little more complicgener-ated
because the processor has to switch the current user-mode stack to kernel mode stack
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 12The following diagram is for 32-bit x86 processor:
Offset(0 15) : Uint2BSelector : Uint2BAccess : Uint2BExtendedOffset(16 31) : Uint2B
IDT[VectorNumber].ExtendedOffset<<16 + IDT[VectorNumber].Offset
Let’s see all this in some kernel memory dump The address of IDT can be found
by using !pcr [processor number] command Every processor on a multiprocessor
ma-chine has its own IDT:
Trang 13Interrupts and Exceptions Explained 73
Every entry in IDT has the type _KIDTENTRY and we can get the first entry for
di-vide by zero exception which has vector number 0:
By gluing together ExtendedOffset and Offset fields we get the address of the
interrupt handling procedure (0×808347ca) which is KiTrap00:
0: kd> ln 0x808347ca
(808347ca) nt!KiTrap00 | (808348a5) nt!Dr_kit1_a
Exact matches:
nt!KiTrap00
We can also see the interrupt trace in raw stack For example, we have the
following stack trace and registers in the output of !analyze -v command:
Trang 14Dumping memory around ESP value (f2178c1c) shows the values processor
pushes when divide by zero exception happens:
Trang 15Interrupts and Exceptions Explained 75
ErrorCode is not the same as interrupt vector number although it is the same
number here (0) I won’t cover interrupt error codes here If you are interested please
consult Intel Architecture Software Developer’s Manual
If we try to execute !idt extension command it will show us only user-defined
hardware interrupt vectors:
93: 89f1e044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 89f1e008)
a3: 894fa044 USBPORT!USBPORT_InterruptService (KINTERRUPT 894fa008)
a4: 894a3044 cpqcidrv+0×22AE (KINTERRUPT 894a3008)
b1: 89f697dc ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 89f697a0)
b3: 89498824 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 894987e8)
b4: 894a2044 cpqasm2+0×5B99 (KINTERRUPT 894a2008)
c1: 80a7d410 hal!HalpBroadcastCallService
d1: 80a7c754 hal!HalpClockInterrupt
e1: 80a7d830 hal!HalpIpiHandler
e3: 80a7d654 hal!HalpLocalApicErrorService
fd: 80a7e11c hal!HalpProfileInterrupt
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 16X64 INTERRUPTS
Now I describe changes in 64-bit Windows The size of IDTR is 10 bytes where 8
bytes hold 64-bit address of IDT The size of IDT entry is 16 bytes and it holds the
ad-dress of an interrupt procedure corresponding to an interrupt vector However interrupt
procedure names are different in x64 Windows, they do not follow the same pattern like
KiTrapXX
The following UML class diagram describes the relationship and also shows what
registers are saved In native x64 mode SS and RSP are saved regardless of kernel or user
mode
OffsetLow(0 15) : Uint2BSelector : Uint2BAccess : Uint2BOffsetMiddle(16 31) : Uint2BOffsetHigh(32:63) : Uint4BReserverd : Uint4B
IDT[VectorNumber].OffsetHigh<<32 + IDT[VectorNumber].OffsetMiddle<<16 + IDT[VectorNumber].OffsetLow
Let’s dump all architecture-defined interrupt procedure names This is a good
exercise because we will use WinDbg scripting !pcr extension reports wrong IDT base
so we use dt command:
Trang 17Interrupts and Exceptions Explained 77
Trang 18Next we dump the first entry of IDT array and glue together OffsetHigh,
OffsetMiddle and OffsetLow fields to form the interrupt procedure address
correspond-ing to the interrupt vector 0, divide by zero exception:
fffff800`0103f254 c645ab01 mov byte ptr [rbp-55h],1
fffff800`0103f258 488945b0 mov qword ptr [rbp-50h],rax
nt!KiDivideErrorFault = <no type information>
We see that the name of the procedure is KiDivideErrorFault and not KiTrap00
We can dump the second IDT entry manually by adding a 0×10 offset but in order to
automate this I wrote the following WinDbg script to dump the first 20 vectors and get
their interrupt procedure names:
r? $t0=(_KIDTENTRY64 *)0xfffff800`00124070; for (r $t1=0; @$t1 <= 13; r?
$t0=(_KIDTENTRY64 *)@$t0+1) { printf ―Interrupt vector %d (0x%x):\n‖,
@$t1, @$t1; ln @@c++(>OffsetHigh*0×100000000 +
@$t0->OffsetMiddle*0×10000 + @$t0->OffsetLow); r $t1=$t1+1 }
Trang 19Interrupts and Exceptions Explained 79
Here is the same script but formatted:
nt!KiInvalidOpcodeFault = <no type information>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 20nt!KiNpxSegmentOverrunAbort = <no type information>
Interrupt vector 10 (0xa):
nt!KiGeneralProtectionFault = <no type information>
Interrupt vector 14 (0xe):
Trang 21Interrupts and Exceptions Explained 81
nt!KiXmmException = <no type information>
Let’s look at some dump
BugCheck 1E, {ffffffffc0000005, fffffade5ba2d643, 0, 28}
This is KMODE_EXCEPTION_NOT_HANDLED bugcheck and obviously it could have
been an invalid memory access And indeed the stack WinDbg shows after opening a
dump and entering !analyze -v command is:
Last set context:
rax=fffffade4e8907f4 rbx=fffffade6de0c2e0 rcx=fffffa8014412000
rdx=fffffade71e7e2ac rsi=0000000000000000 rdi=fffffadffff03000
rip=fffffade5ba2d643 rsp=fffffade4e890780 rbp=fffffade71e7ffff
fffffade`5ba2d643 8b4e28 mov ecx,dword ptr [rsi+28h] ds:0950:0028=????????
KiPageFault function was called and from the dumped IDT we see that it
corres-ponds to the interrupt vector 14 (0xE) called on any memory reference that is not
present in physical memory
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
Trang 22Now I’m going to dump the raw stack around fffffade`4e890780 address to see
data the processor saved before calling KiPageFault function:
We see that the values are exactly the same as WinDbg shows in the saved
con-text above Actually if we look at Page Fault Error Code bits in Intel Architecture
Soft-ware Developer’s Manual Volume 3A, you would see that for this case, all zeroes, we
have:
the page was not present in memory
the fault was caused by the read access
the processor was executing in kernel mode
no reserved bits in page directory were set to 1 when 0s were expected
it was not caused by instruction fetch
Trang 23Interrupts and Exceptions Explained 83
INTERRUPT FRAMES AND STACK RECONSTRUCTION
When an interrupt happens and an x86 processor is in privileged protected mode
(ring 0) it pushes interrupt frame shown in the following pseudo-code:
This is an interrupt frame that is created by CPU and not a trap frame created by
a software interrupt handler to save CPU state (_KTRAP_FRAME)
If an interrupt happens when an x86 processor is in user mode (ring 3) then the
stack switch occurs before the processor saves user mode stack pointer SS:ESP and
pushes the rest of the interrupt frame Pushing both SS:RSP always happens on x64
processor regardless of the current execution mode, kernel or user The following x86
pseudo-code shows how interrupt frame is pushed on the current stack (to be precise,
on the kernel space stack if the interrupt happened in user mode):
Usually CS is 0×1b and SS is 0×23 for x86 Windows flat memory model so we can
easily identify this pattern on raw stack data
Why should we care about an interrupt frame? This is because in complete
mem-ory dumps we can see exceptions that happened in user space and processed at the
time the dump was saved
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.