windbg를 이용한 poll 추척 – Gflags

이 도구는 Windbg를 설치하면 함께 설치되는 도구로서, 커널 디버깅 시 유용한 기능을 제공한다.

대표적으로 윈도우에서 발생하는 페이지 풀 오류를 찾기 위해 먼저 Enable poll tagging를 활성화 하여, 힙 태그를 통해 오류시 사용된 힙의 태그을 확인할 수 있어, 장애 해결에 도움을 준다.

HeapMon을 사용할 때에도 Gflags를 이용하여 활성화 하여야 한다.

[그림] Heap, Pool 관련 문제를 추적하기 위해 활성화해야 한다

만약 위 옵션이 활성화 되어 있지 않다면, 메모리 풀 사용현황을 확인하는 !poolused 명령어 사용시, 아래와 같이 활성화를 진행하라는 메시지를 생성한다.

[그림] 먼저 풀 태그를 활성화 하여야 한다

Enable pool tagging를 활성화 하면 정상적으로 각 메모리 풀 사용 현황을 확인할 수 있게 된다(비스타 이후 버전에서는 기본적으로 활성화 된다).

[그림] 메모리 풀 사용현황을 드라이버 별로 확인할 수 있다

메모리 풀 문제점은 윈도우 기본으로 내장된 드라이버 확인 도구인 Verifier를 함께 이용하면 더욱 효과적으로 분석할 수 있다(바로 다음에 다룬다).

[그림] 힙 추적 관련 옵션

Enable heap tagging는 무엇일까? 힙 관련 문제 발생시 이를 추적할 수 있도록 사용할 수 있는 옵션으로, 이 역시 활성화 하지 않으면 메모리 힙 분석이 어려워진다.

[그림] 메모리 힙 플래그를 활성화 하여야 확인이 가능하다

-v: 지정한 힙의 내용을 확인한다.

-a: 지정한 힙에 대한 전체 정보를 표시한다. 지정하지 않고 사용하면 모든 힙에 대한 정보를 사용할 수 있다(–a 옵션은 -h -f –m 옵션과 함께 이용할 수 있다).

-h: 지정한 힙과 연결된 모든 항목을 표시한다.

-f: 지정한 힙의 현재 프리 상태의 리스트 항목을 표시한다.

-m: 지정한 힙의 모든 세그먼트 항목을 표시한다.

-t: 지정한 힙의 태그정보를 표시한다(-T: pseudo-tag, -g: global tag).

-s: 지정한 힙의 요약정보를 표시한다.

-k: (32비트 시스템에서 사용가능) 스택을 추적하여 사용되었던 항목을 함께 표시한다.

// 먼저 메모리 힙의 전체 정보를 확인해보자.
kd> !heap -aIndex Address Name Debugging options enabled

1: 00070000

Segment at 00070000 to 00170000 (00100000 bytes committed)

Segment at 01450000 to 01550000 (000e0000 bytes committed)

Segment at 01960000 to 01b60000 (00069000 bytes committed)

2: 00170000

Segment at 00170000 to 00180000 (00007000 bytes committed)

3: 001a0000 – heap headers inaccessible, skipping

4: 002f0000

…중략

// 현재 메모리 힙 정보를 각 상태를 확인해 보기 위해 요약정보를 확인하자.

kd> !heap -s

NtGlobalFlag enables following debugging aids for new heaps:

tail checking

free checking

validate parameters

validate on call

heap tagging

Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast

(k) (k) (k) (k) length blocks cont. heap

—————————————————————————–

Virtual block: 00e00000 – 00e00000 (size 00000000)

00070000 70000062 4096 2340 2340 53 7 2 1 3e7 L

00170000 70001062 64 28 28 4 1 1 0 0 L

Error: Heap 001a0000 has an invalid signature eeffeeff

Front-end heap type info is not available

Front-end heap type info is not available

Virtual block: 00000000 – 00000000 (size 00000000)

HEAP 001a0000 (Seg 00000000) At 00000000 Error: Unable to read virtual block

001a0000 00000000 0 0 0 0 0 0 1 0

002f0000 70001062 1088 48 228 5 2 3 0 0 L

004f0000 70001062 1088 844 924 48 5 4 0 0 L

00520000 70001062 1024 1024 1024 1014 2 0 0 0 L

00ad0000 70001062 64 16 16 1 0 1 0 0 L

00b10000 70001063 256 8 8 1 1 1 0 bad

00b90000 70001063 256 4 4 0 0 1 0 bad

