Welcome to MSDN Blogs Sign in | Join | Help

[Ntdebugging]Event ID 2021, 2022가 기록된 서버 행

"이 문서는 http://blogs.msdn.com/ntdebugging blog 의 번역이며 원래의 자료가 통보 없이 변경될 수 있습니다. 이 자료는 법률적 보증이 없으며 의견을 주시기 위해 원래의 blog 를 방문하실 수 있습니다. (http://blogs.msdn.com/ntdebugging/archive/2007/06/19/server-hangs-with-event-id-2021-and-2022.aspx)

Event ID 2021, 2022 가 기록된 서버 행

안녕하세요. 저는 CPR 팀에서 일하고 있는 Tate 입니다. Server Service hang 증상과 가끔 두려움의 대상이 되는 Event ID: 2021 과 Event ID: 2022 에 대해서 설명 드리도록 하겠습니다. 두 이벤트에 대해 분석하는데 두려움을 가질 필요는 없고 적절한 방법으로 빠르게 디버그 할 수 있습니다.

이전에 일반적인 Server Service Event 2019 와 2020 에 대해서 말씀 드린 적이 있습니다. (Event ID: 20xx 는 Srv.sys 의 Event를 나타냅니다.) 또한, 지금부터 Server Service를 SRV 라고 하도록 하겠습니다.

이전에 이 이벤트들을 본적이 있다면 웹에서 아이디를 찾아 보았을 것이고 KB317249를 보았을 것 입니다. (이 문서는 오래된 것이지만 좋은 문서 입니다.)하지만 이 문서가 굉장히 방대하다는 것이 문제 입니다. 이 문서는 다양한 원인에 대해 설명되어 있고 또한 SRV의 동작을 이해할 수 있습니다. 그러나 실제로는 이중 몇 가지 문제만이 있을 뿐 입니다.

1) I/O 스택에서 블럭되어 일반적인 양의 SMB 요청이 적절한 시간에 처리되는 것을 막을 때 (이것이 가장 일반적인 것으로 SMB 처리가 실패하는 케이스 입니다.)

2) SRV에게 비정상적인 많은 요청이 전달될 때 (요청을 많이 하는 SMB 응용프로그램 또는 ChangeNotify 요청이 누적 될 때)

문제 발생시 이 두 가지 중 어떤 경우인지 확인하는 방법을 알려 드릴 것 입니다. 하지만 먼저 이벤트 메시지를 확인해 보도록 하겠습니다.

Event ID: 2022
Source: Srv
Description: Server was unable to find a free connection
n times in the last s seconds.

Event ID: 2021
Source: Srv
Description: Server was unable to create a work item %2 times in the last %3 seconds.

 

어떤 의미 인지?

먼저 Connection 과 Work Item에 대해서 설명 하겠습니다. 이 것들은 SRV에서 내부적으로 사용하는 구조체로 SRV에 대한 컨넥션과 워크아이템을 나타냅니다. 이해가 되시나요? SRV에 요청이 들어올 때 어떤 곳으로부터 요청이 들어왔는지, 하려는 작업이 무엇인지 그리고 어떻게 응답을 할 수 있는지 알기 위해 엔드포인트, 컨넥션, 워크아이템 그리고 다른 구조체들이 사용됩니다.

SRV의 핵심 내용으로 커널 모드 드라이버에서 SMB를 처리하는 것 입니다. 네트워크 SMB/CIFS 요청을 받아 로컬I/O로 변환한 후 처리하고 클라이언트에 돌려 줍니다. 하지만 문제는 I/O를 처리하는 도중 “찾을 수 없다” 또는 “생성할 수 없다” 라는 에러가 발생하는 것 입니다.

먼저 저장소에 대해 확인해 보도록 하겠습니다. 커널 메모리가 해제되지 않은 경우 다시 말하면 이전 블로그에서 이야기 한 것과 같이 커널 풀이 사용할 수 있는 메모리를 모두 사용하였거나 SRV의 제한( KB317249 에서 MaxWorkItems 확인)까지 사용하였을 경우가 있습니다. 이 두 가지 제한 중 하나라도 충족되면 이벤트가 기록 됩니다. 이전에 배운 것과 같이 페이지드 풀 또는 넌 페이지드 풀이 부족할 경우 행이 발생할 수 있고 SRV가 메모리를 할당할 수 없다는 이벤트를 기록할 수 있습니다. (Event ID: 2019 2020 블로그를 확인하여 정보와 해결 방법을 확인할 수 있습니다.) 워크아이템과 관련되어 블럭 되어 있거나 높은 부하가 걸릴 경우 EventID:2019, 2020 … 이 기록될 경우 어떤 일이 있을까요?( 풀을 많이 소모한 태그로 LSwn – LanmanWerverWorkItmes 가 기록될 경우) 이벤트 2021, 2022가 기록되는 것을 볼 수 있습니다. Event ID 2021, 2022가 SRV에서 기록될 경우 SRV가 커널 메모리 풀을 지나치게 사용하였는지 확인해 봐야 합니다.

 

KB317249 의 내용과 같이 \LanmanServer 의 모드 제한을 최대로 늘린다면 문제가 해결될까요?

문제가 발생할 때 얼마나 많이 블록 되어 있는지 그리고 얼마나 많은 부하가 걸리는지에 따라 해결될 수도 있고 아닐 수 도 있습니다. 만일 서버가 정상 상태가 된 후 이 문제를 확인하였다면 이미 문제의 증상이 사라진 후로 이 내용 만으로 문제가 해결되지 않고 추가 작업이 필요 할 것 입니다. 명심할 것은 이 문서에서 하는 것과 같이 레지스트리를 변경하여 최대 값까지 설정하면 풀 메모리의 사용이 최대치까지 늘어나는 시간이 빨라질 것 입니다. KB317249에 있는 값은 높지 않은 값으로 문제를 일으키지 않을 것 입니다. 하지만 문제를 완전히 해결해 주지도 않을 것 입니다. 또한 충분히 이해를 하기 전에 값을 변경하는 것을 원치 않는다면 계속 이 문서를 읽어 주십시오.

 

Event ID: 2021, 2020의 원인 #1

위에서 늘린다 라고 이야기 한 것은 Srv!WorkerThread 와 KQUEUE 구조 입니다. 요청이 들어올 때 상상하시는 것과 같이 KeInsertQueue를 사용해서 KQUEUEs 에 넣습니다. 이 큐를 처리하고 비우는 것은 Srv!WorkerThread(s) 에서 KeRemoveQueue 를 호출하는 것 입니다. 이 스레드들은 SRV 의 핵심이고 SMB 처리에서 멈추게 되는 원인이어서 해답을 찾기 위해 이 부분을 살펴 봅니다.

SRV 를 확인하기 위해 문제가 발생하였을 때 KB244139를 통해서 커널 메모리 덤프를 수집하거나 livekd(Sysinternals) 를 사용할 수 있습니다. 하지만 최선의 방법은 KB244139 나 커널 디버거를 사용하는 것 입니다. 덤프를 수집하기에 최적의 시간은 서버가 네트워크(SMB)에 응답하지 못하거나 2021/2022 이벤트가 계속 기록되고 있거나 장비가 이 이벤트 들이 기록된 후 멈춘 상태 입니다.

 

Memory.dmp 를 로드한 후 system 프로세스의 EPROCESS 구조체를 !process 0 0 system 명령을 통해 구할 수 있습니다.

0: kd> !process 0 0 system

PROCESS 89067520 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000

DirBase: 00030000 ObjectTable: 89067008 TableSize: 490.

Image: System

 

얻은 주소를 사용해서 .process /r /p <system EPROCESS 주소> 를 사용해서 프로세스 컨텍스트를 변경 합니다.

0: kd> .process /r /p 89067520

Implicit process is now 89067520

Loading User Symbols

 

!process <system EPROCESS 주소> 를 사용해서 모든 시스템 스레드를 출력 합니다.

0: kd> !process 89067520

PROCESS 89067520 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000

DirBase: 00030000 ObjectTable: 89067008 TableSize: 490.

Image: System

.

.

Dumping out the threads…

.

.

 

출력이 끝난 후 아래에서 위로 “Start Address srv!WorkerThread” 를 스택에서 검색 합니다.

참고 : Windows 2003 의 경우 “!stacks 2 Srv!WorkerThread” 명령으로 .process 명령을 사용하지 않고도 쉽게 결과를 얻을 수 있습니다.

 

요청이 들어오기를 KeRemoveQueue를 사용해서 기다리고 있는 “정상적인 스레드”가 있습니다.

THREAD 887c14e0 Cid 8.474 Teb: 00000000 Win32Thread: 00000000 WAIT: (WrEventPairLow) KernelMode Non-Alertable

be9fd324 Unknown

Not impersonating

Owning Process 89067520

Wait Start TickCount 160832116 Elapsed Ticks: 203335

Context Switch Count 134294

UserTime 0:00:00.0000

KernelTime 0:00:09.0343

Start Address srv!WorkerThread (0xbea06880)

Stack Init beb11000 Current beb10d3c Base beb11000 Limit beb0e000 Call 0

Priority 10 BasePriority 9 PriorityDecrement 0 DecrementCount 0

ChildEBP RetAddr Args to Child

beb10d54 8042ef5f 88a10eb8 be9fd320 00000001 nt!KiSwapThread+0x1b1

beb10d78 bea068ec 00000000 00000000 00000000 nt!KeRemoveQueue+0x197

beb10da8 804578c2 be9fd300 00000000 00000000 srv!WorkerThread+0x6c

beb10ddc 8046c966 bea06880 be9fd320 00000000 nt!PspSystemThreadStartup+0x54

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

 

“비 정상적인 스레드” 는 어떻게 보일까요?

FindFirst SMB 를 처리 하기 위해 멈추어 있는 Srv!WorkerThread 를 가 아래에 있습니다.

THREAD 887dd4e0 Cid 8.45c Teb: 00000000 Win32Thread: 00000000 WAIT: (UserRequest) KernelMode Non-Alertable

88255db8 SynchronizationEvent

IRP List:

86941288: (0006,01fc) Flags: 00000884 Mdl: 00000000

Impersonation token: e5d56990 (Level Impersonation)

Owning Process 89067520

Wait Start TickCount 160783273 Elapsed Ticks: 252178

참고 : 이 스레드의 Tick 개수를 보면 오랫동안 기다리고 있는 좋지 않은 상태임을 알 수 있습니다.

 

