Session, Window Station과 Desktop

1. Session, Window Station, 그리고 Desktop

WindowsUser Interface, GDI(Graphics Device Interface) , 과 Kernel 이라는 3가지 큰 부류의 개체를 제공합니다. 이 중에서 User Interface 개체는 Windows StationsDesktops를 사용하여 관리됩니다.

그러면 Window Station은 무엇일까요? Window StationClipboard, Atom Table, 그리고 한두개 이상의 Desktop 개체를 포함하고 있습니다. 그리고 이를 관리하게 됩니다. Windows Station이 생성될 때, 현재 호출 프로세스와 관련되며 현재 세션에 할당되게 됩니다.

Interactive Window Station, WinSta0은 User Interface 를 보여주거나 사용자 입력을 받는 유일한 Window Station입니다. 이것은 Interactive User의 Logon Session에 할당됩니다. 그리고 Keyboard, Mouse, 그리고 Display Device를 포함합니다. 그리고 모든 다른 Windows Station 들은 noninteractive 입니다. 이것은 즉, 다른 Windows Station 들은 User Interface를 보여주거나 사용자 입력을 받을 수 없다는 것을 의미합니다.

image

사용자가 Terminal Services를 제공하는 컴퓨터에 로그온할 때, 사용자를 위해 한 개의 Session을 제공하게 됩니다. 각 세션에는 세션만의 Interactive Window Station을 가지고 있습니다. 이렇게 생성된 세션은 고유한 Session ID를 가지게 됩니다. 이렇게 생성된 세션들은 Remote Desktop Client를 사용하여 사용자들이 로그온하기 때문에 이 Session 들을 Remote Desktop Session이라고 부릅니다. 이 세션에서 Interactive Window Station을 지원하는 유일한 Window Station 명은 WinSta0 입니다. 각 Window Station에는 Winlogon Desktop, Disconnect Desktop, 그리고 Default Desktop 이라는 3개의 Desktop이 있습니다.

clip_image002[4]

한 개의 Session에서 Interactive Window Station 으로 연결된 User를 Interactive User라고 부릅니다. Remote Desktop Connection client에서 Console Session 상에 있는 Interactive User 이외에 여려 Interactive User들이 있을 수 있습니다.

2. Window Station은 어떻게 생성하는가?

 

Window Station은 CreateWindowStation 함수를 호출하여 생성할 수 있습니다. MSDN에서 아래 경로를 참조하면 자세한 내용을 확인할 수 가 있습니다.

CreateWindowStation Function

https://msdn.microsoft.com/en-us/library/ms682496(VS.85).aspx

HWINSTA WINAPI CreateWindowStation(

__in_opt LPCTSTR lpwinsta,

DWORD dwFlags,

__in ACCESS_MASK dwDesiredAccess,

__in_opt LPSECURITY_ATTRIBUTES lpsa

);

CreateWindowStation 함수는 win32k.sys 드라이버에서 이와 연결된 함수를 호출하게 됩니다. 이 함수가 호출된 후 실제 WindowStation에 대한 인스턴스를 생성시킵니다. Window Station 인스턴스는 “WindowStation”이라는 TypeName을 가진 Kernel Object이기 때문에 생성될 때 ObCreateObject에 의해 생성이 되며 Nonpaged Pool에 할당이 됩니다. 이 내용은 Memory Dump에서 아래와 같이 확인할 수가 있습니다.

6: kd> !objects

808ac654 nt!ObpTypeDirectoryObject = e10008c0

HASH OBJ_TYPE Objects Handles Diff Type

00 : 8cd8e118 133 225 -92 Directory

01 : 8cd51380 1843 2055 -212 Mutant

01 : 8cd56ad0 1767 3675 -1908 Thread

03 : 8c928a28 0 0 0 FilterCommunicationPort

05 : 8cd2eca0 2 0 2 Controller

07 : 8cd50ad0 0 0 0 Profile

07 : 8cd51720 11458 11878 -420 Event

07 : 8cd8e2e8 31 0 31 Type

09 : 8cd50390 1818 795 1023 Section

09 : 8cd51550 0 0 0 EventPair

09 : 8cd56040 255 5 250 SymbolicLink

10 : 8cd50560 17 121 -104 Desktop

11 : 8cd50ca0 72 72 0 Timer

12 : 8cd2e560 12112 7328 4784 File

12 : 8cd50730 12 180 -168 WindowStation

16 : 8cd2e900 94 0 94 Driver

18 : 8cd82040 147 147 0 WmiGuid

18 : 8cd50900 1 89 -88 KeyedEvent

19 : 8cd2ead0 452 0 452 Device

19 : 8cd56e70 532 660 -128 Token

20 : 8cd55e70 0 0 0 DebugObject

21 : 8cd2e730 227 1292 -1065 IoCompletion

22 : 8cd56ca0 97 339 -242 Process

24 : 8cd2ee70 16 0 16 Adapter

26 : 8cd4f520 2775 2764 11 Key

28 : 8cd56900 2 2 0 Job

31 : 8cd2f4f0 3 3 0 WaitablePort

31 : 8cd2f6c0 941 928 13 Port

32 : 8cd50040 6 0 6 Callback

33 : 8c928bf8 0 0 0 FilterConnectionPort

34 : 8cd50e70 2700 2943 -243 Semaphore

아래 명령어를 사용하면 생성된 WindowStation의 상세한 내용을 확인할 수 있습니다. 아래에서 WindowStations 개체 아래에 있는 실제 생성된 WindowStation 들을 확인할 수가 있습니다. 그리고 Interactive Window Station인 WinSta0 을 확인할 수가 있습니다.

6: kd> !object \windows\WindowStations

Object: e1652530 Type: (8cd8e118) Directory

ObjectHeader: e1652518 (old version)

HandleCount: 0 PointerCount: 12

Directory Object: e16755b0 Name: WindowStations

Hash Address Type Name

---- ------- ---- ----

00 8c8a5f20 WindowStation Service-0x0-187ba$

01 8c38a088 WindowStation Service-0x0-3e4$

04 8c3f94f0 WindowStation Service-0x0-3e5$

08 8c763fa8 WindowStation Service-0x0-c4e36$

11 8c7de258 WindowStation Service-0x0-3e7$

15 8bfbce50 WindowStation __X78B95_89_IW

18 8c7b4508 WindowStation Service-0x0-2b5ac$

27 8c8b30e8 WindowStation WinSta0

28 8c068d28 WindowStation Service-0x0-be387$

31 8c47f6e0 WindowStation SAWinSta

35 8bef6b58 WindowStation Service-0x0-1b67c$

그러면 현재 WindowStation들이 있는 세션은 어떻게 확인할 수 있을까요? 아래와 같이 Session 명령어를 쓰면 쉽게 확인할 수가 있습니다.

6: kd> !session

Sesssions on machine: 3

Valid Sessions: 0 2 3

Using session CURRENT (0)

자! 그러면 WindowStation에 대해 자세하게 알아보겠습니다. WindowStation은 Win32k.sys 드라이버에서 _WINDOWSTATION 이라는 구조체를 사용하여 저장하게 됩니다. 이것을 사용하여 위에 Hash 27인 WinSta0를 한 번 살펴보도록 하겠습니다. 아래 내용을 보면 SessionId 가 0이고, Window Station이 Clipboard, AtomTable을 가진다고 설명하였는데, 그 내용을 확인할 수 있을 것입니다.

6: kd> dt win32k!WINDOWSTATION 8c8b30e8

+0x000 dwSessionId : 0

+0x004 rpwinstaNext : 0x8c7de258 tagWINDOWSTATION

+0x008 rpdeskList : 0x8c4182d0 tagDESKTOP

+0x00c pTerm : 0xbf9b6180 tagTERMINAL

+0x010 dwWSF_Flags : 0

+0x014 spklList : 0xbc064fb8 tagKL

+0x018 ptiClipLock : (null)

+0x01c ptiDrawingClipboard : (null)

+0x020 spwndClipOpen : (null)

+0x024 spwndClipViewer : (null)

+0x028 spwndClipOwner : (null)

+0x02c pClipBase : 0xbc56ea18 tagCLIP

+0x030 cNumClipFormats : 4

+0x034 iClipSerialNumber : 6

+0x038 iClipSequenceNumber : 0x1f

+0x03c pGlobalAtomTable : 0xe164f628

+0x040 luidEndSession : _LUID

+0x048 luidUser : _LUID

+0x050 psidUser : 0xbc376820

그리고, WinSta0가 가지고 있는 Desktop 목록이 rpdeskList에 저장되어 있습니다. 이 주소를 이용하여 Desktop 목록을 확인할 수가 있습니다. 또한 위 WinSta0 주소를 가지고 어디에 저장되어 있는지 아래와 같이 체크해 볼 수가 있습니다. 예상한 대로 Nonpaged Pool에 저장되어 있습니다.

6: kd> !pool 8c8b30e8 2

Pool page 8c8b30e8 region is Nonpaged pool

*8c8b30b0 size: 90 previous size: 10 (Allocated) *Wind (Protected)

Owning component : Unknown (update pooltag.txt)

 

3. Desktop은 어떻게 생성하는가?

 

WindowStation인스턴스인 WinSta0는 보통 3개의 Desktop을 가지고 있습니다: Default, Disconnect, Winlogon. 이러한 Desktop을 어떻게 생성하는지 알아보도록 하겠습니다. CreateDesktopEx 함수를 사용하면 새로운 Desktop을 생성하여 사용할 수가 있습니다. CreateDesktopEx의 함수 원형은 아래와 같습니다. 이 함수에는 Desktop Heap의 크기를 설정할 수가 있도록 되어 있습니다.

HDESK WINAPI CreateDesktopEx(

__in LPCTSTR lpszDesktop,

__reserved LPCTSTR lpszDevice,

__reserved LPDEVMODE pDevmode,

__in DWORD dwFlags,

__in ACCESS_MASK dwDesiredAccess,

__in_opt LPSECURITY_ATTRIBUTES lpsa,

__in ULONG ulHeapSize,

__reserved PVOID pvoid

);

CreateDesktopEx 함수는 Win32k.sys 드라이버에서 이와 연결된 함수를 호출하고 이 함수에서 Desktop을 생성하게 됩니다. 생성된 Desktop 정보는 Win32k.sys 드라이버에서 _DESKTOP 구조체를 사용하여 저장하고 이 정보는 Kernel Object로서 생성되게 됩니다. 따라서 !winde.objects 명령어를 사용하면 Desktop 개체의 개수를 확인할 수가 있습니다.

6: kd> !objects

808ac654 nt!ObpTypeDirectoryObject = e10008c0

HASH OBJ_TYPE Objects Handles Diff Type

00 : 8cd8e118 133 225 -92 Directory

01 : 8cd51380 1843 2055 -212 Mutant

01 : 8cd56ad0 1767 3675 -1908 Thread

03 : 8c928a28 0 0 0 FilterCommunicationPort

05 : 8cd2eca0 2 0 2 Controller

07 : 8cd50ad0 0 0 0 Profile

07 : 8cd51720 11458 11878 -420 Event

07 : 8cd8e2e8 31 0 31 Type

09 : 8cd50390 1818 795 1023 Section

09 : 8cd51550 0 0 0 EventPair

09 : 8cd56040 255 5 250 SymbolicLink

10 : 8cd50560 17 121 -104 Desktop

11 : 8cd50ca0 72 72 0 Timer

12 : 8cd2e560 12112 7328 4784 File

12 : 8cd50730 12 180 -168 WindowStation

16 : 8cd2e900 94 0 94 Driver

18 : 8cd82040 147 147 0 WmiGuid

18 : 8cd50900 1 89 -88 KeyedEvent

19 : 8cd2ead0 452 0 452 Device

19 : 8cd56e70 532 660 -128 Token

20 : 8cd55e70 0 0 0 DebugObject

21 : 8cd2e730 227 1292 -1065 IoCompletion

22 : 8cd56ca0 97 339 -242 Process

24 : 8cd2ee70 16 0 16 Adapter

26 : 8cd4f520 2775 2764 11 Key

28 : 8cd56900 2 2 0 Job

31 : 8cd2f4f0 3 3 0 WaitablePort

31 : 8cd2f6c0 941 928 13 Port

32 : 8cd50040 6 0 6 Callback

33 : 8c928bf8 0 0 0 FilterConnectionPort

34 : 8cd50e70 2700 2943 -243 Semaphore

Desktop Kernel Object가 생성된 후 Desktop이 사용할 Desktop Heap을 설정하게 됩니다. 첫 번째 Desktop인 Logon Desktop은 Small Heap (128K)를 사용하게 됩니다. 그리고 Disconnect Heap은 조금 더 작은 Heap(64K) 을 사용합니다. 그리고 Default Desktop은 아래의 레지스트리 경로에 설정된 값으로 설정됩니다. 기본값은 SharedSection에 있는 값입니다. SharedSection=1024,3072,512 값중에서 3072 입니다. 이 값의 단위는 KB(Kilobytes)이기 때문에 설정된 값은 3072K가 됩니다.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows

이 데이터에 대한 기본값은 아래와 같이 설정되어 있습니다.

%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16

그러면 SharedSection=1024,3072,512의 값의 의미를 좀 더 자세하게 살펴보도록 하겠습니다.

  • 첫번째값(1024)는 모든 Desktop에 공통된 Shared Heap의 크기를 의미합니다. 이 값은 Desktop Heap의 사용량에 문제가 발생 경우 이를 위해서 수정되면 안됩니다. (단위는 Kilobytes)
  • 두번째값(3072)는 Interactive Windows Station, WinSta0에 생성된 Disconnect와 Winlogon을 제외한 Default Desktop의 Heap Size를 의미합니다. (단위 Kilobytes)
  • 세번째값(512)는 Noninteractive Window Station에서 생성된 Desktop에 필요한 Desktop Heap의 크기를 의미합니다. (단위 Kilobytes)

Desktop이 생성될 때 또 하나 아주 중요한 사실이 하나 있습니다. Section 생성 함수를 사용하여 설정한 Desktop Heap Size 만큼의 공간을 가진 Section Object를 생성합니다. 이 Section Object는 SessionSpace에 View를 Mapping하는 함수를 사용하여 생성되는 Session 의 Session Space에 Mapping이 되게 됩니다. 여기서 Mapping된 Session Space는 Session에 할당된 Session View의 크기를 이야기 합니다. 아래 레지스트리 키를 통해 Session에 할당된 View 크기를 설정할 수가 있습니다. 이 값은 16의 배수로 변경시켜야 합니다. 각 OS에 할당된 Session View의 크기는 아래와 같습니다.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management

SessionViewSize = 기본값 48, (0x30) (단위 Megabytes)

OS

Size if no registry value configured

Default registry value

Windows 2000 *

20 MB

None

Windows XP

20 MB

48 MB

Windows Server 2003

20 MB

48 MB

만일 위의 Desktop Heap의 기본 크기를 늘리게 되면 Session에서 생성할 수 있는 Desktop 수가 줄어들게 됩니다. 따라서 Desktop의 개수를 면밀히 조사하여 사용 개수를 파악한 후 Desktop Heap의 크기와 Session View 크기를 동시에 변경시키는 것을 권합니다. 예를 들어, SessionView의 크기가 48MB인 상태에서 Desktop Heap의 크기를 8192 KB로 증가 시킨 후 문제가 발생한다면 다시 SessionView의 크기를 16의 배수인 64 MB로 늘린 후 모니터링하여야 합니다.