00bd0000 70001063 256 4 4 0 0 1 0 bad

00c10000 70001063 256 4 4 0 0 1 0 bad

00fe0000 70001062 64 12 12 2 1 1 0 0 L

01240000 70000062 1024 24 24 2 1 1 0 0 L

00fc0000 70001062 64 48 48 38 2 1 0 0 L

00ff0000 70001062 64 16 16 0 0 1 0 0 L

01130000 70001062 64 64 64 7 1 0 0 0 L

—————————————————————————–

// 각 영역 별로, 예약된 영역과 프리 상태 등의 정보를 확인 할 수 있었다. 세부 세그먼트 내용을 확인하고자 한다면, 특정 힙을 지정하여, 세그먼트를 확인하는 명령을 이용해 확인해 보도록 하자.

kd> !heap -m 002f0000

Index Address Name Debugging options enabled

– heap headers inaccessible, skipping

4: 002f0000

Segment at 002f0000 to 00300000 (00008000 bytes committed)

Segment at 01be0000 to 01ce0000 (00004000 bytes committed)

Flags: 70001062

ForceFlags: 60000060

Granularity: 8 bytes

Segment Reserve: 00200000

Segment Commit: 00002000

DeCommit Block Thres: 00000200

DeCommit Total Thres: 00002000

Total Free Size: 0000028e

Max. Allocation Size: 7ffdefff

Lock Variable at: 002f0c14

Next TagIndex: 0000

Maximum TagIndex: 0000

Tag Entries: 00000000

PsuedoTag Entries: 002f0608

Virtual Alloc List: 002f0050

UCR FreeList: 002f05b8

FreeList Usage: 0010120c 00000000 00000000 00000000

FreeList[ 00 ] at 002f0178: 01c10380 . 002f7a18 (2 blocks)

FreeList[ 02 ] at 002f0188: 01be0d88 . 002f3fd0 (5 blocks)

FreeList[ 03 ] at 002f0190: 01be0bd8 . 01be0bd8 (1 block )

FreeList[ 09 ] at 002f01c0: 002f51d0 . 01c0e060 (2 blocks)

FreeList[ 0c ] at 002f01d8: 01be0708 . 01be0708 (1 block )

FreeList[ 14 ] at 002f0218: 01c0efe8 . 01c0efe8 (1 block )

Segment00 at 002f0c50:

Flags: 00000000

Base: 002f0000

First Entry: 002f0c90

Last Entry: 00300000

Total Pages: 00000010

Total UnCommit: 00000008

Largest UnCommit:00008000

UnCommitted Ranges: (1)

002f8000: 00008000

Segment01 at 01be0000:

Flags: 00000000

Base: 01be0000

First Entry: 01be0040

Last Entry: 01ce0000

Total Pages: 00000100

Total UnCommit: 000000fc

Largest UnCommit:000cf000

UnCommitted Ranges: (2)

01be1000: 0002d000

01c11000: 000cf000

// 세그먼트 확인을 통해 세그먼트 별 사용현황을 쉽게 확인할 수 있으며, 특정 메모리 힙 내 세그먼트를 –a 옵션을 이용하면 실제 사용한 힙 항목들 까지 확인이 가능하다.

kd> !heap -a 002f0000

Index Address Name Debugging options enabled

– heap headers inaccessible, skipping

4: 002f0000

Segment at 002f0000 to 00300000 (00008000 bytes committed)

Segment at 01be0000 to 01ce0000 (00004000 bytes committed)

Flags: 70001062

ForceFlags: 60000060

Granularity: 8 bytes

Segment Reserve: 00200000

Segment Commit: 00002000

DeCommit Block Thres: 00000200

DeCommit Total Thres: 00002000

Total Free Size: 0000028e

Max. Allocation Size: 7ffdefff

Lock Variable at: 002f0c14

Next TagIndex: 0000

Maximum TagIndex: 0000

Tag Entries: 00000000

PsuedoTag Entries: 002f0608

Virtual Alloc List: 002f0050

UCR FreeList: 002f05b8

FreeList Usage: 0010120c 00000000 00000000 00000000

// 프리리스트의 세부 정보를 출력한다.

FreeList[ 00 ] at 002f0178: 01c10380 . 002f7a18

002f7a10: 000a0 . 005f0 [14] – free

01c10378: 01140 . 00c88 [14] – free

FreeList[ 02 ] at 002f0188: 01be0d88 . 002f3fd0

002f3fc8: 00030 . 00010 [04] – free

002f52a0: 00060 . 00010 [04] – free

002f58e8: 00070 . 00010 [04] – free

002f5ea8: 00098 . 00010 [04] – free

01be0d80: 000a0 . 00010 [04] – free

FreeList[ 03 ] at 002f0190: 01be0bd8 . 01be0bd8

01be0bd0: 00030 . 00018 [04] – free

FreeList[ 09 ] at 002f01c0: 002f51d0 . 01c0e060

01c0e058: 00058 . 00048 [04] – free

002f51c8: 00058 . 00048 [04] – free

FreeList[ 0c ] at 002f01d8: 01be0708 . 01be0708

01be0700: 00020 . 00060 [04] – free

FreeList[ 14 ] at 002f0218: 01c0efe8 . 01c0efe8

01c0efe0: 00488 . 000a0 [04] – free

Segment00 at 002f0c50:

Flags: 00000000

Base: 002f0000

First Entry: 002f0c90

Last Entry: 00300000

Total Pages: 00000010

Total UnCommit: 00000008

Largest UnCommit:00008000

UnCommitted Ranges: (1)

002f8000: 00008000

// 사용중인 세그먼트 리스트의 정보를 표시한다.

Heap entries for Segment00 in Heap 002f0000

002f0000: 00000 . 00c50 [01] – busy (c50)

002f0c50: 00c50 . 00040 [01] – busy (40)

002f0c90: 00040 . 01818 [07] – busy (1800), tail fill – unable to read heap entry extra at 002f24a0

002f24a8: 01818 . 000a0 [07] – busy (88), tail fill – unable to read heap entry extra at 002f2540

002f2548: 000a0 . 00498 [07] – busy (480), tail fill – unable to read heap entry extra at 002f29d8

002f29e0: 00498 . 00098 [07] – busy (80), tail fill – unable to read heap entry extra at 002f2a70

002f2a78: 00098 . 00098 [07] – busy (80), tail fill – unable to read heap entry extra at 002f2b08
…중략

[실습 1] 메모리 힙 분석

여기에 추가로 Enable page heap을 활성화하면, !heap -p –a <address>를 통해 힙을 사용한 스택을 추적하여 확인할 수 있게 된다(특정 프로그램에 대해서만 활성화 하고자 한다면 gflags /i 프로그램파일명 +hpa 로 진행할 수 있다).

[그림] 스택 사용 현황을 추적하고자 한다면 “Enable page heap”을 활성화하자

다음 사이트를 방문해 보면 Gflags의 이용방법에 대해 조금이나마 이해할 수 있을 것이다.

http://www.osronline.com/ddkx/ddtools/gflags_00s3.htm

-박스시작-

Verifier – 드라이버 문제 해결

앞서 확인한 Gflags와 함께 풀 관련 문제점을 해결하는데 도움이 되는 도구로 유용하게 사용되는 도구를 소개하고자 한다. 이 도구는 드라이버 페이지 풀 문제점 분석에 용의한 도구이다.

드라이버 확인 도구 사용을 활성화 한 후 시작
à
실행
à
verifier.exe를 통해 특수 풀, 풀 추적을 체크하고 확인하고자 하는 드라이버에 설정함으로써 해당 드라이버가 사용하는 풀에 대해 자세히 추적, 분석이 가능하다(Gflags에 Enagle pool tagging도 함께 활성화 하자).

[그림] 특수 풀을 이용해, 문제점 분석에 용이하다

Verifier가 특수 풀(Special Pool)을 활성화 하고 이용하고자 하는 드라이버 선택하면, 해당 드라이버는 특수 풀을 이용하여 동작하게 된다. 특수 풀은 앞쪽에 크기, 상태, 태그 등의 정보가 있고, 풀 뒤에는 Verifier 관리 정보가 들어가, 문제가 발생하였을 때 BSOD를 발생하여, 쉽게 분석할 수 있도록 정보를 남기게 된다. 따라서 이를 활성화하고 페이지 풀 관련 장애가 발생하였을 때, 정확한 장애 위치와 값을 확인할 수 있다(단 특수 풀 활성화 이후 장시간 시스템을 운영하는 것은 바람직하지 않다. 따라서 필요 시에만 이용하고, 해결되었다면 비활성화하기 바란다).

