[program-l] Re: C#: creating a service that logs mouse and keyboard input

Hi Pranav,

Your problem is related to window stations and desktops. Windows contains a type of kernel object known as a window station, which can be thought of as a security boundary. Each window station contains a clipboard, an atom table, and a collection of desktops. Typically a new window station is created for each account that is running. This is where your first problem is occuring, and that is services and the current user, known as the interactive user, run using different window stations. This means the service and the user will be using different desktops.

Window messages can only be sent between windows on the same desktop and hooks can only intercept messages sent to processes running on the same desktop as the hook process is running on. This means that services that contain hooks cannot ordinarily intercept things like keyboard input because they are running on a different desktop. It's also the reason why services can't ordinarily draw a visible UI.

In order to get the hook to work you need the process containing the hook to be running on the desktop of the interactive user. There's two ways to do this. The first is to forget about running as a service and run as a normal windows app, maybe from the system tray. The second, and a technique that is not recommended, is to run the service as an interactive service. This is not recommended for security reasons. The service is likely to be running under the local system account, which has admin level privileges. As the service would be running on the same desktop as the user any UI it exposed would be able to receive window messages from any app running on that desktop. This combination can have some pretty nasty effects and has been the source of attacks in the past, although since them Microsoft have done some work.

Will
----- Original Message ----- From: "Pranav Lal" <pranav.lal@xxxxxxxxx>
To: <program-l@xxxxxxxxxxxxx>
Sent: Thursday, August 03, 2006 12:51 PM
Subject: [program-l] C#: creating a service that logs mouse and keyboard input



Hi all,

I am trying to write an aplication in c# that logs all keystrokes to a
file. The application uses a global system hook. The program runs as
expected when I run it as a Windows application. I have tried to
convert the application to run as a service. This is not working. The
application runs but the key logging does not happen. I could be doing
something wrong when running it as a service. I will paste that code
at the end of the message. As far as I can tell, no exceptions are
thrown.

Jaws and een Narrator become sluggish when I run the service so I am
wondering whether screen  readers and the application are not getting
along. I have tried tracing through the code but Visual Studio 2005
locks up when I reach the line installing the keyboard hook.
(actHook.KeyDown += new KeyEventHandler(MyKeyDown);) Mind you, the
task manager still shows Visual Studio as running. This does not
happen when I run the application as a windows application.
Note:
I have used threading which has improved the performance of the app
but I am missing something since the logging is not happening.

I have read http://www.albahari.com/threading/ for threading and
several articles over the web while creating this application.

The code for creating the global hooks has been taken from
http://www.codeproject.com/csharp/globalhook.asp I have created the
windows service after referencing
http://www.codeproject.com/csharp/WindowsService.asp.
Note:
I am using Visual studio .net (express) 2005.

Pranav

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using gma.System.Windows;



namespace WindowsService
{
   class WindowsService : ServiceBase
   {
       UserActivityHook actHook;
       Thread t;
       /// <summary>
       /// Public Constructor for WindowsService.
       /// - Put all of your Initialization code here.
       /// </summary>
       public WindowsService()
       {
           this.ServiceName = "keyLog";
           this.EventLog.Source = "KeyLog";
           this.EventLog.Log = "Application";

           // These Flags set whether or not to handle that specific
           //  type of event. Set to true if you need it, false otherwise.
           this.CanHandlePowerEvent = true;
           this.CanHandleSessionChangeEvent = true;
           this.CanPauseAndContinue = true;
           this.CanShutdown = true;
           this.CanStop = true;


if (!EventLog.SourceExists("My Windows Service"))
EventLog.CreateEventSource("My Windows Service", "Application");
}


       /// <summary>
       /// The Main Thread: This is where your Service is Run.
       /// </summary>
       static void Main()
       {
           ServiceBase.Run(new WindowsService());



       }

       /// <summary>
       /// Dispose of objects that need it here.
       /// </summary>
       /// <param name="disposing">Whether or not disposing is going
on.</param>
       protected override void Dispose(bool disposing)
       {
           base.Dispose(disposing);
       }

