Crash Dump Analysis Patterns (Part 11)

Crash Dump Analysis Patterns (Part 11)

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

번역일: 2007-11-07

 

초보자들이 자주 범하는 실수 중의 하나가 스택 트레이스를 보여주는 명령어인 WinDbg의 !analyzekv 명령을 신뢰하는 것입니다. WinDbg는 툴일 뿐이고, 때로는 올바른 스텍 트레이스를 하는데 필요한 크리티컬한 정보가 빠져있기도 합니다. 저는 이 패턴을 잘못된 스택 트레이스(Incorrect Stack Trace) 라고 부릅니다. 잘못된 스택 트레이스는 일반적으로,

  • WinDbg warning 메시지가 뜹니다: “Following frames may be wrong”

  • (유저모드에서) Kernel32!BaseThreadStart와 같은 올바른 시작 프레임을 가지고 있지 못합니다

  • 함수 호출 관계들이 어색하거나 올바르지 않습니다

  • 이상한 모양의 디스어셈블 함수 코드를 가지고 있거나 컴파일러 관점에서 올바르지 않은 코드를 가지고 있습니다

  • 올바르지 않은 ChildEBP와 RetAddr 주소를 가지고 있습니다

다음 스택 트레이스를 살펴보면:

0:011> k
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0184e434 7c830b10 0×184e5bf
0184e51c 7c81f832 ntdll!RtlGetFullPathName_Ustr+0×15b
0184e5f8 7c83b1dd ntdll!RtlpLowFragHeapAlloc+0xc6a
00099d30 00000000 ntdll!RtlpLowFragHeapFree+0xa7

 

잘못된 스택 트레이스의 거의 모든 속성을 갖추고 있습니다. 한눈에 보아 heap corruption이 발생한 것으로 추측(runtime heap alloc과 free 함수들이 존재하니까)되지만 조금 더 자세히 다시 살펴보면 low fragmentation heap Free 함수가 low fragmentation heap Alloc 함수를 호출해서는 안되고 이후에 full path name을 query 해서는 안 된다는 것을 확인할 수 있습니다. 이것은 말이 안되지요.

여기에서 무엇을 해야 할까요? Raw stack을 살펴보고 올바른 스택 트레이스를 우리가 직접 만들어봅시다. 이 경우에 매우 쉽습니다. BaseThreadStart+0x34부터 어떠한 함수 호출을 찾을 수 없을 때까지 또는 최상단에 이르기까지 스택 프레임들을 traverse하면 됩니다. 함수들이 호출될 때 최적화가 꺼져있고, 대부분의 컴파일러에서 EBP 레지스터들은 아래 슬라이드 13에서 설명된 것처럼 서로 연결되어 있습니다.

Practical Foundations of Debugging (6.1)

 

0:011> !teb
TEB at 7ffd8000
    ExceptionList:        0184ebdc
    StackBase:            01850000
    StackLimit:           01841000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7ffd8000
    EnvironmentPointer:   00000000
    ClientId:             0000061c . 00001b60
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          7ffdf000
    LastErrorValue:       0
    LastStatusValue:      c0000034
    Count Owned Locks:    0
    HardErrorMode:        0
0:011> dds 01841000 01850000
01841000  00000000



0184eef0 0184ef0c
0184eef4  7615dff2 localspl!SplDriverEvent+0×21
0184eef8  00bc3e08
0184eefc  00000003
0184ef00  00000001
0184ef04  00000000
0184ef08  0184efb0
0184ef0c 0184ef300184ef10  7615f9d0 localspl!PrinterDriverEvent+0×46
0184ef14  00bc3e08
0184ef18  00000003
0184ef1c  00000000
0184ef20  0184efb0
0184ef24  00b852a8
0184ef28  00c3ec58
0184ef2c  00bafcc0
0184ef30 0184f3f80184ef34  7614a9b4 localspl!SplAddPrinter+0×5f3
0184ef38  00c3ec58
0184ef3c  00000003
0184ef40  00000000
0184ef44  0184efb0
0184ef48  00c117f8



