예외를 무작정 따라가보자.
MYDN2009. 5. 7. 17:14
예외정보를 무작정... 확인하기 위하여,
Access Violation 을 일으키는 간단한 어플리케이션을 만들었습니다.
다음은 에러가 발생한 Process 의 정보입니다.
MS Windows 는 예외나 인터럽트가 발생할 경우 Trap Frame을 생성하여 이를 관리합니다.
이 Trap Frame 은 KTHREAD 에서 확인할 수 있고, 해당 쓰레드의 Thread Context 정보를 확인할 수 있습니다.
예외(에러)가 발생한 경우, 프로세스는 예외 핸들러를 호출하게 되고,
예외 핸들러가 등록되지 않은 경우 시스템의 기본 예외 핸들러를 호출합니다.
그리고, 예외 핸들러가 호출 될때에는 _EXCEPTION_RECORD 구조체를 넘겨주게 됩니다.
따라서, 예외가 발생한 쓰레드의 Call Stack 을 이용하여, _EXCEPTION_RECORD 를 확인할 수 있습니다.
위의 _KTHREAD 구조체에서, 현재 Stack 의 Base Pointer 는 0x12dbb0 입니다.
Stack Frame 의 구조에 따라, 복귀 주소가 0x0012dbcc 이므로,
호출 관계를 따라갈 수 있습니다.
호출 관계를 따라가면서, 함수의 호출 위치를 확인합니다.
아래는 faultrep!ReportFault+0x533 의 복귀주소를 갖고 호출된 함수의 호출부입니다.
StartDWException 함수는 DrWatson 의 예외처리 프로세스를 시작하는 함수로 추측되며,
다섯개의 인자를 받고 있습니다.
DrWatson 에서 해당 프로세스의 에러 정보를 처리하므로,
저 다섯가지의 인자에서 _EXCEPTION_RECORD 를 확인할 수 있다는 것을 유추할 수 있습니다.
해당 인자들의 위치를, 호출 스택의 구조에 따라 확인하면 다음과 같습니다.
첫번째 인자를 확인하면 다음과 같습니다.
해당 인자가 스택을 가리키고 있으므로, 포인터로 볼 수 있습니다.
따라서, 다시 내용을 확인하면,
위와 같이 _EXCEPTION_RECORD 인 것을 확인할 수 있고,
예외가 발생한 주소와, 예외 코드를 알 수 있습니다.
Access Violation 을 일으키는 간단한 어플리케이션을 만들었습니다.
다음은 에러가 발생한 Process 의 정보입니다.
kd> !process 81d2b9e0
PROCESS 81d2b9e0 SessionId: 0 Cid: 06d8 Peb: 7ffdf000 ParentCid: 05a4
DirBase: 065402c0 ObjectTable: e1d46190 HandleCount: 43.
Image: accessviolation.exe
VadRoot 81d2d9a0 Vads 45 Clone 0 Private 91. Modified 9. Locked 0.
DeviceMap e188e0b0
Token e10927d0
ElapsedTime 00:00:21.625
UserTime 00:00:00.015
KernelTime 00:00:00.031
QuotaPoolUsage[PagedPool] 33308
QuotaPoolUsage[NonPagedPool] 1800
Working Set Sizes (now,min,max) (540, 50, 345) (2160KB, 200KB, 1380KB)
PeakWorkingSetSize 540
VirtualSize 16 Mb
PeakVirtualSize 20 Mb
PageFaultCount 592
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 111
PROCESS 81d2b9e0 SessionId: 0 Cid: 06d8 Peb: 7ffdf000 ParentCid: 05a4
DirBase: 065402c0 ObjectTable: e1d46190 HandleCount: 43.
Image: accessviolation.exe
VadRoot 81d2d9a0 Vads 45 Clone 0 Private 91. Modified 9. Locked 0.
DeviceMap e188e0b0
Token e10927d0
ElapsedTime 00:00:21.625
UserTime 00:00:00.015
KernelTime 00:00:00.031
QuotaPoolUsage[PagedPool] 33308
QuotaPoolUsage[NonPagedPool] 1800
Working Set Sizes (now,min,max) (540, 50, 345) (2160KB, 200KB, 1380KB)
PeakWorkingSetSize 540
VirtualSize 16 Mb
PeakVirtualSize 20 Mb
PageFaultCount 592
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 111
THREAD 81d34a48 Cid 06d8.06d4 Teb: 7ffde000 Win32Thread: e1092ac0 WAIT: (UserRequest) UserMode Non-Alertable
82034198 SynchronizationEvent
8207f768 ProcessObject
81d34b38 NotificationTimer
Not impersonating
DeviceMap e188e0b0
Owning Process 81d2b9e0 Image: accessviolation.exe
Attached Process N/A Image: N/A
Wait Start TickCount 9254 Ticks: 1 (0:00:00:00.015)
Context Switch Count 104 LargeStack
UserTime 00:00:00.000
KernelTime 00:00:00.031
Win32 Start Address accessviolation (0x004013e2)
Start Address kernel32!BaseProcessStartThunk (0x7c810867)
Stack Init b26f6000 Current b26f595c Base b26f6000 Limit b26f1000 Call 0
Priority 9 BasePriority 8 PriorityDecrement 0 DecrementCount 16
82034198 SynchronizationEvent
8207f768 ProcessObject
81d34b38 NotificationTimer
Not impersonating
DeviceMap e188e0b0
Owning Process 81d2b9e0 Image: accessviolation.exe
Attached Process N/A Image: N/A
Wait Start TickCount 9254 Ticks: 1 (0:00:00:00.015)
Context Switch Count 104 LargeStack
UserTime 00:00:00.000
KernelTime 00:00:00.031
Win32 Start Address accessviolation (0x004013e2)
Start Address kernel32!BaseProcessStartThunk (0x7c810867)
Stack Init b26f6000 Current b26f595c Base b26f6000 Limit b26f1000 Call 0
Priority 9 BasePriority 8 PriorityDecrement 0 DecrementCount 16
MS Windows 는 예외나 인터럽트가 발생할 경우 Trap Frame을 생성하여 이를 관리합니다.
이 Trap Frame 은 KTHREAD 에서 확인할 수 있고, 해당 쓰레드의 Thread Context 정보를 확인할 수 있습니다.
kd> dt nt!_KTHREAD 81d34a48
+0x000 Header : _DISPATCHER_HEADER
...
+0x128 Preempted : 0 ''
+0x129 ProcessReadyQueue : 0 ''
+0x12a KernelStackResident : 0x1 ''
+0x12b NextProcessor : 0 ''
+0x12c CallbackStack : (null)
+0x130 Win32Thread : 0xe1092ac0
+0x134 TrapFrame : 0xb26f5d64 _KTRAP_FRAME
+0x138 ApcStatePointer : [2] 0x81d34a7c _KAPC_STATE
+0x140 PreviousMode : 1 ''
+0x141 EnableStackSwap : 0x1 ''
+0x142 LargeStack : 0x1 ''
+0x143 ResourceIndex : 0 ''
+0x144 KernelTime : 2
+0x148 UserTime : 0
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : 0 ''
+0x165 ApcStateIndex : 0 ''
+0x166 ApcQueueable : 0x1 ''
+0x167 AutoAlignment : 0 ''
+0x168 StackBase : 0xb26f6000
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY [ 0x81d2ba30 - 0x81d2ba30 ]
+0x1b8 FreezeCount : 0 ''
+0x1b9 SuspendCount : 0 ''
+0x1ba IdealProcessor : 0 ''
+0x1bb DisableBoost : 0 ''
+0x000 Header : _DISPATCHER_HEADER
...
+0x128 Preempted : 0 ''
+0x129 ProcessReadyQueue : 0 ''
+0x12a KernelStackResident : 0x1 ''
+0x12b NextProcessor : 0 ''
+0x12c CallbackStack : (null)
+0x130 Win32Thread : 0xe1092ac0
+0x134 TrapFrame : 0xb26f5d64 _KTRAP_FRAME
+0x138 ApcStatePointer : [2] 0x81d34a7c _KAPC_STATE
+0x140 PreviousMode : 1 ''
+0x141 EnableStackSwap : 0x1 ''
+0x142 LargeStack : 0x1 ''
+0x143 ResourceIndex : 0 ''
+0x144 KernelTime : 2
+0x148 UserTime : 0
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : 0 ''
+0x165 ApcStateIndex : 0 ''
+0x166 ApcQueueable : 0x1 ''
+0x167 AutoAlignment : 0 ''
+0x168 StackBase : 0xb26f6000
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY [ 0x81d2ba30 - 0x81d2ba30 ]
+0x1b8 FreezeCount : 0 ''
+0x1b9 SuspendCount : 0 ''
+0x1ba IdealProcessor : 0 ''
+0x1bb DisableBoost : 0 ''
kd> dt nt!_KTRAP_FRAME 0xb26f5d64
+0x000 DbgEbp : 0x12dbb0
+0x004 DbgEip : 0x7c90eb94
+0x008 DbgArgMark : 0xbadb0d00
+0x00c DbgArgPointer : 0x12db1c
+0x010 TempSegCs : 0
+0x014 TempEsp : 0
+0x018 Dr0 : 0
+0x01c Dr1 : 0
+0x020 Dr2 : 0
+0x024 Dr3 : 0
+0x028 Dr6 : 0
+0x02c Dr7 : 0
+0x030 SegGs : 0
+0x034 SegEs : 0x23
+0x038 SegDs : 0x23
+0x03c Edx : 0x7c90eb94
+0x040 Ecx : 0x1000
+0x044 Eax : 0x8e0000
+0x048 PreviousPreviousMode : 1
+0x04c ExceptionList : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : 0x3b
+0x054 Edi : 0x7ffdf000
+0x058 Esi : 0
+0x05c Ebx : 0x12db3c
+0x060 Ebp : 0x12dbb0
+0x064 ErrCode : 0
+0x068 Eip : 0x7c90eb94
+0x06c SegCs : 0x1b
+0x070 EFlags : 0x246
+0x074 HardwareEsp : 0x12db14
+0x078 HardwareSegSs : 0x23
+0x07c V86Es : 0
+0x080 V86Ds : 0
+0x084 V86Fs : 0
+0x088 V86Gs : 0
+0x000 DbgEbp : 0x12dbb0
+0x004 DbgEip : 0x7c90eb94
+0x008 DbgArgMark : 0xbadb0d00
+0x00c DbgArgPointer : 0x12db1c
+0x010 TempSegCs : 0
+0x014 TempEsp : 0
+0x018 Dr0 : 0
+0x01c Dr1 : 0
+0x020 Dr2 : 0
+0x024 Dr3 : 0
+0x028 Dr6 : 0
+0x02c Dr7 : 0
+0x030 SegGs : 0
+0x034 SegEs : 0x23
+0x038 SegDs : 0x23
+0x03c Edx : 0x7c90eb94
+0x040 Ecx : 0x1000
+0x044 Eax : 0x8e0000
+0x048 PreviousPreviousMode : 1
+0x04c ExceptionList : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : 0x3b
+0x054 Edi : 0x7ffdf000
+0x058 Esi : 0
+0x05c Ebx : 0x12db3c
+0x060 Ebp : 0x12dbb0
+0x064 ErrCode : 0
+0x068 Eip : 0x7c90eb94
+0x06c SegCs : 0x1b
+0x070 EFlags : 0x246
+0x074 HardwareEsp : 0x12db14
+0x078 HardwareSegSs : 0x23
+0x07c V86Es : 0
+0x080 V86Ds : 0
+0x084 V86Fs : 0
+0x088 V86Gs : 0
예외(에러)가 발생한 경우, 프로세스는 예외 핸들러를 호출하게 되고,
예외 핸들러가 등록되지 않은 경우 시스템의 기본 예외 핸들러를 호출합니다.
그리고, 예외 핸들러가 호출 될때에는 _EXCEPTION_RECORD 구조체를 넘겨주게 됩니다.
따라서, 예외가 발생한 쓰레드의 Call Stack 을 이용하여, _EXCEPTION_RECORD 를 확인할 수 있습니다.
위의 _KTHREAD 구조체에서, 현재 Stack 의 Base Pointer 는 0x12dbb0 입니다.
kd> dds 0x12dbb0
0012dbb0 0012dbcc ; 이전의 스택프레임
0012dbb4 7c809c86 kernel32!WaitForMultipleObjects+0x18 ; 복귀주소
0012dbb8 00000002
0012dbbc 0012dce0
0012dbc0 00000000
0012dbb0 0012dbcc ; 이전의 스택프레임
0012dbb4 7c809c86 kernel32!WaitForMultipleObjects+0x18 ; 복귀주소
0012dbb8 00000002
0012dbbc 0012dce0
0012dbc0 00000000
Stack Frame 의 구조에 따라, 복귀 주소가 0x0012dbcc 이므로,
호출 관계를 따라갈 수 있습니다.
kd> dds 0012dbcc
0012dbcc 0012e560
0012dbd0 6945763c faultrep!StartDWException+0x5df
0012dbd4 00000002
0012dbd8 0012dce0
0012dbdc 00000000
kd> dds 0012e560
0012e560 0012f5d4
0012e564 694582b1 faultrep!ReportFault+0x533
0012e568 0012f884
0012e56c ffffffff
0012e570 00198310
0012dbcc 0012e560
0012dbd0 6945763c faultrep!StartDWException+0x5df
0012dbd4 00000002
0012dbd8 0012dce0
0012dbdc 00000000
kd> dds 0012e560
0012e560 0012f5d4
0012e564 694582b1 faultrep!ReportFault+0x533
0012e568 0012f884
0012e56c ffffffff
0012e570 00198310
호출 관계를 따라가면서, 함수의 호출 위치를 확인합니다.
아래는 faultrep!ReportFault+0x533 의 복귀주소를 갖고 호출된 함수의 호출부입니다.
6945828d 7405 je faultrep!ReportFault+0x516 (69458294)
6945828f b8a4184569 mov eax,offset faultrep!`string'+0x3c8 (694518a4)
69458294 8985fcefffff mov dword ptr [ebp-1004h],eax
6945829a 6aff push 0FFFFFFFFh
6945829c 50 push eax
6945829d ffb520f0ffff push dword ptr [ebp-0FE0h]
694582a3 ff750c push dword ptr [ebp+0Ch]
694582a6 ffb510f0ffff push dword ptr [ebp-0FF0h]
694582ac e8acedffff call faultrep!StartDWException (6945705d)
694582b1 eb3e jmp faultrep!ReportFault+0x573 (694582f1)
694582b3 399d24f0ffff cmp dword ptr [ebp-0FDCh],ebx
694582b9 741f je faultrep!ReportFault+0x55c (694582da)
6945828f b8a4184569 mov eax,offset faultrep!`string'+0x3c8 (694518a4)
69458294 8985fcefffff mov dword ptr [ebp-1004h],eax
6945829a 6aff push 0FFFFFFFFh
6945829c 50 push eax
6945829d ffb520f0ffff push dword ptr [ebp-0FE0h]
694582a3 ff750c push dword ptr [ebp+0Ch]
694582a6 ffb510f0ffff push dword ptr [ebp-0FF0h]
694582ac e8acedffff call faultrep!StartDWException (6945705d)
694582b1 eb3e jmp faultrep!ReportFault+0x573 (694582f1)
694582b3 399d24f0ffff cmp dword ptr [ebp-0FDCh],ebx
694582b9 741f je faultrep!ReportFault+0x55c (694582da)
StartDWException 함수는 DrWatson 의 예외처리 프로세스를 시작하는 함수로 추측되며,
다섯개의 인자를 받고 있습니다.
DrWatson 에서 해당 프로세스의 에러 정보를 처리하므로,
저 다섯가지의 인자에서 _EXCEPTION_RECORD 를 확인할 수 있다는 것을 유추할 수 있습니다.
해당 인자들의 위치를, 호출 스택의 구조에 따라 확인하면 다음과 같습니다.
kd> dds 0012e560
0012e560 0012f5d4
0012e564 694582b1 faultrep!ReportFault+0x533
0012e568 0012f884 ; 첫번째 인자
0012e56c ffffffff ; 두번째 인자
0012e570 00198310 ; 세번째 인자
0012e574 0012f2ac ; 네번째 인자
0012e578 ffffffff ; 다섯번째 인자
0012e57c 00000000
0012e560 0012f5d4
0012e564 694582b1 faultrep!ReportFault+0x533
0012e568 0012f884 ; 첫번째 인자
0012e56c ffffffff ; 두번째 인자
0012e570 00198310 ; 세번째 인자
0012e574 0012f2ac ; 네번째 인자
0012e578 ffffffff ; 다섯번째 인자
0012e57c 00000000
첫번째 인자를 확인하면 다음과 같습니다.
kd> dd 0012f884
0012f884 0012f978 0012f994 0012f8b0 7c9037bf
0012f884 0012f978 0012f994 0012f8b0 7c9037bf
해당 인자가 스택을 가리키고 있으므로, 포인터로 볼 수 있습니다.
따라서, 다시 내용을 확인하면,
kd> dd 0012f978
0012f978 c0000005 00000000 00000000 00401de4
0012f988 00000002 00000001 00401000 0001003f
kd> dt nt!_EXCEPTION_RECORD 0012f978
+0x000 ExceptionCode : c0000005 ; Access Violation
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x00401de4
+0x010 NumberParameters : 2
+0x014 ExceptionInformation : [15] 1
0012f978 c0000005 00000000 00000000 00401de4
0012f988 00000002 00000001 00401000 0001003f
kd> dt nt!_EXCEPTION_RECORD 0012f978
+0x000 ExceptionCode : c0000005 ; Access Violation
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x00401de4
+0x010 NumberParameters : 2
+0x014 ExceptionInformation : [15] 1
위와 같이 _EXCEPTION_RECORD 인 것을 확인할 수 있고,
예외가 발생한 주소와, 예외 코드를 알 수 있습니다.