Mfartura's blog

Marcelo Fartura's blog about debugging and general troubleshooting

Kernel dump analysis - Bugcheck 1E (KMODE_EXCEPTION_NOT_HANDLED)

Kernel dump analysis - Bugcheck 1E (KMODE_EXCEPTION_NOT_HANDLED)

  • Comments 3
  • Likes

It’s been a long time since my last post, but for some reason lately I’ve been receiving so many nice feedbacks about the blog and the other posts that I feel really motivated again to post a new article.  See how important your feedback is for me? J

Ok, normally I post about user mode debugging, but last week I was working on developing some content and I got this kernel memory dump I decided to take the risk of analyzing it, even considering Kernel debugging is not my specialty. 

Normally the first step when you’re analyzing dumps resultant from Blue Screens (I will write about the differences between kernel mode dumps and user mode dumps in another post) is to run the command !analyze –v.  Well, for this specific dump, !analyze –v is just not pointing to the right driver, at least I did not think so and that’s what motivated me to make a manual analysis of this specific dump.  Ok, too much said and very little action here… Let’s start with some hands on!

 

After opening the dump with Windbg.exe, setting the correct symbol path and running the command !analyze –v, this is what we have in summary:

 

KMODE_EXCEPTION_NOT_HANDLED (1e)

Arguments:

Arg1: c0000005, The exception code that was not handled

Arg2: a012a6c5, The address that the exception occurred at

Arg3: 00000000, Parameter 0 of the exception

Arg4: 00000014, Parameter 1 of the exception

 

FAULTING_IP:

win32k!DdDxApiOpenCaptureDevice+18

a012a6c5 8b5814          mov     ebx,dword ptr [eax+14h]

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000014

READ_ADDRESS:  00000014

DEFAULT_BUCKET_ID:  DRIVER_FAULT

BUGCHECK_STR:  0x1E

 

FOLLOWUP_IP:

Dxapi!DxApi+43

bdae59f7 8bc6            mov     eax,esi

SYMBOL_STACK_INDEX:  3

SYMBOL_NAME:  Dxapi!DxApi+43

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: Dxapi

IMAGE_NAME:  Dxapi.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  382b8097

FAILURE_BUCKET_ID:  0x1E_Dxapi!DxApi+43

BUCKET_ID:  0x1E_Dxapi!DxApi+43

Followup: MachineOwner

 

So it’s telling us a lot of useful information like what was the bugcheck (1E), what kind of unhandled exception happened (Access Violation – 0xC0000005) and what was the instruction pointer (IP) when the exception happened.  In this case it was address 0xa012a6c5.  It’s also pointing to the driver Dxapi.sys as the probable guilty for the problem.  Well, my curiosity on that was exactly because the instruction pointer was pointing to an address that doesn’t belong to Dxapi.sys.  Let’s check that:

kd> lmvm Dxapi

start    end        module name

bdae4000 bdae6640   Dxapi      (pdb symbols)          c:\debuggers\sym\dxapi.pdb\382B80971\dxapi.pdb

    Loaded symbol image file: c:\debuggers\sym\Dxapi.dbg\382B80972640\Dxapi.dbg

    Image path: \SystemRoot\System32\DRIVERS\Dxapi.sys

    Image name: Dxapi.sys

    Timestamp:        Fri Nov 12 00:51:03 1999 (382B8097)

    CheckSum:         0000730A

    ImageSize:        00002640

    File version:     5.0.2180.1

    Product version:  5.0.2180.1

    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(R) Windows (R) 2000 Operating System

    InternalName:     dxapi.sys

    OriginalFilename: dxapi.sys

    ProductVersion:   5.00.2180.1

    FileVersion:      5.00.2180.1

    FileDescription:  DirectX API Driver

    LegalCopyright:   Copyright (C) Microsoft Corp. 1981-1999

 

