Transcript ebp

Debugging Application
황종하
웨어밸리, 기술 연구소
www.warevalley.com
디버깅 툴바
Build controls
Set Default
Project Configuration
Debug controls
The Debug Toolbar
Restart
Stop
Debugging
Step Out
Variables
Disassembly
Step Into
QuickWatch
Memory
Watch
Call Stack
Step Over
Registers
Step to Cursor
Debug Window Controls
코딩하는 동안에 디버깅하기
Assertion
- 프로그램의 어떤 지점에서 어떤 조건이 꼭 참이라고 단언하는 것
: ASSERT()
: 조건이 0으로 평가되면 경고 메시지 표시
- Assertion은 함수나 매크로이며, 디버그 빌드에서만 실행
- Assertion은 코드에서 또 다른 문서 자료로 활용
- 함수 데이터에 대해서 작성한 사람이 어떤 것을 예상하고 있었는지를
알려 줌
- 변수나 프로그램의 상태를 변경하지 않음
- Assertion에서 데이터를 변경하면 디버그 빌드와 릴리즈 빌드에서 서로
다르게 동작하게 됨
Assertion 규칙
- 한번에 하나씩 확인
- 한번에 여러 개의 조건을 확인하면, 어떤 조건 때문에 오류가 발생
했는지 알 수 없음
ASSERT( (i>0) &&
(NULL != szItem) &&
((iLen > 0) && (iLen < MAX_PATH)) &&
(FALSE == IsBadWritePtr(szItem, iLen))
);
ASSERT(i>0);
ASSERT(NULL != szItem);
ASSERT((iLen > 0) && (iLen < MAX_PATH));
ASSERT(FALSE == IsBadWritePtr(szItem, iLen));
Assertion 규칙
- 오류 조건을 완벽하게 확인
-포인터 확인 시 NULL만 확인하지 말고 완전하게 확인
ASSERT(FALSE == IsBadCodePtr(pfnCallBack));
Assertion의 helper 함수
// 메모리가 실행 될 수 있는지 확인
BOOL IsBadCodePtr(
FARPROC lpfn
// memory address
);
// 명시된 수의 바이트에 대해서 메모리 포인터를 읽을 수 있는지 확인
BOOL IsBadReadPtr(
CONST VOID *lp,
// memory address
UINT_PTR ucb
// size of block
);
// 스트링 포인터가 명시된 최대 수나 스트링의 NULL까지 읽을 수 있는지 확인
BOOL IsBadStringPtr(
LPCTSTR lpsz,
// string
UINT_PTR ucchMax
// maximum size of string
);
// 메모리 포인터가 명시된 바이트 수만큼 기록할 수 있는지 확인
BOOL IsBadWritePtr(
LPVOID lp,
// memory address
UINT_PTR ucb
// size of memory block
);
// HWND 매개변수가 사용 가능한 윈도우인지 확인
BOOL IsWindow(HWND hwnd);
VERIFY 매크로
- 디버그 빌드에서는 VERIFY 매크로가 보통 Assertion과 동일하게 작동
: 조건이 0으로 평가되면, VERIFY 매크로는 경고 메시지 표시
- 릴리즈 빌드에서는 VERIFY 매크로는 소스 코드에서 그냥 남아 있음
: 경고 메시지 표지하지 않음
- 변수를 만드는 부담을 줄일 수 있음
: 반환 값 확인 용
: VERIFY(ResetEvent())
Trace 문 사용하기
- 본질적으로는 printf 스타일의 디버깅
- Trace문은 [Output] 윈도우의 [Debug] 탭에 결과 출력
- “함수 : Trace문” 형식으로 표시 (권장)
// example for TRACE
int i = 1;
char sz[] = "one";
TRACE( "Integer = %d, String = %s\n", i, sz );
// Output: 'Integer = 1, String = one'
Advanced Break Point
Skip Count
1 Break Point 설정
2
BreakPoint Dialog
4
3
5
Skip Count
Conditional Expression
1 Break Point 설정
2
주의 사항
- C Style의 연산
자만 이용 가능
- 함수 호출 지원 안함
- 매크로 사용할 수 없음
BreakPoint Dialog
4
5
3
Global Expression
Break Point를 먼저 설정하지 않음
g_strText : 0x004168f8
값의 변화가
생기면 Break
가
걸림
Window Message
[Messages]탭 선택
1
2
Window Procedure
메시시 설정
3
디버깅 시작
함수 이름 사용
디버깅하고자 하는 함수 이름을 아는 경우
: [Location] 탭의 “Break at”에 함수 이름 등록
1
2
함수 이름 사용
Advance Breakpoint 대화 상자 이용하여 등록하기
1
2
Call Stack에서 Breakpoint 걸기
Call Stack 윈도우에서 원하는 위치에 팝업 메뉴 혹은 F9을 이용해서
Breakpoint를 걸 수 있음
Remote Debugging
Remote Debugging
- Remote Machine에서 TCP/IP를 통해 통신하면서 프로그램을 디버깅
- Local Machine에 디버깅을 위한 소스가 존재하고 Remote Machine에서
디버깅할 프로그램 수행
- Window Activation Code를 디버깅해야 할 때
: WM_SETFOCUS 등
- 그림을 그리는 코드를 디버깅해야 할 때
- 하나의 운영체제에서 또 다른 운영체제로 디버깅할 때
: Local(Target) Machine : Windows 2000  Remote Machine : Windows 98
: Local Machine : Desktop  Remote Machine : Notebook
- 전체 Visual C++를 설치해서 Machine의 설정을 많이 바꾸는 것을 원하지 않을 때
: 시스템 DDL 버전을 변경하는 문제  버그 재현 어려움
예제 : Graphic Code
예제 프로그램
결과 모습
Local Machine 설정하기
1 [Build]Debug Remote Connection…
2 Network(TCP/I_P)
3
4
Remote Machine
주소 입력
Remote Machine에 있어야 할 파일들
공유 폴더 설정  관련 파일 복사
-
MSVCMON.EXE
TLN0T.DLL
DM.DLL
MSVCRT.DLL
…
Debugging하고자 하는
실행 파일
Remote Machine 설정하기
2
1
Target Machine IP
MSVCMON.EXE 실행
3
Connection
Remote Debugging 시작
1 Remote Debugging을 위한 실행 경로 및 파일 이름 설정
2 디버깅 시작
(F5)
Watch 윈도우
Watch 윈도우
Formatting Symbol
Watch 윈도우
Formatting Symbol의 예
Watch 윈도우
Memory Location Formatting Symbol
Watch 윈도우
Memory Location Formatting Symbol
Watch 윈도우
Watch 윈도우에서 함수 호출
Automatically Expanding Data
- [Watch] 윈도우 등에서 자신의 타입에 대해서 CRect, CPoint 등과 같이 확장 가능
- InstallDir/Microsoft Visual Studio/Common/MSDev98/Bin/AUTOEXP.DAT
CRect rect에 대해
[Watch] 윈도우에서
자동 확장되어 표시
Automatically Expanding Data
자동 확장이 설정 안된 상태
User Defined Type과 같이 자신이 정의한 타입에 대해서는 직접 확장을 시켜줘야
자신이 원하는 값을 볼 수 있음
Automatically Expanding Data Rules
type = [text]<member[,format]> …
-
type : 타입 이름
text : 멤버의 표시할 이름
member : 멤버의 이름
format : watch formatting 심볼과 동일
[] : optional
Automatically Expanding Data Samples
CPoint =x=<x> y=<y>
CRect =top=<top> bottom=<bottom> left=<left> right=<right>
CSize =cx=<cx> cy=<cy>
Set Next Statement
- 디버깅 할 때만 [Source] 윈도우와 [Disassembly] 윈도우
에 표시
- 이 명령은 다음 수행할 위치를 선택하는 것으로서 EIP
(Instruction pointer) 레지스터의 값을 변경시켜 줌
- 하나의 함수 내에서 프로그램을 수정하며 디버깅 시 유리
: 이 명령은 단지 EIP를 바꿔 줄 뿐이다.그러므로
: 조심해서 사용하지 않으면 프로그램 Crash 우려
- Release Build에서는 [Disassembly] 윈도우에서만 사용
Disassembly
Disassembly
Disassembly
1
Original Source
2
Prolog
3
Local Variable
영역 확보
스택 증가
방향
Stack
ebp
Local Variable
영역
(회색 영역)
60h = 96 byte
3
high
ebp
.
.
.
60h
esp
Low
Prolog
Stack
7
ebp
ebp
.
.
.
60h
edi
ebx
esi
7
edi
esp
Prolog
Local Variable영역
을 모두
0xCCCCCCCC로 채
움
Stack
10
ebp
ebp
0xCCCCCCCC
0xCCCCCCCC
10
rep stos : repeat store string
ecx : 18h
eax : 0CCCCCCCCh
edi
.
.
.
60h
0xCCCCCCCC
ebx
esi
edi
esp
할당 문장 수행
Local Variable은 ebp에
서 값을 빼는 스타일
: ebp-14h, ebp-10h …
2
1
char szSrc[] = “This is a Source”
문장에 대한 실행
 Local Variable 할당
 ebp – 14h  0x12FF6C에 할당
