겸손한 개발을 위한 자양분

사용자 삽입 이미지
예외정보를 무작정... 확인하기 위하여,
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
        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

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 ''


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


예외(에러)가 발생한 경우, 프로세스는 예외 핸들러를 호출하게 되고,
예외 핸들러가 등록되지 않은 경우 시스템의 기본 예외 핸들러를 호출합니다.
그리고, 예외 핸들러가 호출 될때에는 _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

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


호출 관계를 따라가면서, 함수의 호출 위치를 확인합니다.
아래는 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)

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

첫번째 인자를 확인하면 다음과 같습니다.
kd> dd 0012f884
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

위와 같이 _EXCEPTION_RECORD 인 것을 확인할 수 있고,
예외가 발생한 주소와, 예외 코드를 알 수 있습니다.