0

I am trying to record a video of a user desktop when he logged in via RDP, the monitoring is based on the windows service onSessionChange event, due the nature of it then I am facing what is so called Session 0 isolation, so what I am looking for is how to switch to the logged in user in order to be able record his session?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceProcess;



namespace RdpMonitoringService
{
    struct RdpSession
    {
        public int sessionId;
        public string userName;
        public string ipAddress;
        public string sessionDateTime;
        public string recordFileName;
        public Recorder sessionRecorder;

        public string generateRecordFileName()
        {
            string dirName = "RecordedSessions";
            string path = AppDomain.CurrentDomain.BaseDirectory + "\\" + dirName;
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\" + dirName + "\\" + userName + " - " + sessionDateTime + ".mp4";
            return filepath;
        }

        public override string ToString()
        {
            return "SessionId: " + sessionId + ", userName: " + userName; 
        }
    }
    public partial class RdpMonitoringService : ServiceBase
    {
        RdpSession onRdpSession;
        Dictionary<int, RdpSession> rdpSessionList;
        public RdpMonitoringService()
        {
            InitializeComponent();

            this.CanPauseAndContinue = true;
            this.CanHandleSessionChangeEvent = true;

            rdpSessionList = new Dictionary<int, RdpSession>();
        }
        protected override void OnStart(string[] args)
        {
            WriteToFile("Service is started at: ============ " + DateTime.Now);
            WriteToFile("this.CanHandleSessionChangeEvent: " + this.CanHandleSessionChangeEvent);
        }
        protected override void OnStop()
        {
            WriteToFile("Service is stopped at " + DateTime.Now);
        }

        protected override void OnSessionChange(SessionChangeDescription sessionChangeDescription)
        {
            try { 
                var userInfo = TermServicesManager.GetSessionInfo(Dns.GetHostEntry("").HostName, sessionChangeDescription.SessionId);
                
                    IPAddress ipAddress = new IPAddress(userInfo.ClientAddress.Address.Skip(2).Take(4).ToArray());
                    if (!rdpSessionList.ContainsKey(sessionChangeDescription.SessionId))
                    {
                        onRdpSession.sessionId = sessionChangeDescription.SessionId;
                        onRdpSession.ipAddress = ipAddress.ToString();
                        onRdpSession.userName = userInfo.UserName;
                        onRdpSession.sessionDateTime = DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH'-'mm'-'ss");
                        onRdpSession.recordFileName = onRdpSession.generateRecordFileName();
                        //onRdpSession.sessionRecorder = Recorder.CreateRecorder();
                        //onRdpSession.sessionRecorder.Record(onRdpSession.generateRecordFileName());
                        rdpSessionList.Add(sessionChangeDescription.SessionId, onRdpSession);
                    }
                    else
                    {
                        onRdpSession = rdpSessionList[sessionChangeDescription.SessionId];
                    }


                    WriteToFile("SessionChange event");

                    switch (sessionChangeDescription.Reason)
                    {
                        case SessionChangeReason.SessionLogon:
                        case SessionChangeReason.RemoteConnect:
                        case SessionChangeReason.SessionUnlock:
                            WriteToFile("SessionChange" + DateTime.Now.ToLongTimeString() + ", SessionUnlock|RemoteConnect|SessionLogon [" + sessionChangeDescription.SessionId.ToString() + "]" + ", User: " + userInfo.UserName + ", Connect state: " + userInfo.ConnectState.ToString() + ", Client address: " + ipAddress.ToString() + ", user: " + userInfo.UserName + ", WinStationName: " + userInfo.WinStationName.ToString());
                            // Currently the bellow line crash the service because there is no desktop
                            // onRdpSession.sessionRecorder = new Recorder(new RecorderParams(onRdpSession.generateRecordFileName(), 60, SharpAvi.KnownFourCCs.Codecs.MotionJpeg, 70));
                        
                            // Switch to the user desktop based on his session id and start recording
                        break;
                        case SessionChangeReason.SessionLock:
                        case SessionChangeReason.SessionLogoff:
                        case SessionChangeReason.RemoteDisconnect:
                            WriteToFile("SessionChange: " + onRdpSession.ToString() + ", " + DateTime.Now.ToLongTimeString() + " RemoteDisconnect|SessionLogoff|SessionLock [" + sessionChangeDescription.SessionId.ToString() + "]" + ", User: " + userInfo.UserName + ", Connect state: " + userInfo.ConnectState.ToString() + ", Client address: " + ipAddress.ToString() + ", user: " + userInfo.UserName + ", WinStationName: " + userInfo.WinStationName.ToString());
                            onRdpSession.sessionRecorder.Dispose();
                            break;
                        default:
                                break;
                    }

            } 
            catch(Exception ex)
            {
                WriteToFile("SessionChange exception: " + ex.Message + " || " + sessionChangeDescription.SessionId.ToString() + " || " + onRdpSession.sessionId.ToString());

            }
        }

        public void WriteToFile(string Message)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "\\Logs";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
            if (!File.Exists(filepath))
            {
                // Create a file to write to.   
                using (StreamWriter sw = File.CreateText(filepath))
                {
                    sw.WriteLine(Message);
                }
            }
            else
            {
                using (StreamWriter sw = File.AppendText(filepath))
                {
                    sw.WriteLine(Message);
                }
            }
        }
    }
}
H Aßdøµ
  • 2,925
  • 4
  • 26
  • 37
  • 1
    Divide and conquer. Run an application in user-context and let it record the screen. Then use a named-pipe to send the stream to your service-session. – Oliver Sep 04 '22 at 14:01
  • @Oliver That will require me to make app auto start with each user and from there detect the logon and start the recording, this approach I would leave it if I have no solution based on windows service. – H Aßdøµ Sep 04 '22 at 15:34
  • You don't need autostart. Instead let your service watch the user processes and check if *your* process is running. If not, start it [in the current user process](https://stackoverflow.com/questions/4278373/how-to-start-a-process-from-windows-service-into-currently-logged-in-users-sess). – Oliver Sep 05 '22 at 05:35
  • @Oliver Does that method works also with users who logged via RDP? – H Aßdøµ Sep 05 '22 at 13:12
  • AFAIK each user has his own session, regardless if direct or RDP login. The only difference is, on an e.g. terminal server you have dozens of users parallel and on a normal machine there is just one. A user application can access the _screen_ of its own user and it doesn't matter if it is a real display or just a RDP session. – Oliver Sep 05 '22 at 14:30
  • @Oliver I will try it and update here later. – H Aßdøµ Sep 05 '22 at 15:19

0 Answers0