성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
[정성태] The Windows Registry Adventure #1: ...
[정성태] systemd for Developers I ; https:/...
[정성태] 엄밀히 object 타입의 인스턴스가 다른 타입으로 형변환 가능...
[정성태] 아래의 글에서 나오는 "Windows Application Pa...
[정성태] The history of calling conventions,...
[정성태] Secure and Deploy .NET Windows Form...
[정성태] Get Started with Milvus Vector DB i...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>windbg - RtlReportCriticalFailure로부터 parameters 정보 찾는 방법</h1> <p> 이전 글에서는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례 - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12062'>http://www.sysnet.pe.kr/2/0/12062</a> </pre> <br /> Visual Studio를 이용해 RtlReportCriticalFailure와 관련한 "parameters"를 쉽게 구해 heap 관련 정보를 얻을 수 있었는데요. 그렇다면 windbg로는 어떻게 처리할 수 있을지 설명해 보겠습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 우선, windbg에서 RtlReportCriticalFailure 예외가 발생한 덤프를 열면 곧바로 이런 메시지가 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > This dump file has an exception of interest stored in it. <span style='color: blue; font-weight: bold'>The stored exception information can be accessed via .ecxr.</span> (11e4.8e0): Unknown exception - code c0000374 (first/second chance not available) ntdll!RtlReportCriticalFailure+0x97: 00007ffd`05d882d3 eb00 jmp ntdll!RtlReportCriticalFailure+0x99 (00007ffd`05d882d5) </pre> <br /> 메시지에서 보이는 것처럼 ".ecxr" 명령어를 수행해도 되지만 어차피 지금 문제가 발생한 스레드의 문맥에 있으므로 그냥 "r" 명령어로 봐도 무방합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:016> <span style='color: blue; font-weight: bold'>.ecxr</span> rax=0000000000000000 rbx=0000002cab7fe470 rcx=0000002cab7fea18 rdx=0000000000000008 rsi=0000002cab7fdf80 rdi=0000002cab7fdf80 rip=00007ffd05d882d3 rsp=0000002cab7fed40 rbp=0000000000000000 r8=fffffff800000021 r9=0000000000000000 r10=0000000000000000 r11=0000002cab7feaa0 r12=0000000000000000 r13=0000002cab7ff2e0 r14=0000014e8cba0000 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000204 ntdll!RtlReportCriticalFailure+0x97: 00007ffd`05d882d3 eb00 jmp ntdll!RtlReportCriticalFailure+0x99 (00007ffd`05d882d5) 0:016> <span style='color: blue; font-weight: bold'>r</span> Last set context: rax=0000000000000000 rbx=0000002cab7fe470 rcx=0000002cab7fea18 rdx=0000000000000008 rsi=0000002cab7fdf80 rdi=0000002cab7fdf80 rip=00007ffd05d882d3 rsp=0000002cab7fed40 rbp=0000000000000000 r8=fffffff800000021 r9=0000000000000000 r10=0000000000000000 r11=0000002cab7feaa0 r12=0000000000000000 r13=0000002cab7ff2e0 r14=0000014e8cba0000 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000204 ntdll!RtlReportCriticalFailure+0x97: 00007ffd`05d882d3 eb00 jmp ntdll!RtlReportCriticalFailure+0x99 (00007ffd`05d882d5) </pre> <br /> 자, 그럼 우리가 여기서 궁금한 것은 Visual Studio에서 보여준 "parameters"에 해당하는 정보입니다. 이를 위해 우선 k 명령어로 callstack을 보고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:016> <span style='color: blue; font-weight: bold'>k</span> *** Stack trace for last set context - .thread/.cxr resets it # Child-SP RetAddr Call Site <span style='color: blue; font-weight: bold'>00 0000002c`ab7fed40 00007ffd`05d88c2a ntdll!RtlReportCriticalFailure+0x97</span> 01 0000002c`ab7fee50 00007ffd`05d35b7a ntdll!RtlpHeapHandleError+0x12 02 0000002c`ab7fee80 00007ffd`05cccc33 ntdll!RtlpLogHeapFailure+0x96 03 0000002c`ab7feeb0 00007ffc`f061fbaf ntdll!RtlFreeHeap+0x823 04 0000002c`ab7fef60 00007ffc`f0628a29 OraOps19!ssmem_free+0xf 05 0000002c`ab7fef90 00007ffc`99b372fe OraOps19!OpsMetFreeValCtx+0x89 06 0000002c`ab7fefc0 00007ffc`99b7d635 0x00007ffc`99b372fe 07 0000002c`ab7ff070 00007ffc`f85757c6 0x00007ffc`99b7d635 08 0000002c`ab7ff0b0 00007ffc`f8606a91 clr!FastCallFinalizeWorker+0x6 09 0000002c`ab7ff0e0 00007ffc`f8606a19 clr!FastCallFinalize+0x55 ...[생략]... 1c 0000002c`ab7ffad0 00007ffd`03228364 clr!Thread::intermediateThreadProc+0x86 1d 0000002c`ab7ffb90 00007ffd`05cf7091 kernel32!BaseThreadInitThunk+0x14 1e 0000002c`ab7ffbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 </pre> <br /> ntdll!RtlReportCriticalFailure의 코드를 살펴보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:016> <span style='color: blue; font-weight: bold'>uf ntdll!RtlReportCriticalFailure</span> ntdll!RtlReportCriticalFailure: 00007ffd`05d8823c 48895c2418 mov qword ptr [rsp+18h],rbx ...[생략]... 00007ffd`05d88278 7427 je ntdll!RtlReportCriticalFailure+0x65 (00007ffd`05d882a1) Branch ...[생략]... ntdll!RtlReportCriticalFailure+0x65: 00007ffd`05d882a1 895c2450 mov dword ptr [rsp+50h],ebx 00007ffd`05d882a5 b901000000 mov ecx,1 00007ffd`05d882aa 894c2454 mov dword ptr [rsp+54h],ecx 00007ffd`05d882ae 488364245800 and qword ptr [rsp+58h],0 00007ffd`05d882b4 488d05452df5ff lea rax,[ntdll!RtlRaiseException (00007ffd`05cdb000)] 00007ffd`05d882bb 4889442460 mov qword ptr [rsp+60h],rax 00007ffd`05d882c0 894c2468 mov dword ptr [rsp+68h],ecx 00007ffd`05d882c4 48897c2470 mov qword ptr [rsp+70h],rdi 00007ffd`05d882c9 488d4c2450 lea rcx,[rsp+50h] <span style='color: blue; font-weight: bold'>00007ffd`05d882ce e82d2df5ff call ntdll!RtlRaiseException (00007ffd`05cdb000)</span> 00007ffd`05d882d3 eb00 jmp ntdll!RtlReportCriticalFailure+0x99 (00007ffd`05d882d5) Branch ntdll!RtlReportCriticalFailure+0x99: 00007ffd`05d882d5 488b8c24f0000000 mov rcx,qword ptr [rsp+0F0h] 00007ffd`05d882dd 4833cc xor rcx,rsp 00007ffd`05d882e0 e8cbe5f9ff call ntdll!_security_check_cookie (00007ffd`05d268b0) 00007ffd`05d882e5 4c8d9c2400010000 lea r11,[rsp+100h] 00007ffd`05d882ed 498b5b20 mov rbx,qword ptr [r11+20h] 00007ffd`05d882f1 498b7328 mov rsi,qword ptr [r11+28h] 00007ffd`05d882f5 498be3 mov rsp,r11 00007ffd`05d882f8 5f pop rdi 00007ffd`05d882f9 c3 ret </pre> <br /> k 명령어로 본 "RtlReportCriticalFailure+0x97"의 0x97 위치에 해당하는 기계어 코드를 확인할 수 있습니다. 그리고 그것은 RtlRaiseException이고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > RtlRaiseException function ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/rtlsupportapi/nf-rtlsupportapi-rtlraiseexception'>https://learn.microsoft.com/en-us/windows/win32/api/rtlsupportapi/nf-rtlsupportapi-rtlraiseexception</a> NTSYSAPI __analysis_noreturn VOID RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord); </pre> <br /> 전달받는 인자가 EXCEPTION_RECORD에 대한 포인터 하나입니다. 간단해서 좋군요. ^^ 그렇다면 "call ntdll!RtlRaiseException"을 호출하기 전 수행한 "lea rcx, [rsp+50h]"의 "rcx" 레지스터에 담긴 값이 바로 EXCEPTION_RECORD 포인터 인자로 예상할 수 있습니다. 여기서 문제는, 비록 k 명령어의 결과에는 없지만 어쨌든 "RtlRaiseException" 함수가 수행 중이므로 현재 "r" 명령어로 출력한 "rcx" 값은 이미 RtlRaiseException 함수의 내부 코드에 의해서 변조되었을 가능성이 높다는 점입니다.<br /> <br /> 대신 frame 문맥 자체는 RtlReportCriticalFailure 함수 실행 시로 바뀐 상태이므로 "rsp" 레지스터의 값은 유효하므로 "lea rcx, [rsp+50h]"의 결과를 "r" 명령어에 출력된 rsp == 0000002cab7fed40 값에 +50h를 해 "2CAB7FED90"로 쉽게(?) EXCEPTION_RECORD의 포인터를 구할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이쯤에서 잠시 EXCEPTION_RECORD를 한번 살펴볼까요? ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > EXCEPTION_RECORD structure ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record'>https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record</a> typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD <span style='color: blue; font-weight: bold'>NumberParameters</span>; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; </pre> <br /> 오호~~~ 우리에게 익숙한 필드명 NumberParameters가 보이는군요. ^^ 아마도 저 값이 Visual Studio에서 보여줬던 바로 그 "parameters"일 것입니다. 이러한 레코드 정보에 기반을 둬 ntdll!RtlReportCriticalFailure+0x65 위치의 코드를 다시 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ntdll!RtlReportCriticalFailure+0x65: 00007ffd`05d882a1 895c2450 <span style='color: blue; font-weight: bold'>mov dword ptr [rsp+50h],ebx</span> 00007ffd`05d882a5 b901000000 mov ecx,1 00007ffd`05d882aa 894c2454 <span style='color: blue; font-weight: bold'>mov dword ptr [rsp+54h],ecx</span> 00007ffd`05d882ae 488364245800 <span style='color: blue; font-weight: bold'>and qword ptr [rsp+58h],0</span> 00007ffd`05d882b4 488d05452df5ff lea rax,[ntdll!RtlRaiseException (00007ffd`05cdb000)] 00007ffd`05d882bb 4889442460 <span style='color: blue; font-weight: bold'>mov qword ptr [rsp+60h],rax</span> 00007ffd`05d882c0 894c2468 <span style='color: blue; font-weight: bold'>mov dword ptr [rsp+68h],ecx</span> 00007ffd`05d882c4 48897c2470 <span style='color: blue; font-weight: bold'>mov qword ptr [rsp+70h],rdi</span> 00007ffd`05d882c9 488d4c2450 lea rcx,[rsp+50h] 00007ffd`05d882ce e82d2df5ff call ntdll!RtlRaiseException (00007ffd`05cdb000) 00007ffd`05d882d3 eb00 jmp ntdll!RtlReportCriticalFailure+0x99 (00007ffd`05d882d5) Branch </pre> <br /> 결국, EXCEPTION_RECORD의 구조체 위치가 "rsp+50h"이고 그곳에 다음과 같이 필드를 채운 것과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; // mov dword ptr [rsp+50h],ebx // mov dword ptr [rsp+54h],ecx DWORD ExceptionFlags; // and qword ptr [rsp+58h],0 // [rsp+58h] == 0 struct _EXCEPTION_RECORD *ExceptionRecord; // mov qword ptr [rsp+60h],rax // rax == 00007ffd`05cdb000 PVOID ExceptionAddress; // mov dword ptr [rsp+68h],ecx // (int)[rsp+68h] == 1 DWORD NumberParameters; // mov qword ptr [rsp+70h],rdi // (int)[rsp+70h] == 전달된 두 번째 인자 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; </pre> <br /> 따라서 이전에 구했던 rsp+50h 주솟값인 2CAB7FED90 위치를 덤프해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:016> dq rsp+50h 0000002c`ab7fed90 00000001`c0000374 00000000`00000000 0000002c`ab7feda0 00007ffd`05d882d3 00007ffc`00000001 0000002c`ab7fedb0 <span style='color: blue; font-weight: bold'>00007ffd`05ddf6b0</span> 00007ffc`f8575191 0000002c`ab7fedc0 00000ec4`00000000 0000014e`8dc53610 0000002c`ab7fedd0 00000000`00000000 00007ffd`05d042e3 0000002c`ab7fede0 00007a66`762bbd2a 0000002c`ab7fef30 0000002c`ab7fedf0 00000000`00000000 0000014e`8cba0000 0000002c`ab7fee00 00000000`00000000 00000000`00000000 </pre> <br /> NumberParameters의 위치(rsp+70h)에 해당하는 값을 구할 수 있고, 실제로 Visual Studio가 출력했던 parameters 값과 일치합니다. 게임 끝났군요. ^^ 이 값을 메모리 창에 보면 HeapFree에 전달되었던 hHandle과 해제하려는 메모리 주소를 알아낼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // [00007ffd05ddf6b0] 00007ffd`05ddf6b0 000006e000000002 00007ffd`05ddf6b8 0000000000000004 00007ffd`05ddf6c0 00000286f28f0000 // hHandle 00007ffd`05ddf6c8 0000028b412d3be0 // pVoid - 0x10 00007ffd`05ddf6d0 0000000000000000 </pre> <br /> <hr style='width: 50%' /><br /> <br /> 여기까지 읽으신 분은 다소 허무하겠지만 ^^ windbg에는 EXCEPTION_RECORD에 대한 정보를 출력할 수 있는 명령어가 이미 있어서 ^^ 위와 같이 복잡하게 작업하지 마시고 RtlReportCriticalFailure가 발생한 경우 처음부터 ".exr -1" 명령어를 수행하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:016> <span style='color: blue; font-weight: bold'>.exr -1</span> ExceptionAddress: <span style='color: blue; font-weight: bold'>00007ffd05d882d3</span> (ntdll!RtlReportCriticalFailure+0x0000000000000097) ExceptionCode: <span style='color: blue; font-weight: bold'>c0000374</span> ExceptionFlags: 00000001 NumberParameters: 1 Parameter[0]: <span style='color: blue; font-weight: bold'>00007ffd05ddf6b0</span> </pre> <br /> 보는 바와 같이 Visual Studio가 출력했던 예외 메시지의,<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Unhandled exception at <span style='color: blue; font-weight: bold'>0x00007FFD05D882D3</span> (ntdll.dll) in dump4580.dmp: <span style='color: blue; font-weight: bold'>0xC0000374</span>: A heap has been corrupted (parameters: <span style='color: blue; font-weight: bold'>0x00007FFD05DDF6B0</span>) </div><br /> <br /> 바로 그 정보들을 동일하게 구할 수 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1532
(왼쪽의 숫자를 입력해야 합니다.)