Context Switch Count 12993

UserTime 0:00:00.0000

KernelTime 0:00:00.0843

Start Address srv!WorkerThread (0xbea06880)

Stack Init bec29000 Current bec284f4 Base bec29000 Limit bec26000 Call 0

Priority 10 BasePriority 9 PriorityDecrement 0 DecrementCount 0

ChildEBP RetAddr Args to Child

bec2850c 8042d893 00000000 882549c8 00000000 nt!KiSwapThread+0x1b1

bec28534 f5ae3408 88255db8 00000006 00000000 nt!KeWaitForSingleObject+0x1a3

bec285bc f5ae4066 bec285e4 00000000 00000000 SomeFiltr+0x3408<<<<<<<이 코드가 멈추게 한 것으로 보입니다.…

bec285fc f5ae19fb 884ce3a0 01941288 8041eecb SomeFiltr+0x4066

bec287a4 804c4994 884ce3a0 00000000 bec28894 SomeFiltr+0x19fb

bec287dc 804531c2 884766c8 00000000 bec28894 nt!IopParseFile+0x44

bec28854 804da4d8 0000051c bec28950 00000040 nt!ObpLookupObjectName+0xf8

bec28964 804a4495 00000000 00000000 00000000 nt!ObOpenObjectByName+0xc8

bec28a40 804a403a 8690d008 00000001 bec28b4c nt!IopCreateFile+0x407

bec28a88 bea0282c 8690d008 00000001 bec28b4c nt!IoCreateFile+0x36

bec28af8 bea050f6 88a0d2f8 8690d008 00000001 srv!SrvIoCreateFile+0x32a

bec28b8c bea0acdb 88a0d2f8 00000001 8046b000 srv!SrvQueryDirectoryFile+0x1c8

bec28c5c bea0936d 88a0d2f8 00000001 00000000 srv!SrvFind2Loop+0x171

bec28cd0 bea07d2f 88a0d201 bea0a9c8 88a0d2f8 srv!DoFindFirst2+0x373

bec28cd8 bea0a9c8 88a0d2f8 88a0d2f8 804636f0 srv!SrvSmbFindFirst2+0x31

bec28cf0 bea089b7 88994f38 88a0d2f8 00000000 srv!ExecuteTransaction+0xdc

bec28d68 be9f27ae 88a0d2f8 be9f2cde 88a0d300 srv!SrvSmbTransaction+0x5af

bec28d70 be9f2cde 88a0d300 88a144a0 bec28da8 srv!SrvProcessSmb+0x46

bec28d84 bea069a9 00000000 00000000 00000000 srv!SrvRestartReceive+0xa2

bec28da8 804578c2 88a14400 00000000 00000000 srv!WorkerThread+0x129

bec28ddc 8046c966 bea06880 88a144a0 00000000 nt!PspSystemThreadStartup+0x54

00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

참고 : Tick count(Elapsed Ticks) 는 이전의 정상 상태의 Thread 보다 나빠 보이지는 않습니다. KeRemoveQuueu 에서 오랜 시간 대기 하면서 요청이 오기를 기다렸기 때문 입니다. 이 Queue 에 작업이 없다는 것은 문제가 아닙니다. 하지만 이 비 정상적인 스레드는 작업이 처리 되는 것을 오랜 시간 동안 막고 (1시간 5분) 있습니다.

무엇이 잘못되었는가?

Srv!WorkerThread 가 작업 스레드로 KQUEUE에 들어있는 작업을 비우는 역할을 하고 작업 스레드의 개수가 제한 적이라면 문제가 발생 합니다. 많은 요청이 srv로 들어오는 경우 다양한 트렌스포트 레이어 이벤트 핸들러가 KQUEUE 에 작업을 등록할 것 입니다. 하지만 작업을 처리할 작업 스레드가 없다면 (컨넥션과 작업이 풀에 저장되어 있고 풀 또한 한계가 있습니다.) 작업이 처리되지 않아 이벤트가 기록됩니다. KB317249 에서 이야기 하는 것과 같이 레지스트리를 수정하여 SRV, 넌 페이지드 풀등의 문제에서 벗어날 수 없습니다. 이 상태에서는 클라이언트에 정상적으로 응답을 할 수 없습니다.

 

만약 정상과 비 정상 스레드가 !process 명령의 결과에서 같이 보인다면?

두 개의 요청이 처리되고 있다고 할 수 있습니다. 블록 되어있는 큐와 블록되어 있지 않은 큐가 있고 KeRemoveQueue 를 호출하여 대기하고 있는 정상 스레드와 CreateFile, Find 등과 같은 요청에서 멈추어 있는 비 정상적인 스레드를 볼 수 있을 것 입니다. 일반적으로 Srv!WorkerThread 가 블록 되어 있고 KeRemoveQueue 가 오랜 시간 지연되었다면 비 정상 입니다.

해결방법은 무엇인가요?

SomeFiltr.sys 에 블록되어 있는 비 정상 스레드를 보면 필터 드라이버가 KeWaitForSingleObject를 호출하여 블록된 것을 볼 수 있습니다. 클라이언트가 FindFirst 요청을 하였으나 Srv!WorkerThread 에서 필터 드라이버가 어떠한 작업을 하기 위해 멈추었으므로 필터 드라이버에서 원인을 찾아야 합니다.

지금까지 설명한 것은 첫 번째 블록 문제로 Srv!WorkerThread 에서 오랜 시간 블록 되어 있는 것은 나쁜 결과를 가져올 수 있다는 것입니다. I/O 스택에 있는 어떠한 필터 또는 다바이스 드라이버가 다른 작업을 위해 멈추어 있다면 Srv 가 다른 모듈에 의존적인 문제가 발생할 수 있다는 것을 볼 수 있습니다.

다른 트릭은 다른 스레드 스택을 모두 확인하여 필터 드라이버 이름이 있는지 확인한 후 다른 커널 API 나 필터 등을 기다리고 있는지 확인하여 다른 것에 의해 블록 되어 있는 것인지 확인할 수 있습니다. 일반적인 경우 LSASS 에 LPC 를 사용하여 보안 확인 과정에 멈추어 있는 경우도 있습니다.

블록을 하고 있는 SomeFiltr.sys 를 확인할 수는 없고 다른 스택에서는 이 드라이버를 찾을 수 없다면 이 드라이버를 업데이트 하거나 제거 또는 SomeFilter.sys 를 만든 제작사에 연락하여 문제를 수정하도록 할 수 있습니다. “lmvm SomeFiltr.sys” 명령을 사용하여 시간/날짜 정보를 얻을 수 있습니다.

만약 모든 스레드가 정상 스레드 상태로 대기하고 있다면?

불행하게도 하나의 덤프만으로 문제를 찾을 수 없습니다. Ctrl+Scroll+Scroll 을 누르는 순간(Keyboard 로 덤프를 만드는 것) Srv!WorkerThread 의 블록이 풀릴 수 있습니다. 일시적으로 블록되는 문제에서는 무엇을 할 수 있을까요? 아니면 어 srv의 어떤 스택을 확인해야 할 까요?(아래 devstack 을 확인). 일반적으로 어떠한 필터 드라이버가 동작중인지 확인하고 해결방법 1과 같이 업데이트가 일반적으로 답이 될 수 있습니다. 일반적으로 동일 문제를 제작사에서 확인한 수 최신 버전에서 수정했을 수 있습니다.

그래서 디버깅을 하기 전에 필터 드라이버 업데이트를 요청하는 이유는 많은 경우 원인으로 확인 되었기 때문 입니다.

Srv.sys I/O 처리하는 디바이스 스택에서 어떻게 문제의 필터 드라이버를 확인할 있을 까요?

잘못된 스레드에 나열되어 있는 IRP들을 기억하십니까?

확인해 보면

0: kd> !irp 86941288

Irp is active with 11 stacks 11 is current (= 0x86941460)

No Mdl Thread 887dd4e0: Irp stack trace.

cmd flg cl Device File Completion-Context

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

[ 0, 0] 0 0 00000000 00000000 00000000-00000000

Args: 00000000 00000000 00000000 00000000

>[ 0, 0] 1 0 884ce3a0 878977a8 00000000-00000000

\FileSystem\SomeFitr

Args: bec28778 01000000 00070000 00000000

SomeFitr 로부터 Device Object 를 얻을 수 있습니다.

 

0: kd> !devstack 884ce3a0

!DevObj !DrvObj !DevExt ObjectName

> 884ce3a0 \FileSystem\SomeFiltr884ce458

88ba5c80 \Driver\InnocentDriver 88ba5d38

88ba5460 \FileSystem\Ntfs 88ba5518

이제는 어떻게 동작하는지 확인할 수 있습니다. Ntfs는 InnocentDriver 과 통신하고 이것은 다시 SomeFiltr 과 통신 합니다. 모든 드라이버 들이 정상적으로 동작 하여야 srv에 영향을 주지 않습니다.

 

IRP 를 모른다면… 문제 업습니다. 모든 행은 Ntfs를 가지고 확인하면 됩니다.

0: kd> !drvobj \FileSystem\Ntfs  <<<<!drvobj 명령으로 드라이버 오브젝트 확인

Driver object (88dd2710) is for:

\FileSystem\Ntfs

Driver Extension List: (id , addr)

Device Object list:

882b4120 8717a620  88b91800  88b981e0

88b9e600  88ba5460  88f583e0  88dd25f0

0: kd> !devstack 882b4120

  !DevObj   !DrvObj            !DevExt   ObjectName

  86576020  \FileSystem\SomeFiltr865760d8  <<<<<<SomeFiltr를 확인할 수 있습니다.

> 882b4120  \FileSystem\Ntfs   882b41d8 

0: kd> !devstack 8717a620

  !DevObj   !DrvObj            !DevExt   ObjectName

  8792ae00  \FileSystem\SomeFiltr8792aeb8  <<<<<<이곳에도 있습니다.

> 8717a620  \FileSystem\Ntfs   8717a6d8 

.

.etc…

.

0: kd> !devstack 88dd25f0

  !DevObj   !DrvObj            !DevExt   ObjectName

  886c9020  \FileSystem\SomeFiltr886c90d8  <<<<<<<이곳에도 있습니다.

  88f74f00  \FileSystem\DfsDriver88f74fb8 

  88f74020  \Driver\InnocentDriver       88f740d8 

> 88dd25f0  \FileSystem\Ntfs   00000000  Ntfs

