다음의 내용은 Quad Dimensions에서 Debugging 교육 자료로 만든 내용입니다.
본부장
가져 가실때는 이 정보 남겨 주시는것이 예의 겠지요 ^^
1. Heap 손상의 제일 기본적인 체크
힙 손상은 찾기 제일 어려운 것들 중에 하나이다. 문제가 난 부분에서 에러가 나 바로 죽는 경우는 괜찮지만 , 많은 경우 손상된 heap을 유지한 상태로 프로그램이 작동되는 경우가 있기 때문이다.
아래 경우는 heap 오류로 보기는 곤란하지만 제일 기본적인 코드이기 때문에 만들어 보았다.
char * p = NULL;
*p = 10;
할당 되지 않은 공간에 값을 넣는 초보적 오류이다.
WinDbg에서 돌리면 에러가 발생하며 바로 정지한다.
0:000> kb
ChildEBP RetAddr Args to Child
0013ff6c 00416cab 0013ff80 00416cc8 0041fde6 Timetest!errTestHeap1+0xe
0013ff74 00416cc8 0041fde6 0013ffc0 00418b40 Timetest!ErrorTest+0x8
0013ff80 00418b40 00000001 0375afa0 0375cf40 Timetest!main+0x1b
0013ffc0 7c82f23b 00000000 00000000 7ffd8000 Timetest!mainCRTStartup+0xb4
0013fff0 00000000 00418a8c 00000000 78746341 kernel32!BaseProcessStart+0x23
스택 손상이 아니기 때문에 위처럼 확인 가능하다.
전과 동일하게 uf 명령어로 어디까지 진행했는지 확인한다.
0:000> uf Timetest!errTestHeap1+0xe
Timetest!errTestHeap1+0xe [G:\byun\test_code\100716_umdh_test\Timetest.cpp @ 98]:
98 00416bd3 c6000a mov byte ptr [eax],0Ah
99 00416bd6 8be5 mov esp,ebp
99 00416bd8 5d pop ebp
99 00416bd9 c3 ret
0:000> uf Timetest!errTestHeap1
Timetest!errTestHeap1 [G:\byun\test_code\100716_umdh_test\Timetest.cpp @ 95]:
95 00416bc5 55 push ebp
95 00416bc6 8bec mov ebp,esp
95 00416bc8 51 push ecx
96 00416bc9 c745fc00000000 mov dword ptr [ebp-4],0
98 00416bd0 8b45fc mov eax,dword ptr [ebp-4]
98 00416bd3 c6000a mov byte ptr [eax],0Ah
99 00416bd6 8be5 mov esp,ebp
99 00416bd8 5d pop ebp
99 00416bd9 c3 ret
EIP : 00416bd0 에서 ebp-4 주소에 있는 값을 eax에 넣은 것을 볼 수 있다.
그 값이 뭔지 확인해 보자.
0:000> dd ebp-4
0013ff68 00000000 0013ff74 00416cab 0013ff80
00000000 값이 들어 있다. 뭔가 이상하다.
그리고 다음 EIP에서는 eax주소에 10을 넣으려고 하는 것을 알 수 있다.
그러나 eax 주소는 위에서 본 것처럼 잘못된 값이 들어 있다. 확인해 보자
0:000> dd eax
00000000 ???????? ???????? ???????? ????????
00000000 즉 할당 되지 않은 주소로 값을 넣으려고 했기 때문에 문제가 발생한 것을 확인할 수 있다.
위와 같은 heap 손상 계열의 에러에서는 d (dump) 명령어가 매우 중요하다.
해당 heap주소의 da, dd, dv 사용 습관을 꼭 가져야 한다.
2. Debug mode의 Heap memory 확인 하기
이전 페이지의 메모리 블록 다이어그램에서 디버그 모드에서는 여러 가지 옵션값이 들어 있다고 했었다.
dump 명령어 사용법을 익히는 차원에서 해당 값을 검증해 보자.
소스 코드는 아래와 같다.
char * p = NULL;
p = new char[10];
__asm { int 3 } // windbg에서 보기위해서 여기까지 진행하고 멈추게 인터럽트 요청
역어셈블 해보자.
0:000> uf Timetest!errTestHeap2
Timetest!errTestHeap2 [G:\byun\test_code\100716_umdh_test\Timetest.cpp @ 102]:
102 00403720 55 push ebp
102 00403721 8bec mov ebp,esp
102 00403723 83ec48 sub esp,48h
… 중간 생략 …
105 0040374f 8945fc mov dword ptr [ebp-4],eax
107 00403752 cc int 3
… 이하 생략 …
메모리의 값을 확인해 보자.
0:000> dd eax
03803ff0 cdcdcdcd cdcdcdcd fdfdcdcd c0c0fdfd
03804000 ???????? ???????? ???????? ????????
초기화 하지 않은 메모리가 생성되어 있다.
변수 p에 들어 있는 값도 확인해 보자.
0:000> dv p
p = 0x03803ff0 "???"
값은 알아 보기 힘들지만 주소는 위의 값과 일치하는 것을 알 수 있다.
메모리 블록 다이어그램에서처럼 값이 있는지 확인하기 위해 그 전의 메모리 내용까지 덤프를 해보자.
0:000> dd eax-20
03803fd0 03800fd0 00000000 00000000 00000000
03803fe0 0000000a 00000001 00000037 fdfdfdfd
03803ff0 cdcdcdcd cdcdcdcd fdfdcdcd c0c0fdfd
실제 10바이트만큼 할당되어 있는 것이 확인 가능하다. ( intel x86에서는 상위 하위 워드가 반대로 저장되어 있다.)
또 4바이트씩 위-아래에 메모리 가드가(0xfd) 되어 있고 , 프로그램상에서 0x37번째로 생성된 heap 메모리이며 , Normal(1) type의 메모리 이며 , 10(a)바이트로 할당된 것이 확인 가능하다.
메모리의 정보도 출력을 해보자.
0:000> !address 0x03803ff0
03790000 : 03803000 - 00001000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsagePageHeap
Handle 03791000
Heap 메모리임을 확인 할 수 있고, 만약 stack이라면 RegionUsageStack 이라고 표시된다.
댓글 없음:
댓글 쓰기