반응형
이번에는 윈도우 예외 처리 이해 하기로 KeBugCheckEx에 대해서 알아보도록 하겠다.
윈도우에서는 IDT(Inturrupt Descriptor Table)를 이용해 인터럽트 및 예외(Exception)를 처리하는데, BSOD와 같은 예외 상황 역시 IDT 테이블 통해 처리되며, 최종 KeBugCheckEx를 통해 오류상황에 대처한다. 그럼 KeBugCheckEx의 구조체를 알아보자.
VOID KeBugCheckEx( __in ULONG BugCheckCode,
__in ULONG_PTR BugCheckParameter1,
__in ULONG_PTR BugCheckParameter2,
__in ULONG_PTR BugCheckParameter3,
__in ULONG_PTR BugCheckParameter4
);
첫 번째 값은 버그 체크 코드(BugCheckCode)이며, 그 외 4가지 값은 해당 버그체크코드의 파라미터가 표시되는데 이 파라미터는, 버그 체크 코드 별로 해석이 달리 된다.
그럼 커널 모드 에러가 발생하면 어떻게 진행되는 것일까?
1. 커널 디버거로 처리를 넘긴다(커널 디버거를 연결한 경우 Chance 3).
2. 블루스크린을 표시하며, 해당 버그 체크 코드를 화면에 표시함
3. 메모리 덤프 파일을 생성한다(가능한 경우).
4. 시스템을 재부팅한다(재부팅 설정이 되었을 경우).
2번의 상황이 제일 기본적인 상황으로 많이 경험해 봤을 것이며, 1번의 경우 앞서 진행한 실시간 커널 디버깅 상태에서 이루어 진다. 그 외 3, 4번의 경우 상황이 맞을 경우에 한하여 구현되게 된다. 그럼 이 장애 처리 흐름을 조금 더 자세히 애기해보자.
그럼 커널 모드 에러가 발생하면 어떻게 진행되는 것일까?
1. 커널 디버거로 처리를 넘긴다(커널 디버거를 연결한 경우 Chance 3).
2. 블루스크린을 표시하며, 해당 버그 체크 코드를 화면에 표시함
3. 메모리 덤프 파일을 생성한다(가능한 경우).
4. 시스템을 재부팅한다(재부팅 설정이 되었을 경우).
2번의 상황이 제일 기본적인 상황으로 많이 경험해 봤을 것이며, 1번의 경우 앞서 진행한 실시간 커널 디버깅 상태에서 이루어 진다. 그 외 3, 4번의 경우 상황이 맞을 경우에 한하여 구현되게 된다. 그럼 이 장애 처리 흐름을 조금 더 자세히 애기해보자.
이제 위 진행 흐름을 실제 데이터 분석을 통해 확인하기 위해 생성된 메모리 덤프를 Windbg로 열어서 분석을 진행하자.
아래 상황은 CSRSS 프로세스를 강제로 종료하여 메모리 덤프를 생성하여 분석한 내용이다.
// 0번 프로세서의 컨트롤 영역 정보를 확인한다.0: kd> !pcr 0KPCR for Processor 0 at ffdff000:Major 1 Minor 1NtTib.ExceptionList: ffffffffNtTib.StackBase: b1a16df0NtTib.StackLimit: b1a13000NtTib.SubSystemTib: 00000000NtTib.Version: 00000000NtTib.UserPointer: 00000000NtTib.SelfTib: 7ffdd000SelfPcr: ffdff000Prcb: ffdff120Irql: 00000000IRR: 00000000IDR: ffffffffInterruptMode: 00000000IDT: 8003f400GDT: 8003f000TSS: 80042000CurrentThread: 81c10da8NextThread: 00000000IdleThread: 80563cc0DpcQueue:// IDT 는 Int21Descriptor로 KIDTENTRY 구조체를 통해 확인해 볼수 있다.0: kd> dt nt!_KIDTENTRY 8003f400+0x000 Offset : 0x51e+0x002 Selector : 8+0x004 Access : 0x8e00+0x006 ExtendedOffset : 0x804e// ExtendedOffset과 Offset을 조합하여 해당 주소의 심볼 값을 확인해 보자.0: kd> ln 0x804e051e(804e051e) nt!KiTrap00 | (804e05f1) nt!Dr_kit1_aExact matches:nt!KiTrap00 = <no type information>// 해당 주소는 nt!KiTrap00이 사용하는 주소로 확인되었다.0: kd> !idt -aDumping IDT:00: 804e051e nt!KiTrap0001: 804e069d nt!KiTrap0102: Task Selector = 0x005803: 804e0ab1 nt!KiTrap0304: 804e0c34 nt!KiTrap0405: 804e0d99 nt!KiTrap0506: 804e0f1a nt!KiTrap0607: 804e1593 nt!KiTrap0708: Task Selector = 0x005009: 804e1998 nt!KiTrap090a: 804e1ab6 nt!KiTrap0A0b: 804e1bf3 nt!KiTrap0B0c: 804e1e50 nt!KiTrap0C0d: 804e214c nt!KiTrap0D0e: 804e2889 nt!KiTrap0E0f: 804e2bbe nt!KiTrap0F10: 804e2cdc nt!KiTrap1011: 804e2e16 nt!KiTrap1112: Task Selector = 0x00A013: 804e2f7b nt!KiTrap1314: 804e2bbe nt!KiTrap0F…중략// 현재 레지스터 정보를 확인하자.0: kd> reax=ffdff13c ebx=82103020 ecx=00000000 edx=00000034 esi=82103020 edi=81c10da8eip=805396ba esp=b1a16ce8 ebp=b1a16d00 iopl=0 nv up ei ng nz na pe nccs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286nt!KeBugCheckEx+0x1b:805396ba 5d pop ebp// ESP 레지스터를 통해 ESP 저장된 스택 값을 확인하자.0: kd> dds esp-50 esp+50b1a16c98 81c10020b1a16c9c b1a16d40b1a16ca0 8125d070b1a16ca4 c01e7bd4b1a16ca8 80703a4c hal!KeAcquireQueuedSpinLockb1a16cac 00000001b1a16cb0 00000000b1a16cb4 00000000b1a16cb8 00000000b1a16cbc 00000538b1a16cc0 00000000b1a16cc4 b1a16c78b1a16cc8 00000000b1a16ccc 00000000b1a16cd0 0000bb40b1a16cd4 b1a16aacb1a16cd8 00000000b1a16cdc 0000bb40b1a16ce0 b1a16d00 ß 복귀할 EBP 포인터가 저장된다.b1a16ce4 805396ba nt!KeBugCheckEx+0x1b ß스택에 현재 EIPb1a16ce8 000000f4 ß 오류코드b1a16cec 00000003 ß 파라미터1b1a16cf0 82103020 ß 파라미터2b1a16cf4 82103194 ß 파라미터3b1a16cf8 8060777e nt!PspTerminateThreadByPointer+0xfa ß 파라미터4b1a16cfc 00000000b1a16d00 b1a16d24b1a16d04 80637e9d nt!PspCatchCriticalBreak+0x75b1a16d08 000000f4b1a16d0c 00000003b1a16d10 82103020b1a16d14 82103194b1a16d18 8060777e nt!PspTerminateThreadByPointer+0xfab1a16d1c 82103020b1a16d20 82103020b1a16d24 b1a16d54// EIP 레지스터를 통해 현재 처리중인 명령을 확인할 수 있는데 nt!KeBugCheckEx 실행후 마지막임을 알 수 있다.0: kd> u eip-10 l10nt!KeBugCheckEx+0xb:805396aa 7514 jne nt!KeBugCheckEx+0x21 (805396c0)805396ac ff7510 push dword ptr [ebp+10h]805396af ff750c push dword ptr [ebp+0Ch]805396b2 ff7508 push dword ptr [ebp+8]805396b5 e878f4ffff call nt!KeBugCheck2 (80538b32)8053969f 8bff mov edi,edi805396a1 55 push ebp805396a2 8bec mov ebp,esp805396a4 6a00 push 0805396a6 ff7518 push dword ptr [ebp+18h] ß 파라미터2805396a9 ff7514 push dword ptr [ebp+14h] ß 파라미터2805396ac ff7510 push dword ptr [ebp+10h] ß 파라미터2805396af ff750c push dword ptr [ebp+0Ch] ß 파라미터1805396b2 ff7508 push dword ptr [ebp+8] ß 오류코드 의 주소 포인터805396b5 e878f4ffff call nt!KeBugCheck2 (80538b32)805396ba 5d pop ebp ß 현재 EIP 위치// 콜스택 정보를 확인하여 ESP 레지스터에서 저장된 EBP 포인터와 비교0: kd> kChildEBP RetAddrb1a16d00 80637e9d nt!KeBugCheckEx+0x1b 조금전 확인한 EBP 포인터들b1a16d24 8060773c nt!PspCatchCriticalBreak+0x75b1a16d54 804df99f nt!NtTerminateProcess+0x7db1a16d54 7c93e514 nt!KiFastCallEntry+0xfcWARNING: Frame IP not in any known module. Following frames may be wrong.0012f3e4 00000000 0x7c93e514// 현재 버그체크 코드를 확인해 보자.0: kd> .bugcheckBugcheck code 000000F4Arguments 00000003 82103020 82103194 8060777e// 그럼 언제 트랩프레임을 저장할 것일까? kv를 통해 FPO정보를 추가하여 확인하면, 트랩 프레임이 저장된 영역정보도 확인할 수 있다. FPO는 스택 영역 분석에 유용하므로, 다시 상기하기 바란다.0: kd> kvChildEBP RetAddr Args to Childb1a16d00 80637e9d 000000f4 00000003 82103020 nt!KeBugCheckEx+0x1b (FPO: [5,0,0]) FPO 관련해서 바로 다음에 나온다.b1a16d24 8060773c 8060777e 82103020 82103194 nt!PspCatchCriticalBreak+0x75 (FPO: [3,0,0])b1a16d54 804df99f 82103268 ffffffff 0012f3e4 nt!NtTerminateProcess+0x7d (FPO: [2,4,4])b1a16d54 7c93e514 82103268 ffffffff 0012f3e4 nt!KiFastCallEntry+0xfc (FPO: [0,0] TrapFrame @ b1a16d64)WARNING: Frame IP not in any known module. Following frames may be wrong.0012f3e4 00000000 00000000 00000000 00000000 0x7c93e514// 트랩 프레임은 .trap 명령을 통해 전환하여, 예외 발생당시의 레지스터와 콜스택과 정보등을 확인할 수 있다.0: kd> reax=ffdff13c ebx=82103020 ecx=00000000 edx=00000034 esi=82103020 edi=81c10da8eip=805396ba esp=b1a16ce8 ebp=b1a16d00 iopl=0 nv up ei ng nz na pe nccs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286nt!KeBugCheckEx+0x1b:805396ba 5d pop ebp0: kd> .trap b1a16d64ErrCode = 00000000eax=0012f3b0 ebx=00160050 ecx=00000001 edx=001771b0 esi=0012f428 edi=0012f688eip=7c93e514 esp=0012f3d4 ebp=0012f3e4 iopl=0 nv up ei pl nz na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206001b:7c93e514 ?? ???// .trap 명령을 재실행하면, 초기화 하여 기본 범위로 되돌아 온다.0: kd> .trapResetting default scope0: kd> reax=ffdff13c ebx=82103020 ecx=00000000 edx=00000034 esi=82103020 edi=81c10da8eip=805396ba esp=b1a16ce8 ebp=b1a16d00 iopl=0 nv up ei ng nz na pe nccs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286nt!KeBugCheckEx+0x1b:805396ba 5d pop ebp// 트랩 프레임은 _KTRAP_FRAME의 구조체를 이용하여 각 데이터를 확인할 수 있다.0: kd> dt nt!_KTRAP_FRAME b1a16d64+0x000 DbgEbp : 0x12f3e4+0x004 DbgEip : 0x7c93e514+0x008 DbgArgMark : 0xbadb0d00+0x00c DbgArgPointer : 0x12f3dc+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 : 0x1771b0+0x040 Ecx : 1+0x044 Eax : 0x12f3b0+0x048 PreviousPreviousMode : 1+0x04c ExceptionList : 0xffffffff _EXCEPTION_REGISTRATION_RECORD+0x050 SegFs : 0x3b+0x054 Edi : 0x12f688+0x058 Esi : 0x12f428+0x05c Ebx : 0x160050+0x060 Ebp : 0x12f3e4+0x064 ErrCode : 0+0x068 Eip : 0x7c93e514+0x06c SegCs : 0x1b+0x070 EFlags : 0x206+0x074 HardwareEsp : 0x12f3d4+0x078 HardwareSegSs : 0x23+0x07c V86Es : 0+0x080 V86Ds : 0+0x084 V86Fs : 0+0x088 V86Gs : 0
반응형
'WebBook > 윈도우 구조' 카테고리의 다른 글
윈도우 구조 - 메모리 덤프 생성과 분석 기초 (0) | 2024.04.12 |
---|---|
윈도우/리눅스 – 가상 메모리 관리(Paging, Swap) (0) | 2024.02.23 |
윈도우 예외 처리 이해 하기 - KeBugCheckEx (0) | 2022.09.13 |
윈도우 구조 - 커널 진입 - Ntdll.dll 세부 분석 (0) | 2022.04.02 |
시스템 프로세스(Windows Startup Process) - 자동 실행 - Userinit.exe (0) | 2022.03.23 |