// 현재 스페셜 풀 위치를 확인하기 위해 심볼 정보를 확인하였다. 아래와 같이 5개가 스페셜 풀 용도로 이용됨을 확인할 수 있다. 하지만 구조체 정보를 확인이 되지 않는다. 아마 마이크로소프트 내부에만 공개한 것으로 보인다.
kd> dt -v nt!MmSpecialPool*Enumerating symbols matching nt!MmSpecialPool*

Address Size Symbol

8055ce80 000 ntoskrnl!MmSpecialPoolRejected (no type info)

8055cea4 000 ntoskrnl!MmSpecialPoolEnd (no type info)

8055ceac 000 ntoskrnl!MmSpecialPoolTag (no type info)

805537c0 000 ntoskrnl!MmSpecialPoolCatchOverruns (no type info)

8055cea8 000 ntoskrnl!MmSpecialPoolStart (no type info)

// 그럼 시작 위치의 메모리와 끝 위치의 메모리 주소가 어떻게 되는지 확인해보자.

kd> dd ntoskrnl!MmSpecialPoolStart

8055cea8 82400000 00000000 00050000 00200000

8055ceb8 00000000 00000000 00000000 00000000

8055cec8 0000621b 00000000 00000000 00000000

8055ced8 00000000 00000000 00000000 00000000

8055cee8 00000000 00000000 00000000 00000000

8055cef8 00000000 00000000 00000000 00000000

8055cf08 00000000 00000000 00000000 00000000

8055cf18 00000000 00000000 00000000 00000000

kd> dd ntoskrnl!MmSpecialPoolEnd

8055cea4 82fff000 82400000 00000000 00050000

8055ceb4 00200000 00000000 00000000 00000000

8055cec4 00000000 0000621b 00000000 00000000

8055ced4 00000000 00000000 00000000 00000000

8055cee4 00000000 00000000 00000000 00000000

8055cef4 00000000 00000000 00000000 00000000

8055cf04 00000000 00000000 00000000 00000000

8055cf14 00000000 00000000 00000000 00000000

// 위 정보를 통해 스페셜풀 시작 위치와 끝 위치를 확인할 수 있었다. 시작위치로 가서 현재 스페셜 풀을 어떤 것이 이용 중인지 확인해보자.

kd> dds 82400000

82400000 002d0340

82400004 2b707249

82400008 2d2d2d2d

8240000c 2d2d2d2d

82400010 2d2d2d2d

82400014 2d2d2d2d

82400018 2d2d2d2d

8240001c 2d2d2d2d

82400020 2d2d2d2d

82400024 2d2d2d2d

82400028 2d2d2d2d

8240002c 2d2d2d2d

82400030 2d2d2d2d

82400034 2d2d2d2d

82400038 2d2d2d2d

8240003c 2d2d2d2d

…중략

// 분석을 쉽게 진행하기 위해 !pool 옵션을 이용하여 확인해 보자.

kd> !pool 82400000

Pool page 82400000 region is Special pool ß 스페셜 풀임을 알려준다.

*82400000 size: 340 data: 82400cc0 (NonPaged) *Irp+ ß IRP가 사용중으로 나온다.

Pooltag Irp+ : I/O verifier allocated IRP packets, Binary : nt!vf

// 스페셜 풀에 저장된 값이 위 값과 맞는지 스트링을 통해 확인해보면, Irp+라는 것을 알 수 있다.

kd> .formats 0x2b707249

Evaluate expression:

Hex: 2b707249

Decimal: 728789577

Octal: 05334071111

Binary: 00101011 01110000 01110010 01001001

Chars: +prI

Time: Thu Feb 04 10:32:57 1993

Float: low 8.54237e-013 high 0

Double: 3.6007e-315

// !poolval 명령을 이용하여 리스트 항목들을 확인 할 수 있다. 2, 3 옵션을 추가하면 더 자세한 정보를 표시한다.

kd> !poolval 82400004 <2|3>

Pool page 82400004 region is Special pool

Validating Pool headers for pool page: 82400004

Pool page [ 82400000 ] is __inVALID.

Analyzing linked list…

[ 82400000 ]: invalid previous size [ 0x140 ] should be [ 0x0 ]

[ 82400168 ]: invalid previous size [ 0x12d ] should be [ 0x2d ]

Scanning for single bit errors…

[ 82400168 ]: previous size [ 0x12d ] should be [ 0x2d ]

[실습 2] 스페셜 풀 분석

해당 도구에 대해 더 세부적인 내용은

http://msdn.microsoft.com/en-us/library/windows/hardware/ff551832(v=vs.85).aspx에서 Verifier 각 항목별 설명을 참고하기 바란다.

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.