Hello, my name is Danila Galimov and i will write here sometimes :-)
My first post is about communications between Terminal Server sessions and Terminal Server service process (termsrv.exe/dll). Terminal Server service needs to communicate with each session for many tasks, such as sending window message, getting message reply and so on. So, on init, Terminal Server creates a SmSsWinStationApiPort port in global namespace and runs a few WinStationLpcThread threads, which are listening on port and are used to process port messages. When csrss.exe is started, it parses its command line, which usually looks like:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=4096,4096,1024 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16and loads the required dlls (winsrv.dll in our case). Initialization of winsrv.dll creates a thread, which connects to SmSsWinStationApiPort port and does the loop for processing Terminal Server messages until it receives WinStationTerminate message.
We’ll try to fool Terminal Server
and send some messages to the port directly. At first, we need to connect to the port. A structure of 3 DWORDS is used to for connection:
type
TConnecttionStructure = record
dwVersion : DWORD; // must be VERSION_1
DWORD2 : DWORD; // unknown
ErrorCode : NTSTATUS; // returned error code (if any)
end;Let’s do the connection:
procedure DoWork();
var
PortHandle : THandle;
PortName : UNICODE_STRING;
SequrityQOS : TSecurityQualityOfService;
ConnectionDataLength : DWORD;
ConnectionStructure : TConnecttionStructure;
begin
RtlInitUnicodeString(@PortName, '\SmSsWinStationApiPort');
ConnectionDataLength := SizeOf(ConnectionStructure);
with ConnectionStructure do
begin
dwVersion := VERSION_1;
DWORD2 := 0; // is it a parameter, does it mean something?
end;
ZeroMemory(@SequrityQOS, SizeOf(SequrityQOS));
NtCheck('Connecting to port \SmSsWinStationApiPort', NtConnectPort(@PortHandle, @PortName, @SequrityQOS, nil, nil, nil, @ConnectionStructure, @ConnectionDataLength));
try
if ConnectionDataLength = SizeOf(ConnectionStructure) then
NTCheck('Connecting to port \SmSsWinStationApiPort error code', ConnectionStructure.ErrorCode);
finally
CloseHandle(PortHandle);
end;
end;Now we’re connected. We need to prepare and send a message. Each message (after standard PORT_MESSAGE header) has common header:
TWinstationMessageHeader = record
Unused1 : DWORD;
ApiNumber : DWORD; // see API_NUMBER_XXX constants
DoRequireReply : BOOL;// if true when NtRequestWaitReplyPort must be used to request reply,
// otherwise, NtRequestPort must be used.
ProcResult : NTSTATUS;
end;For creating idle winstation, 2 fields are added to this message: WinstationName, which is optional, and OutSessionId, which returns, on success, id of recently created winstation. So the whole structure will look like this:
TCreateIdleWinstationMessage = record
Base : TBaseWinstationMessage;
Data : record
WinstationName : array [0..32] of WideChar;
OutSessionId : DWORD;
end;
end;So now we can send this message and check for the results:
procedure CreateIdleWinstation(const PortHandle : THandle);
var
CreateIdleWinstationMessage : TCreateIdleWinstationMessage;
begin
ZeroMemory(@CreateIdleWinstationMessage, SizeOf(CreateIdleWinstationMessage));
with CreateIdleWinstationMessage, Base, Header, Data, WinstationHeader do
begin
MessageSize := SizeOf(CreateIdleWinstationMessage);
DataSize := MessageSize - SizeOf(Header);
ApiNumber := API_NUMBER_WinStationInternalCreate;
DoRequireReply := True;
CallAndCheckResult(PortHandle, CreateIdleWinstationMessage.Base);
Writeln(Format('Session id %d has been created', [OutSessionId]));
end;
end;Disconnect and Reset (logoff messages) are very simple: just target session id is added to the structure.
TResetWinstationMessage = record
Base : TBaseWinstationMessage;
Data : record
SessionId : DWORD;
end;
end;That’s all! Now we can just add command line parameters parsing features, and it’s ready for usage!
As only system is allowed to access SmSsWinStationApiPort port, we need to run our program as system. You can use RunAsSys program for that propose.
Unfortunately, in windows vista/2008 the Terminal Server internal functions have been changed, so this program (IdleWinsta) does not work on vista.
[...] Yes, I was told that more is to come. So dig into the first article Terminal Service Internals. [...]