0184ff28  00000000
0184ff2c  00000000
0184ff30 0184ff840184ff34  77c75286 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0×3a
0184ff38  0184ff4c
0184ff3c  77c75296 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0×4a
0184ff40  7c82f2fc ntdll!RtlLeaveCriticalSection
0184ff44  000de378
0184ff48  00097df0
0184ff4c  4d2fa200
0184ff50  ffffffff
0184ff54  ca5b1700
0184ff58  ffffffff
0184ff5c  8082d821
0184ff60  0184fe38
0184ff64  00097df0
0184ff68  000000aa
0184ff6c  80020000
0184ff70  0184ff54
0184ff74  80020000
0184ff78  000b0c78
0184ff7c  00a50180
0184ff80  0184fe38
0184ff84 0184ff8c0184ff88  77c5778f RPCRT4!RecvLotsaCallsWrapper+0xd
0184ff8c 0184ffac0184ff90  77c5f7dd RPCRT4!BaseCachedThreadRoutine+0×9d
0184ff94  0009c410
0184ff98  00000000
0184ff9c  00000000
0184ffa0  00097df0
0184ffa4  00097df0
0184ffa8  00015f90
0184ffac 0184ffb80184ffb0  77c5de88 RPCRT4!ThreadStartRoutine+0×1b
0184ffb4  00088258
0184ffb8  0184ffec
0184ffbc  77e6608b kernel32!BaseThreadStart+0×34
0184ffc0  00097df0
0184ffc4  00000000
0184ffc8  00000000
0184ffcc  00097df0
0184ffd0  8ad84818
0184ffd4  0184ffc4
0184ffd8  8980a700
0184ffdc  ffffffff
0184ffe0  77e6b7d0 kernel32!_except_handler3
0184ffe4  77e66098 kernel32!`string’+0×98
0184ffe8  00000000
0184ffec  00000000
0184fff0  00000000
77c5de6d  RPCRT4!ThreadStartRoutine
0184fff8  00097df0
0184fffc  00000000
01850000  00000008

 

다음에 우리는 k 명령어를 사용해서 베이스 포인터를 지정해줍니다. 우리의 경우에 EBP 포인터와 링크된 마지막으로 찾은 스택 주소가 0184eef0였습니다:

 

0:011> k L=0184eef0
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0184eef0 7615dff2 0×184e5bf
0184ef0c 7615f9d0 localspl!SplDriverEvent+0×21
0184ef30 7614a9b4 localspl!PrinterDriverEvent+0×46
0184f3f8 761482de localspl!SplAddPrinter+0×5f3
0184f424 74067c8f localspl!LocalAddPrinterEx+0×2e
0184f874 74067b76 SPOOLSS!AddPrinterExW+0×151
0184f890 01007e29 SPOOLSS!AddPrinterW+0×17
0184f8ac 01006ec3 spoolsv!YAddPrinter+0×75
0184f8d0 77c70f3b spoolsv!RpcAddPrinter+0×37
0184f8f8 77ce23f7 RPCRT4!Invoke+0×30
0184fcf8 77ce26ed RPCRT4!NdrStubCall2+0×299
0184fd14 77c709be RPCRT4!NdrServerCall2+0×19
0184fd48 77c7093f RPCRT4!DispatchToStubInCNoAvrf+0×38
0184fd9c 77c70865 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0×117
0184fdc0 77c734b1 RPCRT4!RPC_INTERFACE::DispatchToStub+0xa3
0184fdfc 77c71bb3 RPCRT4!LRPC_SCALL::DealWithRequestMessage+0×42c
0184fe20 77c75458 RPCRT4!LRPC_ADDRESS::DealWithLRPCRequest+0×127
0184ff84 77c5778f RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0×430
0184ff8c 77c5f7dd RPCRT4!RecvLotsaCallsWrapper+0xd

 

이제 스택 트레이스가 좀더 그럴듯해졌지만 BaseThreadStart+0x34는 여전히 보이지 않습니다. WinDbg가 오직 함수 호출(스택 프레임들)의 일정 양 만큼만을 기본적으로 보여주기 때문에, 스텍 프레임 카운터를 100정도로 지정해줍니다.

 

0:011> k L=0184eef0 100
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0184eef0 7615dff2 0×184e5bf
0184ef0c 7615f9d0 localspl!SplDriverEvent+0×21
0184ef30 7614a9b4 localspl!PrinterDriverEvent+0×46
0184f3f8 761482de localspl!SplAddPrinter+0×5f3
0184f424 74067c8f localspl!LocalAddPrinterEx+0×2e
0184f874 74067b76 SPOOLSS!AddPrinterExW+0×151
0184f890 01007e29 SPOOLSS!AddPrinterW+0×17
0184f8ac 01006ec3 spoolsv!YAddPrinter+0×75
0184f8d0 77c70f3b spoolsv!RpcAddPrinter+0×37
0184f8f8 77ce23f7 RPCRT4!Invoke+0×30
0184fcf8 77ce26ed RPCRT4!NdrStubCall2+0×299
0184fd14 77c709be RPCRT4!NdrServerCall2+0×19
0184fd48 77c7093f RPCRT4!DispatchToStubInCNoAvrf+0×38
0184fd9c 77c70865 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0×117
0184fdc0 77c734b1 RPCRT4!RPC_INTERFACE::DispatchToStub+0xa3
0184fdfc 77c71bb3 RPCRT4!LRPC_SCALL::DealWithRequestMessage+0×42c
0184fe20 77c75458 RPCRT4!LRPC_ADDRESS::DealWithLRPCRequest+0×127
0184ff84 77c5778f RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0×430
0184ff8c 77c5f7dd RPCRT4!RecvLotsaCallsWrapper+0xd
0184ffac 77c5de88 RPCRT4!BaseCachedThreadRoutine+0×9d
0184ffb8 77e6608b RPCRT4!ThreadStartRoutine+0×1b
0184ffec 00000000 kernel32!BaseThreadStart+0×34

스택 트레이스가 보기에 훨씬 나아졌습니다.

- Dmitry Vostokov -