디버깅을 하기 위한 방법에는 여러가지가 있지만
이 방법은 정공법으로써 pdb 와시스템이 남기는 디버깅 정보를 활용하는 방법이다.
참고로 나는 drwtsn32 를 띄우지 않고 ollydbg 를 레지스트리에 등록해놓고 디버깅을 하지만..
1탄.
초보 분들에게 도움이 되었으면 하고, 제가 사용하는 방법을 정리해 볼까 합니다.
우선, 덤프를 확보하는 방법부터 얘기해보겠습니다.
윈도우에서 동작하는 어플리케이션이 예외처리가 되지 않은 오류를 일으키게 되면 윈도우는 시스템에 설정되어 있는 디버거를 호출하게 됩니다.
HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug
이 경로의 레지스트리를 보게되면, 현재 시스템의 디버거로 어떤 툴이 사용되는지를 알 수 있습니다.
일단 덤프가 확보되도록 설정하는 방법을 말씀드리면,
콘솔 창(cmd.exe)에서 drwtsn32.exe 를 입력하여 실행해 주시기 바랍니다.
그러면 닥터 왓슨 이라는 윈도우 기본 덤프 생성 프로그램이 실행이 됩니다.
보시면, 덤프 파일의 생성 위치 등을 설정할 수 있습니다.
이어서 콘솔 창에서 drwtsn32.exe -i 을 입력해 봅니다.
그러면 닥터 왓슨 프로그램이 해당 시스템의 기본 디버거로 등록이 됩니다.
( 이때 미리 저 위의 레지스트리 값을 저장해 두셨다가, 나중에 원래대로 복구 하시기 바랍니다 ;; )
자 여기까지 하셨으면, 시스템에서 응용 프로그램이 오류를 일으키면 덤프 파일이 생성 되도록 설정 되었습니다.
덤프 분석을 하기위해서는 덤프 파일 외에도 디버깅 심벌 파일 이라는 것이 필요합니다.
바로 pdb 확장자를 가지는 파일이 이것인데요.
디버그 모드와 릴리즈 모드 모두! 이 pdb 파일을 이용하여 덤프 분석 및 디버깅을 할 수 있습니다.
( 질문 게시판에서 릴리즈 모드 일때에만 에러가 발생한다고 하시던 분들이 계시더군요. 릴리즈 모드에서도 디버깅을 할 수 있습니다. )
디버그 모드에서는 컴파일을 하면, 보통 실행파일이 생성되는 위치에 심벌 파일도 함께 생성이 됩니다.
릴리즈 모드에서도 심벌 파일을 생성하기 위해서는 약간의 세팅을 해주면 되는데요.
프로젝트 세팅 메뉴에서 Link 탭을 가보시면, Generate Debug Info 체크 박스를 체크해 주시면 됩니다. (VC6.0 기준)
이렇게 해서 컴파일을 하시면, 실행 파일과 함께 디버깅 심벌 파일이 확보됩니다.
이어서 발생한 덤프 파일을 분석하는 방법을 얘기해 보겠습니다.
참조 : http://www.debuginfo.com/articles/ntsdwatson.html
참조 : http://blogs.msdn.com/arvindsh/archive/2006/08/30/731150.aspx
추가 : VC 6.0 을 사용하시는 분들은 Release 버전의 pdb 파일을 생성하실 때, 추가적인 옵션 설정이 필요합니다.
- 프로젝트 세팅 메뉴를 엽니다
- Link 탭으로 이동합니다.
- Generate Debug Info 체크박스를 체크해 줍니다.
- 다시 C/C++ 탭으로 이동합니다.
- Debug Info 부분에서 Program Database 를 선택합니다.
- C/C++ 탭에서 카테고리를 Listing Files 로 선택합니다.
- Listing file type 부분에서 'Assembly, Machine Code, and Source' 를 선택합니다.
- 재 빌드 하시면, pdb 가 생성이 됩니다.
출처 : 외계달팽
2탄.
저번 글에 이어서 덤프 파일을 분석하는 환경을 구축하는 방법에 대해 정리해 보도록 하겠습니다.
일단 예제로 사용할 런타임 오류를 일으키는 프로그램을 하나 간단하게 만들어 보았습니다.
#define REG_PATH_AEDEBUG "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
#define REG_VALUENAME_DEBUGGER "Debugger"
void CCrashSampleDlg::OnButton1()
{
HKEY RegHandle = NULL;
LPDWORD Ret = 0;
char * pCrashPointer;
pCrashPointer = (char*)0x1234;
RegCreateKeyEx(HKEY_LOCAL_MACHINE , REG_PATH_AEDEBUG , 0 , NULL ,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL , &RegHandle, Ret);
RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
(LPBYTE)pCrashPointer , 0x10 );
RegCloseKey( RegHandle );
return;
}
보시는 바와 같이 잘못된 메모리 0x1234 의 메모리를 읽어서 레지스트리에 저장하려고 하다가 오류가 발생하는 소스 입니다.
저번 글에 소개한 drwtsn32.exe 를 시스템 기본 디버거로 설정하고,
위의 소스를 컴파일한 실행 파일을 윈도우 탐색기에서 실행해 봅니다.
그리고 Button1 을 눌러서 위의 루틴을 실행해서 오류를 일으켜 봅니다.
그러면 닥터 왓슨이 해당 프로세스의 덤프를 생성해서 설정된 위치에 저장해 둘 것입니다.
( drwtsn32.exe 를 실행해 보시면, 덤프 생성 위치를 확인할 수 있습니다. )
이제 분석할 덤프 파일을 확보하였습니다.
저번 글에 말씀드린 Generate Debug Info 옵션이 체크되어 있는 상태로 컴파일 하셨다면,
디버깅 심벌 파일 또한 이미 만들어져 있을 것입니다. ( 위의 예제에선 CrashSample.pdb 파일 입니다. )
이제 이 덤프 파일을 분석할 도구가 필요합니다.
저는 windbg 를 선택하였습니다.
http://www.microsoft.com/whdc/devtools/debugging/default.mspx
위의 링크에서 windbg 를 다운로드 받고, 설치할 수 있습니다.
설치가 완료되었다면, WinDbg 를 실행해 보겠습니다.
WinDbg 를 실행하면, File -> Open Crash Dump 메뉴를 이용하여 조금 전에 생성한 dmp 파일을 열어봅니다.
이제 덤프 파일을 분석할 준비가 되었는데요.
본격적인 분석을 하기 위해선, 운영체제의 디버깅 심벌이 필요합니다.
WinDbg 의 Command 창에서 다음과 같이 입력해 보겠습니다.
.symfix+ c:\MySymbol
이때, c:\MySymbol 이라는 디렉토리가 생성되어 있어야 하며,
이 디렉토리에는 WinDbg 가 필요한 심벌 파일을 다운 받아서 저장하게 됩니다.
그리고 File -> Symbol File Path 메뉴를 선택하여, 우리가 분석할 프로그램의 pdb 파일도 등록을 합니다.
여기에선 CrashSample.pdb 가 존재하는 디렉토리를 선택해 주시면 됩니다. ( browse 버튼을 이용하여, 추가해 주십시요. )
디버그 버전으로 컴파일 하셨으면 debug 폴더를, 릴리즈 버전으로 컴파일 하셨으면, release 폴더를 선택해주시면 됩니다.
또한 File -> Source File Path 메뉴를 선택하여, 소스가 들어있는 폴더도 적당히 선택해 줍니다.
이렇게 디버깅 심벌 파일들의 설정이 끝났으면, 분석을 해보도록 하겠습니다.
다음 명령을 command 창에 입력해 주십시요.
!analyze -v
이렇게 입력을 했더니, 저는 다음과 같은 결과가 나왔네요.
0:000> !analyze -v
*** WARNING: Unable to verify checksum for CrashSample.exe
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
advapi32!RegSetValueExA+b9
77f5ec75 8078ff00 cmp byte ptr [eax-1],0
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77f5ec75 (advapi32!RegSetValueExA+0x000000b9)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00001243
Attempt to read from address 00001243
DEFAULT_BUCKET_ID: APPLICATION_FAULT
PROCESS_NAME: CrashSample.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"
READ_ADDRESS: 00001243
BUGCHECK_STR: ACCESS_VIOLATION
LAST_CONTROL_TRANSFER: from 00401378 to 77f5ec75
STACK_TEXT:
0012f89c 00401378 00000740 00403020 00000000 advapi32!RegSetValueExA+0xb9
0012f8bc 73d124c0 004022d0 00000111 0012f8fc CrashSample!CCrashSampleDlg::OnButton1+0x48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
0012f8cc 73d123bf 0012fe94 000003e8 00000000 mfc42!_AfxDispatchCmdMsg+0x82
0012f8fc 73d7dead 000003e8 00000000 00000000 mfc42!CCmdTarget::OnCmdMsg+0x10a
0012f920 73d13244 000003e8 00000000 00000000 mfc42!CPropertySheet::OnCmdMsg+0x1d
0012f970 73d11bf1 00000000 006111ea 0012fe94 mfc42!CWnd::OnCommand+0x53
0012f9f0 73d11b9b 00000111 000003e8 006111ea mfc42!CWnd::OnWndMsg+0x2f
0012fa10 73d11b05 00000111 000003e8 006111ea mfc42!CWnd::WindowProc+0x24
0012fa70 73d11a58 0012fe94 00000000 00000111 mfc42!AfxCallWndProc+0x91
0012fa90 73da847d 00360eba 00000111 000003e8 mfc42!AfxWndProc+0x36
0012fabc 77cf8724 00360eba 00000111 000003e8 mfc42!AfxWndProcBase+0x39
0012fae8 77cf8806 73da8444 00360eba 00000111 user32!InternalCallWinProc+0x28
0012fb50 77cfb88b 00000000 73da8444 00360eba user32!UserCallWinProcCheckWow+0x150
0012fb8c 77cfb8f3 007cc170 007cb068 000003e8 user32!SendMessageWorker+0x4a5
0012fbac 77d2fe95 00360eba 00000111 000003e8 user32!SendMessageW+0x7f
0012fbc4 77d2658d 007cd9b0 00000000 007cd9b0 user32!xxxButtonNotifyParent+0x41
0012fbe0 77d0783f 00149f9c 00000001 00000000 user32!xxxBNReleaseCapture+0xf8
0012fc64 77d1b06a 007cd9b0 00000202 00000000 user32!ButtonWndProcWorker+0x6df
0012fc84 77cf8724 006111ea 00000202 00000000 user32!ButtonWndProcA+0x5d
0012fcb0 77cf8806 77d1b01e 006111ea 00000202 user32!InternalCallWinProc+0x28
0012fd18 77cf89bd 00000000 77d1b01e 006111ea user32!UserCallWinProcCheckWow+0x150
0012fd78 77cf8a00 004030bc 00000000 0012fdac user32!DispatchMessageWorker+0x306
0012fd88 77d0e0a7 004030bc 004030bc 004030c4 user32!DispatchMessageW+0xf
0012fdac 77d1c6bb 00360eba 007cd9b0 004030bc user32!IsDialogMessageW+0x572
0012fdcc 73d26795 00360eba 004030bc 0012fe94 user32!IsDialogMessageA+0xfd
0012fddc 73d1a76e 004030bc 73d2675a 004030bc mfc42!CWnd::IsDialogMessageA+0x31
0012fde4 73d2675a 004030bc 004030bc 00360eba mfc42!CWnd::PreTranslateInput+0x29
0012fdf4 73d113aa 004030bc 004030bc 0012fe94 mfc42!CDialog::PreTranslateMessage+0x96
0012fe04 73d11351 00360eba 004030bc 00403088 mfc42!CWnd::WalkPreTranslateTree+0x21
0012fe18 73d11248 004030bc 00000000 0012fe94 mfc42!CWinThread::PreTranslateMessage+0x31
0012fe28 73d26b99 00000004 0012fe94 0012fe88 mfc42!CWinThread::PumpMessage+0x2a
0012fe4c 73d26a2e 00000004 00403088 00403088 mfc42!CWnd::RunModalLoop+0xd9
0012fe88 004010f3 00403088 00402338 00000001 mfc42!CDialog::DoModal+0xe8
0012ff00 73d1cf74 00000000 001423ad 00000000 CrashSample!CCrashSampleApp::InitInstance+0x43 [E:\work\CrashSample\CrashSample.cpp @ 71]
0012ff10 004017e9 00400000 00000000 001423ad mfc42!AfxWinMain+0x49
0012ff24 00401704 00400000 00000000 001423ad CrashSample!WinMain+0x15 [appmodul.cpp @ 30]
0012ffc0 7c816fd7 00000000 7c94d496 7ffd4000 CrashSample!WinMainCRTStartup+0x134
0012fff0 00000000 004015d0 00000000 78746341 kernel32!BaseProcessStart+0x23
STACK_COMMAND: ~0s; .ecxr ; kb
FAULTING_THREAD: 00000954
FOLLOWUP_IP:
CrashSample!CCrashSampleDlg::OnButton1+48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
00401378 8b542400 mov edx,dword ptr [esp]
FAULTING_SOURCE_CODE:
115:
116: RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
117: (LPBYTE)pCrashPointer , 0x10 );
118:
> 119: RegCloseKey( RegHandle );
120:
121: return;
122:
123:
124: }
SYMBOL_STACK_INDEX: 1
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: CrashSample
IMAGE_NAME: CrashSample.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 47730c41
SYMBOL_NAME: CrashSample!CCrashSampleDlg::OnButton1+48
FAILURE_BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
Followup: MachineOwner
---------
다음 글에서는 간단하게 WinDbg 명령어를 이용하여 오류를 분석해 보도록 하겠습니다.
3탄..
마지막으로 덤프 파일을 WinDbg 를 이용하여 분석해 보도록 하겠습니다.
지난 글에서 !analyze - v 명령을 이용하여 덤프를 분석해 보았었는데요.
우선 그 결과를 살펴보겠습니다.
0:000> !analyze -v
*** WARNING: Unable to verify checksum for CrashSample.exe
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
advapi32!RegSetValueExA+b9
77f5ec75 8078ff00 cmp byte ptr [eax-1],0 : 문제의 명령어를 가리키고 있습니다.
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: 77f5ec75 (advapi32!RegSetValueExA+0x000000b9)
ExceptionCode: c0000005 (Access violation) : 예외 상황 코드이네요. 접근 위반 이로군요.
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00001243
Attempt to read from address 00001243 : 00001243 주소를 읽으려고 했답니다.
DEFAULT_BUCKET_ID: APPLICATION_FAULT
PROCESS_NAME: CrashSample.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"
READ_ADDRESS: 00001243
BUGCHECK_STR: ACCESS_VIOLATION
LAST_CONTROL_TRANSFER: from 00401378 to 77f5ec75 : 실행 제어가 마지막으로 옮겨간 것에 대한 내용이네요.
STACK_TEXT: : 아래부터 스택 상황을 보여줍니다.
0012f89c 00401378 00000740 00403020 00000000 advapi32!RegSetValueExA+0xb9
0012f8bc 73d124c0 004022d0 00000111 0012f8fc CrashSample!CCrashSampleDlg::OnButton1+0x48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119] : 제가 만든 부분이 보이는군요.
0012f8cc 73d123bf 0012fe94 000003e8 00000000 mfc42!_AfxDispatchCmdMsg+0x82
0012f8fc 73d7dead 000003e8 00000000 00000000 mfc42!CCmdTarget::OnCmdMsg+0x10a
0012f920 73d13244 000003e8 00000000 00000000 mfc42!CPropertySheet::OnCmdMsg+0x1d
0012f970 73d11bf1 00000000 006111ea 0012fe94 mfc42!CWnd::OnCommand+0x53
0012f9f0 73d11b9b 00000111 000003e8 006111ea mfc42!CWnd::OnWndMsg+0x2f
0012fa10 73d11b05 00000111 000003e8 006111ea mfc42!CWnd::WindowProc+0x24
0012fa70 73d11a58 0012fe94 00000000 00000111 mfc42!AfxCallWndProc+0x91
0012fa90 73da847d 00360eba 00000111 000003e8 mfc42!AfxWndProc+0x36
0012fabc 77cf8724 00360eba 00000111 000003e8 mfc42!AfxWndProcBase+0x39
0012fae8 77cf8806 73da8444 00360eba 00000111 user32!InternalCallWinProc+0x28
0012fb50 77cfb88b 00000000 73da8444 00360eba user32!UserCallWinProcCheckWow+0x150
0012fb8c 77cfb8f3 007cc170 007cb068 000003e8 user32!SendMessageWorker+0x4a5
0012fbac 77d2fe95 00360eba 00000111 000003e8 user32!SendMessageW+0x7f
0012fbc4 77d2658d 007cd9b0 00000000 007cd9b0 user32!xxxButtonNotifyParent+0x41
0012fbe0 77d0783f 00149f9c 00000001 00000000 user32!xxxBNReleaseCapture+0xf8
0012fc64 77d1b06a 007cd9b0 00000202 00000000 user32!ButtonWndProcWorker+0x6df
0012fc84 77cf8724 006111ea 00000202 00000000 user32!ButtonWndProcA+0x5d
0012fcb0 77cf8806 77d1b01e 006111ea 00000202 user32!InternalCallWinProc+0x28
0012fd18 77cf89bd 00000000 77d1b01e 006111ea user32!UserCallWinProcCheckWow+0x150
0012fd78 77cf8a00 004030bc 00000000 0012fdac user32!DispatchMessageWorker+0x306
0012fd88 77d0e0a7 004030bc 004030bc 004030c4 user32!DispatchMessageW+0xf
0012fdac 77d1c6bb 00360eba 007cd9b0 004030bc user32!IsDialogMessageW+0x572
0012fdcc 73d26795 00360eba 004030bc 0012fe94 user32!IsDialogMessageA+0xfd
0012fddc 73d1a76e 004030bc 73d2675a 004030bc mfc42!CWnd::IsDialogMessageA+0x31
0012fde4 73d2675a 004030bc 004030bc 00360eba mfc42!CWnd::PreTranslateInput+0x29
0012fdf4 73d113aa 004030bc 004030bc 0012fe94 mfc42!CDialog::PreTranslateMessage+0x96
0012fe04 73d11351 00360eba 004030bc 00403088 mfc42!CWnd::WalkPreTranslateTree+0x21
0012fe18 73d11248 004030bc 00000000 0012fe94 mfc42!CWinThread::PreTranslateMessage+0x31
0012fe28 73d26b99 00000004 0012fe94 0012fe88 mfc42!CWinThread::PumpMessage+0x2a
0012fe4c 73d26a2e 00000004 00403088 00403088 mfc42!CWnd::RunModalLoop+0xd9
0012fe88 004010f3 00403088 00402338 00000001 mfc42!CDialog::DoModal+0xe8
0012ff00 73d1cf74 00000000 001423ad 00000000 CrashSample!CCrashSampleApp::InitInstance+0x43 [E:\work\CrashSample\CrashSample.cpp @ 71]
0012ff10 004017e9 00400000 00000000 001423ad mfc42!AfxWinMain+0x49
0012ff24 00401704 00400000 00000000 001423ad CrashSample!WinMain+0x15 [appmodul.cpp @ 30]
0012ffc0 7c816fd7 00000000 7c94d496 7ffd4000 CrashSample!WinMainCRTStartup+0x134
0012fff0 00000000 004015d0 00000000 78746341 kernel32!BaseProcessStart+0x23
STACK_COMMAND: ~0s; .ecxr ; kb : ~0s; .ecxr ; kb 이 명령어를 command 창에 입력하란 얘기네요.
FAULTING_THREAD: 00000954 : 문제가 발생한 쓰레드의 아이디 입니다.
FOLLOWUP_IP:
CrashSample!CCrashSampleDlg::OnButton1+48 [E:\work\CrashSample\CrashSampleDlg.cpp @ 119]
00401378 8b542400 mov edx,dword ptr [esp]
FAULTING_SOURCE_CODE: : 소스 코드 까지도 보여주고 있습니다.
115:
116: RegSetValueEx(RegHandle, REG_VALUENAME_DEBUGGER , 0 , REG_SZ ,
117: (LPBYTE)pCrashPointer , 0x10 );
118:
> 119: RegCloseKey( RegHandle ); : return 될 부분을 가리키고 있죠. 우리는 이 바로 윗 라인에서 에러가 발생했을거라 추측할 수 있습니다.
120:
121: return;
122:
123:
124: }
SYMBOL_STACK_INDEX: 1
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: CrashSample
IMAGE_NAME: CrashSample.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 47730c41
SYMBOL_NAME: CrashSample!CCrashSampleDlg::OnButton1+48
FAILURE_BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
BUCKET_ID: ACCESS_VIOLATION_CrashSample!CCrashSampleDlg::OnButton1+48
Follow: MachineOwner
---------
예, WinDbg 의 분석 결과를 대충 살펴보니, 잘못된 메모리를 읽으려고 시도했던것 같습니다.
일단 WinDbg 가 시키는대로 다음의 명령을 입력해 보겠습니다.
~0s; .ecxr ; kb
나오는 결과는 위에서 보는것과 그렇게 큰 차이가 없네요. ( 생략하겠습니다. )
현재의 스택 프레임은 advapi32!RegSetValueExA 함수의 것으로 맞추어져 있습니다.
이건 우리가 만든게 아니죠.
일단 우리가 만든 함수의 스택 프레임으로 가보겠습니다.
다음의 명령을 입력하면, 각 스택의 번호를 확인할 수 있습니다.
kn
0:000> kn
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
00 0012f880 004013f4 advapi32!RegSetValueExA+0xb9
01 0012f8b0 73d124c0 CrashSample!CCrashSampleDlg::OnButton1+0x5c [E:\work\CrashSample\CrashSampleDlg.cpp @ 115]
02 0012f8c0 73d123bf mfc42!_AfxDispatchCmdMsg+0x82
03 0012f8f0 73d7dead mfc42!CCmdTarget::OnCmdMsg+0x10a
04 0012f914 73d13244 mfc42!CPropertySheet::OnCmdMsg+0x1d
제가 만든 루틴의 스택 번호가 01 이라는걸 알 수 있네요.
이 스택을 현재 프레임으로 맞춰보겠습니다.
.frame 1
예, 맞춰졌습니다. 소스 폴더까지 등록해 놓으셨으면, 소스 코드까지도 보여줄 것 같습니다.
일단 지역 변수를 살펴 보겠습니다.
dv
0:000> dv
this = 0x0012fe8c
pCrashPointer = 0x00001234 ""
Ret = 0x00000000
RegHandle = 0x00000740
예, pCrashPointer 라는 지역 변수가 0x1234 라는걸 확인할 수 있습니다.
여기까지만 보아도 어떤 문제가 있었는지 감을 잡을 수 있을겁니다.
( 최적화 옵션으로 인해서 지역 변수가 깔끔하게 나타나지 않아서 프로젝트 세팅 부분에서 최적화 옵션을 Disable 로 설정하였습니다. )
이 덤프 분석에서는
Attempt to read from address 00001243
가 문제의 원인이 되고 있습니다.
왜 1234 를 넣었는데, 1243 을 읽으려고 했는지를 조금만 더 살펴 보도록 하죠.
WinDbg 의 View 메뉴에서
Disassembly (Alt + 7)
메뉴를 선택해 봅니다.
그리고 새로나온 디스어셈블 창의 offset 부분에
ExceptionAddress: 77f5ec75
예외 발생 지점인 77f5ec75 를 입력해 봅시다.
77f5ec3d 0f84f9840200 je advapi32!RegSetValueExA+0x58 (77f8713c)
77f5ec43 8d45cc lea eax,[ebp-34h]
77f5ec46 8945d8 mov dword ptr [ebp-28h],eax
77f5ec49 668345cc02 add word ptr [ebp-34h],2
77f5ec4e 0f84fa840200 je advapi32!RegSetValueExA+0x77 (77f8714e)
77f5ec54 8b4518 mov eax,dword ptr [ebp+18h]
77f5ec57 8945dc mov dword ptr [ebp-24h],eax
77f5ec5a 3bc3 cmp eax,ebx
77f5ec5c 0f84e6000000 je advapi32!RegSetValueExA+0x12a (77f5ed48)
77f5ec62 837d1401 cmp dword ptr [ebp+14h],1
77f5ec66 0f85c8000000 jne advapi32!RegSetValueExA+0xa4 (77f5ed34)
77f5ec6c 8b751c mov esi,dword ptr [ebp+1Ch]
77f5ec6f 3bf3 cmp esi,ebx
77f5ec71 760c jbe advapi32!RegSetValueExA+0xe1 (77f5ec7f)
77f5ec73 03c6 add eax,esi
77f5ec75 8078ff00 cmp byte ptr [eax-1],0 ds:0023:00001243=??
네 디스어셈블된 명령어 들이 쭈욱 보입니다.
친절하게도 eax-1 의 주소가 ds:0023:00001243 이 값이고, 이는 ?? 이렇게 표현할 수 없는 메모리 주소(Page Fault)라고 알려주네요.
eax 에 어떻게 저런 값이 들어갔는지를 뚫어져라 살펴보니... ( 빨간색 칠을 한 부분 )
인자값으로 받은 0x1234 에서 0x10 (이것도 인자로 입력하였죠) 만큼의 길이 를 더해서(그리고 -1) 이 값이 널( 0 ) 값인지를 체크하고 있는것 같네요.
우리는 advapi32.dll 의 RegSetValueExA 함수 내부에 인자로 받은 문자열의 끝부분을 NULL 체크하는 루틴이 있을거 같다고 추측할 수 있습니다.
정리해보면, 우리가 0x1234 라는 잘못된 주소값을 넘겨주었기 때문에,
RegSetValueEx 라는 API 함수 내부에서 이 잘못된 메모리에 접근하려다가 문제가 발생했다고 결론내릴 수 있을것 같습니다.
이렇게 해서 간단하게 덤프 파일을 분석하는 방법에 대해 살펴보았습니다.
사실 디어셈블까지 하지 않더라도, 디버깅 심벌 파일을 세팅해놓고, !analyze -v 명령을 입력하는 것만으로도
많은 경우의 에러상황을 분석할 수 있습니다.