Share via


Postmortem 디버깅 사용

사용자 모드 예외 처리

예외 및 중단점

가장 일반적인 애플리케이션 오류를 예외라고 합니다. 여기에는 액세스 위반, 0으로 나누기 오류, 숫자 오버플로, CLR 예외 및 기타 여러 종류의 오류가 포함됩니다. 애플리케이션은 중단점 인터럽트도 발생할 수 있습니다. Windows에서 애플리케이션을 실행할 수 없거나(예: 필요한 모듈을 로드할 수 없는 경우) 또는 중단점이 발생할 때 발생합니다. 중단점은 디버거를 통해 코드에 삽입하거나 DebugBreak와 같은 함수를 통해 호출할 수 있습니다.

예외 처리기 우선 순위

Windows는 구성 값 및 활성 디버거에 따라 다양한 방법으로 사용자 모드 오류를 처리합니다. 다음 시퀀스는 사용자 모드 오류 처리에 사용되는 우선 순위를 보여줍니다.

  1. 사용자 모드 디버거가 현재 오류 프로세스에 연결된 경우 모든 오류로 인해 대상이 이 디버거에 침입합니다.

    사용자 모드 디버거가 연결된 한 gn(처리되지 않은 예외로 이동) 명령이 사용되더라도 다른 오류 처리 메서드는 사용되지 않습니다.

  2. 사용자 모드 디버거가 연결되어 있지 않고 실행 코드에 자체 예외 처리 루틴(예: try - except)이 있는 경우 이 예외 처리 루틴은 오류를 처리하려고 시도합니다.

  3. 사용자 모드 디버거가 연결되어 있지 않고 Windows에 열린 커널 디버깅 연결이 있고 오류가 중단점 인터럽트인 경우 Windows는 커널 디버거에 연결하려고 시도합니다.

    Windows 부팅 프로세스 중에 커널 디버깅 연결을 열어야 합니다. 사용자 모드 인터럽트가 커널 디버거에 침입하지 않도록 하려면 KDbgCtrl 유틸리티를 -du 매개 변수와 함께 사용할 수 있습니다. 커널 디버깅 연결을 구성하는 방법과 KDbgCtrl을 사용하는 방법에 대한 자세한 내용은 디버깅을 위한 설정 가져오기를 참조하세요.

    커널 디버거에서 gh(예외 처리됨으로 이동) 를 사용하여 오류를 무시하고 대상을 계속 실행할 수 있습니다. gn(처리되지 않은 예외로 이동)을 사용하여 커널 디버거를 바이패스하고 4단계로 진행할 수 있습니다.

  4. 1, 2, 3단계의 조건이 적용되지 않으면 Windows는 AeDebug 레지스트리 값에 구성된 디버깅 도구를 활성화합니다. 이 상황에서 사용할 도구로 모든 프로그램을 미리 선택할 수 있습니다. 선택한 프로그램을 사후 관리 디버거라고 합니다.

  5. 1, 2, 3단계의 조건이 적용되지 않고 등록된 사후 시스템 디버거가 없는 경우 WER(Windows 오류 보고)은 메시지를 표시하고 사용 가능한 경우 솔루션을 제공합니다. 또한 적절한 값이 레지스트리에 설정된 경우 WER은 메모리 덤프 파일을 씁니다. 자세한 내용은 WER 사용User-Mode 덤프 수집을 참조하세요.

DebugBreak 함수

사후 관리 디버거가 설치된 경우 DebugBreak 함수를 호출하여 의도적으로 사용자 모드 애플리케이션에서 디버거에 침입할 수 있습니다.

Postmortem 디버거 지정

이 섹션에서는 WinDbg와 같은 도구를 사후 시스템 디버거로 구성하는 방법을 설명합니다. 구성되면 애플리케이션이 충돌할 때마다 사후 시스템 디버거가 자동으로 시작됩니다.

Post Mortem 디버거 레지스트리 키

WER(Windows 오류 보고)은 AeDebug 레지스트리 키에 설정된 값을 사용하여 사후 시스템 디버거 프로세스를 만듭니다.

Hklm\소프트웨어\Microsoft\\ Windows NT CurrentVersion\AeDebug

관심 있는 두 가지 기본 레지스트리 값인 디버거자동이 있습니다. 디버거 레지스트리 값은 사후 시스템 디버거에 대한 명령줄을 지정합니다. 자동 레지스트리 값은 사후 시스템 디버거가 자동으로 시작되는지 또는 확인 메시지 상자가 먼저 표시되는지 여부를 지정합니다.

디버거(REG_SZ)

이 REG_SZ 값은 사후 시스템 디버깅을 처리할 디버거를 지정합니다.

디버거가 기본 경로에 있는 디렉터리에 있지 않으면 디버거의 전체 경로가 나열되어야 합니다.

명령줄은 3개 매개 변수가 포함된 printf 스타일 호출을 통해 디버거 문자열에서 생성됩니다. 순서가 고정되어 있지만 사용 가능한 매개 변수를 모두 사용할 필요는 없습니다.

DWORD(%ld) - 대상 프로세스의 프로세스 ID입니다.

DWORD(%ld) - 사후 시스템 디버거 프로세스에 중복된 이벤트 핸들입니다. 사후 시스템 디버거가 이벤트를 알리는 경우 WER은 사후 시스템 디버거가 종료될 때까지 기다리지 않고 대상 프로세스를 계속합니다. 문제가 해결된 경우에만 이벤트를 신호로 보내야 합니다. 사후 시스템 디버거가 이벤트를 알리지 않고 종료되면 WER은 대상 프로세스에 대한 정보 수집을 계속합니다.

void* (%p) - 대상 프로세스의 주소 공간에 할당된 JIT_DEBUG_INFO 구조체의 주소입니다. 구조체에는 추가 예외 정보 및 컨텍스트가 포함됩니다.

자동(REG_SZ) 이 REG_SZ 값은 항상 0 또는 1입니다.

자동0으로 설정된 경우 사후 관리 디버깅 프로세스가 시작되기 전에 확인 메시지 상자가 표시됩니다.

Auto1로 설정하면 사후 시스템 디버거가 즉시 만들어집니다.

레지스트리를 수동으로 편집할 때 레지스트리를 잘못 변경해도 Windows 부팅을 허용하지 않을 수 있으므로 매우 신중하게 수행합니다.

예제 명령줄 사용

많은 사후 시스템 디버거는 -p 및 -e 스위치를 포함하는 명령줄을 사용하여 매개 변수가 PID 및 이벤트(각각)임을 나타냅니다. 예를 들어 를 통해 windbg.exe -I WinDbg를 설치하면 다음 값이 만들어집니다.

Debugger = "<Path>\WinDbg -p %ld -e %ld -g"
Auto = 1

WER %ld %ld %p 매개 변수를 사용하는 방법에 유연성이 있습니다. 예를 들어 모바일 서비스는 스크립트 실행 간에 상태를 유지하지 않으므로 스크립트 실행 간에 지속되어야 하는 모든 데이터를 테이블에 저장해야 합니다. WER 매개 변수 주위 또는 간에 스위치를 지정할 필요가 없습니다. 예를 들어 를 사용하여 procdump.exe -iWindows Sysinternals ProcDump를 설치하면 WER %ld %ld %p 매개 변수 간에 스위치가 없는 다음 값이 만들어집니다.

Debugger = "<Path>\procdump.exe" -accepteula -j "c:\Dumps" %ld %ld %p
Auto = 1

32비트 및 64비트 디버거

64비트 플랫폼에서 디버거(REG_SZ) 및 자동(REG_SZ) 레지스트리 값은 64비트 및 32비트 애플리케이션에 대해 개별적으로 정의됩니다. WOW(Windows on Windows) 키는 32비트 애플리케이션 사후 디버깅 값을 저장하는 데 사용됩니다.

Hklm\소프트웨어\Wow6432Node\Microsoft\\ Windows NT CurrentVersion\AeDebug

64비트 플랫폼에서는 32비트 프로세스에 32비트 사후 디버거를 사용하고 64비트 프로세스에는 64비트 디버거를 사용합니다. 이렇게 하면 32비트 프로세스에서 32비트 스레드 대신 WOW64 스레드에 초점을 맞춘 64비트 디버거가 방지됩니다.

