Crash Dump Analysis Patterns (Part 12)

Crash Dump Analysis Patterns (Part 12)

번역: Heejune Kim (https://insidekernel.net , drost@naver.com)

번역일: 2007-11-13

 

크래쉬 덤프에서 자주 발생하는 다른 패턴은 컴포넌트 심볼 없음(No Component Symbols) 패턴입니다. 이 경우에 컴포넌트가 어떤 일을 하는가는 그것의 이름이나, 호출된 전체 스레드 스택, import table들을 살펴보면서 추측할 수 있습니다. 예를 하나 들어봅시다. 커널 덤프에서 어떤 스레드 스택에 component.sys 드라이버가 보이지만, 그 컴포넌트가 잠재적으로 무엇을 할 수 있을지에 대해서는 모릅니다. 우리는 심볼을 가지고 있지 않기 때문에, 그것의 import된 함수들 또한 볼 수 없습니다:

 

kd> x component!*
kd>

 

그것의 image header를 덤프하기 위해서 !dh 명령어를 사용해봅시다:

 

kd> lmv m component
start             end                 module name
fffffadf`e0eb5000 fffffadf`e0ebc000   component   (no symbols)
    Loaded symbol image file: component.sys
    Image path: \??\C:\Component\x64\component.sys
    Image name: component.sys
    Timestamp:        Sat Jul 01 19:06:16 2006 (44A6B998)
    CheckSum:         000074EF
    ImageSize:        00007000
    Translations:     0000.04b0 0000.04e0 0409.04b0 0409.04e0
kd> !dh fffffadf`e0eb5000
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
    8664 machine (X64)
       6 number of sections
44A6B998 time date stamp Sat Jul 01 19:06:16 2006
       0 file pointer to symbol table
       0 number of symbols
      F0 size of optional header
      22 characteristics
            Executable
            App can handle >2gb addresses
OPTIONAL HEADER VALUES
     20B magic #
    8.00 linker version
     C00 size of code
     A00 size of initialized data
       0 size of uninitialized data
    5100 address of entry point
    1000 base of code
         ----- new -----
0000000000010000 image base
    1000 section alignment
     200 file alignment
       1 subsystem (Native)
    5.02 operating system version
    5.02 image version
    5.02 subsystem version
    7000 size of image
     400 size of headers
    74EF checksum
0000000000040000 size of stack reserve
0000000000001000 size of stack commit
0000000000100000 size of heap reserve
0000000000001000 size of heap commit
       0 [       0] address [size] of Export Directory
    51B0 [      28] address [size] of Import Directory
    6000 [     3B8] address [size] of Resource Directory
    4000 [      6C] address [size] of Exception Directory
       0 [       0] address [size] of Security Directory
       0 [       0] address [size] of Base Relocation Directory
    2090 [      1C] address [size] of Debug Directory
       0 [       0] address [size] of Description Directory
       0 [       0] address [size] of Special Directory
       0 [       0] address [size] of Thread Storage Directory
       0 [       0] address [size] of Load Configuration Directory
       0 [       0] address [size] of Bound Import Directory
2000 [      88] address [size] of Import Address Table Directory
       0 [       0] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory


이제 Import Address Table Directory의 내용을 dps 명령어를 사용해서 볼 수 있습니다:

kd> dps fffffadf`e0eb5000+2000 fffffadf`e0eb5000+2000+88
fffffadf`e0eb7000  fffff800`01044370 nt!IoCompleteRequest
fffffadf`e0eb7008  fffff800`01019700 nt!IoDeleteDevice
fffffadf`e0eb7010  fffff800`012551a0 nt!IoDeleteSymbolicLink
fffffadf`e0eb7018  fffff800`01056a90 nt!MiResolveTransitionFault+0x7c2
fffffadf`e0eb7020  fffff800`0103a380 nt!ObDereferenceObject
fffffadf`e0eb7028  fffff800`0103ace0 nt!KeWaitForSingleObject
fffffadf`e0eb7030  fffff800`0103c570 nt!KeSetTimer
fffffadf`e0eb7038  fffff800`0102d070 nt!IoBuildPartialMdl+0x3
fffffadf`e0eb7040  fffff800`012d4480 nt!PsTerminateSystemThread
fffffadf`e0eb7048  fffff800`01041690 nt!KeBugCheckEx
fffffadf`e0eb7050  fffff800`010381b0 nt!KeInitializeTimer
fffffadf`e0eb7058  fffff800`0103ceb0 nt!ZwClose
fffffadf`e0eb7060  fffff800`012b39f0 nt!ObReferenceObjectByHandle
fffffadf`e0eb7068  fffff800`012b7380 nt!PsCreateSystemThread
fffffadf`e0eb7070  fffff800`01251f90 nt!FsRtlpIsDfsEnabled+0x114
fffffadf`e0eb7078  fffff800`01275160 nt!IoCreateDevice
fffffadf`e0eb7080  00000000`00000000
fffffadf`e0eb7088  00000000`00000000

 

이 드라이버는 특정한 상황 아래에서 KeBugCheckEx를 사용해서 시스템을 bugcheck할 수 있고, PsCreateSystemThread를 이용해서 시스템 스레드를 만들며, KeInitializeTimer와 KeSetTimer를 이용해서 타이머를 사용한다는 것을 보여줍니다.

만약 여러분이 import table에서 name+offset (OMAP 코드 최적화 때문에 발생했다고 생각됨)을 본다면, 여러분은 ln 명령어(list nearest symbols, 가장 가까운 심볼을 나열하라)를 사용해서 함수를 얻어낼 수 있습니다:

 

kd> ln fffff800`01056a90
(fffff800`01056760)   nt!MiResolveTransitionFault+0x7c2   |  (fffff800`01056a92)   nt!RtlInitUnicodeString
kd> ln fffff800`01251f90
(fffff800`01251e90)   nt!FsRtlpIsDfsEnabled+0×114   |  (fffff800`01251f92)   nt!IoCreateSymbolicLink

 

이 테크닉은 드라이버가 특정 함수를 호출할 때마다 bugcheck가 발생하거나, 또는 bugcheck 0x20처럼 특정 함수를 짝으로 반드시 호출할 때 발생하는 bugcheck 상황에서 유용하게 사용할 수 있습니다:

 

kd> !analyze -show 0x20
KERNEL_APC_PENDING_DURING_EXIT (20)
The key data item is the thread's APC disable count. If this is non-zero, then this is the source of the problem. The APC disable count is decremented each time a driver calls KeEnterCriticalRegion, KeInitializeMutex, or FsRtlEnterFileSystem. The APC disable count is incremented each time a driver calls KeLeaveCriticalRegion, KeReleaseMutex, or FsRtlExitFileSystem.  Since these calls should always be in pairs, this value should be zero when a thread exits. A negative value indicates that a driver has disabled APC calls without re-enabling them. A positive value indicates that the reverse is true. If you ever see this error, be very suspicious of all drivers installed on the machine — especially unusual or non-standard drivers. Third party file system redirectors are especially suspicious since they do not generally receive the heavy duty testing that NTFS, FAT, RDR, etc receive. This current IRQL should also be 0.  If it is not, that a driver’s cancelation routine can cause this bugcheck by returning at an elevated IRQL.  Always attempt to note what you were doing/closing at the time of the crash, and note all of the installed drivers at the time of the crash.  This symptom is usually a severe bug in a third party driver.

 

그러면 여러분은 최소한 의심가는 드라이버가 이런 함수들을 잠재적으로 사용했는지 아닌지를 위 방법을 이용하여 살펴보고 만약 그것들 중의 하나를 import 했다면 그 대응되는 짝이 되는 함수를 import 했는지 아닌지를 살펴볼 수 있을 것입니다.

- Dmitry Vostokov -