       /// <summary>
       /// OnStart: Put startup code here
       ///  - Start threads, get inital data, etc.
       /// </summary>
       /// <param name="args"></param>
       protected override void OnStart(string[] args)
       {
           base.OnStart(args);
           actHook = new UserActivityHook(); // crate an instance
with global hooks
           actHook.OnMouseActivity += new MouseEventHandler(MouseMoved);
           actHook.KeyDown += new KeyEventHandler(MyKeyDown);
           actHook.KeyPress += new KeyPressEventHandler(MyKeyPress);
           actHook.KeyUp += new KeyEventHandler(MyKeyUp);


t = new Thread(delegate() { actHook.Start(); }); t.IsBackground = true; t.Start();


//actHook.Start(); }

       /// <summary>
       /// OnStop: Put your stop code here
       /// - Stop threads, set final data, etc.
       /// </summary>
       protected override void OnStop()
       {
           base.OnStop();


actHook.Stop(); }

       /// <summary>
       /// OnPause: Put your pause code here
       /// - Pause working threads, etc.
       /// </summary>
       protected override void OnPause()
       {
           actHook.Stop();
           base.OnPause();
       }

       /// <summary>
       /// OnContinue: Put your continue code here
       /// - Un-pause working threads, etc.
       /// </summary>
       protected override void OnContinue()
       {
           base.OnContinue();
           actHook.Start();
       }

       /// <summary>
       /// OnShutdown(): Called when the System is shutting down
       /// - Put code here when you need special handling
       ///   of code that deals with a system shutdown, such
       ///   as saving special data before shutdown.
       /// </summary>
       protected override void OnShutdown()
       {
           actHook.Stop();
           LogWrite("shutdown detected");
           base.OnShutdown();
       }

/// <summary>
/// OnCustomCommand(): If you need to send a command to your
/// service without the need for Remoting or Sockets, use
/// this method to do custom methods.
/// </summary>
/// <param name="command">Arbitrary Integer between 128 & 256</param>
protected override void OnCustomCommand(int command)
{
// A custom command can be sent to a service by using this method:
//# int command = 128; //Some Arbitrary number between 128 & 256
//# ServiceController sc = new ServiceController("NameOfService");
//# sc.ExecuteCommand(command);


           base.OnCustomCommand(command);
       }

/// <summary>
/// OnPowerEvent(): Useful for detecting power status changes,
/// such as going into Suspend mode or Low Battery for laptops.
/// </summary>
/// <param name="powerStatus">The Power Broadcase Status
(BatteryLow, Suspend, etc.)</param>
protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
{
return base.OnPowerEvent(powerStatus);
}


       /// <summary>
       /// OnSessionChange(): To handle a change event from a
Terminal Server session.
       ///   Useful if you need to determine when a user logs in
remotely or logs off,
       ///   or when someone logs into the console.
       /// </summary>
       /// <param name="changeDescription"></param>
       protected override void
OnSessionChange(SessionChangeDescription changeDescription)
       {
           base.OnSessionChange(changeDescription);
       }

       public void MouseMoved(object sender, MouseEventArgs e)
       {


if (e.Clicks > 0) LogWrite("MouseButton - " + e.Button.ToString());
}



public void MyKeyDown(object sender, KeyEventArgs e) { LogWrite("{" + e.KeyData.ToString()+"}"); }

       public void MyKeyPress(object sender, KeyPressEventArgs e)
       {
           LogWrite("KeyPress - " + e.KeyChar);
       }

       public void MyKeyUp(object sender, KeyEventArgs e)
       {
           //LogWrite("KeyUp - " + e.KeyData.ToString());
       }

       private void LogWrite(string txt)
       {
           writeFile(txt);
       }

       private void writeFile(string s)
       {

           try
           {
               //Open the File
               StreamWriter sw = new StreamWriter("C:\\Test1.txt",
true, Encoding.ASCII);

               sw.Write(s);

               //close the file
               sw.Close();
           }
           catch (Exception e)
           {
               Console.WriteLine("Exception: " + e.Message);
           }
           finally
           {
//exception supressed
           }
       }



   }
}
** To leave the list, click on the immediately-following link:-
** [mailto:program-l-request@xxxxxxxxxxxxx?subject=unsubscribe]
** If this link doesn't work then send a message to:
** program-l-request@xxxxxxxxxxxxx
** and in the Subject line type
** unsubscribe
** For other list commands such as vacation mode, click on the
** immediately-following link:-
** [mailto:program-l-request@xxxxxxxxxxxxx?subject=faq]
** or send a message, to
** program-l-request@xxxxxxxxxxxxx with the Subject:- faq



** To leave the list, click on the immediately-following link:-
** [mailto:program-l-request@xxxxxxxxxxxxx?subject=unsubscribe]
** If this link doesn't work then send a message to:
** program-l-request@xxxxxxxxxxxxx
** and in the Subject line type
** unsubscribe
** For other list commands such as vacation mode, click on the
** immediately-following link:-
** [mailto:program-l-request@xxxxxxxxxxxxx?subject=faq]
** or send a message, to
** program-l-request@xxxxxxxxxxxxx with the Subject:- faq

Other related posts: