Crash Dump Analysis Patterns (Part 11)
Crash Dump Analysis Patterns (Part 11)
번역: Heejune Kim (https://insidekernel.net drost@naver.com)
번역일: 2007-11-07
초보자들이 자주 범하는 실수 중의 하나가 스택 트레이스를 보여주는 명령어인 WinDbg의 !analyze나 kv 명령을 신뢰하는 것입니다. 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 -
Comments
- Anonymous
September 17, 2008
PingBack from http://www.easycoded.com/crash-dump-analysis-patterns-part-11/