TERMSRV!WinStationLpcThread函数和TERMSRV!WinStationLpcHandleConnectionRequest函数分析
WinStationLpcThread函数和WinStationLpcHandleConnectionRequest函数分析
第一部分:调试记录部分:
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WinStation LPC Service Thread got a message
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WinStation LPC Service Thread got connection message
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WinStationLpcHandleConnectionRequest called
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WSTAPI: Creating View memory
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WSTAPI: Calling AcceptConnectPort, Accept 1
04:05:41.953 8981FF7C.E13E5450 TERMSRV: pContext 000F0670, ConnectionRequest 027FFEAC, info 027FFEC4
04:05:41.953 8981FF7C.E13E5450 TERMSRV: ViewBase 024B0000, ViewSize 0x2000, ViewRemoteBase 00640000
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WSTAPI: Calling CompleteConnect port 00000334
04:05:41.953 8981FF7C.E13E5450 TERMSRV: WinStation LPC Connection Accepted, Logonid 9 pContext 000F0670 Status 0x0
04:05:41.953 8984492C.E12576D8 TERMSRV: WinStation LPC Service Thread got a message
04:05:41.953 8984492C.E12576D8 TERMSRV: WinStation LPC Service Thread got WinStationGetSMCommand message
04:05:41.953 8984492C.E12576D8 TERMSRV: WinStationGetSMCommand, LogonId=9
04:05:41.953 8984492C.E12576D8 TERMSRV: WinStationGetSMCommand queue empty port 00000334
第二部分:
/*****************************************************************************
*
* WinStationLpcThread
*
* Main service thread for internal Winstation LPC connections.
*
* ENTRY:
* ThreadParameter (input)
* Not used standard NT ThreadCreate() parameter
*
* EXIT:
* Should never exit
*
****************************************************************************/
NTSTATUS
WinStationLpcThread( IN PVOID ThreadParameter )
{
WINSTATION_APIMSG ApiMsg;
PWINSTATION_APIMSG ReplyMsg;
PLPC_CLIENT_CONTEXT pContext;
NTSTATUS Status;
HANDLE Handle;
ReplyMsg = NULL;
/*
* Loop forever processing API requests
*/
for ( ; ; ) {
/*
* If there are more than the minimum number of API threads active,
* and at least 1 waiting thread, then this thread will terminate.
* But first, any pending reply message must be sent.
*/
RtlEnterCriticalSection( &ApiThreadLock );
#ifdef notdef
if ( NumApiThreads > MinApiThreads && WaitingApiThreads ) {
NumApiThreads--;
RtlLeaveCriticalSection( &ApiThreadLock );
if ( ReplyMsg ) {
(VOID) NtReplyPort( SsWinStationLpcPort,
(PPORT_MESSAGE) ReplyMsg );
}
break;
}
#endif
/*
* Increment the number of waiting threads and wait for an LPC request
*/
WaitingApiThreads++;
RtlLeaveCriticalSection( &ApiThreadLock );
Status = NtReplyWaitReceivePort( SsWinStationLpcPort,
(PVOID *) &pContext,
(PPORT_MESSAGE) ReplyMsg,
(PPORT_MESSAGE) &ApiMsg );
RtlEnterCriticalSection( &ApiThreadLock );
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got a message\n" ));
/*
* If there are no more waiting threads,
* then create a new API thread to process requests.
*/
if ( --WaitingApiThreads == 0 && NumApiThreads < MaxApiThreads ) {
DWORD ThreadId;
NumApiThreads++;
RtlLeaveCriticalSection( &ApiThreadLock );
Handle = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)WinStationLpcThread,
NULL,
THREAD_SET_INFORMATION,
&ThreadId );
if ( !Handle ) {
RtlEnterCriticalSection( &ApiThreadLock );
NumApiThreads--;
RtlLeaveCriticalSection( &ApiThreadLock );
} else {
NtClose( Handle );
}
} else {
RtlLeaveCriticalSection( &ApiThreadLock );
}
if ( !NT_SUCCESS(Status) ) {
ReplyMsg = NULL;
continue;
}
try {
/*
* Process connection request from a new client
*/
if ( ApiMsg.h.u2.s2.Type == LPC_CONNECTION_REQUEST ) {
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got connection message\n" ));
// CONNECT_INFO is in ApiMsg from NtReplyWaitReceivePort() when
// a connection request is received. This differs from
// NtListenPort() which passes separate pointers for CONNECT_INFO.
WinStationLpcHandleConnectionRequest( (PPORT_MESSAGE)&ApiMsg );
ReplyMsg = NULL;
continue;
}
/*
* Process port closed message
*/
if ( ApiMsg.h.u2.s2.Type == LPC_PORT_CLOSED ) {
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got PORT_CLOSED message pContext %p\n",
pContext));
// NOTE: This function frees the CONTEXT struct
WinStationLpcClientHasTerminated( pContext );
ReplyMsg = NULL;
continue;
}
ASSERT(sizeof(WinStationLpcDispatch)/sizeof(WinStationLpcDispatch[0]) == SMWinStationMaxApiNumber);
ASSERT(sizeof(WinStationLpcName)/sizeof(WinStationLpcName[0]) == SMWinStationMaxApiNumber);
/*
* Process API request from client
*/
ReplyMsg = &ApiMsg;
if ((ULONG) ApiMsg.ApiNumber >= (ULONG)SMWinStationMaxApiNumber ) {
DBGPRINT(( "TERMSRV: WinStation LPC Service Thread Bad API number %d\n",
ApiMsg.ApiNumber ));
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
} else {
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got %s message\n",
WinStationLpcName[ApiMsg.ApiNumber] ));
if ( WinStationLpcDispatch[ApiMsg.ApiNumber] ) {
// Save Msg for use by CheckClientAccess
NtCurrentTeb()->Win32ThreadInfo = &ApiMsg;
// The functions set ApiMsg.ReturnedStatus
Status = (WinStationLpcDispatch[ApiMsg.ApiNumber])( pContext, &ApiMsg );
// Clear thread Msg pointer
NtCurrentTeb()->Win32ThreadInfo = NULL;
} else {
// This API is not implemented in Session Manager
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
}
/*
* If client does not expect a reply or reply is pending
* (will be sent asynchronously), then clear ReplyMsg pointer.
*/
if ( !ApiMsg.WaitForReply || Status == STATUS_PENDING )
ReplyMsg = NULL;
}
} except( WinStationExceptionFilter( L"WinStationLpcThread trapped!!",
GetExceptionInformation() ) ) {
ReplyMsg = NULL;
}
}
return( STATUS_SUCCESS );
}
第三部分:
/*****************************************************************************
*
* WinStationLpcHandleConnectionRequest
*
* Handle connection requests and create our local data structures
*
* ENTRY:
* ConnectionRequest (input)
* NT LPC PORT_MESSAGE describing the request
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
WinStationLpcHandleConnectionRequest(
IN PPORT_MESSAGE ConnectionRequest
)
{
NTSTATUS st;
HANDLE CommunicationPort = NULL;
BOOLEAN Accept;
PWINSTATIONAPI_CONNECT_INFO info;
REMOTE_PORT_VIEW ClientView;
REMOTE_PORT_VIEW *pClientView = NULL;
PORT_VIEW ServerView;
PORT_VIEW * pServerView = NULL;
LARGE_INTEGER SectionSize;
HANDLE PortSection = NULL ;
PWINSTATION pWinStation;
PLPC_CLIENT_CONTEXT pContext = NULL;
ULONG ClientLogonId;
PTERMSRVLPCCONTEXT pLpcContextEntry = NULL;
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationLpcHandleConnectionRequest called\n" ));
Accept = TRUE; // Assume we will accept
// An undocumented NT LPC feature is that the CONNECT_INFO structure
// follows the PORT_MESSAGE header when the connection request is
// received through NtReplyWaitReceivePort(), which is useful since we
// only have to maintain (1) thread for WinStation LPC API's, and
// do not have to dedicated one to NtListenPort() just for connection
// requests.
if ( ConnectionRequest->u1.s1.DataLength != sizeof(WINSTATIONAPI_CONNECT_INFO) ) {
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WSTAPI: Bad CONNECTINFO length %d\n",
ConnectionRequest->u1.s1.DataLength ));
Accept = FALSE;
} else {
info = (PWINSTATIONAPI_CONNECT_INFO)
((ULONG_PTR)ConnectionRequest + sizeof(PORT_MESSAGE));
//
// We can set Accept to FALSE at anytime here for certain types
// of requests and/or caller identities.
//
if ( ConnectionRequest->ClientViewSize == 0 ) {
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WSTAPI: Creating View memory\n" ));
pServerView = &ServerView;
// Setup Port memory for larger data transfers
SectionSize.LowPart = WINSTATIONAPI_PORT_MEMORY_SIZE;
SectionSize.HighPart = 0;
st = NtCreateSection(&PortSection, SECTION_ALL_ACCESS, NULL,
&SectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
if (!NT_SUCCESS(st)) {
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Error Creating Section 0x%x\n", st));
Accept = FALSE;
info->AcceptStatus = st;
} else {
ServerView.Length = sizeof(ServerView);
ServerView.SectionHandle = PortSection;
ServerView.SectionOffset = 0;
ServerView.ViewSize = SectionSize.LowPart;
ServerView.ViewBase = 0;
ServerView.ViewRemoteBase = 0;
}
}
if ( Accept ) {
// Init the REMOTE_VIEW structure
ClientView.Length = sizeof(ClientView);
ClientView.ViewSize = 0;
ClientView.ViewBase = 0;
pClientView = &ClientView;
info->AcceptStatus = STATUS_SUCCESS;
if ( info->Version != CITRIX_WINSTATIONAPI_VERSION ) {
info->AcceptStatus = 1; // Fill in bad version param code
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: WSTAPI: Bad Version %d\n", info->Version));
Accept = FALSE;
}
// Add checks for info.RequestedAccess against the requesting
// threads security rights for WinStation access. Use the Se* stuff
// to do the checking and audit generation
// On Security Access failure:
// Accept = FALSE;
// info->AcceptStatus = NT invalid rights message
}
}
//
// Get the ClientLogonId
//
if ( Accept ) {
HANDLE ClientProcessHandle = NULL;
OBJECT_ATTRIBUTES ObjA;
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
st = NtOpenProcess( &ClientProcessHandle, GENERIC_READ,
&ObjA, &ConnectionRequest->ClientId );
if (NT_SUCCESS(st)) {
GetProcessLogonId( ClientProcessHandle, &ClientLogonId );
NtClose( ClientProcessHandle );
} else {
Accept = FALSE;
info->AcceptStatus = st;
}
}
//
// Allocate a context connection control block.
// The address of this block is used as the
// port context in all calls from a client process
//
if ( Accept ) {
pContext = MemAlloc( sizeof(LPC_CLIENT_CONTEXT) );
if ( pContext ) {
pContext->CommunicationPort = NULL;
pContext->AccessRights = info->RequestedAccess;
} else {
Accept = FALSE;
info->AcceptStatus = STATUS_NO_MEMORY;
}
}
// More undocumented NT. Many parameters are missing here and in ntlpcapi.h
// from the documentation. The CONNECTION_INFO message is part
// of the message body following PORT_MESSAGE, just like
// NtReplyWaitReceivePort().
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WSTAPI: Calling AcceptConnectPort, Accept %d\n", Accept));
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: pContext %p, ConnectionRequest %p, info %p\n",
pContext, ConnectionRequest, info));
if (!Accept) {
pClientView = NULL;
pServerView = NULL;
}
st = NtAcceptConnectPort(
&CommunicationPort,
(PVOID)pContext,
ConnectionRequest,
Accept,
pServerView,
pClientView
);
if (!NT_SUCCESS(st)) {
if (PortSection != NULL) {
NtClose(PortSection);
}
if (pContext != NULL) {
MemFree( pContext );
}
return st;
}
// Close the PortSection (LPC will hold the reference now)
if ( pServerView )
NtClose(PortSection);
// Insert the context before completing the connect because as soon
// as the complete is done, the client thread can send a request and
// if this request is serviced by another LPC thread then the context
// won't be found (WinStationBrokenConnection case, by instance).
RtlEnterCriticalSection( &ApiThreadLock );
pLpcContextEntry = MemAlloc(sizeof(TERMSRVLPCCONTEXT));
if (pLpcContextEntry != NULL) {
pLpcContextEntry->pContext = pContext;
InsertTailList( &gTermsrvLpcListHead, &pLpcContextEntry->Links );
}
// Don't leave the critical section before you complete connection. Because if context switch occurs, there
// are chances that communication port might get distroyed and we might end up working on invalid handle.
if ( Accept ) {
pContext->ClientViewBase = ClientView.ViewBase;
pContext->ClientViewBounds = (PVOID)((ULONG_PTR)ClientView.ViewBase + ClientView.ViewSize);
if ( pServerView ) {
pContext->ViewBase = ServerView.ViewBase;
pContext->ViewSize = ServerView.ViewSize;
pContext->ViewRemoteBase = ServerView.ViewRemoteBase;
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: ViewBase %p, ViewSize 0x%x, ViewRemoteBase %p\n",
pContext->ViewBase, pContext->ViewSize, pContext->ViewRemoteBase));
} else {
pContext->ViewBase = NULL;
pContext->ViewSize = 0;
pContext->ViewRemoteBase = NULL;
}
pContext->ClientLogonId = ClientLogonId;
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WSTAPI: Calling CompleteConnect port %p\n",CommunicationPort));
pContext->CommunicationPort = CommunicationPort;
st = NtCompleteConnectPort(CommunicationPort);
}
RtlLeaveCriticalSection( &ApiThreadLock );
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WinStation LPC Connection %sAccepted, Logonid %d pContext %p Status 0x%x\n",
(Accept?"":"Not "), pContext->ClientLogonId, pContext, st));
return( st );
}
第四部分:
/*****************************************************************************
*
* WinStationGetSMCommand
*
* This is the API that the Winstations call in order to get
* work to do. We send Winstations commands from SendWinStationCommand()
* once they have called this API.
*
* NOTE: Only WinStations may call this command!
*
* ENTRY:
* pContext (input)
* Pointer to our context structure describing the connection.
*
* pMsg (input/output)
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
*
* EXIT:
* STATUS_SUCCESS - no error
*
****************************************************************************/
NTSTATUS
WinStationGetSMCommand( PLPC_CLIENT_CONTEXT pContext,
PWINSTATION_APIMSG pMsg )
{
PLIST_ENTRY Head;
PWINSTATION pWinStation;
PCOMMAND_ENTRY pCommand;
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand, LogonId=%d\n",
pContext->ClientLogonId ));
第五部分:
/*
* ICASRV WinStation LPC Dispatch Table
*
* If this table is changed, the table below must be modified too.
*/
PWINSTATION_API WinStationLpcDispatch[SMWinStationMaxApiNumber] = {
WinStationInternalCreate, // for ICASRV internal use only
WinStationInternalReset, // for ICASRV internal use only
WinStationInternalDisconnect, // for ICASRV internal use only
WinStationWCharLog, // for ICASRV internal use only
WinStationGetSMCommand,
WinStationBrokenConnection,
WinStationIcaReplyMessage,
WinStationIcaShadowHotkey,
NULL, // WinStationDoConnect, // needed for connect and reconnect (I.E. InitMouse)
NULL, // WinStationDoDisconnect, // needed for disconnect (I.E. disable screen)
NULL, // WinStationDoReconnect // Reconnect
NULL, // WinStationExitWindows, // Logoff
NULL, // WinStationTerminate, // Terminate process (less gentle than logoff?)
NULL, // WinStationNtSecurity, // CTL-ALT-DEL screen
NULL, // WinStationDoMessage, // Message box
NULL, // WinStationDoBreakPoint // WinStation breakpoint
NULL, // WinStationThinwireStats // Get thinwire stats
NULL, // WinStationShadowSetup,
NULL, // WinStationShadowStart,
NULL, // WinStationShadowStop,
NULL, // WinStationShadowCleanup,
NULL, // WinStationPassthruEnable,
NULL, // WinStationPassthruDisable,
NULL, // WinStationSetTimeZone, // Set Time Zone
NULL, // WinStationInitialProgram,
NULL, // WinStationNtsdDebug,
NULL, // WinStationBroadcastSystemMessage // For PNP: This is the counter part to BroadcastSystemMessage on console
NULL, // WinStationSendWindowMessage // General Window's SendMessage() API
NULL, // SMWinStationNotify
NULL, // SMWinStationDoLoadStringNMessage
WinStationWindowInvalid
};
#if DBG
PSZ WinStationLpcName[SMWinStationMaxApiNumber] = {
"WinStationInternalCreate",
"WinStationInternalReset",
"WinStationInternalDisconnect",
"WinStationWCharLog",
"WinStationGetSMCommand",
"WinStationBrokenConnection",
"WinStationIcaReplyMessage",
"WinStationShadowHotkey",
"WinStationDoConnect",
"WinStationDoDisconnect",
"WinStationDoReconnect",
"WinStationExitWindows",
"WinStationTerminate",
"WinStationNtSecurity",
"WinStationDoMessage",
"WinStationDoBreakPoint",
"WinStationThinwireStats",
"WinStationShadowSetup",
"WinStationShadowStart",
"WinStationShadowStop",
"WinStationShadowCleanup",
"WinStationPassthruEnable",
"WinStationPassthruDisable",
"WinStationSetTimeZone",
"WinStationInitialProgram",
"WinStationNtsdDebug",
"WinStationBroadcastSystemMessage",
"WinStationSendWindowMessage",
"SMWinStationNotify",
"SMWinStationDoLoadStringNMessage",
"WinStationWindowInvalid"
};
PSZ WinStationStateName[] = {
"Active",
"Connected",
"ConnectQuery",
"VirtualIO",
"Disconnected",
"Idle",
"Off",
"Reset",
"Down",
"Init",
};
#endif // DBG