By checking the base address (yellow) of the module and its end address (green) we notice that the address for the instruction pointers is not in between them.  Actually the instruction point address was from Win32k.sys:

 

kd> lmvm win32k

start    end        module name

a0000000 a01a5e60   win32k     (pdb symbols)          c:\debuggers\sym\win32k.pdb\38E910C02\win32k.pdb

    Loaded symbol image file: c:\debuggers\sym\win32k.dbg\3947E2231a5e60\win32k.dbg

    Image path: \??\D:\WINNT\system32\win32k.sys

    Image name: win32k.sys

    Timestamp:        Wed Jun 14 16:50:59 2000 (3947E223)

    CheckSum:         001B02D1

    ImageSize:        001A5E60

    Translations:     0000.04b0 0000.04e0 0409.04b0 0409.04e0

 

So the address 0xa012a6c5 is indeed in between those addresses which means it was an instruction from this driver (Win32k.sys) that tried to an illegal operation.  But let’s wait a little bit before blaming the poor kernel image of the Win32 Subsystem (This is basically what the Win32k.sys is).  Let’s check our assembly code and try to see what really happened here…

 

Checking our thread’s stack:

 

kd> k

ChildEBP RetAddr 

bd2858a0 80463b2b nt!KiDispatchException+0x30e

bd285908 80463add nt!CommonDispatchException+0x4d

bd285908 a012a6c5 nt!KiUnexpectedInterruptTail+0x1f4

bd2859b4 f0593a8e Dxapi!DxApi+0x43

WARNING: Stack unwind information not available. Following frames may be wrong.

bd285a14 f0592bb4 3dfxV3TV+0x3a8e

bd285a34 f05928cc 3dfxV3TV+0x2bb4

bd285a4c f05a0cfd 3dfxV3TV+0x28cc

00000000 00000000 STREAM!SCIssueRequestToDevice+0xad

 

This actually happened after the exception happened – It was dispatching the exception.  The original context (call stack and registers) was actually saved on a trap frame.  From the debugger help we get the information which states that the third parameter passed to the function KiDispatchException is a pointer to our trap frame.  The command .trap will load the context saved on the trap.

 

kd> kb1

ChildEBP RetAddr  Args to Child             

bd2858a0 80463b2b bd2858bc 00000000 bd285910 nt!KiDispatchException+0x30e

 

kd> .trap bd285910

ErrCode = 00000000

eax=00000000 ebx=84f0ba10 ecx=a012a6ad edx=00000020 esi=bd2859dc edi=80d724a8

eip=a012a6c5 esp=bd285984 ebp=bd2859a0 iopl=0         nv up ei pl zr na pe nc

cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246

win32k!DdDxApiOpenCaptureDevice+0x18:

a012a6c5 8b5814          mov     ebx,dword ptr [eax+14h] ds:0023:00000014=????????

 

Ok, now we have the right context where the exception happened.  Notice that our current EIP on this context matches the one pointed by the !analyze –v as the one where the exception happened (in red above).  We basically crashed because we’re trying to move the content pointed by EAX+0x14 to EBX.  Since EAX = 0 (green above) we failed when trying to read from the invalid address 0x00000014 (which is the resultant address from 0x0 + 0x14).

 

Let’s look at the assembly code that execute immediately before this a012a6c5.

 

kd> ub a012a6c8

win32k!DdDxApiOpenCaptureDevice+0x7:

a012a6b4 56              push    esi

a012a6b5 8b7508          mov     esi,dword ptr [ebp+8]

a012a6b8 57              push    edi

a012a6b9 8365fc00        and     dword ptr [ebp-4],0

a012a6bd 8b06            mov     eax,dword ptr [esi]

a012a6bf 8b7814          mov     edi,dword ptr [eax+14h]

a012a6c2 8b4604          mov     eax,dword ptr [esi+4]

a012a6c5 8b5814          mov     ebx,dword ptr [eax+14h] ß This is where we crashed

 

 

So we need to find out on the assembly block above where EAX gets set to 0x0. 

 

kd> ub a012a6c8

win32k!DdDxApiOpenCaptureDevice+0x7:

a012a6b4 56              push    esi

a012a6b5 8b7508          mov     esi,dword ptr [ebp+8]

a012a6b8 57              push    edi

a012a6b9 8365fc00        and     dword ptr [ebp-4],0

a012a6bd 8b06            mov     eax,dword ptr [esi] 

a012a6bf 8b7814          mov     edi,dword ptr [eax+14h]

 

a012a6c2 8b4604          mov     eax,dword ptr [esi+4] ß This is where EAX gets updated for the last time before the crash

 

a012a6c5 8b5814          mov     ebx,dword ptr [eax+14h]

 

 

So EAX is actually receiving its value from the content of ESI+4.  Following the same logic we need to find out where ESI is getting updated for the last time before being involved on the instruction with EAX.

 

 

kd> ub a012a6c8

win32k!DdDxApiOpenCaptureDevice+0x7:

a012a6b4 56              push    esi

 

a012a6b5 8b7508          mov     esi,dword ptr [ebp+8] ß This where ESI gets updated for the last time before the instruction at a012a6c2

 

a012a6b8 57              push    edi

a012a6b9 8365fc00        and     dword ptr [ebp-4],0

a012a6bd 8b06            mov     eax,dword ptr [esi] 

a012a6bf 8b7814          mov     edi,dword ptr [eax+14h]

a012a6c2 8b4604          mov     eax,dword ptr [esi+4]

a012a6c5 8b5814          mov     ebx,dword ptr [eax+14h]

 

So ESI is getting its value from the content of EBP+8.  EBP+8 happens to be a fixed position on the stack used as a reference for the first parameter passed to the function (considering the calling convention used is not fast call).  EBP+8 being the first parameter, EBP+0xC the second, EBP+0x10 the third and so on… There are other pre-defined positions on the stack you should be aware of like EBP+4 is where the return address gets saved or EBP-4 for the first local variable etc, but this is subject for another post…

 

Let’s confirm our theory:

 

kd> dd poi(ebp+8)+4 l1

bd2859e0  00000000

 

kd> dd ebp+8 l1

bd2859a8  bd2859dc

 

Ok, so this is really where the 0x0 on EAX is coming from.  At this point we exclude Win32K.sys from our list of suspects after all it’s just receiving an invalid parameter being passed from the previous function on the stack:

 

kd> kb3

ChildEBP RetAddr  Args to Child             

bd2859a0 bdae59f7 bd2859dc bd285a0c 00028bb1 win32k!DdDxApiOpenCaptureDevice+0x18

bd2859b4 f0593a8e 00000513 bd2859dc 00000020 Dxapi!DxApi+0x43

bd285a14 f0592bb4 81c83228 84f0ba10 822efac8 3dfxV3TV+0x3a8e

 

As we can see from the stack above, the first parameter passed to the Win32k!DdDxApiOpenCaptureDevice is really the one with the corrupted value.  The parameter is being passed from Dxapi!dDxApi.

 

Now take at that stack again…  Pay attention to the details on the stack..  Can you give me a reason for excluding DxApi.sys from our suspect list also?  No?  Ok, let’s do it on the funniest way then J,  shall we?

 

Now we need to look at the assembly code of Dxapi!Dxapi.  Here is the entire assembly block for such function:

 

kd> uf Dxapi!DxApi

Dxapi!DxApi:

bdae59b4 55              push    ebp

bdae59b5 8bec            mov     ebp,esp

bdae59b7 8b4508          mov     eax,dword ptr [ebp+8]

bdae59ba 56              push    esi

bdae59bb 2d00050000      sub     eax,500h

bdae59c0 33f6            xor     esi,esi

bdae59c2 83f816          cmp     eax,16h

bdae59c5 7330            jae     Dxapi!DxApi+0x43 (bdae59f7)

 