Windows Postmortem 디버거용 디버깅 도구를 비롯한 많은 사후 시스템 디버거의 경우 설치 명령을 두 번 실행해야 합니다. x86 버전으로 한 번, x64 버전으로 한 번 예를 들어 WinDbg를 대화형 사후 관리 디버거로 사용하려면 명령이 windbg.exe -I 각 버전에 대해 한 번씩 두 번 실행됩니다.

64비트 설치:

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe –I

이렇게 하면 레지스트리 키가 이러한 값으로 업데이트됩니다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug
Debugger = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -p %ld -e %ld –g

32비트 설치:

C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe –I

이렇게 하면 레지스트리 키가 이러한 값으로 업데이트됩니다.

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug
Debugger = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe" -p %ld -e %ld –g

Post Mortem 디버거 구성

Windows용 디버깅 도구

Windows용 디버깅 도구 디버거는 사후 관리 디버거로 설정되는 모든 지원을 지원합니다. 설치 명령은 프로세스를 대화형으로 디버깅하려고 합니다.

WinDbg

사후 시스템 디버거를 WinDbg로 설정하려면 을 실행합니다 windbg -I. (은 I 대문자로 표시되어야 합니다.) 이 명령은 사용 후 성공 또는 실패 메시지를 표시합니다. 32비트 및 64비트 애플리케이션을 모두 사용하려면 64 및 32 디버거 모두에 대한 명령을 실행합니다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe –I
C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe –I

이는 가 실행될 때 windbg -I AeDebug 레지스트리 항목이 구성되는 방법입니다.

Debugger = "<Path>\WinDbg -p %ld -e %ld -g"
Auto = 1

예제 <에서 Path> 는 디버거가 있는 디렉터리입니다.

-p 및 -e 매개 변수는 앞에서 설명한 대로 프로세스 ID 및 이벤트를 전달합니다.

-g는 g(Go) 명령을 WinDbg에 전달하고 현재 명령에서 실행을 계속합니다.

참고 g(Go) 명령을 전달하는 데 중요한 문제가 있습니다. 이 방법의 문제는 일반적으로 코드를 다시 시작할 때 더 이상 존재하지 않는 일시적인 조건으로 인해 예외가 항상 반복되지는 않는다는 것입니다. 이 문제에 대한 자세한 내용은 .jdinfo(JIT_DEBUG_INFO 사용)를 참조하세요.

이 문제를 방지하려면 .jdinfo 또는 .dump /j를 사용합니다. 이 방법을 사용하면 디버거가 관심 있는 코드 오류의 컨텍스트에 있을 수 있습니다. 자세한 내용은 이 항목의 뒷부 분에 있는 JIT(Just In Time) 디버깅을 참조하세요.

CDB

postmortem 디버거를 CDB로 설정하려면 cdb -iae (AeDebug 설치) 또는 cdb -iaecKeyString (명령을 사용하여 AeDebug 설치)을 실행합니다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe -iae
C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe -iae

-iaec 매개 변수를 사용하는 경우 KeyString은 사후 시스템 디버거를 시작하는 데 사용되는 명령줄의 끝에 추가할 문자열을 지정합니다. KeyString에 공백이 포함된 경우 따옴표로 묶어야 합니다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe -iaec [KeyString]
C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe -iaec [KeyString]

이 명령은 성공하면 아무 것도 표시하지 않으며 실패할 경우 오류 메시지를 표시합니다.

Ntsd

사후 시스템 디버거를 NTSD로 설정하려면 ntsd -iae (AeDebug 설치) 또는 ntsd -iaecKeyString (명령을 사용하여 AeDebug 설치)을 실행합니다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\ntsd.exe -iae
C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\ntsd.exe -iae

-iaec 매개 변수를 사용하는 경우 KeyString은 사후 관리 디버거를 시작하는 데 사용되는 명령줄 끝에 추가할 문자열을 지정합니다. KeyString에 공백이 포함된 경우 따옴표로 묶어야 합니다.

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\ntsd.exe -iaec [KeyString]
C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\ntsd.exe -iaec [KeyString]

