In previous posts we talked about virtual address space and how virtual memory is managed. I've never posted anything about virtual address translation though, and for the ones interested on the details behing this operation I recommend reading the chapter 7 - Memory Management (specifically the Address Translation section) of the Windows Internals 4th Edition book. So I won't go too deep on the details of how the system does the translation itself, my goal here is just how to show you how easily translate from one address to another once you have a memory dump or a live debugging session.
Once you choose the virtual address you want to do the translation the first step is to run the command !pte and find out some additional information for the corresponding PTE. For this example I'm going to use the virtual address used as a base address to load the tcpip driver itself on a 32-bit Windows 2003 SP2 system:
0: kd> lmvm tcpip
start end module name
f7625000 f7686000 tcpip (deferred)
Image path: \SystemRoot\system32\DRIVERS\tcpip.sys
Image name: tcpip.sys
Timestamp: Thu Mar 24 19:40:31 2005 (42435DFF)
CheckSum: 0005606F
ImageSize: 00061000
Above highlighted in yellow we have the base address where the tcpip driver has been loaded (virtual address). Now we run the command !pte passing this address as an argument:
0: kd> !pte f7625000
VA f7625000
PDE at C0300F74 PTE at C03DD894
contains 00ACC963 contains 1C055963
pfn acc -G-DA--KWEV pfn 1c055 -G-DA—KWEV
The highlighted information above is about the PFN correlated to the physical address associated with the virtual address we passed as an input to !pte. However you noticed that 1c055 uses up to 20 bits (which are used to locate the PDE and PTE itself which will point to the physical page) and we know the completed addresses are 32 bits wide. The last 12 bits on the translation process are used as a byte index to the physical page where the content is actually stored, so they come straight from the virtual address.
The final physical address will then be 1c055 followed by the last 12 bits of the virtual address which in this case is 000. So putting them together we have that 1c055000 is the physical address correlated to the virtual address f7652000. Let’s verify that by dumping both address and comparing their content:
Dumping the virtual address first:
0: kd> dc f7625000
f7625000 00905a4d 00000003 00000004 0000ffff MZ..............
f7625010 000000b8 00000000 00000040 00000000 ........@.......
f7625020 00000000 00000000 00000000 00000000 ................
f7625030 00000000 00000000 00000000 000000e0 ................
f7625040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
f7625050 70207369 72676f72 63206d61 6f6e6e61 is program canno
f7625060 65622074 6e757220 206e6920 20534f44 t be run in DOS
f7625070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
Now let’s dump the physical address and compare the results:
0: kd> dc /p 1c055000
1c055000 00905a4d 00000003 00000004 0000ffff MZ..............
1c055010 000000b8 00000000 00000040 00000000 ........@.......
1c055020 00000000 00000000 00000000 00000000 ................
1c055030 00000000 00000000 00000000 000000e0 ................
1c055040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
1c055050 70207369 72676f72 63206d61 6f6e6e61 is program canno
1c055060 65622074 6e757220 206e6920 20534f44 t be run in DOS
1c055070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
The content is identical as expected J.
Yet another kernel memory dump to be analyzed - The bugcheck this time is the 0xA - IRQL_NOT_LESS_OR_EQUAL. To better understand what this message means we would need a little background on Windows Internals but basically when executing anything at a interrupt request level (IRQL) = 2 or higher (in normal circumstances instructions get executed at IRQL = 0) we can't page fault. On the situation of this specific dump, we would crash even on IRQL = 0 since we're trying to write to the memory address 0x0 which is in user mode address space and is reserved (the first useable address in the user mode address space is 0x00010000)
Let's jump to the analysis.
Bugcheck info:
0: kd> .bugcheck
Bugcheck code 0000000A
Arguments 00000000 00000002 00000001 8087bb19
| | | |à This is the failing instruction address
| | |à 0x1 means WRITE, 0x0 means READ, so it crashed when trying to WRITE to 0x0
| |à This is the IRQL at which problem happened (IRQL=2)
|à This is the memory address where the problem happened
So it crashed basically because it failed to access a memory address (which at a lower IRQLs would result in a page fault) on the IRQL = 2. At IRQL = 2 and above page faults are not allowed to happen.
Current thread stack:
0: kd> kvnL
# ChildEBP RetAddr Args to Child
00 b7d6ebbc 8087bb19 badb0d00 00000000 00000000 nt!_KiTrap0E+0x2a7 (FPO: [0,0] TrapFrame @ b7d6ebbc)
01 b7d6ec40 80972b03 f7ca7008 80a56be4 00000000 nt!ExDeleteResourceLite+0x1f (FPO: [Non-Fpo]) (CONV: stdcall)
02 b7d6ec5c 80932ca2 ef4d0860 ef4d0848 00000000 nt!SepTokenDeleteMethod+0x9d (FPO: [Non-Fpo]) (CONV: stdcall)
03 b7d6ec74 8086c1a5 ef4d0860 00000000 8b478df0 nt!ObpRemoveObjectRoutine+0xde (FPO: [Non-Fpo]) (CONV: stdcall)
04 b7d6ec94 b7765ada f7ca3444 b7764007 f7ca8460 nt!ObfDereferenceObject+0x67 (FPO: [Non-Fpo]) (CONV: fastcall)
WARNING: Stack unwind information not available. Following frames may be wrong.
05 b7d6ecb0 b776037a f7cbf004 b7d6ece0 f82cde28 naiavf5x+0xcada
06 b7d6ecf0 b775b520 f82cde28 f82cde28 8b572548 naiavf5x+0x737a
07 b7d6ed04 8081dcdf 8b478990 f82cde28 f82cde38 naiavf5x+0x2520
08 b7d6ed18 808f8be4 00000000 00000000 f7ca8448 nt!IofCallDriver+0x45 (FPO: [Non-Fpo]) (CONV: fastcall)
09 b7d6ed50 80932ca2 00ca8460 f7ca8460 00000001 nt!IopDeleteFile+0x13a (FPO: [Non-Fpo]) (CONV: stdcall)
0a b7d6ed68 80932cec f7ca8460 00000001 8ad47770 nt!ObpRemoveObjectRoutine+0xde (FPO: [Non-Fpo]) (CONV: stdcall)
0b b7d6ed80 8087f92f 00000000 00000000 8ad47770 nt!ObpProcessRemoveObjectQueue+0x36 (FPO: [1,0,0]) (CONV: stdcall)
0c b7d6edac 80948bd0 00000000 00000000 00000000 nt!ExpWorkerThread+0xeb (FPO: [Non-Fpo]) (CONV: stdcall)
0d b7d6eddc 8088d4e2 8087f844 80000000 00000000 nt!PspSystemThreadStartup+0x2e (FPO: [Non-Fpo]) (CONV: stdcall)
0e 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
The current frame being executed is a post-exception frame, I mean, it's just handling the situation. The problem really happened here:
0: kd> .trap b7d6ebbc
ErrCode = 00000002
eax=00000000 ebx=ef4d0848 ecx=00000000 edx=00000000 esi=f7ca7008 edi=00000000
eip=8087bb19 esp=b7d6ec30 ebp=b7d6ec40 iopl=0 nv up ei ng nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010282
nt!ExDeleteResourceLite+0x1f:
8087bb19 8901 mov dword ptr [ecx],eax ds:0023:00000000=????????
0: kd> kvnL
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr Args to Child
00 b7d6ec40 80972b03 f7ca7008 80a56be4 00000000 nt!ExDeleteResourceLite+0x1f (FPO: [Non-Fpo]) (CONV: stdcall)
01 b7d6ec5c 80932ca2 ef4d0860 ef4d0848 00000000 nt!SepTokenDeleteMethod+0x9d (FPO: [Non-Fpo]) (CONV: stdcall)
02 b7d6ec74 8086c1a5 ef4d0860 00000000 8b478df0 nt!ObpRemoveObjectRoutine+0xde (FPO: [Non-Fpo]) (CONV: stdcall)
03 b7d6ec94 b7765ada f7ca3444 b7764007 f7ca8460 nt!ObfDereferenceObject+0x67 (FPO: [Non-Fpo]) (CONV: fastcall)
WARNING: Stack unwind information not available. Following frames may be wrong.
04 b7d6ecb0 b776037a f7cbf004 b7d6ece0 f82cde28 naiavf5x+0xcada
05 b7d6ecf0 b775b520 f82cde28 f82cde28 8b572548 naiavf5x+0x737a
06 b7d6ed04 8081dcdf 8b478990 f82cde28 f82cde38 naiavf5x+0x2520
07 b7d6ed18 808f8be4 00000000 00000000 f7ca8448 nt!IofCallDriver+0x45 (FPO: [Non-Fpo]) (CONV: fastcall)
08 b7d6ed50 80932ca2 00ca8460 f7ca8460 00000001 nt!IopDeleteFile+0x13a (FPO: [Non-Fpo]) (CONV: stdcall)
09 b7d6ed68 80932cec f7ca8460 00000001 8ad47770 nt!ObpRemoveObjectRoutine+0xde (FPO: [Non-Fpo]) (CONV: stdcall)
0a b7d6ed80 8087f92f 00000000 00000000 8ad47770 nt!ObpProcessRemoveObjectQueue+0x36 (FPO: [1,0,0]) (CONV: stdcall)
0b b7d6edac 80948bd0 00000000 00000000 00000000 nt!ExpWorkerThread+0xeb (FPO: [Non-Fpo]) (CONV: stdcall)
0c b7d6eddc 8088d4e2 8087f844 80000000 00000000 nt!PspSystemThreadStartup+0x2e (FPO: [Non-Fpo]) (CONV: stdcall)
0d 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
So it's crashing when trying to write to the address pointed by ECX - mov dword ptr [ecx], eax - This is the assembly code of the function where it crashed from the start to the crash instruction :
0: kd> u nt!ExDeleteResourceLite nt!ExDeleteResourceLite+0x1f + 1
nt!ExDeleteResourceLite [d:\nt\base\ntos\ex\resource.c @ 2250]:
8087bafa 8bff mov edi,edi
8087bafc 55 push ebp
8087bafd 8bec mov ebp,esp
8087baff 83ec0c sub esp,0Ch
8087bb02 56 push esi
8087bb03 8d55f4 lea edx,[ebp-0Ch]
8087bb06 b9c0418b80 mov ecx,offset nt!ExpResourceSpinLock (808b41c0)
8087bb0b ff1508118080 call dword ptr [nt!_imp_KeAcquireInStackQueuedSpinLock (80801108)]
8087bb11 8b7508 mov esi,dword ptr [ebp+8] ßESI is obtaining its value from the content of EBP+8
8087bb14 8b4e04 mov ecx,dword ptr [esi+4] ß ECX is obtaining its value from the content of ESI+4
8087bb17 8b06 mov eax,dword ptr [esi]
8087bb19 8901 mov dword ptr [ecx],eax ß This is where it crashed
ECX is getting its value from the address pointed by ESI + 4, and ESI is getting its value from the address pointed by EBP + 8 which is a pointer to the first parameter passed from the previous function on the stack. Confirming:
0: kd> dd esi+4 l1
f7ca700c 00000000
0: kd> dd poi(ebp+8) l1
f7ca7008 00000000
Next step is look to the previous function on the stack. The assembly for the next function up to the point it calls the current one is this:
0: kd> u nt!SepTokenDeleteMethod nt!SepTokenDeleteMethod+0x9d
nt!SepTokenDeleteMethod [d:\nt\base\ntos\se\token.c @ 2652]:
80972a66 8bff mov edi,edi
80972a68 55 push ebp
80972a69 8bec mov ebp,esp
80972a6b 51 push ecx
80972a6c 51 push ecx
80972a6d 56 push esi
80972a6e 8b7508 mov esi,dword ptr [ebp+8] ß ESI is coming from the content EBP + 8 (first parameter passed from the previous function)
80972a71 f6868800000020 test byte ptr [esi+88h],20h
80972a78 57 push edi
80972a79 7538 jne nt!SepTokenDeleteMethod+0x4d (80972ab3)
80972a7b 8bbe94000000 mov edi,dword ptr [esi+94h]
80972a81 53 push ebx
80972a82 8d4f0c lea ecx,[edi+0Ch]
80972a85 eb0f jmp nt!SepTokenDeleteMethod+0x30 (80972a96)
80972a87 8d42ff lea eax,[edx-1]
80972a8a 8bd8 mov ebx,eax
80972a8c 8bc2 mov eax,edx
80972a8e f00fb119 lock cmpxchg dword ptr [ecx],ebx
80972a92 3bc2 cmp eax,edx
80972a94 741c je nt!SepTokenDeleteMethod+0x4c (80972ab2)
80972a96 8b11 mov edx,dword ptr [ecx]
80972a98 83fa01 cmp edx,1
80972a9b 75ea jne nt!SepTokenDeleteMethod+0x21 (80972a87)
80972a9d 8b4704 mov eax,dword ptr [edi+4]
80972aa0 8945f8 mov dword ptr [ebp-8],eax
80972aa3 8b4708 mov eax,dword ptr [edi+8]
80972aa6 8945fc mov dword ptr [ebp-4],eax
80972aa9 8d45f8 lea eax,[ebp-8]
80972aac 50 push eax
80972aad e890ecffff call nt!SepDeReferenceLogonSession (80971742)
80972ab2 5b pop ebx
80972ab3 8d4638 lea eax,[esi+38h]
80972ab6 8b08 mov ecx,dword ptr [eax]
80972ab8 0b4804 or ecx,dword ptr [eax+4]
80972abb 6a00 push 0
80972abd 5f pop edi
80972abe 7407 je nt!SepTokenDeleteMethod+0x61 (80972ac7)
80972ac0 57 push edi
80972ac1 50 push eax
80972ac2 e8b5140000 call nt!SepModifyTokenPolicyCounter (80973f7c)
80972ac7 8b4678 mov eax,dword ptr [esi+78h]
80972aca 3bc7 cmp eax,edi
80972acc 7407 je nt!SepTokenDeleteMethod+0x6f (80972ad5)
80972ace 57 push edi
80972acf 50 push eax
80972ad0 e86ff8f1ff call nt!ExFreePoolWithTag (80892344)
80972ad5 8b868c000000 mov eax,dword ptr [esi+8Ch]
80972adb 3bc7 cmp eax,edi
80972add 7406 je nt!SepTokenDeleteMethod+0x7f (80972ae5)
80972adf 50 push eax
80972ae0 e85766ffff call nt!SeFreeCapturedObjectTypeList (8096913c)
80972ae5 8b8690000000 mov eax,dword ptr [esi+90h]
80972aeb 3bc7 cmp eax,edi
80972aed 7407 je nt!SepTokenDeleteMethod+0x90 (80972af6)
80972aef 57 push edi
80972af0 50 push eax
80972af1 e84ef8f1ff call nt!ExFreePoolWithTag (80892344)
80972af6 8b4630 mov eax,dword ptr [esi+30h] ß EAX is coming from the content of ESI + 0x30
80972af9 3bc7 cmp eax,edi
80972afb 740f je nt!SepTokenDeleteMethod+0xa6 (80972b0c)
80972afd 50 push eax ß EAX is pushed on the stack as the first parameter
80972afe e8f78ff0ff call nt!ExDeleteResourceLite (8087bafa)
So once again we need to look at the previous function since this one is also receiving the bad value as parameter. By revisiting the stack we see the first parameter it passes is also received as below:
0: kd> kbL5
ChildEBP RetAddr Args to Child
b7d6ec40 80972b03 f7ca7008 80a56be4 00000000 nt!ExDeleteResourceLite+0x1f
b7d6ec5c 80932ca2 ef4d0860 ef4d0848 00000000 nt!SepTokenDeleteMethod+0x9d
b7d6ec74 8086c1a5 ef4d0860 00000000 8b478df0 nt!ObpRemoveObjectRoutine+0xde
b7d6ec94 b7765ada f7ca3444 b7764007 f7ca8460 nt!ObfDereferenceObject+0x67
WARNING: Stack unwind information not available. Following frames may be wrong.
b7d6ecb0 b776037a f7cbf004 b7d6ece0 f82cde28 naiavf5x+0xcada
As we can go directly to the function nt!ObfDereferenceObject+0x67 and try to see where its setting up that parameter from. The assembly code from this function to the point it calls the next one is this:
0: kd> u nt!ObfDereferenceObject nt!ObfDereferenceObject+0x67 ß This is a fastcall function so the first two parameters are passed through the registers ECX and EDX instead of using the stack (EBP as a reference)
nt!ObfDereferenceObject [d:\nt\base\ntos\ob\obref.c @ 2441]:
8086c13e 8bff mov edi,edi
8086c140 55 push ebp
8086c141 8bec mov ebp,esp
8086c143 51 push ecx
8086c144 803de0088a8000 cmp byte ptr [nt!ObpTraceEnabled (808a08e0)],0
8086c14b 53 push ebx
8086c14c 56 push esi
8086c14d 57 push edi
8086c14e 894dfc mov dword ptr [ebp-4],ecx ß The location pointed by EBP-0x4 which is local variable is receiving the value of ECX which is the first parameter passed to this function
8086c151 8d71e8 lea esi,[ecx-18h]
8086c154 7408 je nt!ObfDereferenceObject+0x20 (8086c15e)
8086c156 6a00 push 0
8086c158 56 push esi
8086c159 e8c8fcffff call nt!ObpPushStackInfo (8086be26)
8086c15e 83cbff or ebx,0FFFFFFFFh
8086c161 f00fc11e lock xadd dword ptr [esi],ebx
8086c165 4b dec ebx
8086c166 7547 jne nt!ObfDereferenceObject+0x71 (8086c1af)
8086c168 8b3d90108080 mov edi,dword ptr [nt!_imp__KeGetCurrentIrql (80801090)]
8086c16e ffd7 call edi
8086c170 64a124010000 mov eax,dword ptr fs:[00000124h]
8086c176 6683787200 cmp word ptr [eax+72h],0
8086c17b 752c jne nt!ObfDereferenceObject+0x6b (8086c1a9)
8086c17d ffd7 call edi
8086c17f 3c01 cmp al,1
8086c181 7326 jae nt!ObfDereferenceObject+0x6b (8086c1a9)
8086c183 803de0088a8000 cmp byte ptr [nt!ObpTraceEnabled (808a08e0)],0
8086c18a 740f je nt!ObfDereferenceObject+0x5d (8086c19b)
8086c18c 833dd8078a8000 cmp dword ptr [nt!ObpTraceNoDeregister (808a07d8)],0
8086c193 7506 jne nt!ObfDereferenceObject+0x5d (8086c19b)
8086c195 56 push esi
8086c196 e805fcffff call nt!ObpDeregisterObject (8086bda0)
8086c19b 6a00 push 0
8086c19d ff75fc push dword ptr [ebp-4] ß Here it is pushing the content of EBP-4 which we know is coming from ECX (first parameter) as a first parameter to the next function
8086c1a0 e81f6a0c00 call nt!ObpRemoveObjectRoutine (80932bc4) ß this is the function call to the next function
So we know the bad value is coming from the previous function on the stack which is naiavf5x+0xcada. By looking at the assembly for that we have this:
0: kd> u b7765ac1-10 b7765ad5+2
naiavf5x+0xcab1:
b7765ab1 ff1550a476b7 call dword ptr [naiavf5x+0x11450 (b776a450)]
b7765ab7 5f pop edi
b7765ab8 8bc6 mov eax,esi
b7765aba 5e pop esi
b7765abb 5b pop ebx
b7765abc 5d pop ebp
b7765abd c20800 ret 8
b7765ac0 56 push esi
b7765ac1 8bf1 mov esi,ecx ß ESI is coming from ECX
b7765ac3 8d86a8000000 lea eax,[esi+0A8h]
b7765ac9 50 push eax
b7765aca e8818affff call naiavf5x+0x5550 (b775e550)
b7765acf 85c0 test eax,eax
b7765ad1 7516 jne naiavf5x+0xcae9 (b7765ae9)
b7765ad3 8bce mov ecx,esi ß ECX which will be the first parameter to the next function is coming from ESI
b7765ad5 e8b6f7ffff call naiavf5x+0xc290 (b7765290)
CONCLUSION:
So at this point we can’t move further since we have no symbols for the NAIAVF5X.SYS driver – which is a file system filter driver from McAfee. The conclusion would be that this driver is bad until the McAfee analyze this same dump and prove us wrong J. Here is some addition info about the driver:
0: kd> lmvm naiavf5x
start end module name
b7759000 b776d440 naiavf5x (no symbols)
Loaded symbol image file: naiavf5x.sys
Image path: \SystemRoot\system32\drivers\naiavf5x.sys
Image name: naiavf5x.sys
Timestamp: Mon Aug 04 20:08:00 2003 (3F2F0370) ß By the way, this is old stuff. They might have already corrected this on newer versions of the driver and I wouldn’t be surprised to find some documentation about this on McAfee’s web site.
CheckSum: 00020D5B
ImageSize: 00014440
Translations: 0000.04b0 0000.04e0 0409.04b0 0409.04e0
By the way, for this dump, “!analyze –v” does the job and points to the right guilty driver.
Until the next dump analysis, I mean, next post :)
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]