0xE06D7363 ?
평소 Windbg 를 통해 메모리 덤프를 분석하다보면, 가끔씩 ExceptionCode: e06d7363 (C++ EH exception)을 만나볼 수 있습니다. e06d7363은 Visual C++ 컴파일러가 C++ 예외에 사용하는 예외 코드입니다. 따라서 프로그램이 C++ 예외로 인해 종료되었을 때, 이 코드와 함께 메모리 덤프가 남게 됩니다.
이 경우에는 e06d7363 이라는 예외 코드 외에는 별다른 정보가 표시되지 않아서 항상 메모리 덤프가 남게 된 원인을 분석하기 곤란했는데요. Raymond Chen의 MS 블로그 글에 C++ 예외 클래스의 이름을 얻는 방법이 소개되어 공유드리려고 합니다. 예외 클래스의 이름만 알 수 있어도 큰 힌트가 될 수 있죠.
Windbg 분석
!analyze -v 명령어를 실행하고 나면, 예외코드 e06d7363과 함께 3개, 혹은 4개의 파라미터 정보가 남게 됩니다. x86 환경의 경우 3개, x64 환경의 경우 예외가 발생한 DLL의 HINSTANCE 값이 추가되어 4개가 됩니다.
x86 환경
먼저, x86 환경의 메모리 덤프 분석을 예로 들어보겠습니다. Windbg에는 아래와 같은 정보가 표시될겁니다.
ERROR_CODE: (NTSTATUS) 0xe06d7363 - <Unable to get error code text>
EXCEPTION_CODE_STR: e06d7363
EXCEPTION_PARAMETER1: 19930520
EXCEPTION_PARAMETER2: 00d3fb4c
EXCEPTION_PARAMETER3: 000a9038
- EXCEPTION_PARAMETER1: 내부적으로 사용되는 값으로, 설명하는 내용에서는 별로 중요하지 않습니다.
- EXCEPTION_PARAMETER2: 던져진 객체를 가리키는 포인터입니다.
- EXCEPTION_PARAMETER3: 던져진 객체를 설명하는 정보를 가리키는 포인터입니다. (중요)
중요한 것은 세 번째 파라미터로, 우리는 이 주소를 시작으로 몇 번의 참조를 거쳐서 던져진 예외 클래스의 이름을 알아낼 수 있습니다.
- 먼저 세 번째 파라미터의 주소에서 4번째 DWORD 값을 찾고 이것을 포인터로 생각합니다. (x64 환경의 경우 포인터에 항상 4번째 파라미터인 HINSTANCE 값을 더해주어야 합니다.)
- 위에서 구한 포인터 값의 주소에서 2번째 DWORD 값을 찾고 또 다시 이것을 포인터로 생각합니다.
- 다시(…) 위에서 구한 포인터 값의 주소에서 2번째 DWORD 값을 찾고 이것을 포인터로 생각합니다.
- 위에서 구한 포인터 값의 주소에서 2개의 void*를 무시하면, 다음에 나타나는 값이 클래스의 이름입니다.
글로만 읽으면 상당히 혼란스러울 수 있기 때문에, 실제 Windbg 명령어와 결과값을 예로 살펴보겠습니다.
0:000> dd a9038 l4
000a9038 00000001 00000000 00000000 000a904c
0:000> dd a904c l2
000a904c 00000002 000a905c
0:000> dd a905c l2
000a905c 00000001 000aa138
(x86 환경에서 void*는 4byte이므로, 구한 주소에 8을 더해줌)
0:000> da aa138+8
000aa140 ".PA_W"
구해진 값을 보면 “.PA_W” 라고 나타내고 있는데, 이것은 아래 처럼 예외를 던졌을 때 나타나는 이름입니다.
throw L"Throwing exception!";
x64 환경
이번에는 x64 환경에서, std::runtime_error 객체를 예외로 던졌을 때의 상황을 예로 알아보겠습니다. x64 환경의 경우 위에서 언급한 것처럼, 구해지는 포인터 값들을 4번째 파라미터인 HINSTANCE 값에 더해주면서 진행해야합니다.
먼저, !analyze -v 명령어 실행 후 아래와 같은 정보를 확인했습니다.
EXCEPTION_CODE_STR: e06d7363
EXCEPTION_PARAMETER1: 0000000019930520
EXCEPTION_PARAMETER2: 000000a3880ff780
EXCEPTION_PARAMETER3: 00007ff66a6e3a18
EXCEPTION_PARAMETER4: 7ff66a6e0000
x86 환경과는 다르게, EXCEPTION_PARAMETER4가 추가로 표시된 것을 확인할 수 있습니다. 이제 위 정보들을 가지고 같은 방법으로 예외 클래스의 이름을 찾아보겠습니다.
0:000> dd 7ff66a6e3a18 l4
00007ff6`6a6e3a18 00000000 00001120 00000000 00003a38
(위에서 구한 3a38 주소값을 4번째 파라미터값인 7ff66a6e0000에 더해서 구함)
0:000> dd 7ff66a6e0000+3a38 l2
00007ff6`6a6e3a38 00000002 00003a50
(위에서 구한 3a50 주소값을 4번째 파라미터값인 7ff66a6e0000에 더해서 구함)
0:000> dd 7ff66a6e0000+3a50 l2
00007ff6`6a6e3a50 00000000 00005058
(x64 환경에서 void*는 8byte이므로, 구한 주소에 0x10(16)을 더해줌)
0:000> da 7ff66a6e0000+5058+10
00007ff6`6a6e5068 ".?AVruntime_error@std@@"
구해진 값은 “.?AVruntime_error@std@@” 이고, 쉽게 std::runtime_error가 클래스 이름임을 유추할 수 있습니다.
마치며
예외코드 e06d7363 는 꽤 자주 보이는 녀석인데, 국내 웹에는 관련된 정보가 많이 없는 것 같아서 직접 작성하게 되었습니다. 별것 아니지만 도움이 되셨기를 바랍니다.
안녕하세요.
큰 도움이 된 정보입니다.
감사합니다.
도움이 되셨다니 다행입니다. 댓글 감사합니다!