이 명령은 성공하면 아무 것도 표시하지 않으며 실패 시 새 콘솔 창에 오류가 발생합니다.

참고 -p %ld -e %ld -g 매개 변수는 항상 사후 시스템 디버거의 명령줄에 먼저 표시되므로 -iaec 스위치를 사용하여 -server 매개 변수를 지정하면 안 됩니다. -server는 명령줄에 먼저 표시되지 않는 한 작동하지 않기 때문입니다. 이 매개 변수를 포함하는 사후 관리 디버거를 설치하려면 레지스트리를 수동으로 편집해야 합니다.

Visual Studio JIT 디버거

Visual Studio가 설치된 경우 vsjitdebugger.exe 사후 모템 디버거로 등록됩니다. Visual Studio JIT 디버거는 프로세스를 대화형으로 디버그하려고 합니다.

Debugger = "C:\WINDOWS\system32\vsjitdebugger.exe" -p %ld -e %ld

Visual Studio가 업데이트되거나 다시 설치되면 이 항목이 다시 작성되어 설정된 대체 값을 덮어씁니다.

창 Sysinternals ProcDump

Windows Sysinternals ProcDump 유틸리티는 사후 덤프 캡처에도 사용할 수 있습니다. ProcDump를 사용하고 다운로드하는 방법에 대한 자세한 내용은 ProcDump를 참조하세요.

.dump WinDbg 명령과 마찬가지로 ProcDump는 비대화형으로 크래시 덤프를 캡처할 수 있습니다. 캡처는 모든 Windows 시스템 세션에서 발생할 수 있습니다.

덤프 파일 캡처가 완료되면 ProcDump가 종료되고 WER에서 오류를 보고하고 오류 프로세스가 종료됩니다.

procdump -i procdump 및 -you를 설치하여 32비트 및 64비트 사후 디버깅 모두에 대해 ProcDump를 제거합니다.

<Path>\procdump.exe -i

설치 및 제거 명령은 성공 시 수정된 레지스트리 값과 실패 시 오류를 출력합니다.

레지스트리의 ProcDump 명령줄 옵션은 다음으로 설정됩니다.

Debugger = <Path>\ProcDump.exe -accepteula -j "<DumpFolder>" %ld %ld %p

ProcDump는 PID, 이벤트 및 JIT_DEBUG_INFO 3개 매개 변수를 모두 사용합니다. JIT_DEBUG_INFO 매개 변수에 대한 자세한 내용은 아래 JIT(Just In Time) 디버깅을 참조하세요.

캡처된 덤프의 크기는 크기 옵션 집합이 없는 미니(프로세스/스레드/핸들/모듈/주소 공간), -mp 집합이 있는 MiniPlus(미니 및 MEM_PRIVATE 페이지) 또는 -ma 집합이 있는 전체(모든 메모리 - ".dump /mA"에 해당)로 기본 설정됩니다.

드라이브 공간이 충분한 시스템의 경우 전체(-ma) 캡처를 사용하는 것이 좋습니다.

-ma를 -i 옵션과 함께 사용하여 모든 메모리 캡처를 지정합니다. 필요에 따라 덤프 파일의 경로를 제공합니다.

<Path>\procdump.exe -ma -i c:\Dumps

드라이브 공간이 제한된 시스템의 경우 MiniPlus(-mp) 캡처를 사용하는 것이 좋습니다.

<Path>\procdump.exe -mp -i c:\Dumps

덤프 파일을 저장할 폴더는 선택 사항입니다. 기본값은 현재 폴더입니다. 폴더는 C:\Windows\Temp에 사용되는 것보다 같거나 더 나은 ACL로 보호되어야 합니다. 폴더와 관련된 보안을 관리하는 방법에 대한 자세한 내용은 Postmortem 디버깅 중 보안을 참조하세요.

ProcDump를 사후 관리 디버거로 제거하고 이전 설정을 복원하려면 -u(제거) 옵션을 사용합니다.

<Path>\procdump.exe -u