Stack 모습
Stack 모습
1
앞에서 보았던 Stack 모습
2 논리적인 Stack 모습
ebp
ebp-4
ebp-8
ebp-Ch
ebp-10h
ebp-14h
ebp
00 0xCCCCCC
0012FF7C
urce
0012FF78
a So
0012FF74
is
0012FF70
This
0012FF6C
.
.
0XCCCCCC
60h
edi
ebx
esi
edi
esp
함수 호출
1
파라미터 Stack에 삽입 : 오른쪽  왼쪽 삽입
3 논리적인 Stack 모습
ebp
ebp
.
.
.
60h
edi
ebx
esi
edi
10
szDest
szSrc
2 파리미터가 삽입된 후 Stack 모습
esp
0A
ebp-20h
ebp-14h
파라미터
함수 호출
1 함수 호출 전 모습
2
MemCPY 함수가 호출된 상태
반환 주소 : 0x004010C2
3 함수 호출 직후의 Stack 모습 : 반환 주소를 갖고 있음
파라미터 읽기
파라미터는 ebp에서 값
을 더하는 스타일
: ebp+8, ebp+0Ch …
Stack
5
6
7
10
0A
szDest
ebp-20h
szSrc
ebp-14h
파라미터
ret addr
1 MemCPY함수의 inline assembly code
ebp
ebp
.
.
.
edi
40h
ebx
ebp+8
esi
2
edi
esp
Epilog
2 4번까지 수행된 Stack 모습
0A
4
ebp-20h
파라미터
ebp-14h
ret addr
1
Epilog 코드
ebp
ebp
40h
ebx
esi
edi
esp
Epilog
2 8번까지 수행된 Stack 모습
0A
ebp-20h
8
1
Epilog 코드
파라미터
ebp-14h
ret addr
ebp
40h
ebx
esi
edi
esp
Epilog
2 9번까지 수행된 Stack 모습
0A
ebp-20h
ebp-14h
9
1
Epilog 코드
ret addr
ebp
40h
ebx
esi
edi
파라미터
esp
파라미터 영역 해제
2 10번까지 수행된 Stack 모습
0A
ebp-20h
파라미터
10
1
함수 호출 이후의 코드
ebp-14h
ret addr
ebp
40h
ebx
esi
edi
esp
Calling Convention
Calling Convention
Calling Convention
__cdecl
: C/C++의 Default Calling Convention
Calling Convention
__cdecl 함수 호출
Stack에 파라미터 삽입
오른쪽에서 왼쪽으로 삽입
함수 호출
1
Caller : Stack 해제
__cdecl 함수 종료 : Epilog Code
2
Callee : 파라미터를 해제하지 않음
Calling Convention
__ stdcall
: 거의 모든 시스템 함수에서 사용
Calling Convention
__stdcall 함수 호출
Stack에 파라미터 삽입
오른쪽에서 왼쪽으로 삽입
함수 호출
1
__stdcall 함수 종료 : Epilog Code
Caller : 파라미터 해제 코드 없음
2
Callee : 파라미터를 해제
Calling Convention
__fastcall
: Intel 계열의 CPU에서만 적용됨
Calling Convention
Stack에 파라미터 삽입
오른쪽에서 왼쪽으로 삽입
__fastcall 함수 호출
레지스터에 삽입
함수 호출
1
__fastcall 함수 종료 : Epilog Code
Caller : 파라미터 해제 코드 없음
2
Callee : 파라미터를 해제
Calling Convention
naked
: VxD에서 주로 사용
Calling Convention
naked 함수 호출
Stack에 파라미터 삽입
오른쪽에서 왼쪽으로 삽입
함수 호출
1
Stack 해제
naked 함수 종료
Prolog 코드가
존재하지 않음
2
파라미터를 해제하지 않음
크래시된 주소를 이용하여
소스와 라인 정보 찾기
1.
2.
3.
MAP 파일 생성
MAP 파일 내용
크래시 주소에서 소스와 라인 정보 찾기
MAP 파일 생성
MAP 파일은 프로그램 전역 심볼과 소스 파일 라인 번호 정보에 대해서 텍스트로
나타낼 수 있는 유일한 방법
1. Project Setting -> [Link] tab
Path/MapFileName
/MAPINFO:EXPORTS
/MAPINFO:LINES
MAP 파일 내용
모듈 이름
링크 시간
로드 위치
Public 함수 정보
MAP 파일 내용
라인번호
해당 라인의 코드섹션으로부터의 오프셋
소스 파일과 라인번호 찾기
공식
Crash Address – Preferred Load Address – 0x1000
Crash Address
: 0x00401042
Preferred Load Address : 0x00400000
0x00401042 – 0x00400000 – 0x1000 = 0x42
소스 파일과 라인번호 찾기
2
1 문제 소스
3
MAP 파일
크래시 발생
Release Version에서 라인번호 추가하기
Release Version에서 라인번호 추가하기