Dxapi!DxApi+0x13:

bdae59c7 8d0440          lea     eax,[eax+eax*2]

bdae59ca c1e002          shl     eax,2

 

bdae59cd 8b88005baebd    mov     ecx,dword ptr Dxapi!gDxApiEntryPoint (bdae5b00)[eax]

Here we’re loading ECX whith the pointer to an external function – In this case this is the function Win32k!DdDxApiOpenCaptureDevice

 

bdae59d3 85c9            test    ecx,ecx

bdae59d5 7420            je      Dxapi!DxApi+0x43 (bdae59f7)

 

Dxapi!DxApi+0x23:

bdae59d7 8b5510          mov     edx,dword ptr [ebp+10h]

bdae59da 3b90045baebd    cmp     edx,dword ptr Dxapi!gDxApiEntryPoint+0x4 (bdae5b04)[eax]

bdae59e0 7215            jb      Dxapi!DxApi+0x43 (bdae59f7)

 

Dxapi!DxApi+0x2e:

bdae59e2 8b80085baebd    mov     eax,dword ptr Dxapi!gDxApiEntryPoint+0x8 (bdae5b08)[eax]

bdae59e8 394518          cmp     dword ptr [ebp+18h],eax

bdae59eb 720a            jb      Dxapi!DxApi+0x43 (bdae59f7)

 

Dxapi!DxApi+0x39:

bdae59ed ff7514          push    dword ptr [ebp+14h]

bdae59f0 8bf0            mov     esi,eax

bdae59f2 ff750c          push    dword ptr [ebp+0Ch]

 

bdae59f5 ffd1            call    ecx  ß Here is where the function effectively gets called

 

Dxapi!DxApi+0x43:

bdae59f7 8bc6            mov     eax,esi  ß This is our return address as you check on the stack

 

bdae59f9 5e              pop     esi

bdae59fa 5d              pop     ebp

bdae59fb c21400          ret     14h

 

Look at this specific part of the block:

 

Dxapi!DxApi+0x39:

bdae59ed ff7514          push    dword ptr [ebp+14h]

bdae59f0 8bf0            mov     esi,eax

 

bdae59f2 ff750c          push    dword ptr [ebp+0Ch]  ß This is the first parameter being pushed on the stack.  This will be read by the next function at the EBP+8 relative position.

 

bdae59f5 ffd1            call    ecx  ß Here is where the function effectively gets called

 

So our bad parameter comes from EBP+0Ch on this specific function.   EBP+0Ch happens to be the location for the second parameter (the parameters, considering we’re using the calling convention STANDARD or CDECL – FAST_CALL will pass the two first parameters through the registers ECX and EDX instead) passed to this function from the previous one (EBP+0x8 is relative location where the first parameter can be retrieved, EBP+0xC is basically 4 bytes beyond and is the second parameter, the same way EBP+0x10 will store the third parameter and so on).

 

So our next step will be revisiting the stack and investigate the previous function.

 

 

kd> kb3

ChildEBP RetAddr  Args to Child             

bd2859a0 bdae59f7 bd2859dc bd285a0c 00028bb1 win32k!DdDxApiOpenCaptureDevice+0x18

bd2859b4 f0593a8e 00000513 bd2859dc 00000020 Dxapi!DxApi+0x43

bd285a14 f0592bb4 81c83228 84f0ba10 822efac8 3dfxV3TV+0x3a8e

 

So the module 3dfxV3TV.sys is the one whose an internal function is passing the bad parameter which eventually caused our crash.  This is a third party module which means we will have no symbols for it.  In this case it’s time to look at the assembly code once again J

This is the assembly code for the function in question:

kd> ub f0593a8e l10

3dfxV3TV+0x3a5f:

f0593a5f 2bc7            sub     eax,edi

f0593a61 7902            jns     3dfxV3TV+0x3a65 (f0593a65)

f0593a63 f7d8            neg     eax