ProcDump에 대한 자세한 내용은 Microsoft Press에서 게시한 Mark Russinovich 및 Aaron Margosis의 ProcDumpWindows SysInternals 관리자 참조를 참조 하세요.

JIT(Just-In-Time) 디버깅

컨텍스트를 오류 애플리케이션으로 설정

앞에서 설명한 대로 JIT_DEBUG_INFO 매개 변수를 사용하여 충돌을 일으킨 예외로 컨텍스트를 설정하는 것이 매우 바람직합니다. 이에 대한 자세한 내용은 .jdinfo(JIT_DEBUG_INFO 사용)를 참조하세요.

Windows용 디버깅 도구

이 예제에서는 .jdinfo <주소> 명령을 사용하여 추가 예외 정보를 표시하고 컨텍스트를 예외 위치로 변경하는 초기 명령(-c)을 실행하도록 레지스트리를 편집하는 방법을 보여 줍니다(.ecxr을 사용하는 방법과 유사하게 컨텍스트를 예외 레코드로 설정).

Debugger = "<Path>\windbg.exe -p %ld -e %ld -c ".jdinfo 0x%p"
Auto = 1

%p 매개 변수는 대상 프로세스의 주소 공간에서 JIT_DEBUG_INFO 구조체의 주소입니다. %p 매개 변수는 16진수 값으로 해석되도록 0x로 미리 추가됩니다. 자세한 내용은 .jdinfo(JIT_DEBUG_INFO 사용)를 참조하세요.

32비트 및 64비트 앱의 혼합을 디버그하려면 32비트 및 64비트 레지스트리 키(위에서 설명)를 모두 구성하고 64비트 및 32비트 WinDbg.exe 위치에 대한 적절한 경로를 설정합니다.

.dump를 사용하여 덤프 파일 만들기

JIT_DEBUG_INFO 데이터를 포함하는 오류가 발생할 때마다 덤프 파일을 캡처하려면 .dump /j <주소를> 사용합니다.

<Path>\windbg.exe -p %ld -e %ld -c ".dump /j %p /u <DumpPath>\AeDebug.dmp; qd"

/u 옵션을 사용하여 고유한 파일 이름을 생성하여 여러 덤프 파일을 자동으로 만들 수 있습니다. 옵션에 대한 자세한 내용은 .dump(덤프 파일 만들기)를 참조하세요.

만든 덤프에는 기본 예외 컨텍스트로 저장된 JITDEBUG_INFO 데이터가 있습니다. .jdinfo를 사용하여 예외 정보를 보고 컨텍스트를 설정하는 대신 .exr -1을 사용하여 예외 레코드를 표시하고 .ecxr를 사용하여 컨텍스트를 설정합니다. 자세한 내용은 .exr(예외 레코드 표시).ecxr(예외 컨텍스트 레코드 표시)를 참조하세요.

Windows 오류 보고 - q/ qd

디버그 세션이 종료되는 방식에 따라 Windows 오류 보고 오류를 보고하는지 여부가 결정됩니다.

디버거를 닫기 전에 qd를 사용하여 디버그 세션이 분리되면 WER에서 오류를 보고합니다.

q를 사용하여 디버그 세션을 종료하거나 디버거를 분리하지 않고 닫은 경우 WER은 오류를 보고하지 않습니다.

를 추가 합니다. q 또는 ; 원하는 동작을 호출하기 위해 명령 문자열의 끝에 qd합니다.

예를 들어 CDB가 덤프를 캡처한 후 WER에서 오류를 보고할 수 있도록 하려면 이 명령 문자열을 구성합니다.

<Path>\cdb.exe -p %ld -e %ld -c ".dump /j 0x%p /u c:\Dumps\AeDebug.dmp; qd"

이 예제에서는 WinDbg가 덤프를 캡처한 후 WER에서 오류를 보고할 수 있도록 합니다.

<Path>\windbg.exe -p %ld -e %ld -c ".dump /j %p /u <DumpPath>\AeDebug.dmp; qd""

보안 취약성

다른 사용자와 공유하는 컴퓨터에서 사후 관리 디버깅을 사용하도록 설정하려는 경우 사후 관리 디버깅 중 보안을 참조하세요.