스택에 있는 모든 드라이버를 업데이트해 본 후 메모리 덤프에서 Srv!WorkerThreads 를 디버깅해 본 결과 문제점을 확인할 수 없다면 ntfs 아래에서 동작하는 스토리지 드라이버를 신중하게 의심해야 합니다.

Event ID: 2021, 2020의 원인 #1

Event ID 2021, 2022 가 발생하는 문제가 공통적인 문제로 I/O 스택에 있는 필터 또는 기능 드라이버가 블록이라는 것일 앞에서 이야기 하였습니다. 또한 아주 많은 요청이 발생하거나 낮게 설정된 Srv(KB317249 에서 이야기한) 또는 많고 긴 요청이 시스템에 많이 있을 공유 발생할 수 있습니다. 예를 들면 ChangeNotify 를 들 수 있습니다. 어떻게 문제점을 찾을 수 있을 까요? 네트워크 트래픽을 확인해 보도록 하겠습니다.

네트워크 모니터링 툴로 SMB 트래픽이 서버로 얼마나 많이 유입되는지 확인할 수 있습니다. 저는 여기서 자세히 이야기 할 수 없지만 분석을 위해서는 SMB 명령을 잘 알아야 할 것 입니다. 정상적인 경우라면 SMB를 필터링 하여 어떤 클라이언트로부터 어떠한 요청이 들어왔는지 확인할 수 있습니다. 일반적으로 네트워크 응용프로그램이 비 정상적으로 동작하여 동일 파일에 대해 반복적으로 요청을 합니다. Srv!WorkerThreads 에서 높은 CPU 사용률이 기록될 경우에도 확인해 볼만한 내용 입니다.

ChangeNotifications 와 QueryFile 그리고 QueryPathInfo 요청의 부하를 줄일 수 있는 방법은 아래 KB 문서를 통해 클라이언트에서 설정해야 할 값을 확인할 수 있습니다.

 

클라이언트 측에서 SMB 쉘 수정 : NoRemoteChangeNotify

http://support.microsoft.com/kb/812669/ko

디렉터리 알림 SMB 요청을 해제하려면 Windows 업데이트를 사용할 수

 

클라이언트 측에서 (리다이렉터) 수정 : InfoCacheLevel

http://support.microsoft.com/kb/816375/ko

Windows XP 탐색기 창이 매핑된 네트워크 드라이브에서 깜박인다

 

이 글이 많은 도움이 되었기를 바라고 문제 해결에 도움이 되었기를 바랍니다.

Posted by DebugMachine | 0 Comments

[Windbg Command]dt 사용 팁

디버깅을 진행 하다 보면 특정 메모리의 tag는 확인하였으나 구조체를 알 수 없는 경우가 많이 있습니다. 이 때 dt 명령을 적절히 사용하면 손쉽게 원하는 정보를 얻을 수 있습니다.

먼저 process 정보를 확인해 본 후 fffffa8004952c10 가 어떤 구조체를 가지는지 확인해 보도록 하겠습니다.

lkd> !process 0 0 explorer.exe
PROCESS fffffa8004952c10
    SessionId: 1  Cid: 12dc    Peb: 7fffffdb000  ParentCid: 1248
    DirBase: a3fd9000  ObjectTable: fffff8800b1a30f0  HandleCount: 1003.
    Image: explorer.exe

 

!pool 명령을 사용해서 이 주소는 NonPagedPool 이고 Proc 즉 Process object 와 관련이 있다는 것을 알 수 있습니다.

lkd> !pool fffffa8004952c10 2
Pool page fffffa8004952c10 region is Nonpaged pool
*fffffa8004952bd0 size:  430 previous size:  130  (Allocated) *Proc (Protected)
        Pooltag Proc : Process objects, Binary : nt!ps

Process 와 관련이 있다는 것을 알았으므로 아래와 깉은 방법으로 어떤 구조체들이 존재하는지 확인해 볼 수 있습니다.

lkd> dt nt!_*proc*
          ntkrnlmp!_KPROCESSOR_STATE
          ntkrnlmp!_PROCESSOR_POWER_STATE
          ntkrnlmp!_KPROCESS
          ntkrnlmp!_EPROCESS
          ntkrnlmp!_EPROCESS_QUOTA_BLOCK
          ntkrnlmp!_WOW64_PROCESS
          ntkrnlmp!_SE_AUDIT_PROCESS_CREATION_INFO
          ntkrnlmp!_ALPC_PROCESS_CONTEXT
          ntkrnlmp!_WHEA_GENERIC_PROCESSOR_ERROR
          ntkrnlmp!_RTL_USER_PROCESS_PARAMETERS
          ntkrnlmp!_PROCESSOR_CACHE_TYPE
          ntkrnlmp!_WHEA_GENERIC_PROCESSOR_ERROR_VALIDBITS

 

감사합니다.

Posted by DebugMachine | 0 Comments
Filed under:

[Stack Trace]System PTEs 는 무엇일까요?

"이 문서는 http://msdn.microsoft.com/en-us/magazine/default.aspx 의 번역이며 원래의 자료가 통보 없이 변경될 수 있습니다. 이 자료는 법률적 보증이 없으며 의견을 주시기 위해 원래의 site를 방문하실 수 있습니다. (http://msdn.microsoft.com/en-us/magazine/dd420461.aspx)"

System PTEs는 무엇일까요?

Bob Golding and David Butler

 

내용
어떤 API들이 이 주소들을 Map 하는데 사용될까?

Bugcheck 0x3F 상황에서 Track PTEs 가 항상 필요한 것은 아니다

커널 스택

 

System PTEs 는 모든 PTEs 와 비슷하게 시스템 어드레스 공간을 나타내는데 사용 됩니다. 그렇다면 시스템 리소스에서 이 두 PTEs가 다른 점은 무엇일까요? System PTEs 는 시스템을 위해서만 사용되고 System PTEs 는 시스템 어드레스 공간 Map 의 동적인 슬롯 입니다. 다시 말해 많은 어드레스 공간이 시스템 어드레스 공간에 Map 되어 있고 동적으로 버퍼 또는 커널 스택 등에 Map 되는 것 입니다. 이 주소공간은 넌페이지드풀과 페이지드 풀 사이에 위치하고 아래 정보는 mi386.h 에서 발최 한 것 입니다.

E1000000  | 페이지 시스템 공간                       |
                  |   커널 모드만 접근 가능                |
                  |                                                    |
                  |                                                    |
                  +-----------------------------------------+
                  |                                                     |
                  | 시스템 PTE 공간 – 커널 스택,         |
                  |  MDL을 시스템 가상 주소에 Map함 |
                  |   커널 모드만 접근 가능                  |<----- 예약된 동적 map 을 위한 공간
                  |   Kernel mode access only.         |
                  |                                                    |
                  +-----------------------------------------+
                  |                                                     |
                  | 넌페이지 시스템 공간                    |
                  |   커널 모드만 접근 가능                 |
                  |                                                     |
                  +------------------------------------------+
FFBE0000  | 크래시 덤프 드라이버 역억               |
                  |  커널 모드만 접근 가능                    |
                  +------------------------------------------+
 

시스템 PTEs 의 크기는 페이지드와 넌페이지드 풀의 크기에 따라 변경 됩니다. 또한 시스템 주소 공간으로 /3GB 와 같은 옵션에 영향을 받습니다.

 

어떤 API들이 이 주소들을 Map 하는데 사용될까?
MiMapLockedPagesInUserSpace, MiUnmapLockedPagesInUserSpace, MmMapLockedPages, and MmUnmapLockedPages 와 같은 API 들이 주소를 Map 하는데 사용 됩니다. 이 API 들은 버퍼를 동적 주소 공간에 map 하는 역할을 하며 전체 버퍼를 map 하고자 할 때 버퍼의 크기가 얼마나 많은 연속된 page 가 사용될 지 결정 합니다.

위에서 보신 것과 같이 모든 Map API 에는 Unmap API 가 존재 합니다. 이것은 아주 중요한데 모든 버퍼는 Unmap 되어야 합니다. Map 된 page 가 Unmap 되지 않는다면 No more PTEs 문제가 발생할 것 입니다. System PTEs 가 낮아 질 경우 성능 또한 낮아질 것이며 Bugcheck 3F 가 나타날 것 입니다. 하지만 NT 에서 리소스를 잘 처리하기 때문에 이러한 현상은 자주 발생하지 않습니다.

 

Bugcheck 0x3F 상황에서 Track PTEs 가 항상 필요한 것은 아니다
System Ptes 가 부족한 현상은 보통 TrackPtes 를 사용하여 얼마나 많은 PTEs 가 사용되었는지 확인 합니다. 하지만 가끔은 이것이 문제를 찾는데 충분하지 않을 수 있습니다. TrackPtes 는 sysptes.c 에 구현되어 있지 않기 때문에 모든 System PTEs 사용이 기록되지는 않고 MiReserveSystemPtes 와 같은 함수를 호출할 때 기록 됩니다.

 

커널 스택

Sysptes 를 소모하지만 기록되지 않는 요인 중 하나가 커널 스택 입니다. 다행이도 메모리 매니저에서 몇 가지 전역 변수를 통해 얼마나 많은 sysptes 가 커널 스택에 사용되었는지 알 수 있게 해 줍니다. 아래의 예를 보면 23,000 개의 sysptes 가 존재 합니다. 이것은 아주 작은 숫자로 /3GB 가 적용 되었을 경우 많이 나타나는 결과 입니다. 17,860 개의 sysptes 가 커널 스택으로 사용되고 있으며 프로세스와 스레드의 분석 만으로는 문제점을 찾을 수 없습니다. 문제점은 사용자의 응용 프로그램이 많은 GDI 스레드를 사용하고 있다는 것 입니다. 장비의 메모리 상태를 분석 한 후 system ptes 의 갯수를 늘리는 것 과 같은 조취를 취해야 할 것 입니다.

1: kd> !sysptes

System PTE Information
  Total System Ptes 23006

1: kd> dc nt!MmKernelStackPages l 1
80483680  000045c4                             .E..
1: kd> ?45c4
Evaluate expression: 17860 = 000045c4

1: kd> dc MmLargeStacks l 1
8048368c  0000037b
1: kd> dc MmSmallStacks l 1
8048367c  00000385
1: kd> ?37b*f
Evaluate expression: 13365 = 00003435
1: kd> ?385*3
Evaluate expression: 2703 = 00000a8f
1: kd> ?3435+a8f+37b+385
Evaluate expression: 17860 = 000045c4

역자주) UI thread 의 경우 Stack 이 15page(60kb) 이고 일반 Thread 의 경우 3page(12kb) 그리고 각각의 커널 스택에 보호 페이지가 1씩 필요하다.

Bob Golding 은 1997년부터 Microsoft 에서 일하고 있습니다. 그는 Global Escalation Services team 의 Senior Escalation Engineer 로 많은 고객의 중요 문제를 지원하고 있습니다.

David Butler 은 2000년부터 Microsoft 에서 일하고 있습니다. 그는 Global Escalation Services team 의 Escalation Engineer 로 많은 고객의 중요 문제를 지원하고 있습니다.

Posted by DebugMachine | 0 Comments
Filed under:

System PTEs 란 무엇일까요? #2

앞서 MSDN magazine에서 System PTEs 와 관련된 좋은 글을 전달해 드렸습니다. 좀더 System PTEs 에 대한 이해를 도와 드리기 위해 저의 경험에서 나온 정보를 좀 더 정리해 보았습니다.

 

System PTEs가 부족할 경우 어떤 문제가 발생할 수 있을 까요?

일단 시스템이 비 정상적으로 동작할 수 있습니다. System PTEs 가 부족하면 추가적으로 kernel stack 을 할당할 수 없으므로 Thread 의 생성 Process 의 생성은 물론 기존에 Slep 되어 있던 Thread가 깨어나면서 kerenl stack 을 할당 받고자 할 경우에도 문제가 될 것 같습니다. 제가 직접 적으로 경험했던 것으로는 Kernel Driver 다 Load 되지 않았던 것 입니다.

 

아래에서는 실제로 System PTEs 가 어떻게 할당되어 사용되고 있는지 확인해 보도록 하겠습니다.

!sysptes 명령으로 System Ptes 의 시작 PTE 와 마지막 PTE 의 가상 주소를 알 수 있습니다.

lkd> !sysptes

System PTE Information
  Total System Ptes 4294967296
     SysPtes list of size 1 has 127 free
     SysPtes list of size 2 has 65 free
     SysPtes list of size 4 has 94 free
     SysPtes list of size 8 has 30 free
     SysPtes list of size 16 has 72 free
    starting PTE: c0228000
    ending PTE:   c03def78

  free blocks: 289   total free: 167789    largest free block: 136815

 

커널 스택이 System PTEs 를 사용한 경우를 확인해 보면

        THREAD 8896ec48  Cid 4f98.6dd4  Teb: 7ff9c000 Win32Thread: e32edbe8 WAIT: (Unknown) UserMode Non-Alertable
            89a06180  SynchronizationEvent
        Not impersonating
        DeviceMap                 e280eec8
        Owning Process            8976f9f8       Image:         explorer.exe
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      41056853       Ticks: 22 (0:00:00:00.343)
        Context Switch Count      1943501                 LargeStack
        UserTime                  00:00:01.406
        KernelTime                00:00:03.296
        Win32 Start Address 0x75d61c3f
        Start Address 0x7c8217ec
        Stack Init b6947000 Current b6946c50 Base b6947000 Limit b6940000 Call 0
        Priority 10 BasePriority 8 PriorityDecrement 0
        ChildEBP RetAddr 
        b6946c68 8083d5b1 nt!KiSwapContext+0x26
        b6946c94 8083df9e nt!KiSwapThread+0x2e5
        b6946cdc bf89ab63 nt!KeWaitForSingleObject+0x346
        b6946d38 bf89d9d3 win32k!xxxSleepThread+0x1be
        b6946d4c bf89e391 win32k!xxxRealWaitMessageEx+0x12
        b6946d5c 80833bef win32k!NtUserWaitMessage+0x14
        b6946d5c 7c96860c nt!KiFastCallEntry+0xfc
        0446ff2c 00000000 0x7c96860c

커널 스택의 값 중 하나를 선택해서 !pte 명령으로 확인해 보면 PTE 의 가상 주소를 확인할 수 있고 System Pte 영역 안에 있음을 확인할 수 있습니다.

lkd> !pte b6946cdc
               VA b6946cdc
PDE at 00000000C0300B68    PTE at 00000000C02DA518
Unable to get PDE FFFFFFFFC0300B68

 

kernel 에 load 되어 있는 module 을 살펴 보면 아래와 같은데 User 영역의 주소에 Map 되어 있는 파일은 일반 PTE 를 사용하고 Kernel 영역 주소에 Map 되어 있는 파일은 System PTEs 를 사용하는 것으로 보입니다.

lkd> lmvm tdi
start    end        module name
f7577000 f7582000   TDI        (deferred)            
    Image path: \SystemRoot\system32\DRIVERS\TDI.SYS
    Image name: TDI.SYS
    Timestamp:        Sat Feb 17 15:01:19 2007 (45D69A2F)
    CheckSum:         0000C620
    ImageSize:        0000B000
    File version:     5.2.3790.3959
    Product version:  5.2.3790.3959
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        3.6 Driver
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     tdi.sys
    OriginalFilename: tdi.sys
    ProductVersion:   5.2.3790.3959
    FileVersion:      5.2.3790.3959 (srv03_sp2_rtm.070216-1710)
    FileDescription:  TDI Wrapper
    LegalCopyright:   © Microsoft Corporation. All rights reserved.

이것 또한 System PTEs 영역에 위치하는 것을 확인할 수 있습니다.

lkd> !pte f7577000
               VA f7577000
PDE at   C0300F74        PTE at C03DD5DC
contains 00AAC963      contains 7D12E963
pfn aac        -G-DA--KWEV    pfn 7d12e      -G-DA—KWEV

 

하지만 NT 와 HAL 은 다른 영역에 할당 됩니다.

80800000 80a7f000   nt        
80a7f000 80aab000   hal       

lkd> !pte 80800000
               VA 80800000
PDE at   C0300808        PTE at C0202000
contains 008009E3      contains 00000000
pfn 800        -GLDA--KWEV    LARGE PAGE pfn 800      

lkd> !pte 80a7f000
               VA 80a7f000
PDE at   C0300808        PTE at C02029FC
contains 008009E3      contains 00000000
pfn 800        -GLDA--KWEV    LARGE PAGE pfn a7f 

 

감사합니다.

Posted by DebugMachine | 0 Comments

Windows internals 5th가 드디어 Release 되었습니다

기다리고 기다리던 Windows Internals 가 드디어 Release 되었습니다.

http://blogs.msdn.com/microsoft_press/archive/2009/05/11/rtm-d-today-windows-internals-fifth-edition.aspx

Credit list 에 저희 부장님 이름도 보이네요.

Amazon 에서는 아직 Release 되지 않았다고 나오는데 조금 있으면 update되겠죠?

Posted by DebugMachine | 1 Comments

Dump 분석 중 Virus로 의심되는 파일 확인하기

dump 를 분석 하다 보면 악성 코드로 의심되는 파일이 있는 경우가 있습니다. 이 경우 Anti-Virus 제품으로 검사를 해 보고 싶어지는데 이때 .writemem 명령을 사용하면 Dump 상에 존재하는 Memory 를 파일로 Write 할 수 있습니다. 물론 전체 Image 가 Dump 안에 존재해야 파일로 쓸 수 있습니다.

Test 를 위해서 Sysinternals 의 LiveKD 를 test 해 보도록 하겠습니다.

LivekdD 가 올라가 있는 메모리 영역을 알기 위해 lmvm 명령을 사용 합니다.

lkd> lmvm LiveKdD
start    end        module name
f79a7000 f79a8fe0   LiveKdD    (deferred)            
    Image path: \??\C:\WINDOWS\system32\Drivers\LiveKdD.SYS
    Image name: LiveKdD.SYS
    Timestamp:        Thu Jul 21 05:19:44 2005 (42DEB1E0)
    CheckSum:         0000F0A9
    ImageSize:        00001FE0
    File version:     3.0.0.0
    Product version:  3.0.0.0
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        3.7 Driver
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Sysinternals - www.sysinternals.com
    ProductName:      LiveKd
    InternalName:     liveKdd.sys
    OriginalFilename: LiveKdd.sys
    ProductVersion:   3.00
    FileVersion:      3.00
    FileDescription:  LiveKd file system filter
    LegalCopyright:   Copyright (C) Mark Russinovich 2000-2005

.writemem 명령을 사용해서 livekd.sys 로 저장합니다. 파일명 시작 Address 끝 Address 입니다.

 

lkd> .writemem d:\case\livekd.sys f79a7000 f79a8fe0
Writing 1fe1 bytes....

Virus total 이라는 site 에서 검사해 보면 정상 파일임을 확인할 수 있습니다.

image

PS : .readmem 명령을 사용해서 파일의 내용을 Memory 로 올릴 수도 있습니다.

Posted by DebugMachine | 1 Comments

User mode dump 분석 (DbgBreakPoint 로 Dump 가 생성된 경우)

User mode dump 를 분석하는 도중 순간적으로 오류를 일으킬 뻔 한적이 있습니다. Dump 파일을 열어서 Call stack 을 확인해 보면 아래와 같이 되어 있습니다.

0:039> kvL
ChildEBP RetAddr  Args to Child             
030cffc8 7c97fdd8 00000005 00000004 00000001 ntdll!DbgBreakPoint (FPO: [0,0,0])
030cfff4 00000000 00000000 031b0000 01740000 ntdll!DbgUiRemoteBreakin+0x36 (FPO: [Non-Fpo]) (CONV: stdcall)

처음에는 DbgBreakPoint 가 Call stack 의 마지막인 것을 보고 Debug 용으로 설정된 int 3 code 가 실행된 것이 아닌가 싶었으나 Crash dump pattern 1 번에 있던 내용이 기억 났습니다.

http://blogs.msdn.com/kocoreinternals/archive/2008/09/17/part-1.aspx

실제로 문제가 발생한 Thread 가 다른 곳에 있다고 생각하고 모든 Thread 의 Stack 을 확인해 보니 아래와 같이 Exception 이 발생한 Thread 가 있었습니다.

0:002> ~* kvL

...
  29  Id: 91c.a14 Suspend: 1 Teb: 7ff77000 Unfrozen