f0593a65 3bc8            cmp     ecx,eax

f0593a67 7e06            jle     3dfxV3TV+0x3a6f (f0593a6f)

f0593a69 8993c4000000    mov     dword ptr [ebx+0C4h],edx

f0593a6f 8b83c4000000    mov     eax,dword ptr [ebx+0C4h]

f0593a75 8945d8          mov     dword ptr [ebp-28h],eax

f0593a78 8d45f8          lea     eax,[ebp-8]

f0593a7b 6a08            push    8

f0593a7d 50              push    eax

f0593a7e 8d45c8          lea     eax,[ebp-38h]

 

f0593a81 6a20            push    20h   ß  Third parameter being pushed (this will be read by the next function at the EBP+0x10 relative position)

 

f0593a83 50              push    eax  ß  Second parameter being pushed (this will be read by the next function at the EBP+0xC relative position)  This is the one we’re interested in.

 

f0593a84 6813050000      push    513h ß First parameter being pushed (this will be read by the next function at the EBP+0x8 relative position)

 

f0593a89 e874750000      call    3dfxV3TV!SetDecoderSize+0x32ec (f059b002)  ß This is where the next function on the stack gets called

 

Ok, so we identified on the assembly block what would be the second parameter passed to the next function.  EAX was the register  storing its value, so the next step is find out where EAX is being updated for the last time before being used to push the parameter on the stack.  Giving a second look at the same assembly block, now focused on any instruction involving EAX we see this:

 

kd> ub f0593a8e l10

3dfxV3TV+0x3a5f:

f0593a5f 2bc7            sub     eax,edi

f0593a61 7902            jns     3dfxV3TV+0x3a65 (f0593a65)

f0593a63 f7d8            neg     eax

f0593a65 3bc8            cmp     ecx,eax

f0593a67 7e06            jle     3dfxV3TV+0x3a6f (f0593a6f)

f0593a69 8993c4000000    mov     dword ptr [ebx+0C4h],edx

f0593a6f 8b83c4000000    mov     eax,dword ptr [ebx+0C4h]

f0593a75 8945d8          mov     dword ptr [ebp-28h],eax

f0593a78 8d45f8          lea     eax,[ebp-8]

f0593a7b 6a08            push    8

f0593a7d 50              push    eax

f0593a7e 8d45c8          lea     eax,[ebp-38h]

f0593a81 6a20            push    20h

f0593a83 50              push    eax

f0593a84 6813050000      push    513h

f0593a89 e874750000      call    3dfxV3TV!SetDecoderSize+0x32ec (f059b002)

 

The instruction above (lea eax, [ebp-38h]) is basically copying the content of the relative position EBP – 38h to EAX.   So our bad value is whatever EBP-0x38 points to.  Well, I told you EBP+0x8 is the reference for what the first parameter is being passed and then the following parameters would be 4 bytes apart…  EBP is also a reference for some other important things also stored on the stack (as long as we’re not FPO optimized – again this is subject for another post) like EBP+0x4 is always the location where the return address will be stored and EBP-0x4 is where the first local variable for the function will be stored. 

EBP-0x38 is for sure a location of a local variable used by the function (EBP-0x4 is the first local variable.  From this point on, the location of other variables will actually depend on the variable type since this will determine how much space we need to reserve on the stack to store such values).  Being a local variable on the function, there isn’t much we can do without symbols and source code, but we thing is for sure:  Different from what “!analyze –v” told us the problem is actually being caused by the driver 3DFXV3TV.SYS and the solution or part of it would be either remove or update such driver J.

Until the next post J

 

 

 

 

 

 

Comments
  • Thx for breaking it down so clearly for us once again.  Glad you posted something again...

  • Thanks Brad!  I will try to post more often :)

  • Thank you for explaining that. It's sooo hard to explore kernel mode code and crash dump analysis. If I had more time, I'd surely learn to write kernel mode and be fluent in assembler.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment