Client-Server Runtime Subsystem의 약자로, 앞서 Smss가 로드하는 3개의 서브시스템 중 하나로, 윈도우 서브시스템이라고도 불리우며, 윈도우상에서 실행되는 프로세스와 스레드들의 관리를 담당한다.
Csrss.exe(이하 Csrss)는 실행시 Basesrv.dll(Microsoft Windows NT Base API Server Library), Winsrv.dll(Windows Server Library), Csrsrv.dll(Client Server Runtime Process)을 로드하여 콘솔 윈도우(Console windows) 처리, 프로세스와 스레드 생성과 삭제, 16비트 가상 DOS 머신(VDM) 프로세스를 위한 기능 일부 및 SxS(Side-by-Side)를 지원하게 된다. 이와 관련하여 제공하는 API 리스트는 http://j00ru.vexillium.org/csrss_list/api_list.html에서 확인할 수 있다.
Csrss는 윈도우 시스템 프로세스로서 프로세스와 스레드의 설정을 담당하여, 현재 윈도우에서 실행되는 프로세스와 스레드 리스트를 관리하고 예외 처리 포트(13장 디스패칭에서 다룬다)를 생성하는 등, 사용자의 윈도우 환경을 관리하는 윈도우 서브시스템으로서 중요한 위치를 차지하는 프로세스라 할 수 있다. 그리고 Smss와 같이 네이티브 어플리케이션이다. 아래 그림의 프로세스 생성 과정을 통해 Csrss를 조금 더 쉽게 이해할 수 있을 것이다.
Csrss에 대한 정보는 다음 레지스트리에서 설정값을 확인할 수 있다.
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows
그리고 Csrss은 다른 서브시스템들과 빠른 통신을 위해 레지스트리 ServerDll에 등록된 Dll을 통해 LPC(Local Inter-Process Communication)로 처리를 하게 된다. LPC는 서브시스템 간 통신 사용 방식으로 바로 다음에 설명하겠다.
%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
추가로 사용자의 세션과 관련된 Desktop-Heap을 이해해야 Csrss를 온전히 이해하는 데 도움이 된다. 이는 추후 메모리 관리를 설명할 때 다시 다룰 예정으로 여기서는 이 정도만 설명하고, Csrss가 이용하는 LPC에 대해 확인해 보도록 하자.
LPC
LPC는 응용프로그램이 Csrss와 통신하거나, SRM(Security Reference Monitor’s) 및 Winlogon이 Lsass와 정보를 주고 받고자 할때, 그리고 고속으로 서로 다른 구조에서 데이터를 교환하고자 할때 사용된다(서로 다른 구조란 서로 다른 2개의 프로세스, 스레드를 뜻한다). 비스타부터는 강제로 동기화 방식으로 진행되던 LPC를 비동기화 방식으로 진행하는 방법도 새롭게 추가하였는데, 이를 ALPC(Asynchronous Local Inter-Process Communication)라고 한다.
이외에 RPC(Remote Procedure Call)라는 용어도 윈도우에서 종종 접할 수 있는데, 이는 LPC와 방식은 흡사하지만 서로 다른 기기에 위치(네트워크 적으로)하고 있을 때 사용되는 통신 방식을 RPC라 한다. 그럼 LPC 통신 흐름을 그림으로 살펴보자.
위 그림에서 얘기하는 클라이언트는 요청자가 되고, 서버는 제공자라 할 수 있다. Lsass와 SRM을 비교한다면, Lsass가 요청자가 되고 SRM은 서버, 즉 제공자가 된다.
아래는 LPC에서 제공하는 Native API들로서, LPC에는 아래와 같은 API를 이용하여 서로 통신을 진행한다. 이 API들은 Ntdll.dll과 Ntoskrnl.exe를 통해 제공되며, 응용프로그램에서 직접 LPC를 사용할 수 없다.
API | 설명 |
NtCreatePort | 서버가 포트를 생성할 때 사용한다. |
NtConnectPort | 클라이언트가 포트 연결할 때 사용한다. |
NtListenPort | 서버에서 연결된 리스트를 요청이 왔을 때 사용한다. |
NtAcceptConnectPort | 서버에서 연결 허용 요청을 수락할 때 사용한다. |
NtCompleteConnectPort | 서버에서 연결 요청에 대한 연결이 완료되었을 때 사용한다. |
NtRequestPort | 메시지를 보내고 회신을 기다리지 않는다. |
NtRequestWaitReplyPort | 메시지를 보내고 회신을 기다린다. |
NtReplyPort | 회신 메시지를 보낸다. |
NtReplyWaitReplyPort | 회신 메시지를 보내고, 응답을 기다린다. |
NtReplyWaitReceivePort | 서버에서 회신 메시지를 보내고, 클라이언트의 응답을 기다린다. |
NtImpersonateClientOfPort | 서버 스레드에 의해 사용되며, 클라이언트 스레드의 보안 컨테스트를 임시로 획득한다. |
Windbg에서 LPC 상태를 효과적으로 확인할 수 있는 확장 명령을 제공하는데, 바로 !lpc 명령에 파라미터를 추가하여 사용할 수 있다. 각 옵션들에 대해 아래와 같이 정리하였다.
파라미터 | 설명 |
message [id] | 큐 안의 메시지 내용을 표시해 준다. 메시지ID를 주지 않으면 전체를 보여준다. |
port [portaddress] | 포트 상태 정보를 보여준다. 포트 어드레스 주소값를 주지 않으면 전체를 보여 준다. |
scan portaddress | 지정한 포트 어드레스 주소에서 오브젝트 타입이 “Port”거나 “WaitablePort”인 포트 정보를 보여준다. |
thread [threadaddress] | ETHREAD의 LpcReplyChain의 오브젝트가 “Port”거나 “WaitablePort”인 스레드는 회신을 기다리게 되는데, 전체 스레드에서 LpcReceivedMessageId, LpcReplyMessageId가 동일한Port 정보를 보여준다. 특정 스레드를 주지 않으면 전체를 보여준다. |
poolsearch | Kernel Pool에서 LPC tag인 (“LpcM”)로 되어 있는 내용을 “lpc message” 방식으로 검색하여 보여준다. |
Windbg에서 위 옵션을 이용해 LPC 정보를 확인할 수 있다. 만약 LPC 통신을 이용하던 도중 장애가 발생하였다면, !lpc 명령을 이용하여 어디에서 대기(Wait) 상태가 지속되는지를 문제점을 확인할 수 있다. 아래는 Windbg를 통해 대기 상태인 스레드의 LPC 내용을 확인하는 과정이다.
// LPC를 사용중인 스레드를 확인해보자.
kd> !lpc thread
Searching message 0 in threads ...
Server thread 81d17020 is working on message aea
Client thread 81da4020 waiting a reply from cff
Server thread 81d08478 is working on message 1053
Server thread 81ceb660 is working on message ac4
Server thread 81af2920 is working on message cff
Server thread 81aef020 is working on message b3b
Server thread 81e08da8 is working on message 8a1
Server thread 81cfada8 is working on message cf6
Server thread 81bbb020 is working on message 1042
Server thread 81bdbab8 is working on message 51d
Server thread 81b45b30 is working on message 102f
Server thread 81d1c560 is working on message 906
Server thread 81d15da8 is working on message 1041
Server thread 81d4dda8 is working on message 103d
Server thread 81b45020 is working on message 1043
Done.
.// 대기중인 스레드의 LPC ID는 스레드 정보를 통해서도 확인할 수 있다.
kd> !thread 81da4020
THREAD 81da4020 Cid 0284.028c Teb: 7ffde000 Win32Thread: e16f1848 WAIT: (WrLpcReply) UserMode Non-Alertable
81da4214 Semaphore Limit 0x1
Waiting for reply to LPC MessageId 00000cff:
Current LPC port e1b92380
Not impersonating
DeviceMap e10087c0
Owning Process 0 Image: <Unknown>
Attached Process 81ce8da0 Image: Csrss.exe
Wait Start TickCount 1909 Ticks: 795 (0:00:00:07.961)
Context Switch Count 7 LargeStack
UserTime 00:00:00.000
KernelTime 00:00:00.000
Start Address 0x764c7d63
Stack Init f4f1a000 Current f4f19c50 Base f4f1a000 Limit f4f17000 Call 0
Priority 15 BasePriority 15 PriorityDecrement 0 DecrementCount 0
ChildEBP RetAddr Args to Child
f4f19c68 804de0f7 81da4090 81da4020 804de143 nt!KiSwapContext+0x2e (FPO: [Uses EBP] [0,0,4])
f4f19c74 804de143 81da4214 81da41e8 81da4020 nt!KiSwapThread+0x46 (FPO: [0,0,0])
f4f19c9c 80578fc6 00000001 00000011 81da4001 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo])
f4f19d50 804e07ec 00000084 0050ff00 0050ff00 nt!NtRequestWaitReplyPort+0x63d (FPO: [Non-Fpo])
f4f19d50 7c93e4f4 00000084 0050ff00 0050ff00 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f4f19d64)
WARNING: Frame IP not in any known module. Following frames may be wrong.
0050fff4 00000000 00000000 00000000 00000000 0x7c93e4f4
// LPC의 message 옵션을 통해 세부적인 LPC 메시지 내용 및 상태 확인이 가능하다.
// 현재 사용중인 포트 정보와 연결 포트 값, 대기 큐 리스트등을 확인할 수 있다.
kd> !lpc message 00000cff
Searching message cff in threads ...
Client thread 81da4020 waiting a reply from cff
Server thread 81af2920 is working on message cff
Searching thread 81da4020 in port rundown queues ...
Server communication port 0xe16ea378
Handles: 1 References: 1
The LpcDataInfoChainHead queue is empty 현재 대기 큐가 없음을 나타낸다.
Connected port: 0xe1b92380 Server connection port: 0xe1721468 연결 포트와 서버 포트 정보
Client communication port 0xe1b92380 클라이언트 포트 정보
Handles: 1 References: 2
The LpcDataInfoChainHead queue is empty
Server connection port e1721468 Name: SmSsWinStationApiPort
Handles: 1 References: 11
Server process : 81d402d8 (svchost.exe)
Queue semaphore : 81b7f778
Semaphore state 0 (0x0)
The message queue is empty
The LpcDataInfoChainHead queue is empty
Done.