ChildEBP RetAddr  Args to Child             
45c8d35c 7c96771b 7c807f35 ffffffff 7c824830 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
45c8d360 7c807f35 ffffffff 7c824830 00000000 ntdll!ZwQueryVirtualMemory+0xc (FPO: [6,0,0])
45c8d3ac 7c821a99 00000001 45c8d3d0 00000000 kernel32!_ValidateEH3RN+0xb6 (FPO: [Non-Fpo]) (CONV: cdecl)
45c8d3d0 7c968752 45c8d4b4 45c8ffdc 45c8d4d0 kernel32!_except_handler3+0x39 (FPO: [Uses EBP] [3,0,7]) (CONV: cdecl)
45c8d3f4 7c968723 45c8d4b4 45c8ffdc 45c8d4d0 ntdll!ExecuteHandler2+0x26
45c8d49c 7c96855e 45c88000 45c8d4d0 45c8d4b4 ntdll!ExecuteHandler+0x24
45c8d49c 004e085a 45c88000 45c8d4d0 45c8d4b4 ntdll!KiUserExceptionDispatcher+0xe (FPO: [2,0,0]) (CONTEXT @ 45c8d4d0)
WARNING: Stack unwind information not available. Following frames may be wrong.
45c8d7a8 004dd15a 45c8d7dc 00000000 00000006 TestServer+0xe085a
45c8d7ac 45c8d7dc 00000000 00000006 00000002 TestServer+0xdd15a
45c8d7b0 00000000 00000006 00000002 01a4c204 0x45c8d7dc
...

0:002> ~29 s
eax=7c8401a8 ebx=7c824830 ecx=000000f0 edx=00000000 esi=7c824000 edi=ffffffff
eip=7c9685ec esp=45c8d360 ebp=45c8d3ac iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286
ntdll!KiFastSystemCallRet:
7c9685ec c3              ret

문제 발생 당시의 context를 맞추어 보도록 하겠습니다.

0:029> .cxr 45c8d4d0
eax=329f3438 ebx=00000000 ecx=013a5950 edx=013a5950 esi=00000000 edi=013a5988
eip=004e085a esp=45c8d79c ebp=013b5198 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
FieldServer+0xe085a:
004e085a 8b1e            mov     ebx,dword ptr [esi]  ds:0023:00000000=????????

esi 의 값이 NULL 인 것이 문제 입니다.

문제 발생 당시의 Assembly code 를 살펴 보았습니다.

0:029> ub 004e085c L10
TestServer+0xe083c:
004e083c 90              nop
004e083d 90              nop
004e083e 90              nop
004e083f 90               nop
004e0840 51              push    ecx
004e0841 8b4904      mov     ecx,dword ptr [ecx+4]
004e0844 56              push    esi
004e0845 8b742410  mov     esi,dword ptr [esp+10h]
004e0849 57              push    edi                                         <== Push 1번
004e084a 8b4104      mov     eax,dword ptr [ecx+4]
004e084d 8b3d50ba5d00    mov     edi,dword ptr [TestServer+0x1dba50 (005dba50)]
004e0853 3bc7            cmp     eax,edi
004e0855 8bd1            mov     edx,ecx
004e0857 742a            je      TestServer+0xe0883 (004e0883)
004e0859 53              push    ebx                                         <== Push 2번
004e085a 8b1e            mov     ebx,dword ptr [esi]

esi 의 값을 접근하다가 문제가 발생한 것으로 esi 의 값은 esp+10 값 입니다. ebp base 로 Stack 이 이루어져 있지 않은 것으로 보아 최적화가 된 Code 같습니다. 그렇다면 stack 값을 가지고 접근을 해야 하는데 esi 에 esp+10 의 값을 넣은 후 push 가 2회 발생하였으므로 현재 esp 값을 기준으로 보면 esp+18 에 어떤 값이 있는지 확인해 보아야 합니다.

0:029> dd esp+18 L1
45c8d7b4  00000000

결국 TestServer 내에서 잘못된 Parameter 가 전달되어 Application 이 Crash 된 것 입니다.

Posted by DebugMachine | 1 Comments

Kernel pool memory 단편화

가끔 NonPagedPool 이나 PagedPool 의 남은 양보다 적은 Pool 을 할당 하려고 하여도 Pool 할당이 실패하는 경우가 있습니다. 일반적인 Memory 관리와 동일하게 Pool Memory 에도 단편화가 생겨서 실패할 수 있습니다.

!xpoolmap extension 명령은 Pool 이 할당된 Map 을 보여 줍니다.

참고) xpoolmap 은 Windows XP 이상에서  사용 가능 합니다.

 

0 하나가 4kb 를 나타냅니다. 아래의 설명을 참조해서 결과를 확인할 수 있습니다.

 

0: kd> !xpoolmap
unable to get nt!MmSubsectionTopPage
unable to get nt!MmNonPagedMustSucceed
.....................................................
Status Map of Pool Area Pages
==============================
'O': one page in use                              ('P': paged out)
'<': start page of contiguous pages in use        ('{': paged out)
'>': last page of contiguous pages in use         ('}': paged out)
'=': intermediate page of contiguous pages in use ('-': paged out)
'.': one page not used

Non-Paged Pool Area Summary
----------------------------
Maximum Number of Pages  = 52691 pages
Number of Pages In Use   = 52689 pages (100.0%)

          +00000  +08000   +10000  +18000   +20000  +28000   +30000  +38000
81653000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO
81693000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO
816d3000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO

...

86753000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO
86793000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO
867d3000: OOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOO OOOOOOOOOOOO<
f7fba000: .>OOOOOOOOO<==== >O<=><>OOOO<><>O O<=======>OOOO<= >OO<>OOOOOOOOOOO
f7ffa000: OOOO<=><><=><=== >OOOO<>OO<=><>OO OOO<><=>O<=====> <=>OOOOO<>O<==><
f803a000: >O<===><==>OOOOO OOOOOOOOOOOOO<=> OOOOO<=>O<==><>O OOO<>OO<=====><=
f807a000: ><>OOOOO<=>O<=>O <>OOOOOOOO<==>OO <>OOOOOOO<====>O <>OO<><>OOOOO<>O

...

ffafa000: >OOO<>OOOOOOOOOO OOO<==>OOO<>O<>O OO<=======>OOOOO O<===>OOOOOOOOOO
ffb3a000: O<>OO<>OOOOOOOOO O<==>OOOOO<>OO<> OO<=>O<=><>OOOO< >OOOOOOOOOOO<><>
ffb7a000: <==>OO<>OOOOO<=> OOOO<=>O<==><=== ====>OOOO<>OOOOO OOO<>O<>OOO<==><
ffbba000: =>OOO<=====>O<== >OOO<=><=>OOOO<= >OO<=.

Posted by DebugMachine | 1 Comments

User mode 에서 Stack Overflow

Kernel mode 에서 정해진 Stack 보다 많은 Stack 을 사용하면 System 이 Crash 됩니다. 즉 x86 에서 12kb 의 kernel stack 보다 많은 메모리를 사용하려고 하면 Bugcheck 0x7F 가 발생하면서 System 이 Crash 됩니다. (12kb 의 영역 이후의 한 Page 가 접근할 수 없는 메모리로 설정되어 있습니다.)

User mode 에서도 마찬가지로 Stack overflow 가 발생할 수 있습니다.

Stack overflow - code c00000fd

이 값을 가지고 검색을 해보니 아래와 같은 좋은 글을 발견할 수 있었습니다.

Stack Overflow에 대한 Debugging

http://byung.egloos.com/4809522

 

그리고 MSDN 에 다음과 같이 설명이 되어 있네요

Debugging a Stack Overflow

http://msdn.microsoft.com/en-us/library/cc267849.aspx

 

이 문제 발생 시 TIB 에서 StackBase 와 StackLimt 값을 확인하고 StackLimt 을 시작으로 해서 Stack 을 확인해 보면 보통 Stack overflow 또는 재귀호출 또는 비 정상적으로 큰 Local 변수 할당 등의 문제가 나올 수 있을 것 같습니다.

Kernel mode 만 하다가 간만에 User mode 를 볼 수 있는 신선한 경험이었습니다.

Posted by DebugMachine | 1 Comments

x64 에서 Virtual 메모리를 Physical 메모리로 변환

이 번에는 x64 시스템에서 가상 메모리를 물리 메모리로 변환 해 보도록 하겠습니다. Windows Internals 에 보면 아래와 같이 x64 에서 Address 변환에 대해 설명하고 있습니다.

image

 

먼저 Virtual address 의 값을 살펴 보도록 하겠습니다. x64 장비 이므로 dp 또는 dq 명령을 사용해야 합니다.

3: kd> dp fffffa60`07272fb0
fffffa60`07272fb0  00000000`0005afa7 fffffa60`07273070
fffffa60`07272fc0  fffff800`01858000 00000000`00000000
fffffa60`07272fd0  cabf471f`00000000 fffffa60`07273670
fffffa60`07272fe0  fffffa60`0b1e7ac8 fffff800`01b3227f
fffffa60`07272ff0  00000000`00000000 00000000`00000000
fffffa60`07273000  00000000`00000000 fffffa60`07274c20
fffffa60`07273010  fffffa60`0b1e7570 fffff800`01b4692a
fffffa60`07273020  fffff800`00000001 fffffa60`0b1e7500

 

이제 이 Address 를 !pte 명령을 사용해서 확인해 보겠습니다.

 

3: kd> !pte fffffa60`07272fb0
                                 VA fffffa6007272fb0
PXE @ FFFFF6FB7DBEDFA0     PPE at FFFFF6FB7DBF4C00    PDE at FFFFF6FB7E9801C8    PTE at FFFFF6FD30039390
contains 000000010C3C4863  contains 000000010C3C3863  contains 000000009A8FC863  contains 0000000086B3C963
pfn 10c3c4     ---DA--KWEV  pfn 10c3c3     ---DA--KWEV  pfn 9a8fc      ---DA--KWEV  pfn 86b3c      -G-DA—KWEV

 

Address 를 Bit 단위로 쪼개 보겠습니다.

 

3: kd> .formats fffffa60`07272fb0
Evaluate expression:
  Hex:     fffffa60`07272fb0
  Decimal: -6184632897616
  Octal:   1777777646000711627660
  Binary:  11111111 11111111 11111010 01100000 00000111 00100111 00101111 10110000
  Chars:   ...`.'/.
  Time:    ***** Invalid FILETIME
  Float:   low 1.25777e-034 high -1.#QNAN
  Double:  -1.#QNAN

 

쪼개진 Bit 를 다시 정리 해 보면 x64 에서는 48Bit 만 사용하기 때문에 앞의 FFFF 는 무시합니다.
1111111111111111
111110100        1F4      <== Page map level 4 selector
110000000        180      <== Page directory pointer selector
000111001        39        <== Page table selector
001110010        72        <== Page table entry selector
111110110000        FB0 <== Byte within page

 

Cr3 register 에서 Page level 4의 address 를 구합니다.

 

3: kd> r cr3
Last set context:
cr3=0000000063476000

 

위의 쪼개놓은 Bit 값을 가지고 변환을 해 보면 처음에 확인한 Virtual address 와 같은 내용을 가지는 Physical address 를 확인할 수 있습니다.

 

3: kd> dp /p 0000000063476000+1f4*8 L1
00000000`63476fa0  00000001`0c3c4863

3: kd> dp /p 10C3C4000+180*8 L1
00000001`0c3c4c00  00000001`0c3c3863

3: kd> dp /p 10c3c3000+39*8 L1
00000001`0c3c31c8  00000000`9a8fc863

3: kd> dq /p 00000000`9a8fc000+72*8 L1
00000000`9a8fc390  00000000`86b3c963

3: kd> dp /p 0`86b3c000+fb0
00000000`86b3cfb0  00000000`0005afa7 fffffa60`07273070
00000000`86b3cfc0  fffff800`01858000 00000000`00000000
00000000`86b3cfd0  cabf471f`00000000 fffffa60`07273670
00000000`86b3cfe0  fffffa60`0b1e7ac8 fffff800`01b3227f
00000000`86b3cff0  00000000`00000000 00000000`00000000

Posted by DebugMachine | 1 Comments

IDT 와 ISR

오늘은 IDT 와 ISR 에 대해서 알려 드리고자 합니다. IDT, ISR 에 대해 설명하고자 하면 책을 한권을 써야 하고 저의 내공이 그렇게 깊지 않기 때문에 Windbg 를 사용해서 IDT 에서 ISR 을 찾아가는 과정을 설명 드리고자 합니다.

먼저 !idt 명령을 사용하면 IDT 의 내용이 확인 가능 합니다.

 

0: kd> !idt

Dumping IDT:

37:    80a7817c hal!PicSpuriousService37
3d:    80a79560 hal!HalpApcInterrupt
41:    80a793b4 hal!HalpDispatchInterrupt
50:    80a78254 hal!HalpApicRebootService
51:    89535044 serial!SerialCIsrSw (KINTERRUPT 89535008)
52:    8956d044 i8042prt!I8042MouseInterruptService (KINTERRUPT 8956d008)
53:    895f8044 USBPORT!USBPORT_InterruptService (KINTERRUPT 895f8008)
             USBPORT!USBPORT_InterruptService (KINTERRUPT 89571008)
             USBPORT!USBPORT_InterruptService (KINTERRUPT 89569008)
63:    895fe044 VIDEOPRT!pVideoPortInterrupt (KINTERRUPT 895fe008)
72:    8a10f044 atapi!IdePortInterrupt (KINTERRUPT 8a10f008)
73:    89b08044 storport!RaidpAdapterInterruptRoutine (KINTERRUPT 89b08008)
83:    89b38044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 89b38008)
93:    89be6044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 89be6008)
a2:    89537044 serial!SerialCIsrSw (KINTERRUPT 89537008)
a3:    8a34d044 storport!RaidpAdapterInterruptRoutine (KINTERRUPT 8a34d008)
b1:    8a35d044 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 8a35d008)
b3:    8953a044 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 8953a008)
b4:    895f1044 NDIS!ndisMIsr (KINTERRUPT 895f1008)
c1:    80a783e0 hal!HalpBroadcastCallService
d1:    80a77754 hal!HalpClockInterrupt
e1:    80a787f4 hal!HalpIpiHandler
e3:    80a78618 hal!HalpLocalApicErrorService
fd:    80a78d64 hal!HalpProfileInterrupt
fe:    80a78f0c hal!HalpPerfInterrupt

 

자 여기서 키보드 인터럽트를 직접 IDT 에서 ISR 까지 따라가는 작업을 해 보겠습니다.

먼저 r idt 명령으로 idtr 레지스터의 값을 확인하여 idt 의 address 를 찾습니다.

 

0: kd> r idtr
idtr=8003f400

 

keyboard interrupt 에 해당하는 index b3 을 확인 합니다.

 

0: kd> dt _KIDTENTRY 8003f400 + b4 * @@(sizeof(_KIDTENTRY))
nt!_KIDTENTRY
   +0x000 Offset           : 0xa044
   +0x002 Selector         : 8
   +0x004 Access           : 0x8e00
   +0x006 ExtendedOffset   : 0x8953

 

실제로 virtual address 에 있는 내용을 확인해 봅니다.

 

0: kd> dd 8003f400 + b3 * @@(sizeof(_KIDTENTRY)) L2
8003f9a0  0008a044 89538e00

 

여기서 ExtendedOffset 과 Offset 을 합친 0x8953a044 가 실제로 Interrupt 가 발생하였을 때 실행되는 Code 의 위치인데 Windows 에서는 이것을 Interrupt Object 라는 것으로 관리 합니다. 이 구조체는 아래와 같습니다.

 

0: kd> dt _KINTERRUPT
nt!_KINTERRUPT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 InterruptListEntry : _LIST_ENTRY
   +0x00c ServiceRoutine   : Ptr32     unsigned char
   +0x010 ServiceContext   : Ptr32 Void
   +0x014 SpinLock         : Uint4B
   +0x018 TickCount        : Uint4B
   +0x01c ActualLock       : Ptr32 Uint4B
   +0x020 DispatchAddress  : Ptr32     void
   +0x024 Vector           : Uint4B
   +0x028 Irql             : UChar
   +0x029 SynchronizeIrql  : UChar
   +0x02a FloatingSave     : UChar
   +0x02b Connected        : UChar
   +0x02c Number           : Char
   +0x02d ShareVector      : UChar
   +0x030 Mode             : _KINTERRUPT_MODE
   +0x034 ServiceCount     : Uint4B
   +0x038 DispatchCount    : Uint4B
   +0x03c DispatchCode     : [106] Uint4B

 

결국 0x895f1044 는 KINTERRUPT 구조체의 0x3c 위치에 있는 DispatchCode 의 Address 입니다.

 

0: kd> ? 8953a044 - 3c
Evaluate expression: -1991008248 = 8953a008

 

이 값은 !idt 의 결과에서 확인한 8953a044 의 값과 동일 합니다.

 

b3:    8953a044 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 8953a008)

 

자 그럼 확인해 볼까요?

 

0: kd> dt _KINTERRUPT 8953a008
nt!_KINTERRUPT
   +0x000 Type             : 22
   +0x002 Size             : 484
   +0x004 InterruptListEntry : _LIST_ENTRY [ 0x8953a00c - 0x8953a00c ]
   +0x00c ServiceRoutine   : 0xbaabe0a5     unsigned char  i8042prt!I8042KeyboardInterruptService+0
   +0x010 ServiceContext   : 0x899eac88
   +0x014 SpinLock         : 0
   +0x018 TickCount        : 0xffffffff
   +0x01c ActualLock       : 0x899ead48  -> 1
   +0x020 DispatchAddress  : 0x8083a63c     void  nt!KiInterruptDispatch+0
   +0x024 Vector           : 0x1b3
   +0x028 Irql             : 0xa ''
   +0x029 SynchronizeIrql  : 0xa ''
   +0x02a FloatingSave     : 0 ''
   +0x02b Connected        : 0x1 ''
   +0x02c Number           : 0 ''
   +0x02d ShareVector      : 0 ''
   +0x030 Mode             : 1 ( Latched )
   +0x034 ServiceCount     : 0
   +0x038 DispatchCount    : 0xffffffff
   +0x03c DispatchCode     : [106] 0x56535554

 

DispatchCode 에는 어떤 내용이 있는지 확인해 보도록 하겠습니다.

lkd> u 89c03008 + 3c L30
89c03044 54              push    esp
89c03045 55              push    ebp
89c03046 53              push    ebx
89c03047 56              push    esi
89c03048 57              push    edi
89c03049 83ec54          sub     esp,54h
89c0304c 8bec            mov     ebp,esp
89c0304e 89442444        mov     dword ptr [esp+44h],eax
89c03052 894c2440        mov     dword ptr [esp+40h],ecx
89c03056 8954243c        mov     dword ptr [esp+3Ch],edx
89c0305a f744247000000200 test    dword ptr [esp+70h],20000h
89c03062 0f8531010000    jne     89c03199
89c03068 66837c246c08    cmp     word ptr [esp+6Ch],8
89c0306e 7423            je      89c03093
89c03070 8c642450        mov     word ptr [esp+50h],fs
89c03074 8c5c2438        mov     word ptr [esp+38h],ds
89c03078 8c442434        mov     word ptr [esp+34h],es
89c0307c 8c6c2430        mov     word ptr [esp+30h],gs
89c03080 bb30000000      mov     ebx,30h
89c03085 b823000000      mov     eax,23h
89c0308a 668ee3          mov     fs,bx
89c0308d 668ed8          mov     ds,ax
89c03090 668ec0          mov     es,ax
89c03093 648b1d00000000  mov     ebx,dword ptr fs:[0]
89c0309a 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh
89c030a5 895c244c        mov     dword ptr [esp+4Ch],ebx
89c030a9 81fc00000100    cmp     esp,10000h
89c030af 0f82b8000000    jb      89c0316d
89c030b5 c744246400000000 mov     dword ptr [esp+64h],0
89c030bd 648b0d24010000  mov     ecx,dword ptr fs:[124h]
89c030c4 fc              cld
89c030c5 83652c00        and     dword ptr [ebp+2Ch],0
89c030c9 f64103ff        test    byte ptr [ecx+3],0FFh
89c030cd 7522            jne     89c030f1
89c030cf 8b5d60          mov     ebx,dword ptr [ebp+60h]
89c030d2 8b7d68          mov     edi,dword ptr [ebp+68h]
89c030d5 89550c          mov     dword ptr [ebp+0Ch],edx
89c030d8 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
89c030df 895d00          mov     dword ptr [ebp],ebx
89c030e2 897d04          mov     dword ptr [ebp+4],edi
89c030e5 bf0830c089      mov     edi,89C03008h
89c030ea e96d64c3f6      jmp     nt!KiInterruptDispatch (8083955c)

 

Dispatch code 는 대부분의 내용이 모든 Interrupt 에서 공통적으로 사용되는 것인데 파란색으로 되어 있는 부분이 각각의 Interrupt 마다 다르다. edi 에 89c03008h 즉 이 Interrupt 의 Interrupt Object 값을 넣고 nt!KiInterruptDispatch 로 Jump 합니다.

KiInterruptDispatch 코드를 살펴 보면

 

lkd> u nt!KiInterruptDispatch L100
nt!KiInterruptDispatch:
8083955c 8bec            mov     ebp,esp
8083955e 8b4724          mov     eax,dword ptr [edi+24h]
80839561 8b4f29          mov     ecx,dword ptr [edi+29h]
80839564 50              push    eax
80839565 83ec04          sub     esp,4
80839568 54              push    esp
80839569 50              push    eax
8083956a 51              push    ecx
8083956b ff1554108080    call    dword ptr [nt!_imp__HalBeginSystemInterrupt (80801054)]
80839571 0ac0            or      al,al
80839573 7449            je      nt!KiInterruptDispatch+0x62 (808395be)
80839575 64ff0544060000  inc     dword ptr fs:[644h]
8083957c 83ec0c          sub     esp,0Ch
8083957f 648b0d1c000000  mov     ecx,dword ptr fs:[1Ch]
80839586 83790800        cmp     dword ptr [ecx+8],0
8083958a c745f400000000  mov     dword ptr [ebp-0Ch],0
80839591 753f            jne     nt!KiInterruptDispatch+0x76 (808395d2)
80839593 8b771c          mov     esi,dword ptr [edi+1Ch]
80839596 f00fba2e00      lock bts dword ptr [esi],0
8083959b 7229            jb      nt!KiInterruptDispatch+0x6a (808395c6)
8083959d 8b4710          mov     eax,dword ptr [edi+10h]
808395a0 50              push    eax
808395a1 57              push    edi
808395a2 ff570c          call    dword ptr [edi+0Ch]

 

edi+0Ch 즉 edi 에는 InterruptObject 구조체의 포인터가 들어 있고 0Ch 위치에는 ServiceRoutine 이 있으므로 해당 Interrupt 의 Service routine 을 호출하는 것입니다.

 

마지막으로 그럼 ISR 을 개발하는 개발자는 이런 것들을 직접 넣어줘야 할까요?

아래 링크를 확인해 보면 ISR 을 만드는 것에 대한 자세한 설명이 있습니다.

http://msdn.microsoft.com/en-us/library/ms795203.aspx

 

간단히 설명하자면 개발자는 Interrupt 에 대한 ServiceRoutine, SpinLock 등을 정의 한 후 IoConnectInterrupt 함수를 호출하면 결과로 _IO_CONNECT_INTERRUPT 구조체가 나오는 것 입니다.

아래는 WDK Sample 중 PnPi8042 에서 사용된 IoConnectInterrupt 에 대한 내용으로 자세한 내용은 WDK Sample 중 \WinDDK\6001.18000\src\input\pnpi8042 를 참조하면 됩니다.

 

status = IoConnectInterrupt(
    &(KeyboardExtension->InterruptObject),
    (PKSERVICE_ROUTINE) I8042KeyboardInterruptService,
    self,
    &KeyboardExtension->InterruptSpinLock,
    KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector,      
    (KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level,
    configuration->InterruptSynchIrql,
    KeyboardExtension->InterruptDescriptor.Flags
      == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive,
    (BOOLEAN) (KeyboardExtension->InterruptDescriptor.ShareDisposition
        == CmResourceShareShared),
    KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity,   
    configuration->FloatingSave
    );

Posted by DebugMachine | 3 Comments
Filed under:

Sysinternals Live

그 동안 Sysinternals 에서 나온 Tool 들을 많이 사용하셨으리라 생각 합니다. 대체로 Sysinternals site 에서 Tool 을 down 받아서 Zip 형태의 파일을 Local 에 해제한 후 실행시키는 패턴으로 작업을 하셨으리라 생각 합니다. 하지만 고객 지원을 하는 경우에 Local 에 down 을 받는 것을 싫어 하시는 경우도 있고 한 번 실행시켜 볼 것인데 다운 받아 설치하는 것이 귀찮은 작업일 수도 있었습니다.

 

Sysinternals 에서는 아래 URL 을 통해 Web 에서 바로 Sysinternals tool 을 사용할 수 있게 하고 있습니다.

http://live.sysinternals.com/

 

Explorer 에서 아래 경로를 열어보면 파일을 바로 실행할 수 있습니다.

\\live.sysinternals.com\tools

 

Explorer 에서 아래 경로를 열어보면 Zip 파일들을 직접 copy 할 수 있습니다.

\\live.sysinternals.com\Files

Posted by DebugMachine | 1 Comments

VMMap v1.01

Sysinternals 에서 Process 의 메모리 상태를 확인할 수 있는 Tool 을 새로 내놓았습니다. Virtual memory 와 Physical memory 분석 툴로 Process 의 Committed 된 Virtual memory 와 Physical memory (working set) 을 확인할 수 있습니다.

얼마나 Memory 가 사용되었는지 정도는 볼 수 있으나 안타깝게도 Memory 를 할당할 때의 Call stack 을 볼 수 는 없습니다.

Total, Image, Private, Shareable, Mapped File, Heap, Stack, System Free 에 대한 내용을 볼 수 있습니다.

알고 계신 것과 같이 Commit 메모리와 Working set 즉 Physical memory가 할당되어 있는 것은 다릅니다.

 

http://technet.microsoft.com/en-us/sysinternals/dd535533.aspx

Posted by DebugMachine | 0 Comments

가상 메모리를 물리 메모리로 변환

일반적으로 가상 메모리를 물리 메모리로 변환해서 확인해야 할 경우가 거의 없지만 공부하는 차원에서 직접 Virtual address 를 physical address 로 변환하는 것을 Windbg 를 통해 확인해 보고자 합니다.

x86 system 을 기준으로 설명 합니다. x64 의 경우 내용이 좀 다르며 다음 번에 설명하기로 합니다.

Windows 에서는 Virtual address 를 관리하기 위해 page 라는 개념을 사용하는데 일반적인 경우 4KB 의 page 를 관리하게 됩니다. 하지만 Image 를 올리는 것과 같이 큰 Data 를 물리 메모리에 올릴때는 4MB ㅇ의 Large page 라는 것을 사용합니다.

 

일반 page 의 경우

virtual address 0xb742a310 을 기준으로 설명 합니다.

!pte 명령을 사용해서 virtual address 의 정보를 확인할 수 있습니다.

 

7: kd> !pte b742a310
               VA b742a310
PDE at   C0300B74        PTE at C02DD0A8
contains 00BA7963      contains 28857963
pfn ba7        -G-DA--KWEV    pfn 28857      -G-DA-KWEV

 

이 메모리의 내용을 확인해 보도록 하겠습니다.


7: kd> dd b742a310
b742a310  b742a3ac f7123166 b742a730 e51f7cd8
b742a320  00000080 b742a3cc 00000000 b742a2f3

 

자 이제부터 본격적으로 변환 작업을 진행 해 보도록 하겠습니다.

rm ff 명령을 사용해서 register mask 를 변환해서 모든 register 값을 볼 수 있도록 합니다.

kd> rm ff
Last set context:

 

r 명령으로 register 값을 확인 합니다. Page Directory 의 주소를 가지고 있는 cr3 레지스터의 값을 확인 합니다. 여기서는 0x3edb1000 입니다.


7: kd> r
Last set context:
eax=83000aec ebx=84d128d8 ecx=00000000 edx=83000ae4 esi=84d12830 edi=84d128a8
eip=8083df4c esp=b742a224 ebp=b742a264 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
fpcw=0000: rn 24 ------  fpsw=0000: top=0 cc=0000 --------  fptw=0000
fopcode=0000  fpip=0000:00000000  fpdp=0000:00000000
st0= 0.000000000000000000000e+0000  st1= 0.000000000000000000000e+0000
st2= 0.000000000000000000000e+0000  st3= 0.000000000000000000000e+0000
st4= 0.000000000000000000000e+0000  st5= 0.000000000000000000000e+0000
st6= 0.000000000000000000000e+0000  st7= 0.000000000000000000000e+0000
mm0=0000000000000000  mm1=0000000000000000
mm2=0000000000000000  mm3=0000000000000000
mm4=0000000000000000  mm5=0000000000000000
mm6=0000000000000000  mm7=0000000000000000
xmm0=0 0 0 0
xmm1=0 0 0 0
xmm2=0 0 0 0
xmm3=0 0 0 0
xmm4=0 0 0 0
xmm5=0 0 0 0
xmm6=0 0 0 0
xmm7=0 0 0 0
cr0=8001003b cr2=00000000 cr3=3edb1000
dr0=00000000 dr1=00000000 dr2=00000000
dr3=00000000 dr6=00000000 dr7=00000000 cr4=000006d9
nt!KeWaitForSingleObject+0x24f:
8083df4c 8919            mov     dword ptr [ecx],ebx  ds:0023:00000000=????????

 

찾고자 하는 virtual address 를 bit 단위로 쪼갭니다.

 

7: kd> .formats b742a310
Evaluate expression:
  Hex:     b742a310
  Decimal: -1220369648
  Octal:   26720521420
  Binary:  10110111 01000010 10100011 00010000
  Chars:   .B..
  Time:    ***** Invalid
  Float:   low -1.16013e-005 high -1.#QNAN
Double:  -1.#QNAN

 

Virtual address 값을 확인해 보면 다음과 같습니다.

 

1011011101      2DD    Page Directory index
0000101010      2A      Page Table Index
001100010000   310    Offset

 

CR3 에서 얻은 값으로 Page Directory index 로 물리 메모리 확인합니다. dd 는 메모리를 보여주는 명령이고 /p 는 physical memory 를 보여주는 것 입니다.

 

Page directory address + page directory index * size


7: kd> dd /p 3edb1000+2dd*4
3edb1b74  00ba7963 00ba8963 00ba9963 00baa963    !pte 명령의 결과중 첫 번째 Contains 에 있는 값과 동일합니다.
3edb1b84  00bab963 00bac963 00bad963 00bae963

 

가상 메모리에 동일 내용 들어 있는 것을 확인합니다.


7: kd> dd C0300B74
c0300b74  00ba7963 00ba8963 00ba9963 00baa963
c0300b84  00bab963 00bac963 00bad963 00bae963

 

ba7963 을 bit 단위로 확인하여 일반 page 인지 Large page 인지 확인 합니다.

 

7: kd> .formats 00ba7963
Evaluate expression:
  Hex:     00ba7963
  Decimal: 12220771
  Octal:   00056474543
  Binary:  00000000 10111010 01111001 01100011    PS Field가 set 되어 있지 않기 때문에 일반 page 로 4KB 단위
  Chars:   ..yc
  Time:    Fri May 22 19:39:31 1970
  Float:   low 1.71249e-038 high 0
  Double:  6.03786e-317

 

Page Table Index 로 물리메모리 확인 두번째 Contains 에 있는 값과 동일합니다.

Page table address + page table index * size


7: kd> dd /p ba7000+2a*4
00ba70a8  28857963 00000000 00000000 00000000
00ba70b8  00000000 00000000 00000000 00000000

 

마지막으로 Offset 을 사용해서 물리 메모리 확인합니다.

page start address + offset


7: kd> dd /p 28857000+310
28857310  b742a3ac f7123166 b742a730 e51f7cd8
28857320  00000080 b742a3cc 00000000 b742a2f3

 

가상 메모리를 확인하여 동일함을 확인 합니다.


7: kd> dd b742a310
b742a310  b742a3ac f7123166 b742a730 e51f7cd8
b742a320  00000080 b742a3cc 00000000 b742a2f3

 

 

Large page

 

가상메모리 0x83002634 를 확인해 보고자 합니다.

!pte 명령을 통해 정보를 확인해 봅니다. 일반 페이지와는 다르게 LARGE PAGE 라고 나옵니다.

 

7: kd> !pte 83002634
               VA 83002634
PDE at   C0300830        PTE at C020C008
contains 030009E3      contains 00000000
pfn 3000       -GLDA--KWEV    LARGE PAGE pfn 3002

 

0x83002634 에 어떤 값이 있는지 확인해 봅니다.

 

7: kd> dd 83002634
83002634  02110201 03021103 11030211 02110301
83002644  04011102 11020211 01110403 02011104

 

0x83002634 를 Bit 단위로 쪼개 봅니다.

 

7: kd> .formats 83002634
Evaluate expression:
  Hex:     83002634
  Decimal: -2097142220
  Octal:   20300023064
  Binary:  10000011 00000000 00100110 00110100
  Chars:   ..&4
  Time:    ***** Invalid
  Float:   low -3.76597e-037 high -1.#QNAN
  Double:  -1.#QNAN

 

Large Page 이기 때문에 뒤의 22Byte 가 Offset 입니다.

1000001100                       20C Page Directory Index
0000000010011000110100    2634 Offset

 

r 명령을 사용해서 cr3 register 값을 확인 합니다. 이전과 동일하게 3edb1000 입니다.

 

7: kd> r
Last set context:
eax=83000aec ebx=84d128d8 ecx=00000000 edx=83000ae4 esi=84d12830 edi=84d128a8
eip=8083df4c esp=b742a224 ebp=b742a264 iopl=0         nv up ei pl zr na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010246
fpcw=0000: rn 24 ------  fpsw=0000: top=0 cc=0000 --------  fptw=0000
fopcode=0000  fpip=0000:00000000  fpdp=0000:00000000
st0= 0.000000000000000000000e+0000  st1= 0.000000000000000000000e+0000
st2= 0.000000000000000000000e+0000  st3= 0.000000000000000000000e+0000
st4= 0.000000000000000000000e+0000  st5= 0.000000000000000000000e+0000
st6= 0.000000000000000000000e+0000  st7= 0.000000000000000000000e+0000
mm0=0000000000000000  mm1=0000000000000000
mm2=0000000000000000  mm3=0000000000000000
mm4=0000000000000000  mm5=0000000000000000
mm6=0000000000000000  mm7=0000000000000000
xmm0=0 0 0 0
xmm1=0 0 0 0
xmm2=0 0 0 0
xmm3=0 0 0 0
xmm4=0 0 0 0
xmm5=0 0 0 0
xmm6=0 0 0 0
xmm7=0 0 0 0
cr0=8001003b cr2=00000000 cr3=3edb1000
dr0=00000000 dr1=00000000 dr2=00000000
dr3=00000000 dr6=00000000 dr7=00000000 cr4=000006d9
nt!KeWaitForSingleObject+0x24f:
8083df4c 8919            mov     dword ptr [ecx],ebx  ds:0023:00000000=????????

 

cr3 레지스터에 있는 값인 Page directory address 를 사용해서 Page Table 위치를 찾습니다.

page directory + page directory index * size

 

7: kd> dd /p 3edb1000+20c*4
3edb1830  030009e3 034009e3 038009e3 03c009e3
3edb1840  040009e3 044009e3 048009e3 04c009e3

 

가상 메모리에 동일한 값이 있는지 확인 합니다.


7: kd> dd C0300830       
c0300830  030009e3 034009e3 038009e3 03c009e3
c0300840  040009e3 044009e3 048009e3 04c009e3

 

확인된 내용을 Bit 단위로 쪼개서 내용을 확인해 보니 Large page 로 설정되어 있습니다.

 

7: kd> .formats 030009e3
Evaluate expression:
  Hex:     030009e3
  Decimal: 50334179
  Octal:   00300004743
  Binary:  00000011 00000000 00001001 11100011    Large Page
  Chars:   ....
  Time:    Fri Aug 06 22:42:59 1971
  Float:   low 3.76272e-037 high 0
  Double:  2.48684e-316

 

물리 메모리 확인 PS field가 1로 설정되어 있기 때문에 직접 물리 메모리를 가리키는 것입니다. 즉 0x03000000 이 시작 주소 입니다.


Page 시작 address + offset


7: kd> dd /p 03000000 + 2634
03002634  02110201 03021103 11030211 02110301
03002644  04011102 11020211 01110403 02011104

 

Large page 의 처음에서 확인한 Virtual address 의 내용과 일치 합니다.

Posted by DebugMachine | 3 Comments
Filed under:

[CDA]Savedump.exe 그리고 페이지 파일

"이 문서는 http://www.dumpanalysis.org/blog blog 의 번역이며 원래의 자료가 통보 없이 변경될 수 있습니다. 이 자료는 법률적 보증이 없으며 의견을 주시기 위해 원래의 blog 를 방문하실 수 있습니다. (http://www.dumpanalysis.org/blog/index.php/2007/02/14/savedumpexe-and-pagefile/)"

 

Savedump.exe 그리고 페이지 파일

 

오늘 저는 savedump.exe 가 무엇인지 호기심이 생기어 조사를 한 후 몇 가지 정보를 찾았습니다.

Windows 2000 에서는 savedump.exe 는 logon 프로세스의 일부로 pagefile 에서 crash dump section 을 찾아 메모리 덤프 파일과 미니 덤프 파일을 생성 합니다.

 

http://support.microsoft.com/kb/257299

(역자주 : crash dump driver 가 정보를 주 디스크의 paging 파일에 저장하게 되고 미러된 disk 에는 동기화 되지 않습니다. 재 부팅 된 후 savedump.exe 가 페이지 파일에서 dump 정보를 읽어 덤프 파일을 생성할 때 미러된 disk 에서도 정보를 읽으려 하나 덤프 정보가 존재하지 않아 덤프가 손상 됩니다.)

 

http://support.microsoft.com/kb/262077

(역자주 : 메모리 덤프 파일의 헤더에 하나의 필드가 추가되었고 이는 심볼을 가리켜 올바른 미니 덤프를 생성할 수 있도록 해 줍니다. 추가적으로 savedump.exe 는 전체 메모리 덤프를 생성할 때 미니 덤프를 생성하도록 코드가 추가 되었습니다. )

 

Windows 2003 과 XP 에서는 savedump.exe 는 pagefile 을 사용하지 않습니다. 세션 메니저인 smss.exe 가 pagefile 에 작업을 한 후 dumpxxx.dmp 파일로 rename 하고 (필요하다면 boot volume 으로 복사 합니다.) 이후 savedump.exe 가 dl 파일을 올바른 위치로 복사한 후 mini dump 파일을 생성 합니다.

 

http://support.microsoft.com/kb/886429

(역자주 : Windows 가 비 정상적으로 종료 되었을 때 Windows 는 물리 메모리 와 동일한 크기의 임시 파일을 부트 볼륨에 생성 합니다. 만약 디스크 공간이 부족하다면 덤프 파일은 생성 되지만 페이지 파일 크기가 작아 지게 됩니다.

아래 설명은 Windows Server 2003 에서 dump 생성 방법의 변경 사항 입니다.

  • 메모리 덤프를 생성하는 첫 번째 단계에서 세션 메니저 서브시스템 프로세스(SMSS.exe) 는 Savedump.exe 가 하던 작업 중 일부를 직접 수행합니다. SMSS 는 페이지 파일 헤더 블록을 검사하여 메모리 덤프 파일이 손상 되었는지 확인을 합니다. 파일이 손상되지 않았다면 SMSS 는 원본 페이지 파일을 덤프 파일 크기 만큼 잘라낸 후 Dumpxxx.tmp 로 rename 힙니다. SMSS 는 Dumpxxx.tmp 파일을 부트 볼륨에 저장한 후 파일에서 히든과 시스템 속성을 제거 합니다. SMSS TempDestination 값을 설정하고 덤프 파일 값을 레지스트리에 저장 합니다. 이 키는 나중에 Savedump.exe 가 Dumpxxx.tmp 를 Memory.dmp 로 복사할 때 사용합니다.
  • 메모리 덤프 생성의 두 번째 단계에서 Savedump.exe 는 아래 레지스트리 키를 확합니다. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl\MachineCrash 이 키가 존재한다는 것은 정상적인 덤프 파일이 생성되었다는 것이고 savedump.exe 가 이 데이터를 읽어 덤프 파일을 올바른 위치로 복사 합니다.
  • SMSS 는 부트 볼륨에만 쓸 수 있으며 필터 드라이버들은 무시 됩니다. (필터 드라이버가 무시 되기 때문에 RAID 에는 저장할 수 없습니다.)
  • SMSS 는 페이지 파일을 임시 파일로 rename 하기 위해 NtSetFileInformation 을 사용 합니다. 이 함수는 동일 볼륨에 대해서만 rename 을 허용 합니다.)

Savedump.exe 를 Vista 에서 찾을 수 없는 것으로 보아 사라진 것으로 보입니다.

- Dmitry Vostokov -

Posted by DebugMachine | 1 Comments
More Posts Next page »
 
Page view tracker